文字“1”中没有(Num Nat)的实例

时间:2017-07-30 14:15:46

标签: haskell numbers instance

我正在尝试使用自然数创建一些函数。

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相对较新,所以任何帮助都会受到赞赏。 提前致谢

2 个答案:

答案 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)

您还可以将EqOrd Enum 添加到deriving列表中;实际上,实例派生的Enum实例几乎完全“盲目地猜测”Nat值代表什么数字。实际上我只注意到{{1}无法在Enum之类的递归类型上派生;如果有可能你可以写:

Nat

答案 1 :(得分:3)

<强> TL; DR

GHC告诉您确切需要了解的内容:您的Nat类型实际上并不是Num的实例。这是类型类,它允许整数文字是多态的(通过fromInteger)。

Haskell中的整数文字是多态的,因此mult 3 5 可以工作。问题是您还没有解释如何将35转换为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