Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Haskell determine correct data constructor?

Tags:

haskell

Here is my code:

data TC a = DC1 a | DC2 a

getDC           :: TC a -> String  
getDC (DC1 x)   = "created by first data constructor"  
getDC (DC2 x)   = "created by second data constructor"  

In Hugs:

Main> getDC (DC1 10)

"created by first data constructor"

Main> getDC (DC2 10)

"created by second data constructor"


So, the interpreter can determine which data constructor being used. As i know, every value has an associated type. Let's check:


Main> :t (DC1 10)

DC1 10 :: Num a => TC a

Main> :t (DC2 10)  

DC2 10 :: Num a => TC a


Only type constructor (TC) can be seen there.

Why, where and how does interpreter hold additional information about data constructor?

like image 1000
user102417 Avatar asked Mar 08 '14 18:03

user102417


2 Answers

While types provide important compile time information you're still manipulating values at runtime. The constructor used is just a property of the value—in particular, you use pattern matching to determine the choice of constructor used in a value.

getDC           :: TC a -> String  
getDC (DC1 x)   = "created by first data constructor"  
getDC (DC2 x)   = "created by second data constructor"

-- or, to be more clear about the pattern matching

getDC           :: TC a -> String  
getDC dc = case dc of
  (DC1 x) -> "created by first data constructor"  
  (DC2 x) -> "created by second data constructor"

To attempt a little more clarity, let's try making the natural numbers instead of using the abstract TC type.

data Nat = Zero | Succ Nat

In other words, what we write as 0 could be represented as a Nat as

Zero :: Nat

and what we write as 3 could be represented as

Succ (Succ (Succ Zero))

We could write a function on `Nat

isThree :: Nat -> Bool
isThree (Succ (Succ (Succ Zero))) = True
isThree _                         = False

and the behavior of this function isn't indicated in its type (Nat -> Bool), so instead the behavior must be performed by the value. Indeed, we use pattern matching to destruct the value and enforce the behavior.

like image 171
J. Abrahamson Avatar answered Oct 08 '22 13:10

J. Abrahamson


Think of values as boxes containing a tag field that identifies which data constructor was used to create it, as well as the additional arguments required by that constructor. Since Haskell is statically typed, the runtime doesn't have to distinguish between values of different types, so the tag only needs to identify the data constructor within that type. You can only examine the tag indirectly via pattern matching, but knowing the type, and the tag, you can determine the data constructor, and can safely access the arguments.

Note, if a type has only one data constructor, then values of that type do not require a tag.

like image 39
pat Avatar answered Oct 08 '22 13:10

pat