Daniel Fath
Daniel Fath

Reputation: 18069

Alias for datatypes in Haskell

So I've got a structure like this:

data Maybe a = Nothing | Just a  

but I want a structure that is defined as

data MaybeInt = Nothing | Just Int

is there a way to define MaybeInt using Maybe a, and if so how?

Upvotes: 1

Views: 958

Answers (1)

J. Abrahamson
J. Abrahamson

Reputation: 74344

There are a few ways to define MaybeInt. I'll state them then have some commentary.

Direct

data MaybeInt = NothingInt | JustInt Int

Newtype

newtype MaybeInt = MI (Maybe Int)

Type synonym

type MaybeInt = Maybe Int

Plain

-- just use `(Maybe Int)` wherever you would write `MaybeInt`

Commentary

Most commonly, one would use the plain method since most people are familiar with Maybe and thus know to use Just and Nothing to match it. This makes it good for libraries—very transparent. The type synonym method is a common documentation method, but is basically useless for your synonym. It makes it so that foo :: Int -> Maybe Int and bar :: Int -> MaybeInt have identical type signatures. It also means that as soon as someone knows that MaybeInt === Maybe Int they can use the Just/Nothing constructors for matching.

The newtype method gets fairly interesting. Here you have to begin "wrapping" and "unwrapping" the MI constructor every time you want to use the MaybeInt type. Compare:

baz :: MaybeInt -> Bool
baz (MI Nothing) = False
baz (MI (Just int)) = True

this is nice because if you don't export MI then nobody will be able to match on MaybeInt (despite having a pretty good guess at what's going on inside of it). This is really useful for making stable APIs. Another interesting property of newtype is that you can write new instances for MaybeInt which are different from Maybe's built-in ones. For instance, you could override the Monoid instance

instance Monoid MaybeInt where
  mempty = MI Nothing
  mi `mappend` (MI Nothing) = mi
  _  `mappend` mi           = mi

which is just the same as the Last a newtype built-in to Data.Monoid which wraps Maybe as.

Finally, we get the full-blown data instance. It's more verbose, more likely to error, marginally slower (since the compiler has to track a new, unique data type), and requires that people learn new constructors. For functionality so obviously identical to Maybe Int there's really no reason to use it at all.

Upvotes: 10

Related Questions