Home > Blockchain >  Problem with showing/outputting Nat type in a function that converts integers to Nats
Problem with showing/outputting Nat type in a function that converts integers to Nats

Time:07-08

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 type T should be converted to string so I can print them”. Often, declaring the type T 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.

  • Related