51

Say I have the following custom data type and function in Haskell:

data Person = Person { first_name :: String, 
                       last_name :: String,
                       age :: Int 
                     } deriving (Eq, Ord, Show)

If I want to create a function print_age to print a Person's age, like so: print_age (Person "John" "Smith" 21) , how would I write print_age to access the age parameter? I'm an Object Oriented guy, so I'm out of my element here. I'm basically looking for the equivalent of Person.age.

nbro
  • 15,395
  • 32
  • 113
  • 196

3 Answers3

67

Function application is prefix, so age person would correspond to the person.age() common in OOP languages. The print_age function could be defined pointfree by function composition

print_age = print . age

or point-full

print_age person = print (age person)
Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
26

This is called record syntax, LYAH has a good section on it.

When a datatype is defined with records, Haskell automatically defines functions with the same name as the record to act as accessors, so in this case age is the accessor for the age field (it has type Person -> Int), and similarly for first_name and last_name.

These are normal Haskell functions and so are called like age person or first_name person.

Paul
  • 571
  • 3
  • 17
huon
  • 94,605
  • 21
  • 231
  • 225
  • In Frege (Haskell for the JVM) a record introduces a namespace, so you would write ```Person.age person```. On the other hand, Frege allows writing ```person.age```, just like in OO. – 0dB Feb 05 '16 at 16:36
  • 2
    As a complete (!) newbee, I'm finding the namespace clutter produced by records to be ugly and frustrating. The Frege syntax is attractive and, iiuc, avoids this problem. – Alan May 03 '16 at 16:20
  • 1
    Ah, I see I am not alone: https://ghc.haskell.org/trac/ghc/wiki/Records/NameSpacing – Alan May 03 '16 at 18:51
12

In addition to the age function mentioned in other answers, it is sometimes convenient to use pattern matching.

print_age Person { age = a } = {- the a variable contains the person's age -}

There is a pretty innocuous extension that allows you to skip the naming bit:

{-# LANGUAGE NamedFieldPuns #-}
print_age Person { age } = {- the age variable contains the person's age -}

...and another, viewed with varying degrees of distrust by various community members, which allows you to even skip saying which fields you want to bring into scope:

{-# LANGUAGE RecordWildCards #-}
print_age Person { .. } = {- first_name, last_name, and age are all defined -}
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380