I am currently learning about types in Haskell, and a given example in the book is to define the data of Nat by two constructors, one for zero, and another one for a constructor. As depicted here:
data Nat = Zero| Suc Nat
Also, I want to define a functions that converts a non-negative integer to a Nat and vice versa. I have written the following code:
nat2int:: Nat-> Int
nat2int Zero=0
nat2int (Suc n)= 1 nat2int n
int2nat:: Int-> Nat
int2nat 0=Zero
int2nat n = Suc (int2nat (n-1))
However, whenever I execute the command
int2nat 0
in the terminal, the following errors appears:
- No instance for (Show Nat) arising from a use of `print'
- In a stmt of an interactive GHCi command: print it
What does this error mean? and how to solve it? In general, I notice that whenever I define a new data type, and define functions that output objects of this newly defined data when I execute one of these functions, it gives me the same errors.
CodePudding user response:
“No instance for
(Show T)
” means “I don't know how values of typeT
should be converted to string so I can print them”. Often, declaring the typeT
with... deriving (Show)
is enough to provide a basic conversion. – chi
data Nat = Zero | Suc Nat
deriving (Show)
In the (seldom) cases where this auto-generated Show
instance does not behave like you want, you can always implement it yourself. It can be as simple as
data Nat = Zero | Suc Nat
instance Show Nat where
show Zero = "Zero"
show (Suc n) = "(Suc " show n ")"
...however this is actually both inefficient (because of Schlemiel string concatenation) and inelegant because we're blindly putting parentheses around the n
whether or not it's needed. The preferred way to manually write Show
instances is this style:
instance Show Nat where
showsPrec _ Zero = ("Zero" )
showsPrec p (Suc n) = showParen (p>10) $ ("Suc " ) . showsPrec 11 n
(This is equivalent to the deriving Show
version.)
By using showsPrec 11 n
for the recursive call, I'm signalling that this show
is used in a tighly binding (function application) setting, and therefore must add parentheses unless it is the already atomic Zero
case. It won't, however, add outer parentheses if it is only shown in a context like (Suc Zero, Suc (Suc Zero))
, because there's no need here.
You can also make Show
instance a bit smarter, for instance it could make sense to avoid nesting of parentheses entirely and instead showing the version with composition operators, like
Suc.Suc.Suc.Suc$Zero
which is more readable IMO. However, there is a near-mandatory convention that whatever you show must be valid Haskell code and should evaluate to the same value you originally used, so don't be tempted to make it super-succinct but non-Haskell syntax.