Search code examples
data-structureshaskelltypesalgebraic-data-types

Choosing among alternatives in a Haskell algebraic datatype


When type X is defined as:

data X = 
    X { sVal :: String } |
    I { iVal :: Int } |
    B { bVal :: Bool }

and I want the Int inside an X value, if there is one, otherwise zero.

returnInt :: X -> Int

How can I determine which type of X the argument to returnInt is?


Solution

  • Just to clarify a point here, let me rewrite your data type to avoid ambiguities in the meaning of X:

    data SomeType = X { myString :: String} | I {myInt :: Int} | B {myBool :: Bool}
    

    In this definition there are no X, I and B types. X, I and B are constructors that create a value of type Sometype . Note what happens when you ask ghci what is the type of any value constructed with those type constructors:

    *Main> :t (I 5)
    (I 5) :: Sometype 
    
    *Main> :t (B False)
    (B False) :: Sometype
    

    They belong to the same type!!

    Just as you can use X, I and B to construct types, you can use pattern matching to deconstruct the type, like done in the other answers above:

    returnInt :: SomeType -> Int 
    returnInt (I x) = x        -- if the pattern matches (I x) then return x
    returnInt _  = error "I need an integer value, you moron"  -- throw an error otherwise
    

    Just remember that pattern matching occurs in order: if the value matches the pattern in some line, the patterns in lines below that will not be executed.

    Note that when you define your type like you did, using what is called Record Syntax (just look here: http://en.wikibooks.org/wiki/Haskell/More_on_datatypes ), you got functions like that for free!!

    Try looking on the type of myInt, for example:

    *Main> :t myInt
    myInt :: SomeType -> Int
    

    And look what this function do:

    *Main> myInt (I 5)
    5
    
    *Main> myInt (B False)
    *** Exception: No match in record selector Main.myInt
    

    This is exactly the behavior of returnInt above defined. The strange error message just tells you that the function don't know how to deal with a member of the type SomeType that doesn't match (I x).

    If you define your type using the more common syntax:

    data SomeType2 = X String | I Int | B Bool
    

    then you loose those nice record functions.

    The error messages terminate the execution of the program. This is annoying sometimes. If you need safer behavior for your functions GBacon's answer is just the way to do it. Learn about the Maybe a type and use it to cope with this kind of computation that need to return some value or return nothing ( try this: http://en.wikibooks.org/wiki/Haskell/Hierarchical_libraries/Maybe ).