DataTypesBasic.DataTypesBasic
— ModuleNOTE: in julia we can represent closed abstract types with two different systems:
- either Union{Type1, Type2}
- 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.Either
— TypeEither{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.Option
— TypeOption{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.Try
— TypeTry{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.Const
— TypeConst("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.ContextManager
— TypeContextManager(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.Identity
— TypeIdentity(: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.MultipleExceptions
— TypeMultipleExceptions(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.Thrown
— TypeThrown(exception, stacktrace)
Thrown is like Exception, however can also cary stacktraces
Base.isconst
— Methodisconst(Const(3)) -> true
isconst("anythingelse") -> false
returns true only if given an instance of DataTypesBasic.Const
DataTypesBasic.either
— MethodDataTypesBasic.flip_left_right
— Methodflip_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.getOption
— MethodgetOption(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.getleft
— Methodgetleft(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.getleftOption
— MethodgetleftOption(Identity(23)) == Option()
getleftOption(Const(:something)) == Option(:something)
Convert to option, assuming you want to have the left value be preserved.
DataTypesBasic.getright
— Methodgetright(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.getrightOption
— MethodgetrightOption(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.iffalse
— Methodiffalse(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.iftrue
— Methodiftrue(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.iseither
— Methodiseither(::Const) = true
iseither(::Identity) = true
iseither(other) = false
check whether something is an Either
DataTypesBasic.isfailure
— Methodisfailure(::Identity) = false
isfailure(::Const{<:Exception}) = true
Similar to isleft
, but only defined for Const{<:Exception}
. Will throw MethodError when applied on other Const
.
DataTypesBasic.isidentity
— Methodisidentity(Identity(3)) -> true
isidentity("anythingelse") -> false
returns true only if given an instance of Identity
DataTypesBasic.isleft
— Methodisleft(::Const) = true
isleft(::Identity) = false
Identical to isconst
, but might be easier to read when working with Either
.
DataTypesBasic.isnone
— Methodisnone(::Identity) = false
isnone(::Const{Nothing}) = true
Similar to isleft
, but only defined for Const{Nothing}
. Will throw MethodError when applied on other Const
.
DataTypesBasic.isoption
— Methodisoption(::Const{Nothing}) = true
isoption(::Identity) = true
isoption(other) = false
check whether something is an Option
DataTypesBasic.isright
— Methodisright(::Const) = false
isright(::Identity) = true
Identical to isidentity
, but might be easier to read when working with Either
.
DataTypesBasic.issome
— Methodissome(::Identity) = true
issome(::Const{Nothing}) = false
Similar to isright
, but only defined for Const{Nothing}
. Will throw MethodError when applied on other Const
.
DataTypesBasic.issuccess
— Methodissuccess(::Identity) = true
issuccess(::Const{<:Exception}) = false
Similar to isright
, but only defined for Const{<:Exception}
. Will throw MethodError when applied on other Const
.
DataTypesBasic.istry
— Methodisoption(::Const{Nothing}) = true
isoption(::Identity) = true
isoption(other) = false
check whether something is a Try
DataTypesBasic.@ContextManager
— Macro@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.@Try
— Macro@Try begin
your_code
end
Macro which directly captures an Excpetion into a proper Try
representation.
It translates to
try
r = your_code
Identity(r)
catch exc
Const(Thrown(exc, Base.catch_stack()))
end
DataTypesBasic.@TryCatch
— Macro@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.@either
— Macro@either true ? "right" : Symbol("left")
@either if false
"right"
else
:left
end
Simple macro to reuse ? operator and simple if-else for constructing Either.