微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何你能咖喱组合一元函数吗?

如何解决如何你能咖喱组合一元函数吗?

我有以下功能

f: a -> m[b]
g: (b,c) -> m[d]
h: (a,c) -> m[d]

如何将 h 表示为 fg 的组合?

使用 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 等语言(例如 Kliesli andThen)中可以创建这样的 >=> 函数。在 Scala 中,我们可以这样写:(在 Scala 中命名 andThenE 的示例,因为 andThen 已经在 Function1 的实例上定义)。

implicit class AndThenEither[A,B](val e: Function1[A,Either[_,B]]) {
    def andThenE[C](f:Function1[B,C]]): Function1[A,C]] = {
      (v1: A) => e.apply(v1).flatMap(b => f.apply(b))
    }
}

鉴于此,似乎如果我们对函数进行柯里化,我们可能能够实现这样的组合(或者至少看起来是可能的):

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 或一般情况下咖喱组合一元函数

这是已知的事情还是我们是否明确区分了适用于非 monadic 函数的柯里化和为 monadic 函数保留类似 andThen 的运算符,但避免将两者混合?如果是这样,我可以看到 do/for 表示法的有力案例。但是,我并不完全相信这是不可能的,并且想进一步了解这一点。也许代码会很混乱,没关系 - 我只是好奇。我在解决现有问题的过程中偶然发现了这种情况,但我无法像这样投射。

解决方法

在 Haskell 中有一些标准的(即在 base 库中)操作符。

首先,您的 andThen 函数是众所周知的 Kleisli composition

>=> :: (a -> m b) -> (b -> m c) -> a -> m c

        a -> m b
               b -> m c
       -----------------
        a        -> m c

由于 g 在元组中操作而 f 不返回元组,因此此运算符与您的类型不完全匹配。这可以通过 do/for 符号

轻松克服
h :: Monad m => (a -> m b) -> ( (b,c) -> m d ) -> (a,c) -> m d
h f g (a,c) = do
  b <- f a
  g (b,c)

我会去寻找上面的解决方案,但出于好奇,这个问题已经面临,Haskell 的 base 库引入了一个名为 Control.Arrow 的面向类别理论的模块。 Here 您可以找到大量运算符来实现您的目标:

import Control.Arrow

hKleisli :: Monad m => (a -> m b) -> ( (b,c) -> m d
hKleisli f g = runKleisli $ 
  first (Kleisli f) >>> Kleisli g
--|                 |   |- this is just boilerplate
--|                 |- This composes Categories
--|- this converts f into a function operating in tuples

{--
       Kleisli f  :: Kleisli m  a     b         --  a    -> m  b
---------------------------------------------
first (Kleisli f) :: Kleisli m (a,c) (b,c)      -- (a,c) -> m (b,c)
        Kleisli g :: Kleisli m       (b,c) d    --            (b,c) -> m d
---------------------------------------------
first (Kleisli f)  
    >>> Kleisli g :: Kleisli m (a,c)       d    -- (a,c)            -> m d
--}

编辑

关于你的评论:原来的问题是:在柯里化f之后我们如何组合gg?而且我的解决方案看起来更像是让我们取消 fg 一起使用,所以我同意这不是一个完整的解决方案。好的,让我们解决您的问题,但首先要注意一些事项:

  • 来自 h :: a -> c -> m d 类型 很明显,我们想要一些行为类似于 m 但考虑 c 的 monad。
  • f :: a -> m b 的类型我们知道 f 无法访问 c 并且应该以某种方式将其引入范围。否则,fh 永远不会是同一个 monad。
  • 坦率地说,我们可以使用 const . f :: a -> c -> m b
  • 为 f 添加一个额外的参数

到目前为止我们有

{-- 
The name of the type variables are chosen to match the ones used in this post,but are different in ghci

        f :: a      -> m b
        g :: (b,c)  -> m d

const . f :: a -> c -> m b
  curry g :: b -> c -> m d
--}

现在很明显我们需要使用一些带有 const . fcurry g 的单子运算符,但问题是我们需要保留单子 m 并且无法实现除非我们将结果包装成某种新的数据类型,否则,我们将引用的 monad 是函数 monad (->) (这是特定于 Haskell 的吗?我认为不是)。显而易见的选择是使用 Kleisli monad (ghc >= 8.10)。所以现在我们有:

{-- 
The name of the type variables are chosen to match the ones used in this post,c)  -> m d

const . f :: a -> c -> m b
curry   g :: b -> c -> m d
                 |- This result lives in the -> monad

Kleisli . const . f :: a -> Kleisli m c b
Kleisli . curry   g :: b -> Kleisli m c b
--}

import Control.Monad
import Control.Arrow

f :: Monad m => a -> m b
f = undefined

g :: Monad m => (b,c) -> m d
g = undefined

-- And now,We have curryed and composed g
h :: Monad m => a -> c -> m b
h = runKleisli . (f' >=> g')
  where
    f' :: Monad m => a -> Kleisli m c b
    f' = Kleisli . const . f
    g' :: Monad m => b -> Kleisli m c d
    g' = Kleisli . curry g

请注意,这可以使用与 Kleisli 不同的 monad 来完成。可能所有解决方案都是同构的,直到咖喱/非咖喱。只要您可以将 c 带入 f 的范围并找到一个保留 m 行为的 monad,您就可以应用它。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。