anirudh.vyas
anirudh.vyas

Reputation: 572

Higher kinded types scala for List, Options and other complex types

I am still learning Scala - So please forgive me if this is extremely simple to accomplish. I am also not very fluent with Scalaz or Shapeless - I am only beginning to explore those libraries. I am looking for a simple treatise somewhere which explains to me how I can do something like

trait AmazingMalini[F[G[_]]] {
      def fMap[A,B](aa: F[G[A]])(f : A => F[G[B]]) : F[G[B]] 
}

And then I wanna be able to do something like this -

trait SomeCrazyMagicWithList extends  AmazingMalini[Option[List]]{
   def fMap[A,B](aa: Option[List[A]])(f : A => Option[List[B]]) :Option[List[B]] =  ???
}

Note that option or list types are just examples I have a few container types that I would replace there in mind (building a small graph traversal like DSL for for an app).

Upvotes: 3

Views: 235

Answers (1)

dcastro
dcastro

Reputation: 68640

You have to change the signature of AmazingMalini to trait AmazingMalini[F[_], G[_]]

The method could then be implemented like this:

trait AmazingMalini[F[_], G[_]] {
 def fMap[A,B](aa: F[G[A]])(f : A => F[G[B]]) : F[G[B]]
}

trait SomeCrazyMagicWithList extends AmazingMalini[Option, List] {
  def fMap[A, B](aa: Option[List[A]])(f: A => Option[List[B]]): Option[List[B]] =
    aa.flatMap {
      _ match {
        case Nil         => Some(Nil)
        case as: List[A] => as.map(f).reduce(concat(_, _))
      }
    }

  def concat[A](x: Option[List[A]], y: Option[List[A]]): Option[List[A]] = for {
    xs <- x
    ys <- y
  } yield xs ++ ys
}

Note that Scalaz provides monad transformers to enable stacking of certain monads. For example:

  • the OptionT transformer enables stacking of any monad on top of Option: List[Option[A]], Future[Option[A]], etc.
  • the ListT transformer enables stacking of any monad on top of List: Option[List[A]], Future[List[A]].

So, in your case, you could use ListT like this:

import scalaz._
import Scalaz._
import ListT._

listT(List(1,2,3).some).flatMap(i => listT(List(i, i * 3).some))

res0: scalaz.ListT[Option,Int] = ListT(Some(List(1, 3, 2, 6, 3, 9)))

You can also use for-comprehensions with monad transformers:

for {
  x <- listT(List(1,2).some)
  y <- listT(List(3,4).some)
} yield x + y

res2: scalaz.ListT[Option,Int] = ListT(Some(List(4, 5, 5, 6)))

Upvotes: 5

Related Questions