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.scalathe functor trait has been defined. -
Notice that an
implicit classhas also been defined, this allows themapfunction fromFunctorto be called with dot syntax. This is following the same pattern we used when implementing theMonoidtypeclass. -
Notice that the way
maybeFunctorandcanFailFunctorare declared is slightly different. One is avaland the other is adefthat takes a type parameter. This is because theMaybedata type has only one type parameter,Maybe[A], whereas theCanFaildata 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 ofFunctorwe 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]whereEis fixed andAis 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
Functorinstance forMaybe. - Run
FunctorLawsusingsbt 'testOnly *FunctorLaws'to check your implementation. Note, there will still be failing tests for theCanFailfunctor at this stage.
Exercise 2 – CanFail functor
- Implement a
Functorinstance forCanFail - Run
FunctorLawsusingsbt '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
greetingfunction This function should usegetNameto lookup the users name, and transform it into a greeting of the formatHello $name, and welcome to functor town - Run
FunctorSpecusingsbt 'testOnly *FunctorSpec'to check your implementation
