我正在尝试使用自然数创建一些函数。
import Prelude
data Nat = Zero | Succ Nat
add:: Nat -> Nat -> Nat
add Zero n = n
add (Succ m) n = Succ (add m n)
mult :: Nat -> Nat -> Nat
mult x Zero = Zero
mult x (Succ y) = add x (mult x y)
然后我跑
mult 3 5
我收到此错误:
<interactive>:38:6: error:
• No instance for (Num Nat) arising from the literal ‘3’
• In the first argument of ‘mult’, namely ‘3’
In the expression: mult 3 5
In an equation for ‘it’: it = mult 3 5
我对Haskell相对较新,所以任何帮助都会受到赞赏。 提前致谢
答案 0 :(得分:6)
编译器如何知道Zero
旨在表示数字0
,以及Succ . Succ $ Zero
数字2
等等?它必须或多或少盲目地猜测这是你想要的。
但是,您可以明确告诉它以这种方式处理数字文字:
instance Num Nat where
fromInteger 0 = Zero
fromInteger n = Succ . fromInteger $ n - 1
此外,即使编译器知道Nat
如何通过数字文字定义,它也无法显示 a给定/计算Nat
值。为此,有一个自然的自动修复,但是:
data Nat = Zero | Succ Nat
deriving (Show)
您还可以将Eq
,Ord
和添加到Enum
deriving
列表中;实际上,实例派生的实际上我只注意到{{1}无法在Enum
实例几乎完全“盲目地猜测”Nat
值代表什么数字。Enum
之类的递归类型上派生;如果有可能你可以写:
Nat
答案 1 :(得分:3)
<强> TL; DR 强>
GHC告诉您确切需要了解的内容:您的Nat
类型实际上并不是Num
的实例。这是类型类,它允许整数文字是多态的(通过fromInteger
)。
Haskell中的整数文字是多态的,因此mult 3 5
可以工作。问题是您还没有解释如何将3
和5
转换为Nat
。
解决问题的最直接方法是为您的类型实施Num
。如果您不希望实现整个集合(例如减法,这是自然数的部分函数),您也可以创建单独的natFromInteger
。
Num
解决方案:
instance Num Nat where
fromInteger 0 = Zero
fromInteger n = Succ . fromInteger $ n - 1
您可以通过fromEnum
简化解决方案,如@leftroundabout所示。
一个独立的natFromInteger
解决方案关于山姆 e:
natFromInteger :: Integer -> Nat
natFromInteger 0 = Zero
natFromInteger n = Succ . natFromInteger $ n - 1
此解决方案不允许您隐式转换文字。另一方面,正如我前面提到的,它避免了部分或未实现功能的问题。
妥协:RebindableSyntax
还有第三个选项,让您覆盖 fromInteger
,而无需实现Num
中剩余的数学函数和运算符:使用GHC扩展RebindableSyntax
。有关详细信息,请查看this answer。