Scala flattening an Option around a higher kinded type, looking for a more idiomatic approach

122 views Asked by At

Given a higher Kinded Type M, and a monad type class, I can operate on values within M through a for-comprehension. Working with a function that return Options, im looking for a more appropriate way to flatten these options than the solution I have. This is as follows

class Test[M[+_]:Monad](calc:Calculator[M]) {
  import Monad._

  def doSomething(x:Float):M[Option[Float]] = {
    for {
      y:Option[Float] <- calc.divideBy(x)   // divideBy returns M[Option[Float]]
      z:Option[Float] <- y.fold[M[Option[Float]]](implicitly[Monad[M]].point(None))(i => calc.divideBy(i))
    }  yield z
  }
} 

So its the following I'm looking to correct:

y.fold[M[Option[Float]]](implicitly[Monad[M]].point(None))(i => calc.divideBy(i))

Also the case where instead of calling the second divideBy, I call multiplyBy which returns M[Float]

y.fold[M[Option[Float]]](implicitly[Monad[M]].point(None))(i => calc.multipleBy(i).map(Some(_)) 

Maybe this is a case for Monad Transformers but Im not sure how to go about it.

1

There are 1 answers

0
Travis Brown On BEST ANSWER

It seems likely that monad transformers can help you here. For example, the following compiles and I think does roughly what you want:

import scalaz._, Scalaz._

abstract class Calculator[M[_]: Monad] {
  def divideBy(x: Float): M[Option[Float]]
  def multiplyBy(x: Float): M[Float]
}

class Test[M[_]: Monad](calc: Calculator[M]) {
  def doSomething(x: Float): OptionT[M, Float] = for {
    y <- OptionT(calc.divideBy(x))
    z <- calc.multiplyBy(y).liftM[OptionT]
  } yield z
}

Now doSomething returns an OptionT[M, Float], which is kind of wrapper for M[Option[Float]] that allows you to work with the contents all the way inside the Option monadically. To get back an M[Option[Float]] from the OptionT[M, Float] you can just use the run method.