10/20/2012

The Power of Haskell's Typesystem

The title of this post could be also "Haskell's unknown record system", but it is not about records, it's about the typesystem, which can do things, you would'nt expect. In the example below a program is written which defines records of different types, one record with two attributes, another one with three. Then a polymorphic function is defined, which prints one attribute. The nice thing: you can call this funtion on a record and request a label, which this record does not have. What happens? See yourself. This is the essence of "Haskell is a strictly typed language".
-- The below small example show how mighty Haskell's Typesystem
-- actually is. 

-- For this demonstration, it uses Oleg Kiselyov, Ralf
-- Laemmel and Keean Schupke heterogeneous collections.
-- see: http://homepages.cwi.nl/~ralf/HList
 

module Main where

import Data.HList
import Data.HList.Label2
import Data.HList.Record
import Data.HList.GhcSyntax

-- this is a polymorphic function, which selects an attribute from 
-- a record and prints it, as you can easily see, the function does
-- not carry any information on the attributes, it selects.

printAttribute record attribute = do
 print $ record # attribute
 return ()

-- this is the main function, applying printAttribute

main = do

 -- first some record attributes are defined
 let attribOne = firstLabel "MyDomain" "attribOne description"
 let attribTwo = nextLabel attribOne "attribTwo description"
 let attribThree = nextLabel attribTwo "attribThree description"

 -- now, let's define a record which has content for attributes one and two
 let recordOneTwo = attribOne .=. 1 .*. attribTwo .=. "this is content for attrib 2" .*. emptyRecord
 
 -- and another record which is an exension and also has content for label three
 let recordOneTwoThree = hAppend recordOneTwo (attribThree .=. "does it print?" .*. emptyRecord)

 -- now, lets print some content from the records, works all fine
 printAttribute recordOneTwo attribOne -- prints "1"
 printAttribute recordOneTwo attribTwo -- prints "this is content for attrib 2"
 printAttribute recordOneTwoThree attribThree -- prints "does it print?"
 
 -- the next line cannot work, it selects attribThree from a record
 -- which is only containing attribOne and attribTwo.
 printAttribute recordOneTwo attribThree

-- program does not compile, but compiles with above line commented!
-- The interesting point here is, the compiler detects this, although the
-- type of function printAttribute is polymorphic with regard to the different
-- records. This is Haskell!

3 comments:

  1. IMHO you can do this impressive example with dictionary in any language, in Python it's easiest than this very tricky and obscure example.

    def printAttribute(record, attribute):
    print record[attribute]

    # -- first some record attributes are defined
    attribOne = "attribOne"
    attribTwo = "attribTwo"
    attribThree = "attribThree"

    # -- now, let's define a record which has content for attributes one and two
    recordOneTwo = dict(attribOne = 1, attribTwo = "this is content for attrib 2")

    # -- and another record which is an exension and also has content for label three
    recordOneTwoThree = recordOneTwo.copy()
    recordOneTwoThree.update(attribThree = "does it print?")

    # -- now, lets print some content from the records, works all fine
    printAttribute(recordOneTwo, attribOne) # -- prints "1"
    printAttribute(recordOneTwo, attribTwo) # -- prints "this is content for attrib 2"
    printAttribute(recordOneTwoThree, attribThree) # -- prints "does it print?"

    # -- the next line cannot work, it selects attribThree from a record
    # -- which is only containing attribOne and attribTwo.
    printAttribute(recordOneTwo, attribThree) ## -- KeyError

    ReplyDelete
    Replies
    1. The interesting point is not that an error occurs, you are right, this happens in scripting languages and compiled languages commonly, when an attribute is not present. The thing which is magical is the fact, that the error occurs during compile time. Imagine, the compiler does know about the fact that printAttribute accesses the third property when given the label as a parameter and it can compute that record one does not have this during compilation time! This shows that in Haskell all types are strongly and statically typed even in the case of this heterogeneous record system.

      Delete
  2. This comment has been removed by the author.

    ReplyDelete