这可能是关于shapeless的一个非常天真的问题:
假设我有函数A => M[B]
和B => M[C]
。如何撰写它们以获得新功能A => M[B::C::HNil]
?
答案 0 :(得分:5)
如果你想一般性地这样做,你可以使用Scalaz的Arrow
:
import scalaz._, Scalaz._
def andThenButKeep[Arr[_, _]: Arrow, A, B, C](
f: Arr[A, B],
g: Arr[B, C]
): Arr[A, (B, C)] = f >>> (Category[Arr].id &&& g)
或者如果你想要一个HList
而不是一个元组:
import scalaz._, Scalaz._
import shapeless._, shapeless.syntax.std.tuple._
def andThenButKeep[Arr[_, _], A, B, C](
f: Arr[A, B],
g: Arr[B, C]
)(implicit Arr: Arrow[Arr]): Arr[A, B :: C :: HNil] =
f >>> (Arr.id &&& g) >>> Arr.arr((_: (B, C)).productElements)
现在您将函数包装在Kleisli箭头中:
type OptionFunc[A, B] = Kleisli[Option, A, B]
val f: OptionFunc[Int, String] = Kleisli(i => Some("a" * i))
val g: OptionFunc[String, Int] = Kleisli(s => Some(s.length))
val c = andThenButKeep(f, g)
然后:
scala> println(c.run(10))
Some(aaaaaaaaaa :: 10 :: HNil)
通过将箭头限制在M
上方的Kleisli箭头,您可以使类型推断(但也不那么通用)变得不那么繁琐。
答案 1 :(得分:2)
您需要定义M[_]
是Monad
的某种形式,因此您可以flatMap
,我选择使用scalaz
Monad
:< / p>
import scalaz._, Scalaz._
import shapeless._
def compose[M[_] : Monad, A, B, C](f: A => M[B], g: B => M[C]): A => M[B :: C :: HNil] = {
a => f(a).flatMap(b => g(b).map(c => b :: c :: HNil))
}
答案 2 :(得分:1)
这应该可以解决问题
for {
b <- f(a)
c <- g(b)
} yield b :: c :: HNil
当然扩展到
f(a) flatMap { b =>
g(b) map { c => b :: c :: HNil }
}