【发布时间】:2021-05-14 06:47:52
【问题描述】:
我有以下功能:
f: a -> m[b]
g: (b,c) -> m[d]
h: (a,c) -> m[d]
h如何表示为f和g的组合?
使用do/for 表示法,我们可以像这样轻松实现h:
h: (a,c) => {
for {
b <- f(a)
d <- g(b,c)
} yield (d)
}
但是,我很好奇我们是否可以这样表达:h = f andThen g 其中andThen 被用作monadic composition 运算符。例如:
f: a -> m[b]
g: b -> m[c]
h: a -> m[c] = f andThen g
我假设在 Haskell 等语言中创建这样的 andThen 函数是可能的(例如,Kliesli >=>)。在 Scala 中,我们可以这样写:(在 Scala 中命名为 andThenE,因为 andThen 已经在 Function1 的实例上定义)。
implicit class AndThenEither[A,B](val e: Function1[A,Either[_,B]]) {
def andThenE[C](f:Function1[B, Either[_,C]]): Function1[A, Either[_,C]] = {
(v1: A) => e.apply(v1).flatMap(b => f.apply(b))
}
}
鉴于此,如果我们对函数进行 curry,我们可能能够实现这样的组合(或者至少 看起来是可能的):
f: a -> m[b]
g: b -> c -> m[d]
h: a -> c -> m[d] = f andThen g
在理论上这可以工作,但我不知道这是否可能或如何在 Scala(或 Haskell,尽管我更熟悉前者)中实现类似的东西。
假设我们有以下函数:
case class Error(e:String)
case class Output(i: Int, f: Float, s: String)
case class IntermediateOutput(i:Int, f:Float)
def f(i:Int): Either[Error, IntermediateOutput] = Right(IntermediateOutput(i+1, i*0.33)
def g(io: IntermediateOutput, s: String): Either[Error, Output] = Right(Output(io.i, io.f, "hello "+s))
val h: (Int, String) => Either[Error, Output] = f andThen g
val result = h(1, "world!") //Right(Output(2, 0.33, "hello world!")
这甚至可能/可以实现吗?如果不是 Scala,我们如何在 Haskell 或一般情况下curry 组合单子函数?
这是一个已知的事情,还是我们明确区分适用于非单子函数的柯里化和为单子函数保留 andThen like 运算符,但避免将两者混合?如果是这样,我可以看到do/for 符号的有力案例。但是,我并不完全相信这是不可能的,并且想进一步了解这一点。也许代码会很混乱,没关系 - 我只是好奇。由于处理现有问题,我偶然发现了这种情况,但我不能这样投射。
【问题讨论】:
-
在 Haskell 中,您在
Control.Monad中定义了 Kleisi Arrow composition operator(>=>),这正是您的andThen函数 -
我猜是
h = flip (\c -> f >=> flip g c)或者类似 Haskell 的东西。如果您不介意f :: a -> m b; g :: c -> b -> m d; h :: c -> a -> m d(注意g和h的预翻转参数),那么h c = f >=> g c并不算太可怕。不过,我不确定其中任何一个是否比h a c = do { b <- f a; g b c }更具可读性或更可取。 -
@Ismor - 是的。我很清楚。我应该对此更明确/清楚。我会更新问题。
-
偶然出现的一些事情,但我认为值得明确指出:不,这里没有任何障碍。在 Haskell 中,
g和h将被写成 curried,除非你正在做一些非常具体的事情。您可以将andThen写成andThen_1_2 :: Monad m => (a -> m b) -> (b -> c -> m d) -> (a -> c -> m d) ; andThen_1_2 f g = \a c -> do b <- f a ; g b c,这是 Daniel Wagner 所写内容的通用版本。它使用do表示法,但使用它来实现所需的功能;确实,现有的无点操作方法有点笨拙,但没关系。
标签: scala haskell monads currying function-composition