1
votes

I am learning Haskell recently. And I keep wondering if I can use literal number as data constructor, like Integer,Int, Word do. For example, for the following code,

data MyBool = Zero | One
         deriving(Bounded, Eq, Show, Enum)
toBool Zero = False
toBool One = True
fromBool False = Zero
fromBool True = One
myAnd = ff (&&) toBool fromBool
myOr = ff (||) toBool fromBool

ff pp fp pf x y= pf $ pp (fp x) (fp y)

what I want to do is replace Zero with 0, One with 1, so that I can use something like : myAnd (1::MyBool) (0::MyBool). I did look into the source code of ghc, I find something like:

data Int = GHC.Types.I# GHC.Prim.Int#
data Word = GHC.Types.W# GHC.Prim.Word# 

And as I researched Int# is 'unbox type', which must be the key that enabled them to use literal number as data constructor. So how could I make my type use literal number as data constructor like Int and Word do? (Don't hesitate to ask if the question's ambiguous.)

1
Here's a really twisted/ingenious example of using numeric literals as data constructors: augustss.blogspot.com/2009/02/… - ErikR

1 Answers

3
votes

Just create a Num instance for your MyBool type and define:

fromInteger 0 = Zero
fromInteger 1 = One

or perhaps:

fromInteger n = if even n then Zero else One

so that it isn't partial.

And in that case you can write myAnd 0 1 - you won't need the ::MyBool type annotations.

The instance definition can be as short as:

instance Num MyBool where
    fromInteger n = if even n then Zero else One

You'll get warnings about missing implementations, but if you don't use those operations on your MyBool values you'll be okay. If you want a complete Num instance I would probably go this route:

instance Enum MyBool where
  fromEnum Zero = 0
  fromEnum One  = 1
  toEnum n      = if even n then Zero else One

instance Num MyBool where
  fromInteger   = toEnum . fromInteger
  a + b         = toEnum (fromEnum a + fromEnum b)
  a * b         = toEnum (fromEnum a * fromEnum b)
  negate a      = a
  abs a         = a
  signum        = toEnum . signum . fromEnum

Update

I see that you already have derived Enum and Eq, but you probably will want to use the Enum instance I gave since it handles integer values outside of the range [0..1] and it's useful in defining the Num instance.