beginner
I have:
F<A>
.(A) -> G<B>
.I want to have:
F<G<B>>
.Despite having two different effects, they do not interact with each other. This is a regular map
transformation, as provided in the Functor type class.
Consider having an array of values of type String, and trying to parse each value to an Int. Parsing is an effectul operation as it can return a value, if the number can be parsed, or none
, otherwise. In this case, our F
is Array
and our G
is Option:
func parseInt(_ str: String) -> Option<Int> {
Int(str).toOption()
}
// F<A>
let array: Array<String> = ["1", "5", "3"]
// F < G < B >>
let arrayOfOptions: Array<Option<Int>> = array.map(parseInt)
I have:
F<G<A>>
.I want to have:
G<F<A>>
.If we want to swap the relative order of the applied effects, we can use the sequence
function, provided in the Traverse type class.
If we take the previous example, where we have an Array<Option<Int>>
and we want to have an Option<Array<Int>>
, which will be present if all elements in the array are present, or none
if any of the elements is none, we need to apply the
sequence` function:
// Returns Option.some([1, 5, 3])
let optionArray: Option<Array<Int>> = arrayOfOptions.sequence()^
If any of the elements of the array is none
, the result of sequence
is none
:
let arrayWithNone: Array<Option<Int>> = [.some(1), .none()]
let noneArray: Option<Array<Int>> = arrayWithNone.sequence()^
I have:
F<A>
.(A) -> G<B>
.I want to have:
G<F<B>>
.Looking at the type signatures we have above, this pattern is equivalent to doing map
and then sequence
. There is a function that does this in a single step: traverse
, provided by the Traverse type class.
We can run the previous examples in a single step:
let numbers = ["1", "5", "3"]
// Returns Option.some([1, 5, 3])
let parsedNumbers: Option<[Int]> = numbers.traverse(parseInt)^
let nonNumbers = ["9", "abc", "3"]
// Returns Option.none()
let notParsedNumbers: Option<[Int]> = nonNumbers.traverse(parseInt)^