简体   繁体   English

Scala简化了嵌套monad

[英]Scala simplify nested monads

I have some code written in Lift. 我有一些用Lift编写的代码。 Basically its nested Box (similar monad to Option). 基本上它的嵌套Box(类似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. “tryo”是辅助函数,用于捕获并在Box中包含结果,如果发生异常且r是Req对象。 "param" function returns Box[String] (that comes from request param). “param”函数返回Box [String](来自请求参数)。 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). 我想让它适用于Int的String等等,如果可能的话,摆脱嵌套的map / openOr(你认为在Option类型中有getOrElse)。

Monad transformers ? Monad变形金刚?

flatMap that sh*t! 那个sh * t的flatMap

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 . 这是未经测试的...另请注意, scala.util.control.Exception.allCatch.opt()将返回一个Option ,就像tryo返回一个Box

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: 最后,使用pr1001回答,使用隐式提供的转换器值:

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") )

My little bit tweaked version building on @pr1001 and @paradigmatic ground. 我在@ pr1001和@paradigmatic的基础上进行了一些调整版本。

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM