TypeClasses Public API
Functor, Applicative, Monad
Foreach, using Base.foreach
TypeClasses.@syntax_foreach
— Macro@syntax_foreach begin
# Vectors behaves like nested for loops within @syntax_foreach
a = [1, 2, 3]
b = [10, 20]
@pure a + b
end
# [[11, 21], [12, 22], [13, 23]]
This is a variant of the monadic syntax which uses foreach
for both maplike and flatmaplike. See Monadic.@monadic
for more details.
Functor, using Base.map
TypeClasses.@syntax_map
— Macro@syntax_map begin
# Vectors behave similar to nested for loops within @syntax_map
a = [1, 2, 3]
b = [10, 20]
@pure a + b
end
# [[11, 21], [12, 22], [13, 23]]
This is a variant of the monadic syntax which uses map
for both maplike and flatmaplike. See Monadic.@monadic
for more details.
Applicative Core
TypeClasses.pure
— Functionpure(T::Type, a)
wraps value a into container T
TypeClasses.ap
— Functionap(f::F1, a::F2) -> F3
Apply function in container F1
to element in container F2
, returning results in the same container F3
. The default implementation uses flatmap
and map
, so that in general only those two need to be defined.
Applicative Helper
TypeClasses.mapn
— Functionmapn(f, a1::F{T1}, a2::F{T2}, a3::F{T3}, ...) -> F{T}
Apply a function over applicative contexts instead of plain values. Similar to Base.map
, however sometimes the semantic differs slightly. TypeClasses.mapn
always follows the semantics of TypeClasses.flatmap
if defined.
E.g. for Base.Vector
the Base.map
function zips the inputs and checks for same length. On the other hand TypeClasses.mapn
combines all combinations of inputs instead of the zip (which conforms with the semantics of flattening nested Vectors).
TypeClasses.@mapn
— Macro@mapn f(a, b, c, d)
translates to
mapn(f, a, b, c, d)
TypeClasses.tupled
— Functiontupled(Option(1), Option(2), Option(3)) == Option((1,2,3))
tupled(Option(1), None, Option(3)) == None
Combine several Applicative contexts by building up a Tuple
Monad Core
TypeClasses.flatmap
— Functionflatmap(function_returning_A, a::A)::A
flatmap
applies a function to a container and immediately flattens it out. While map would give you A{A{...}}
, flatmap gives you a plain A{...}
, without any nesting.
If you define your own versions of flatmap, the recommendation is to apply a Base.convert
after applying f
. This makes sure your flatmap is typesafe, and actually enables sound interactions with other types which may be convertable to your A.
E.g. for Vector the implementation looks as follows:
TypeClasses.flatmap(f, v::Vector) = vcat((convert(Vector, f(x)) for x in v)...)
Monad Helper
TypeClasses.flatten
— Functionflatten(::A{A})::A = flatmap(identity, a)
flatten
gets rid of one level of nesting. Has a default fallback to use flatmap
.
TypeClasses.:↠
— Functiona ↠ b = flatmap(_ -> b, a) # \twoheadrightarrow
A convenience operator for monads which just applies the second monad within the first one.
The operator ↠ (\twoheadrightarrow) is choosen because the other favourite ≫ (\gg) which would have been in accordance with haskell unicode syntax for monads is unfortunately parsed as boolean comparison with extra semantics which leads to errors with non-boolean applications.
↠ is just the most similar looking other operator which does not have this restriction and is right-associative.
TypeClasses.@syntax_flatmap
— Macro@syntax_flatmap begin
# Vector behave similar to nested for loops within @syntax_flatmap
a = [1, 2, 3]
b = [10, 20]
@pure a + b
end
# [11, 21, 12, 22, 13, 23]
This is the standard monadic syntax which uses map
for maplike and flatmap
for flatmaplike. See Monadic.@monadic
for more details.
Semigroup, Monoid, Alternative
Semigroup
TypeClasses.combine
— Functioncombine(::T, ::T)::T # overload this
⊕(::T, ::T)::T # alias \oplus
combine(a, b, c, d, ...) # using combine(a, b) internally
Associcative combinator operator.
The symbol ⊕
(\oplus) following http://hackage.haskell.org/package/base-unicode-symbols-0.2.3/docs/Data-Monoid-Unicode.html
Following Laws should hold
Associativity
⊕(::T, ⊕(::T, ::T)) == ⊕(⊕(::T, ::T), ::T)
TypeClasses.:⊕
— Functioncombine(::T, ::T)::T # overload this
⊕(::T, ::T)::T # alias \oplus
combine(a, b, c, d, ...) # using combine(a, b) internally
Associcative combinator operator.
The symbol ⊕
(\oplus) following http://hackage.haskell.org/package/base-unicode-symbols-0.2.3/docs/Data-Monoid-Unicode.html
Following Laws should hold
Associativity
⊕(::T, ⊕(::T, ::T)) == ⊕(⊕(::T, ::T), ::T)
julia> using TypeClasses, Dictionaries
julia> dict1 = Dictionary([:a, :b, :c], ["1", "2", "3"])
3-element Dictionaries.Dictionary{Symbol, String}
:a │ "1"
:b │ "2"
:c │ "3"
julia> dict2 = Dictionary([:b, :c, :d], ["4", "5", "6"])
3-element Dictionaries.Dictionary{Symbol, String}
:b │ "4"
:c │ "5"
:d │ "6"
julia> dict1 ⊕ dict2
4-element Dictionaries.Dictionary{Symbol, String}
:a │ "1"
:b │ "24"
:c │ "35"
:d │ "6"
Neutral
TypeClasses.neutral
— Functionneutral
neutral(::Type)
neutral(_default_return_value) = neutral
Neutral element for ⊕
, also called "identity element". neutral
is a function which can give you the neutral element for a concrete type, or alternatively you can use it as a singleton value which combines with everything.
By default neutral(type)
will return the generic neutral
singleton. You can override it for your specific type to have a more specific neutral value.
We decided for name neutral
according to https://en.wikipedia.org/wiki/Identity_element. Alternatives seem inappropriate
- "identity" is already taken
- "identity_element" seems to long
- "I" is too ambiguous
- "unit" seems ambiguous with physical units
Following Laws should hold
Left Identity
⊕(neutral(T), t::T) == t
Right Identity
⊕(t::T, neutral(T)) == t
Monoid Helpers
TypeClasses.reduce_monoid
— Functionreduce_monoid(itr; init=TypeClasses.neutral)
Combines all elements of itr
using the initial element init
if given and TypeClasses.combine
.
TypeClasses.foldr_monoid
— Functionfoldr_monoid(itr; init=TypeClasses.neutral)
Combines all elements of itr
using the initial element init
if given and TypeClasses.combine
.
TypeClasses.foldl_monoid
— Functionfoldl_monoid(itr; init=TypeClasses.neutral)
Combines all elements of itr
using the initial element init
if given and TypeClasses.combine
.
Alternative
TypeClasses.orelse
— Functionorelse(a, b) # overload this
⊘(a, b) # alias \oslash
orelse(a, b, c, d, ...) # using orelse(a, b) internally
Implements an alternative logic, like having two options a and b, taking the first valid one. We decided for "orelse" instead of "alternatives" to highlight the intrinsic asymmetry in choosing.
The operator ⊘ (\oslash) is choosen to have an infix operator which is similar to \oplus, however clearly distinguishable, asymmetric, and somehow capturing a choice semantics. The slash actually is used to indicate choice (at least in some languages, like German), and luckily \oslash exists (and is not called \odiv).
TypeClasses.:⊘
— Functionorelse(a, b) # overload this
⊘(a, b) # alias \oslash
orelse(a, b, c, d, ...) # using orelse(a, b) internally
Implements an alternative logic, like having two options a and b, taking the first valid one. We decided for "orelse" instead of "alternatives" to highlight the intrinsic asymmetry in choosing.
The operator ⊘ (\oslash) is choosen to have an infix operator which is similar to \oplus, however clearly distinguishable, asymmetric, and somehow capturing a choice semantics. The slash actually is used to indicate choice (at least in some languages, like German), and luckily \oslash exists (and is not called \odiv).
orelse(d1::Dict, d2::Dict) -> Dict
Following the orelse semantics on Option values, the first value is retained, and the second is dropped. Hence this is the flipped version of Base.merge
.
orelse(future1, future2, ...)
future1 ⊘ future2
Runs both in parallel and collects which ever result is first. Then interrupts all other futures and returns the found result.
orelse(task1, task2, ...)
task1 ⊘ task2
Runs both in parallel and collects which ever result is first. Then interrupts all other tasks and returns the found result.
orelse(d1::Dict, d2::Dict) -> Dict
Following the orelse semantics on Option values, the first value is retained, and the second is dropped. Hence this is the flipped version of Base.merge
.
FlipTypes
TypeClasses.flip_types
— Functionflip_types(value::T{S{A}})::S{T{A}}
reverses the two outer containers, e.g. making an Array of Options into an Option of an Array.
TypeClasses.default_flip_types_having_pure_combine_apEltype
— Functiondefault_flip_types_having_pure_combine_apEltype(container)
Use this helper function to ease the definition of flip_types
for your own type.
Note that the following interfaces are assumed:
- iterable
- pure
- combine
- ap on eltype
And in case of empty iterable in addition the following:
- neutral
- pure on eltype
We do not overload flip_types
directly because this would require dispatching on whether isAp(eltype(T))
. But relying on eltype
to define different semantics is strongly discouraged.
TypeClasses.DataTypes
Iterable
TypeClasses.DataTypes.Iterables.Iterable
— Typewrapper to clearly indicate that something should be treated as an Iterable
Callable
TypeClasses.DataTypes.Callable
— Typewrapper to clearly indicate that something should be treated as a Callable
Writer
TypeClasses.DataTypes.Writers.Writer
— Typelike Pair, however assumes that combine
is defined for the accumulator acc
Note that neutral
may indeed be undefined
TypeClasses.DataTypes.Writers.getaccumulator
— Functiongetaccumulator(writer::Writer)
Returns the accumulator of the Writer value.
Examples
julia> using TypeClasses
julia> getaccumulator(Writer("example-accumulator"))
"example-accumulator"
State
TypeClasses.DataTypes.States.State
— TypeState(func)
State() do state
....
return value, newstate
end
State monad, which capsulate a state within a monadic type for monadic encapsulation of the state-handling.
You can run a state by either calling it, or by using Base.run
. If no initial state is given, nothing
is used.
TypeClasses.DataTypes.States.getstate
— Constantgetstate
Standard value for returning the hidden state of the State
Monad.
Examples
julia> using TypeClasses
julia> mystate = @syntax_flatmap begin
state = getstate
@pure println("state = $state")
end;
julia> mystate(42)
state = 42
(nothing, 42)
TypeClasses.DataTypes.States.putstate
— Functionputstate(x)
putstate
is a standard constructor for State
objects which changes the underlying state to the given value.
Examples
julia> using TypeClasses
julia> mystate = @syntax_flatmap begin
putstate(10)
state = getstate
@pure println("The state is $state, and should be 10")
end;
julia> mystate()
The state is 10, and should be 10
(nothing, 10)