Functors are confusing. They are intended to separate [[Information]] from [[Derivation]], resulting in two distinct concepts to worry about—the functor, and the value. At compile time, a functor stands in for a runtime value, allowing you to write operations which transform its value into new values. At runtime, however, the value could be missing, there could be multiple values, or the value could be arriving at some arbitrary, unspecified time.
A functors allows you to use a value without worrying about its runtime complexity. You can think of functors as representing a hypothetical value. All functors have a
map function, which allows you to create new values from the old.
A Maybe functor represents values that might or might not be there at runtime. If I have a maybe functor of a string, and I want to check the strings length, I would use
const myLength = myString.map(str => str.length)
myString has a string at runtime,
myLength will get a length at runtime. If
myString represents a missing string at runtime,
myLength will represent a missing length at runtime, and all is right in the world. I could then map the length into many new values—checking if length is greater than 70, truncating it if it is, and adding ellipsis to the end—all without worrying about a missing string.
This allows functor creation to be completely stateless and transparent. A compiler could run all of this code without any runtime values. I could change what type of functor
myString is. If
myString is lazily created, I could turn
myString into a Future[] without altering the code.
There is one hard requirement for functors to behave correctly. Sequential calls to
map must return a functor equivilant to composition. Therefor
const isLong = myString .map(str => str.length) .map(size => size > 70)
must be the equivilant of
const isLong = myString .map(str => str.length > 70)
Functors have a lot of leeway in how they choose to execute maps. They can run many times, at arbitrary times, or not at all, but they can't alter or reject the values received from map. Otherwise a functor could break later computation reliant on those values.