gpt4 book ai didi

haskell - 为什么Num可以表现为分数形式?

转载 作者:行者123 更新时间:2023-12-01 07:34:53 28 4
gpt4 key购买 nike

如预期的那样,可以正常工作:

valFrac :: Fractional a => a
valFrac = undefined

fNum :: Num a => a -> a
fNum a = undefined

resFrac :: Fractional a => a
resFrac = fNum valFrac -- Works as expected because every
-- Fractional is also a Num.
-- So as expected, we can pass
-- a Fractional argument into
-- a Num parameter.

另一方面,以下方法也适用。我不明白为什么。
fFrac :: Fractional a => a -> a
fFrac a = undefined

valNum :: Num a => a
valNum = undefined

valFrac :: Fractional a => a
valFrac = fFrac valNum -- Works unexpectedly! There are
-- Nums that are not Fractionals.
-- So why can I pass a Num argument
-- into a Fractional parameter?

问题在评论中。你可以解释吗?

最佳答案

chi的答案对正在发生的事情给出了很好的高层解释。我认为,提供一种稍微底层(但也更加机械化)的方式来理解这一点也可能很有趣,这样您就可以解决其他类似的问题,发动曲柄并获得正确的答案。我将讨论类型,将其作为该类型的值的用户与实现者之间的一种协议。

  • 对于forall a. t,调用者可以选择一种类型,然后继续使用协议t(其中a中的t已被调用者的选择替换)。
  • 对于Foo a => t,调用者必须向实现者提供证明aFoo的实例。然后,他们继续使用协议t
  • 对于t1 -> t2,调用者可以选择t1类型的值(例如,通过以实现者和调用者的角色运行协议t1)。然后,他们继续使用协议t2
  • 对于任何类型的t(即在任何时候),实现者都可以通过仅产生适当类型的值来缩短协议的时间。如果上述规则均不适用(例如,如果我们已经达到Int这样的基本类型或a这样的裸类型变量),则实现者必须这样做。

  • 现在,给您的术语起一些不同的名称,以便我们区分它们:
    valFrac :: forall a. Fractional a =>      a
    valNum :: forall a. Num a => a
    idFrac :: forall a. Fractional a => a -> a
    idNum :: forall a. Num a => a -> a

    我们还有两个要探索的定义:
    applyIdNum :: forall a. Fractional a => a
    applyIdNum = idNum valFrac

    applyIdFrac :: forall a. Fractional a => a
    applyIdFrac = idFrac valNum

    让我们先谈谈 applyIdNum。该协议说:
  • 呼叫者选择a类型。
  • 呼叫者证明它是Fractional
  • 实现者提供的值为a类型。

  • 实现说明:
  • 实现者以调用者身份启动idNum协议。因此,她必须:
  • 选择类型a。她悄悄地做出了与呼叫者相同的选择。
  • 证明aNum的实例。没问题,因为她实际上知道aFractional,这意味着Num
  • 提供类型为a的值。她在这里选择valFrac。为了完整起见,她必须然后显示valFrac具有类型a
  • 因此,实现者现在运行valFrac协议。她:
  • 选择a类型。在这里,她安静地选择了idNum期望的类型,恰好与呼叫者为a选择的类型相同。
  • 证明aFractional的实例。她使用与呼叫者相同的证据。
  • 然后valFrac的实现者承诺根据需要提供a类型的值。

  • 为了完整起见,这是对 applyIdFrac的类似讨论。该协议说:
  • 呼叫者选择a类型。
  • 呼叫者证明aFractional
  • 实现者必须提供a类型的值。

  • 实现说明:
  • 实现者将执行idFrac协议。因此,她必须:
  • 选择一种类型。在这里,她安静地选择呼叫者选择的任何内容。
  • 证明aFractional。她将来电者的证明转给了她。
  • 选择a类型的值。她将执行valNum协议来执行此操作;并且我们必须检查它是否产生a类型的值。
  • 在执行valNum协议期间,她:
  • 选择一种类型。在这里,她选择idFrac期望的类型,即a;这也恰好是呼叫者选择的类型。
  • 证明Num a成立。她可以这样做,因为呼叫者提供了Fractional a的证明,您可以从Num a的证明中提取Fractional a的证明。
  • 然后,根据需要valNum的实现者提供一个a类型的值。

  • 有了该领域的所有详细信息,我们现在可以尝试缩小并查看大图。 applyIdNumapplyIdFrac具有相同的类型,即 forall a. Fractional a => a。因此,在两种情况下,实现者都可以假定 aFractional的实例。但是由于所有 Fractional实例都是 Num实例,因此这意味着实现者可以假定 FractionalNum都适用。这使得在实现中假设约束的函数或值易于使用。

    附言我反复使用副词“安静地”来选择 forall a. t协议期间所需的类型。这是因为Haskell非常努力地向用户隐藏这些选择。但是,如果愿意,可以使用 TypeApplications扩展名将它们明确显示。在协议 t中选择类型 f使用语法 f @t。但是,实例证明仍将代您静默管理。

    关于haskell - 为什么Num可以表现为分数形式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42820603/

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