Reputation: 1108
I am trying to create some functions using natural numbers.
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)
then when I run
mult 3 5
I get this error:
<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
I am relatively new to Haskell so any help would be appreciated. Thanks in advance
Upvotes: 0
Views: 1405
Reputation: 1578
TL;DR
GHC is telling you exactly what you need to know: that your Nat
type is not in fact an instance of Num
. This is the typeclass that allows integer literals to be polymorphic (via fromInteger
).
Integer literals in Haskell are polymorphic, so mult 3 5
could work. The problem is that you haven't explained how to turn 3
and 5
into Nat
s.
The most straight-forward way of solving the problem is to implement Num
for your type. You could also create a separate natFromInteger
if you don't wish to implement the entire set (such as subtraction, which is a partial function for natural numbers).
Num
solution:
instance Num Nat where
fromInteger 0 = Zero
fromInteger n = Succ . fromInteger $ n - 1
You could simplify the solution via fromEnum
as @leftroundabout suggests.
A free standing natFromInteger
solution looks about the same:
natFromInteger :: Integer -> Nat
natFromInteger 0 = Zero
natFromInteger n = Succ . natFromInteger $ n - 1
This solution will not let you convert literals implicitly. On the other hand, it avoids the issue of having partial or unimplemented functions, as I mentioned before.
A compromise: RebindableSyntax
There is a third option, which let's you override fromInteger
without implementing the remaining litany of mathematical functions and operators from Num
: using the GHC extension RebindableSyntax
. Have a look at this answer for details.
Upvotes: 3
Reputation: 120711
How is the compiler supposed to know that Zero
is intended to represent the number 0
, as well as Succ . Succ $ Zero
the number 2
and so on? It would have to more or less blindly guess that this is what you intend.
You can explicitly tell it to handle number literals this way, though:
instance Num Nat where
fromInteger 0 = Zero
fromInteger n = Succ . fromInteger $ n - 1
Furthermore, even if the compiler thus knows how Nat
s can be defined through number literals, it won't be able to display a given/computed Nat
value. For that, there is a natural automatic fix, though:
data Nat = Zero | Succ Nat
deriving (Show)
You can also add Eq
, Ord
and to the Enum
deriving
list; in fact the derived Actually I've just noticed that Enum
instance does pretty much that “blind guessing” as to what Nat
value represents what number.Enum
can't be derived on recursive types like Nat
; if that were possible you could write:
instance Num Nat where
fromInteger = toEnum . fromInteger
Upvotes: 6