gpt4 book ai didi

haskell - 使数字函数成为 Num 的实例?

转载 作者:行者123 更新时间:2023-12-04 03:04:04 26 4
gpt4 key购买 nike

我希望能够使用二元运算符在 haskell 中编写数字函数。因此,例如,对于一元数值函数:

f*g

应翻译为:
\x -> (f x)*(g x)

和类似的加法。让你自己的运算符(operator)来做这件事很简单,但我真的很想只做 Num a => a -> a函数 Num 的一个实例,但我不知道该怎么做。

我也想让这个 arity 泛型,但是对于在 Haskell 中执行 arity 泛型函数有多么困难,这可能太麻烦了,所以最好单独定义 Num a => a -> a -> a , Num a => a -> a -> a -> a等...实例达到相当大的数量。

最佳答案

惯用的方法

Applicative 的实例对于 (->) a ,这意味着所有函数都是应用仿函数。以您所描述的方式组合任何函数的现代习惯用法是使用 Applicative , 像这样:

(*) <$> f <*> g
liftA2 (*) f g -- these two are equivalent

这样操作就清楚了。这两种方法都略显冗长,但在我看来,组合模式的表达要清楚得多。

此外,这是一种更通用的方法。如果您理解这个成语,您将能够在许多其他情况下应用它,而不仅仅是 Num。 .如果您不熟悉 Applicative ,一个起点是 Typeclassopedia .如果您理论上倾向于,您可以查看 McBride and Patterson's famous article . (为了记录,我在这里使用普通意义上的“成语”,但要注意双关语。)
Num b => Num (a -> b)
您想要的实例(以及其他实例)在 NumInstances package 中可用。 .您可以复制发布的@genisage 实例;它们在功能上是相同的。 (@genisage 写得更明确;比较这两种实现可能会有所启发。)在 Hackage 上导入库的好处是可以向其他开发人员突出显示您正在使用孤立实例。
Num b => Num (a -> b) 有问题, 然而。简而言之, 2现在不仅是一个数字,而且是一个具有无限个参数的函数,所有这些都被它忽略了。 2 (3 + 4)现在等于 2 .将整数文字用作函数几乎肯定会产生意外和不正确的结果,并且没有办法警告程序员缺少运行时异常。

2010 Haskell Report section 6.4.1 中所述, "整数文字表示函数 fromIntegerInteger 类型的适当值的应用。"这意味着写 212345在您的源代码或 GHCi 中相当于编写 fromInteger 2fromInteger 12345 .因此,任何一个表达式都有类型 Num a => a .

结果, fromInteger在 Haskell 中绝对是普遍存在的。通常这会很好地工作。当您在源代码中编写一个数字时,您会得到一个适当类型的数字。但是用你的 Num函数的实例,类型为 fromInteger 2很可能是 a -> Integera -> b -> Integer .事实上,GHC 会很乐意替换文字 2有一个函数,而不是一个数字——还有一个特别危险的函数,它会丢弃任何给它的数据。 ( fromInteger n = \_ -> nconst n ;即丢弃所有参数,只给出 n 。)

通常你可以不实现不适用的类成员,或者通过 undefined 实现它们而侥幸逃脱。 ,其中任何一个都会产生运行时错误。出于同样的原因,这不是解决当前问题的方法。

一个更明智的例子:伪 Num a => Num (a -> a)
如果您愿意将自己限制为 Num a => a -> a 类型的一元函数的乘法和加法,我们可以改善 fromInteger有点问题,或者至少有 2 (3 + 5)等于 16而不是 2 .答案很简单,定义 fromInteger 3(*) 3而不是 const 3 :
instance (a ~ b, Num a) => Num (a -> b) where
fromInteger = (*) . fromInteger
negate = fmap negate
(+) = liftA2 (+)
(*) = liftA2 (*)
abs = fmap abs
signum = fmap signum
ghci> 2 (3 + 4)
14
ghci> let x = 2 ((2 *) + (3 *))
ghci> :t x
x :: Num a => a -> a
ghci> x 1
10
ghci> x 2
40

请注意,尽管这在道德上可能等同于 Num a => Num (a -> a) ,它必须用等式约束(需要 GADTsTypeFamilies )来定义。否则,我们将收到类似 (2 3) :: Int 的歧义错误。 .我懒得解释原因,抱歉。基本上,等式约束 a ~ b => a -> b允许 b 的推断或声明类型传播到 a在推理过程中。

有关此实例为何以及如何工作的详细说明,请参阅 Numbers as multiplicative functions (weird but entertaining) 的答案。 .

孤立实例警告

在不了解 orphan instances 问题的情况下,不要公开任何包含--或导入包含--或导入包含...的模块的模块--这些实例中的任何一个的模块。和/或相应地警告您的用户。

关于haskell - 使数字函数成为 Num 的实例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26515102/

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