Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining recursive data types with tuples in Haskell

Tags:

haskell

I'm having trouble defining a new data type in Haskell.

I'm trying to create a data type NumPair that will be a tuple containing either two integers, or an integer and another NumPair.

So for instance, (2, 2), (0, 5), (1, (2, 3)) and (0, (4, (3, 121))) should all be valid NumPairs.

This is the code I've written to try to do this:

data NumPair = (Int, Int) | (Int, NumPair) deriving (Eq, Show)

Can someone explain why this doesn't work and what I should do instead, please?

like image 259
Tagc Avatar asked Nov 05 '12 18:11

Tagc


People also ask

How do you define new datatype in Haskell?

In general, we define a new data type by using the data keyword, followed by the name of the type we're defining. The type has to begin with a capital letter to distinguish it from normal expression names. To start defining our type, we must provide a constructor.

What is a tuple in Haskell?

A tuple is a fixed-length coupling of values, written in parentheses with the values separated by commas. One way to use this is to pass all parameters into a function as one value, rather than the curried functions we've seen so far.

How data types are combined in Haskell?

You can combine multiple types with an and (for example, a name is a String and another String ), or you can combine types with an or (for example, a Bool is a True data constructor or a False data constructor). Types that are made by combining other types with an and are called product types.

Are tuples immutable in Haskell?

A tuple is a sequence of values. The values can be of any type, and they are indexed by an integer, so tuples are not like lists. Tuples are immutable which means you cannot add more elements to the tuple as the program runs.


1 Answers

You need to add constructor names for each alternative:

data NumPair = Pair (Int, Int) | More (Int, NumPair) deriving (Eq, Show)

Those constructor names are what let you pattern match on the data type like so:

f :: NumPair -> A
f (Pair (x, y )) = ...
f (More (x, np)) = ...

You can then build a value using the constructors to (which is why they are called constructors):

myNumPair :: NumPair
myNumPair = More (1, More (2, Pair (3, 4)))

There are two other ways you can improve your type. Haskell constructors have built-in support for multiple fields, so rather than using a tuple you can just list the values directly in the constructor like this:

data NumPair = Pair Int Int | More Int NumPair deriving (Eq, Show)

Another way you can improve upon it is to recognize that you've just written the type for a non-empty list. The best implementation for non-empty lists resides in Data.List.NonEmpty of the semigroups package, which you can find here.

Then your type just becomes:

type NumPair = NonEmpty Int

... and you get a bunch of function on non-empty lists for free from that module.

Edit: n.m. brought to my attention that what you probably wanted was:

data NumPair = Pair (Int, Int) | More ((Int, Int), NumPair)

... which is equivalent to:

type NumPair = NonEmpty (Int, Int)

The difference is that this latter one lets you append pairs of integers, where as the previous one that followed your question's type only lets you append integers.

like image 112
Gabriella Gonzalez Avatar answered Sep 28 '22 08:09

Gabriella Gonzalez