Functors are useful when dealing with one effectful value
You may not be familiar with the term Functor
, but if you are a proficient Scala programmer you are almost
certainly familiar with the concept. Functor
essentially expresses the ability to transform a wrapped value
using the map
combinator e.g. List.map
, Option.map
, Either.map
, Future.map
etc.
For example
Some(1).map(_ + 1) // Some(2)
List(1, 2, 3).map(_ + 1) // List(2, 3, 4)
The definition of Functor
looks like this. Note that this is a typeclass that acts on a type constructor F[_]
where F[_]
will be Maybe
or CanFail
in this workshop.
trait Functor[F[_]] {
def map[A, B](fa: F[A])(fn: A => B): F[B]
}
-
In
functors.scala
the functor trait has been defined. -
Notice that an
implicit class
has also been defined, this allows themap
function fromFunctor
to be called with dot syntax. This is following the same pattern we used when implementing theMonoid
typeclass. -
Notice that the way
maybeFunctor
andcanFailFunctor
are declared is slightly different. One is aval
and the other is adef
that takes a type parameter. This is because theMaybe
data type has only one type parameter,Maybe[A]
, whereas theCanFail
data type has two type parameters,CanFail[E, A]
. Referring back to our definition ofFunctor
, we can see that the higher kinded type parameter only matches type constructors with one type parameter, but we have two. So, to satisfy the signature ofFunctor
we need to fix one of the parameters, so that there is only one "hole" left to fill.So, we want to say that
F[A] => CanFail[E, A]
whereE
is fixed andA
is a parameter. We can express that with the?
syntax from a compiler plugin called kind-projector that conveniently solves this problem. e.g.CanFail[E, ?]
Exercise 1 – Maybe functor
- Implement a
Functor
instance forMaybe
. - Run
FunctorLaws
usingsbt 'testOnly *FunctorLaws'
to check your implementation. Note, there will still be failing tests for theCanFail
functor at this stage.
Exercise 2 – CanFail functor
- Implement a
Functor
instance forCanFail
- Run
FunctorLaws
usingsbt 'testOnly *FunctorLaws'
to check your implementation. All the tests should pass now.
Exercise 3 – Use functor instances
- Use the functor instance for CanFail to define the
greeting
function This function should usegetName
to lookup the users name, and transform it into a greeting of the formatHello $name, and welcome to functor town
- Run
FunctorSpec
usingsbt 'testOnly *FunctorSpec'
to check your implementation