fun_land v0.9.0 FunLand.Builtin.List
Link to this section Summary
Functions
Something is Appliable if you can apply one of it (containing one or multiple functions) with another
Callback implementation for FunLand.Chainable.chain/2
Callback implementation for FunLand.Semicombinable.combine/2
Callback implementation for FunLand.Combinable.empty/0
This is called internally whenever a YourMonad.chain()
operation fails
Free implementation new Mappable.map as FunLand.Builtin.List is Applicative
Allows you to write multiple consecutive operations using this monad on new lines. This is called ‘monadic do-notation’
A structure is Applicative if it is Appliable, as well as having the ability to create a new structure from any value, by new
ping it
A variant of reduce that accepts anything that is Combinable as second argument. This Combinable will determine what the empty value and the combining operation will be
Callback implementation for FunLand.Reducable.reduce/3
Converts the reducable into a list, by building up a list from all elements, and in the end reversing it
An Example of using traverse
Link to this section Functions
Something is Appliable if you can apply one of it (containing one or multiple functions) with another.
Appliable is mostly born out of the needs to apply a function that is already wrapped in a Mappable:
- If you had a bare function, you could use
Mappable.map/2
to apply it over a Mappable. - If however, you have a function already inside a Mappable, a new operation has to be defined to apply it over a Mappable (of the same kind).
This operation is called apply_with/2
.
‘a function inside a Mappable’ is something that happens when you partially apply functions, which isn’t that common in Elixir because functions are not automatically curried.
Currying and Partial Application
As apply_with
works only applies a single argument per function at a time, it works the best when used with curried functions.
In Elixir, functions are no curried by default.
Fortunately, there exists the Currying library, which transforms your normal functions into curried functions.
If you want to be able to use Applicative to its fullest potential, instead of calling fun.(b)
in your implementation, use Currying.curry(fun).(b)
To be Appliable something also has to be Mappable.
To make your data structure Appliable, use use Appliable
in its module, and implement both Appliable’s apply_with/2
and Mappable’s map/2
.
Fruit Salad Example
Say we have a bowl with a partiall-made fruit-salad. We have a second bowl, which contains some (peeled) bananas.
We would like to add these bananas to the fruit salad.
This would be easy if we had our partially-made fruit-salad, as we could just map the ‘combine a banana with some fruit salad’ operation over the bowl of bananas.
However, we don’t ‘just’ have the partially-made fruit-salad, as this would make a big mess of our kitchen countertop.
In fact, it is very likely that this bowl-with partially-made fruit salad was the result of combining (mapping
) earlier ingredients in bowls.
So, we need something similar to map
, but instead of taking ‘just’ an operation, we use a bowl with that operation.
For the fruit salad bowl, we could define it as ‘take some fruit-salad from Bowl A, combine it with a banana in Bowl B. -> repeat until bananas and fruit-salad are fully combined’.
This is called apply_with
.
Note that, because the part that changes more often is the Appliable with the (partially-applied) function (in other words: The bowl with the partially-made fruit salad),
the parameters of this functions are the reverse of Mappable.map
.
In Other Environments
- In Haskell,
Appliable.apply_with
is known by the uninformative nameap
, often written as<$>
. - In Category Theory, something that is Appliable is called an Apply.
Callback implementation for FunLand.Appliable.apply_with/2
.
Callback implementation for FunLand.Chainable.chain/2
.
Callback implementation for FunLand.Semicombinable.combine/2
.
Callback implementation for FunLand.Combinable.empty/0
.
This is called internally whenever a YourMonad.chain()
operation fails.
For most monads, the default behaviour of crashing is great. For some, you might want to override it.
Free implementation new Mappable.map as FunLand.Builtin.List is Applicative
Allows you to write multiple consecutive operations using this monad on new lines. This is called ‘monadic do-notation’.
For more info, see FunLand.Monad.monadic
Rules:
- Every normal line returns a new instance of the monad.
- You can write
x <- some_expr_returning_a_monad_instance
to bindx
to whatever is inside the monad. You can then usex
on any subsequent lines. - If you want to use one or multiple normal statements, use
let something = some_statement
orlet something = do ...
The final line is of course expected to also return an instance of the monad.
Use new
at any time to new a value back into a monad if you need.
Inside the monadic context, the module of the monad that was defined is automatically imported.
Any local calls to e.g. new
, apply
, chain
or functions you’ve defined yourself in your monad module will thus be called on your module.
A structure is Applicative if it is Appliable, as well as having the ability to create a new structure from any value, by new
ping it.
Being able to create new
, apply
and map
means that we can create new structures with some values, transform them and (partially or fully) apply them to each other.
Therefore, we’re able to re-use all new our old operations in a new, more complex context.
Fruit Salad Example
We’ve already seen that a fruit-salad bowl is Mappable
and Appliable
.
However, we’d like to know how we start out: When we have an apple, how do we end up with a bowl filled with an apple?
Bowl.new(my_apple)
is the implementation that answers this question.
Together with apply
and map
, we can now take arbitrary ingredients, put them in bowls and mix and mash them together to our liking, without soiling the kitchen’s countertop:
new
: We can take an apple, and put it in a bowl: we put the apple in anew
bowl to return abowl with an apple
.apply
: If we have a bowl with a partially-made fruit-salad, and we have a bowl with an apple, we can take the apple and the partially-made fruit salad to create a bowl with a fruit-with-apples-salad.map
: We can take a bowl with any fruit or salad, and do some arbitrary operation with it, such as ‘blending’. In this example, we end up with the same bowl, but now filled with blended fruit-salad.
In Other Environments
- In Haskell,
Applicative.new
is known bypure
as well asreturn
. - In Category Theory, something that is Applicative is know as its more official name Applicative Functor.
Callback implementation for FunLand.Applicative.new/1
.
A variant of reduce that accepts anything that is Combinable as second argument. This Combinable will determine what the empty value and the combining operation will be.
Pass in the combinable module name to start with empty
as accumulator,
or the combinable as struct to use that as starting accumulator.
Callback implementation for FunLand.Reducable.reduce/3
.
Converts the reducable into a list, by building up a list from all elements, and in the end reversing it.
This is an automatic function implementation, made possible because FunLand.Builtin.List
implements the FunLand.Reducable
behaviour.
An Example of using traverse:
iex> FunLand.Traversable.traverse([1, 2, 3], FunLandic.Maybe, fn x -> FunLandic.Maybe.just(x) end)
FunLandic.Maybe.just([1, 2, 3])
iex> FunLand.Traversable.traverse([1, 2, 3], [], fn x -> [x,x] end)
[[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3],
[1, 2, 3]]