getValue('#comment')
which is an action which retrieves text on an element. Now, it might error finding the element or the value string may not exist so it returns Task Error (Maybe String)
. After that, we must map
over both the Task
and the Maybe
to pass our text to validate
, which in turn, gives us back Either
a ValidationError
or our String
. Then onto mapping for days to send the String
in our current Task Error (Maybe (Either ValidationError String))
into postComment
which returns our resulting Task
.join
a few, homogenize them, deconstruct them, and so on. In this chapter, we'll focus on homogenizing them via natural transformations.(Functor f, Functor g) => f a -> g a
. What makes it special is that we cannot, for any reason, peek at the contents of our functor. Think of it as an exchange of highly classified information - the two parties oblivious to what's in the sealed manila envelope stamped "top secret". This is a structural operation. A functorial costume change. Formally, a natural transformation is any function for which the following holds:map
or map
then run our natural transformation and get the same result. Incidentally, that follows from a free theorem though natural transformations (and functors) are not limited to functions on types.Strings
into Booleans
and Integers
into Floats
(though JavaScript only has Numbers
). The difference here is simply that we're working with algebraic containers and we have some theory at our disposal.map
doesn't get lost in the shape shift shuffle. That is the whole point: map
must carry on, according to our definition, even after the transformation.ioToTask
as converting synchronous to asynchronous or arrayToMaybe
from nondeterminism to possible failure. Note that we cannot convert asynchronous to synchronous in JavaScript so we cannot write taskToIO
- that would be a supernatural transformation.sortBy
on a List
. Natural transformations provide a nice way to convert to the target type knowing our map
will be sound.arrayToList
, and voilà ! Our [a]
is a List a
and we can sortBy
if we please.map(f)
to the left of natural transformation as shown in doListyThings_
.Promise
and Task
are isomorphic. We can also write a listToArray
to complement our arrayToList
and show that they are too. As a counter example, arrayToMaybe
is not an isomorphism since it loses information:map
on either side yields the same result. I mention isomorphisms here, mid-chapter while we're on the subject, but don't let that fool you, they are an enormously powerful and pervasive concept. Anyways, let's move on.head :: [a] -> a
can be viewed as head :: [a] -> Identity a
. We are free to insert Identity
wherever we please whilst proving laws since we can, in turn, prove that a
is isomorphic to Identity a
(see, I told you isomorphisms were pervasive).join
able.chain(maybeToTask)
and chain(eitherToTask)
. Both have the same effect; they naturally transform the functor our Task
is holding into another Task
then join
the two. Like pigeon spikes on a window ledge, we avoid nesting right at the source. As they say in the city of light, "Mieux vaut prévenir que guérir" - an ounce of prevention is worth a pound of cure.Task
in most cases).Either b a
to Maybe a
eitherToTask
, simplify findNameById
to remove the nested Either
.