Rui Reis
Rui Reis

Reputation: 63

Defining a High Order function that acesses data records

I pretend to create a high order function that uses as one of its parameters a function which belongs to the record of a certain Data Type.

For Example:

type Debt = Float
type Power = Int

Data State = S String Debt 
Data City = C String Power Debt
Data Country = Country
   { states :: [State]
   , cities :: [City] }

upDetail :: (Country -> [a]) 
         -> ([a] -> b -> [a])
         -> b -> Country -> Country
upDetail f g b country = country { f = new }
         where old = f country
               new = g old b

What the function above is supposed to do is pick an element of the record of the Country (with the function type Country -> [a]) and alter it according to a certain function type [a] -> b -> [a] and a certain b

However, when i try to compile this i get an error saying :

‘f’ is not a (visible) constructor field name

Is there any way i can overcome this problem? I thought of using Maybe Country as my result but i don't know how to do this.

Upvotes: 1

Views: 68

Answers (1)

rampion
rampion

Reputation: 89073

As the comments mention, the normal solution to this is to use lenses:

upDetail :: Lens Country [a]
         -> ([a] -> b -> [a])
         -> b -> Country -> Country
upDetail f g b country = set f country new
         where old = get f country
               new = g old b

However, lenses aren't that hard to get a handle on, especially for so simple a purpose.

The simplest way of expressing a lens is as a getter and a setter function:

data Lens s a = Lens
  { get :: s -> a
  , set :: s -> a -> s
  }

_states :: Lens Country [State]
_states = Lens states $ \c new -> c { states = new }

_cities :: Lens Country [City]
_cities = Lens cities $ \c new -> c { cities = new }

This lets us modify the cities or states of a country pretty easily:

λ Country [] []
Country {states = [], cities = []}
λ upDetail _cities (\cs c -> c:cs) (C "Hereford" 100 3000) it                                                        
Country {states = [], cities = [C "Hereford" 100 3000.0]}
λ upDetail _states (\cs c -> c:cs) (S "Delmarva" 4) it                                                        
Country {states = [S "Delmarva" 4.0], cities = [C "Hereford" 100 3000.0]}

Lens get slightly more complex once you start thinking about composing lenses, which you're not getting into here, but you could.

Upvotes: 5

Related Questions