具有类型签名Integer/Int的函数:"类型Integer与Int不匹配"。

3

为了

x :: Integer -> [a] -> [a]
x = take

y :: Integer -> [a] -> [a]
y _ [] = []
y n xs = take n xs

无论是Hugs还是GHC都会报告类型错误:

ERROR "test.hs":5 - Type error in application
*** Expression     : take n xs
*** Term           : n
*** Type           : Integer
*** Does not match : Int

ERROR "test.hs":8 - Type error in explicitly typed binding
*** Term           : x
*** Type           : Int -> [a] -> [a]
*** Does not match : Integer -> [a] -> [a]

这是因为 "take" 的签名是 "Int -> ..."。有没有办法告诉类型系统直接将 Integer 转换为 Int(而不需要 fromIntegral),或者更好的方法(保留 Integer 的非约束大小),来 "构建" take 的版本(或任何其他明确使用 Int 的函数)用于 Integer?还是我必须编写自己的 Prelude 函数版本?目前,我的代码要么充斥着 fromIntegral(如果 Integer 的大小超过 Int 的维度,则无法运行),要么充满了标准函数的琐碎重新实现,这感觉非常笨重。
2个回答

4
使用 Data.List 中的 genericTake,而不是使用普通的 take 函数。"generic" 列表函数接收 Integer 而不是 Int。
未来,如果您想通过类型签名搜索 Haskell 函数,则可以使用 Hoogle
在更一般的意义上,如果你只想添加一个转换步骤,你可以使用内置的组合运算符来轻松创建自己的函数。
--explicit type signature to force the less general types...
integerToInt :: Integer -> Int
integerToInt = fromIntegral

myGenericTake = take . integerToInt

--You can now use myGenericTake throughout the code.

take 只是一个例子。我仍在学习 Haskell,并尝试通过自己摸索来补充我在大学里所学的有限知识,这就是我偶然发现它的原因。如果我想使用任何 x :: Int -> ... 并且使用 Integer 是不可能的吗?(或者只能使用 fromIntegral ?) - Max Maria Wacholder
@Max:添加了一小段关于函数组合的内容。 - hugomg
谢谢。最后一个问题:我能以任何方式告诉Haskell,当它遇到特定类型时,我希望它使用我的Prelude函数(或任何函数)的实现吗?例如,我想编写一个具有签名take :: Integer -> ...的函数,然后在适当的时候让Haskell选择它,这样我就可以在我的代码中使用标准名称,而不是“myTake”或“take . fromIntegral”。 - Max Maria Wacholder
Haskell不支持那种C++风格的重载,因为它会搞乱类型推断。能够给两个不同的函数取相同的名称的唯一方法是使用类型类。如果您查看genericTake的类型签名(Integral i => i -> [a] -> [a]),那就是重载版本。 - hugomg
你也可以导入Prelude并隐藏原始的take函数,这样你就可以使用该名称来代替自己的另一个函数。然而,我不建议这样做。 - hugomg

1
Haskell采取的方法是进行任何隐式转换。因此,当你卡在像take这样的函数上时,你只需要使用fromIntegral或类似的显式转换即可。
如果这让你感到困扰,那么请发表你对改进Prelude的意见。你肯定不会是唯一希望改进Prelude函数的人。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接