Scala simplify nested monads

2019-07-12 20:29发布

I have some code written in Lift. Basically its nested Box (similar monad to Option). I'd like to simplify it a little bit if possible. Preferably add type parameter so this could be easily changed to string or double if needed. Here is the code

tryo(r.param("boolean parameter").map(_.toBoolean)).map(_.openOr(false)).openOr(false)

"tryo" is helper function to catch and wrap results in Box if exception occurs and r is Req object. "param" function returns Box[String] (that comes from request param). I'd like to make it working for Int's String's etc. and if possible get rid of nested map/openOr (getOrElse in you think in Option types).

Monad transformers ?

3条回答
唯我独甜
2楼-- · 2019-07-12 21:02

My little bit tweaked version building on @pr1001 and @paradigmatic ground.

case class Converter[T]( fromString: String => T )

implicit val boolConverter = Converter(_.toBoolean) 

implicit val intConverter = Converter(_.toInt)

def asOrDefault[T](input: Box[String], default: T)(implicit conv: Converter[T]): T =       
  input.flatMap( s => tryo(conv.fromString(s))).openOr(default)

And usage in my case:

def prettyPrint(implicit r: Req) = asOrDefault(r.param("prettyPrint"), false)

def maxResults(implicit r: Req): Int = asOrDefault(r.param("maxResults"), 20)
查看更多
太酷不给撩
3楼-- · 2019-07-12 21:16

If you want to absract the type, you need to abstract both the default value, and the conversion from a string:

case class Converter[T]( default: T, fromString: String => T )

Then define implicit instances for your types:

implicit val intConverter = Converter[Int]( 0, _.toInt )
implicit val boolConverter = Converter[Boolean]( false, _.toBoolean )

Finally, use pr1001 answer, using an implicitly provided value of converter:

def asOrDefault[T](input: Box[String])(implicit conv: Converter[T]): T = input.flatMap(
  s => tryo( conv.fromString(s))).openOr(conv.default)

The compiler will select the appropriate converter instance for you:

asOrDefault[Int]( input.param("some int param") )
asOrDefault[Boolean]( input.param("some boolean param") )
查看更多
走好不送
4楼-- · 2019-07-12 21:17

flatMap that sh*t!

r.param("boolean parameter").flatMap(tryo(_.toBoolean)).openOr(false)

Or, use a for comprehension:

val result = for {
  param <- r.param("boolean parameter")
  bool <- tryo(param.toBoolean)
} yield bool
result openOr false

But that doesn't solve your ability to get different types. For that I would suggest something like:

def asOrDefault[T](input: Box[Any])(default: => T): T = input.flatMap(tryo(_.asInstanceOf[T])).openOr(default)

asOrDefault(r.param("any param"))(0)

This is untested... Note also that scala.util.control.Exception.allCatch.opt() will return an Option just like tryo returns a Box.

查看更多
登录 后发表回答