DataTypesBasic.DataTypesBasicModule

NOTE: in julia we can represent closed abstract types with two different systems:

  1. either Union{Type1, Type2}
  2. or with a standard abstract type, where all type definitions are on the abstract type level

(if you would only define functions on the concretetypes, the the inference engine will do weird things as it assumes there might be another concretetype in the future)

Approach 1.

Advantages: Providing definitions for the concrete is actually enough for typeinference. More code reuse. Disadvantages: The typeinference like for Dict(:a => Some(1), :b => None()) would infer Dict{Symbol, Any} instead of Dict{Symbol, Union{Some, None}}.

The internal details for this is that in many cases Base.promote_typejoin(type1, type2) is used to come up with a common type. While the type hierarchy is used for this, Base.promote_typejoin of two unrelated types will always result in Any.

You would need to overload promote_typejoin to support Dict(:a => Identity(1), :b => nothing) isa Dict{Symbol, Option{Int}}.

Approach 2.

Advantages: Dict(:a => Some(1), :b => None()) would indeed infer Dict{Symbol, Option} Disadvantages: you need to be careful to always implement functionalities first on separate functions unique to the sealed type, and then point generic functions to the specific one via genericfunction(o::Option) = optionfunction(o)

As we managed to make the promote_typejoin work as intended for Union, we follow Approach 1, in order to enable way higher code-reuse and datatype-reuse.

DataTypesBasic.EitherType
Either{L, R} = Union{Const{L}, Identity{R}}
Either{Int, Bool}(true) == Identity(true)
Either{Int, Bool}(1) == Const(1)
Either{String}("left") == Const("left")
Either{String}(:anythingelse) == Identity(:anythingelse)

A very common type to represent Result or Failure. We reuse Identity as representing "Success" and Const for "Failure".

DataTypesBasic.OptionType
Option{T} = Union{Const{Nothing}, Identity{T}}
Option(nothing) == Const(nothing)
Option("anything but nothing") == Identity("anything but nothing")
Option() == Const(nothing)

Like Union{T, Nothing}, however with container semantics. While Union{T, Nothing} can be thought of like a value which either exists or not, Option{T} = Union{Identity{T}, Const{Nothing}} is a container which is either empty or has exactly one element.

We reuse Identity as representing the single-element-container and Const(nothing) as the empty container.

DataTypesBasic.TryType
Try{T} = Union{Const{<:Exception}, Identity{T}}
@Try(error("something happend")) isa Const(<:Thrown{ErrorException})
@Try(:successfull) == Identity(:successfull)

A specific case of Either, where the Failure is always an Exception. This can be used as an alternative to using try-catch syntax and allows for very flexible error handling, as the error is now captured in a proper defined type. Often it is really handy to treat errors like other values (without the need of extra try-catch syntax which only applies to exceptions).

We reuse Identity for representing the single-element-container and Const(<:Exception) as the Exception thrown.

DataTypesBasic.ConstType
Const("anything")

DataType which behaves constant among map, foreach and the like. Just like an empty container, however with additional information about which kind of empty.

DataTypesBasic.ContextManagerType
ContextManager(func)

As ContextManager we denote a computation which has a pre-computation and possibly a cleaning up step.

The single argument is supposed to be a function which expects one argument, the continuation function. Think of it like the following:

function contextmanagerready(cont)
  # ... do something before
  value = ... # create some value to work on later
  result = cont(value)  # pass the value to the continuation function (think like `yield`)
  # ... do something before exiting, e.g. cleaning up
  result # IMPORTANT: always return the result of the `cont` function
end

Now you can wrap it into ContextManager(contextmanagerready) and you can use all the context manager functionalities right away.

There is a simple @ContextManager for writing less parentheses

mycontextmanager(value) = @ContextManager function(cont)
  println("got value = $value")
  result = cont(value)
  println("finished value = $value")
  result
end

You can run it in two ways, either by just passing Base.identity as the continuation function

julia> mycontextmanager(4)(x -> x)
got value = 4
finished value = 4

or for convenience we also overload Base.run

# without any extra arguments runs the contextmanager with Base.identity
run(mycontextmanager(4))
# also works with a given continuation, which makes for a nice do-syntax
run(x -> x, mycontextmanager(4))
run(mycontextmanager(4)) do x
  x
end
DataTypesBasic.IdentityType
Identity(:anything)

Identity is a simple wrapper, which works as a single-element container.

It can be used as the trivial Monad, and as such can be helpful in monadic abstractions. For those who don't know about Monads, just think of it like container-abstractions.

DataTypesBasic.MultipleExceptionsType
MultipleExceptions(exception1, exception2, ...)
MultipleExceptions(tuple_or_vector_of_exceptions)

Little helper type which combines several Exceptions into one new Exception.

In the several arguments version, and only there, if an MultipleExceptions is given, it will be flattened directly for convenience.

DataTypesBasic.ThrownType
Thrown(exception, stacktrace)

Thrown is like Exception, however can also cary stacktraces

DataTypesBasic.eitherMethod
either(:left, bool_condition, "right")

If bool_condition is true, it returns the right value, wrapped into Identity. Else returns left side, wrapped into Const.

Example

either(:left, 1 < 2, "right") == Identity("right")
DataTypesBasic.flip_left_rightMethod
flip_left_right(Const(1)) == Identity(1)
flip_left_right(Identity(:whatever)) == Const(:whatever)

exchanges left and right, i.e. what was Const, becomes an Identity and the other way around.

DataTypesBasic.getOptionMethod
getOption(Identity(23)) == Option(23)
getOption(Const(:something)) == Option()

Convert to option, assuming you want to have the right value be preserved, and the left value represented as Option().

DataTypesBasic.getleftMethod
getleft(Const(:something)) == :something
getleft(Identity(23))  # throws MethodError

Extract a value from a "left" Const value. Will result in loud error when used on anything else.

DataTypesBasic.getleftOptionMethod
getleftOption(Identity(23)) == Option()
getleftOption(Const(:something)) == Option(:something)

Convert to option, assuming you want to have the left value be preserved.

DataTypesBasic.getrightMethod
getright(Identity(23)) == 23
getright(Const(:something))  # throws MethodError

Extract a value from a "right" Identity value. Will result in loud error when used on anything else. Identical to Base.get but explicit about the site (and not defined for other things)

DataTypesBasic.getrightOptionMethod
getrightOption(Identity(23)) == Option(23)
getrightOption(Const(:something)) == Option()

Convert to option, assuming you want to have the right value be preserved. Identical to getOption, just explicit about the site.

DataTypesBasic.iffalseMethod
iffalse(bool_condition, value)
iffalse(func, bool_condition)
iffalse(bool_condition) do
  # only executed if bool_condition is true
end

Helper to create an Option based on some condition. If bool_condition is false, the function func is called with no arguments, and its result wrapped into Identity. If bool_condition is true, Option() is returned.

Precisely the opposite of iftrue.

DataTypesBasic.iftrueMethod
iftrue(bool_condition, value)
iftrue(func, bool_condition)
iftrue(bool_condition) do
  # only executed if bool_condition is true
end

Helper to create an Option based on some condition. If bool_condition is true, the function func is called with no arguments, and its result wrapped into Identity. If bool_condition is false, Option() is returned.

DataTypesBasic.iseitherMethod
iseither(::Const) = true
iseither(::Identity) = true
iseither(other) = false

check whether something is an Either

DataTypesBasic.isfailureMethod
isfailure(::Identity) = false
isfailure(::Const{<:Exception}) = true

Similar to isleft, but only defined for Const{<:Exception}. Will throw MethodError when applied on other Const.

DataTypesBasic.isleftMethod
isleft(::Const) = true
isleft(::Identity) = false

Identical to isconst, but might be easier to read when working with Either.

DataTypesBasic.isnoneMethod
isnone(::Identity) = false
isnone(::Const{Nothing}) = true

Similar to isleft, but only defined for Const{Nothing}. Will throw MethodError when applied on other Const.

DataTypesBasic.isoptionMethod
isoption(::Const{Nothing}) = true
isoption(::Identity) = true
isoption(other) = false

check whether something is an Option

DataTypesBasic.isrightMethod
isright(::Const) = false
isright(::Identity) = true

Identical to isidentity, but might be easier to read when working with Either.

DataTypesBasic.issomeMethod
issome(::Identity) = true
issome(::Const{Nothing}) = false

Similar to isright, but only defined for Const{Nothing}. Will throw MethodError when applied on other Const.

DataTypesBasic.issuccessMethod
issuccess(::Identity) = true
issuccess(::Const{<:Exception}) = false

Similar to isright, but only defined for Const{<:Exception}. Will throw MethodError when applied on other Const.

DataTypesBasic.istryMethod
isoption(::Const{Nothing}) = true
isoption(::Identity) = true
isoption(other) = false

check whether something is a Try

DataTypesBasic.@ContextManagerMacro
@ContextManager function(cont); ...; end

There is a simple @ContextManager for writing less parentheses

mycontextmanager(value) = @ContextManager function(cont)
  println("got value = $value")
  result = cont(value)
  println("finished value = $value")
  result
end
DataTypesBasic.@TryMacro
@Try begin
  your_code
end

Macro which directly captures an Excpetion into a proper Tryrepresentation.

It translates to

try
  r = your_code
  Identity(r)
catch exc
  Const(Thrown(exc, Base.catch_stack()))
end
DataTypesBasic.@TryCatchMacro
@TryCatch YourException begin
  your_code
end

A version of @Try which catches only specific errors. Every other orrer will be rethrown.

It translates to

try
  r = your_code
  Identity(r)
catch exc
  if exc isa YourException
    Const(Thrown(exc, Base.catch_stack()))
  else
    rethrow()
  end
end
DataTypesBasic.@eitherMacro
@either true ? "right" : Symbol("left")
@either if false
  "right"
else
  :left
end

Simple macro to reuse ? operator and simple if-else for constructing Either.