gpt4 book ai didi

scala - 传递函数返回仿函数或单子(monad)类型的困难

转载 作者:行者123 更新时间:2023-12-01 13:21:41 26 4
gpt4 key购买 nike

我遇到了一个案例,返回类型的 monad 阻碍了高阶函数编程。

val fOpt: (x: Int) => Option[Int]

def g(f: Int=>Int): Int

如何调用 g(fOpt)得到结果为 Option[Int] ?
我的解决方案是 Try(g(fOpt(_).get)).toOption .但我不满意。

有没有我不知道的方法可以解决这个问题。我问这个是因为我对函数式编程知之甚少(太多的模式和理论)。
我希望会有像 functor 这样的东西用于函数返回,使其可以像 val ret:Option[Int] = fOpt.mapReturn(f=>g(f)) 一样工作

最佳答案

您可以轻松实现您提出的语法(我将其称为 toAmbient 而不是 mapReturn,后来我将 f 替换为 h 以使标识符的分离更加明显)。

这是一个在函数上使用隐式包装类的实现:

implicit class UnsafeEmbedAmbientOps[X, Y](f: X => Option[Y]) {
class NoneToAmbientEmbeddingException extends RuntimeException
def toAmbient[Z](a: (X => Y) => Z): Option[Z] = {
try {
Some(a(f(_).getOrElse(throw new NoneToAmbientEmbeddingException)))
} catch {
case e: NoneToAmbientEmbeddingException => None
}
}
}

现在您可以定义 f: Int => Option[Int]和各种 g, g2采取 Int => Int并返回 Int :
val f: Int => Option[Int] = x => Map(1 -> 1, 2 -> 4, 3 -> 9).get(x)
def g(f: Int => Int): Int = f(1) + f(2)
def g2(f: Int => Int): Int = f(1) + f(42)

然后通过 fgg2如下:
println(f.toAmbient(h => g(h)))
println(f.toAmbient(h => g2(h)))

这将打印:
Some(5)
None

扩展评论

我想解释一下我为什么找到 Try(g(fOpt(_).get)).toOption其实很好。

假设有一些自然的方式来改变每一个
f: X => Option[Y]

成一个
fPrime: X => Y

这意味着有一种自然的方法可以转换每个 Unit => Option[Y]进入 Unit => Y .由于 Unit => YY 基本相同,这反过来意味着有某种方法可以转换每个 Option[Y]进入 Y .但是从 Option[Y] 没有自然转换。至 Y .这是一个相当普遍的现象:虽然有 point/ unit , 从 X 进入 monad 总是很容易的。至 M[X] ,通常没有安全/简单/无损的方法可以从 M[X] 中脱离单子(monad)。至 X ,例如:
  • 调用getOption[X]返回 X , 但可以抛出 NoSuchElementException
  • 同样,调用headList可以抛出异常,也可以抛出tail .
  • 等待 Future正在阻止
  • 采样 X来自随机Distribution[X]给你留下一个固定的X ,但会删除所有其他可能的X 的概率信息

  • 等等

    您可以解决类型签名 g(f: Int => Int) 的事实与 Try是因为 Int => Int部分不是很精确:它不是身份单子(monad),而是支持状态和异常的默认环境单子(monad)。在“现实”中, g更像是 g(f: Int => DefaultAmbientMonad[Int]) , 因为 f也可以抛出异常。

    现在,有趣的是,虽然没有保证从 Option[X] 获取的方法。至 X , 实际上有一种方法可以从 Option[X]DefaultAmbientMonad[X] : 扔一些很特别的 NoneEmbeddingException如果 OptionNone .来自 DefaultAmbientMonadOption又是不安全的:你可以捕获你的特殊 NoneEmbeddingException ,但是你必须“祈祷”不会抛出其他异常(这就是它“不安全”的原因)。

    所以,最“系统”的通过方式 fOptg实际上会是
    class NoneEmbeddingException extends RuntimeException

    try {
    Option(g(fOpt(_).getOrElse(throw new NoneEmbeddingException)))
    } catch {
    case e: NoneEmbeddingException => None
    }

    这就是我在上面的代码片段中实现的。

    但这几乎是您使用 Try(...).toOption 所拥有的。 ,除了您使用预定义的 NoSuchElementException而不是有些做作的 NoneEmbeddingException !

    因此,我只想说:您的解决方案是正确的,可以通过系统讨论 Option 中的自然转换来证明这一点。 monad 到默认的环境 monad,它并不是特别令人惊讶。我的个人意见:使用 Try(...).toOption , 没关系。

    关于scala - 传递函数返回仿函数或单子(monad)类型的困难,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49577333/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com