组合两个函数得到一个 returns HList 的函数
Composing two functions to get a function that returns HList
这可能是一个关于 shapeless 的非常幼稚的问题:
假设我有函数 A => M[B]
和 B => M[C]
。我如何组合它们以获得新功能 A => M[B::C::HNil]
?
这应该可以解决问题
for {
b <- f(a)
c <- g(b)
} yield b :: c :: HNil
这当然会扩展到
f(a) flatMap { b =>
g(b) map { c => b :: c :: HNil }
}
你需要定义 M[_]
是某种形式的 Monad
所以你可以 flatMap
它,我选择使用 scalaz
Monad
:
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))
}
如果您想通用地执行此操作,您可以使用 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 箭头,您可以使类型推断不那么挑剔(但也不那么通用)
这可能是一个关于 shapeless 的非常幼稚的问题:
假设我有函数 A => M[B]
和 B => M[C]
。我如何组合它们以获得新功能 A => M[B::C::HNil]
?
这应该可以解决问题
for {
b <- f(a)
c <- g(b)
} yield b :: c :: HNil
这当然会扩展到
f(a) flatMap { b =>
g(b) map { c => b :: c :: HNil }
}
你需要定义 M[_]
是某种形式的 Monad
所以你可以 flatMap
它,我选择使用 scalaz
Monad
:
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))
}
如果您想通用地执行此操作,您可以使用 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
.