简体   繁体   English

Scalaz monad变形金刚。 将f1:A => G [B],f2:B => G [C]函数应用于F [G [A]]对象

[英]Scalaz monad transformers. Applying f1:A => G[B], f2:B => G[C] function to F[G[A]] object

I have two (or more) functions defined as: 我有两个(或更多)函数定义为:

val functionM: String => Option[Int] = s => Some(s.length)
val functionM2: Int => Option[String] = i => Some(i.toString)

I also have some data defined as: 我也有一些数据定义为:

val data: List[Option[String]] = List(Option("abc"))

My question is how to compose (in a nice way) the functions to get a result like: 我的问题是如何组合(以一种很好的方式)函数来获得如下结果:

data.map(_.flatMap(functionM).flatMap(functionM2))
res0: List[Option[String]] = List(Some(3))

I don't like the syntax of the above function calls. 我不喜欢上面函数调用的语法。 If i have many places like this then the code is very unreadable. 如果我有很多像这样的地方那么代码是非常难以理解的。

I tried to play with OptionT scalaz monad transformer, but it still has nested maps and also generates nested Options like: 我尝试使用OptionT scalaz monad转换器,但它仍然具有嵌套映射,并且还生成嵌套的选项,如:

OptionT(data).map(a => functionM(a).map(functionM2)).run
res2: List[Option[Option[Option[String]]]] = List(Some(Some(Some(3))))

What I want to achieve is something more or less like this: 我想要达到的目标或多或少是这样的:

Something(data).map(functionM).map(functionM2)

or even better: 甚至更好:

val functions = functionM andThenSomething functionM2
Something(data).map(functions)

It would be nice if it could work with Try. 如果它可以与Try一起使用会很好。 As I know scalaz doesn't have TryT monad transformer, so is there any way to nicely compose functions which operates on Try? 据我所知,scalaz没有TryT monad变换器,所以有没有办法很好地组合在Try上运行的函数?

As Łukasz mentions, Kleisli seems most relevant here. 正如Łukasz所提到的, Kleisli似乎在这里最相关。 Any time you have some functions of the shape A => F[B] and you want to compose them as if they were ordinary functions A => B (and you have a flatMap for F ), you can represent the functions as Kleisli arrows: 任何时候你有一些形状A => F[B]函数,并且你想要将它们组合成普通函数A => B (并且你有一个FflatMap ),你可以将函数表示为Kleisli箭头:

import scalaz._, Scalaz._

val f1: Kleisli[Option, String, Int] = Kleisli(s => Some(s.length))
val f2: Kleisli[Option, Int, String] = Kleisli(i => Some(i.toString))

And then: 然后:

scala> f1.andThen(f2).run("test")
res0: Option[String] = Some(4)

If you're familiar with the idea of the reader monad, Kleisli is exactly the same thing as ReaderT —it's just a slightly more generic way of framing the idea (see my answer here for more detail). 如果你熟悉的读者单子的想法, Kleisli是完全一样的事情ReaderT -它只是取景的念头(见我的回答稍微更通用的方法在这里进行更详细)。

In this case it seems unlikely that monad transformers are what you're looking for, since you're not reaching all the way inside the List[Option[A]] to work directly with the A s—you're keeping the two levels distinct. 在这种情况下,monad变换器似乎不太可能是你正在寻找的东西,因为你没有到达List[Option[A]]内的所有方式直接使用A s - 你保持两个级别不同。 Given the definitions of f1 and f2 above, I'd probably just write the following: 鉴于上面f1f2的定义,我可能只写下面的内容:

scala> val data: List[Option[String]] = List(Option("abc"))
data: List[Option[String]] = List(Some(abc))

scala> data.map(_.flatMap(f1.andThen(f2)))
res1: List[Option[String]] = List(Some(3))

Lastly, just because Scalaz doesn't provide a Monad (or Bind , which is what you'd need here) instance for Try , that doesn't mean you can't write your own. 最后,只是因为Scalaz不提供一个Monad (或Bind ,这是你需要的东西在这里)实例Try ,这并不意味着你不能写自己的。 For example: 例如:

import scala.util.{ Success, Try }

implicit val bindTry: Bind[Try] = new Bind[Try] {
  def map[A, B](fa: Try[A])(f: A => B): Try[B] = fa.map(f)
  def bind[A, B](fa: Try[A])(f: A => Try[B]): Try[B] = fa.flatMap(f)
}

val f1: Kleisli[Try, String, Int] = Kleisli(s => Success(s.length))
val f2: Kleisli[Try, Int, String] = Kleisli(i => Success(i.toString))

And then: 然后:

scala> val data: List[Try[String]] = List(Try("abc"))
data: List[scala.util.Try[String]] = List(Success(abc))

scala> data.map(_.flatMap(f1.andThen(f2)))
res5: List[scala.util.Try[String]] = List(Success(3))

Some people have some concerns about the lawfulness of a Functor or Monad or Bind instance like this for Try in the presence of exceptions, and these people tend to be loud people, but I find it hard to care (in my view there are better reasons to avoid Try altogether). 有些人对一的合法性一些顾虑FunctorMonadBind实例这样的Try在异常的情况下,这些人往往是响亮的人,但我觉得很难照顾(我认为有更好的理由避免Try完全)。

暂无
暂无

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

相关问题 def compose [A,B,C](f:B => C,g:A => B):A => C = {f(g(_))}是不是有效的Scala声明? - def compose[A,B,C](f: B => C, g: A => B): A => C = {f(g(_))} is noty valid scala declaration? 将'A => F [G [B]]'转换为'F [G [A => B]'标量 - transform 'A => F[G[B]]' into 'F[G[A=>B]' scala Scala如何将Seq [(((A,B,C,D),Seq [(E,F,G)])]]转换为Seq [(A,B,C,D,Seq [(E,F,G) ])]? - Scala How to transform Seq[((A, B, C, D), Seq[(E, F, G)])] to Seq[(A, B, C, D, Seq[(E, F, G)])]? 在Scala(猫或scalaz)中转换仿函数(F [A] => G [A]) - Converting functors (F[A] => G[A]) in Scala (cats or scalaz) 如何将F [A \\ / B]拆分为(F [A],F [B]) - How to split F[A \/ B] into (F[A], F[B]) Scala-使依赖于g的变量的高阶函数g的函数参数f签名 - Scala - Make signature of function parameter f of higher order function g dependent on varars of g Scala中f(a,b)和f(a)(b)之间的差异 - Difference between f(a,b) and f(a)(b) in Scala 如何在List [F [G [A]]]上运行序列得到F [G [List [A]]] - How to run sequence over List[F[G[A]]] to get F[G[List[A]]] List Monad:List.flatMap(f: (A) ⇒ GenTraversableOnce[B]) 签名的后果 - List Monad: Consequences of the signature of List.flatMap(f: (A) ⇒ GenTraversableOnce[B]) Doobie 在通用上下文 F 的事务中运行多个效果 f1 和 f2? - Doobie running multiple effects f1 and f2 in a transaction in the generic context F?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM