I am a Haskell rookie and I often find myself having to decompose a data with pattern matching only to apply a function to one of its member and then reassemble it.
Say I have:
data Car = Car { gas :: Int, licensePlate :: String }
and I want it to halve its gas when it drives, and refuel it, I'm doing:
mapGas:: (Int -> Int) -> Car -> Car
mapGas f (Car aGas aLicensePlate) = Car (f aGas) aLicensePlate
drive:: Car -> Car
drive = mapGas (flip div 2)
refuel:: Int -> Car -> Car
refuel = mapGas . (+)
Is there a way to do just that without having to define the auxiliary function mapGas? Since it can become rather bothersome having to write a map function for every member of the data when it's made of many fields. I know it is possible to assign a value to one of the members with accessors:
runOutOfFuel:: Car -> Car
runOutOfFuel aCar = aCar { gas = 0 }
Is it possible to map a function with accessors too? if so, how?
map is a function that takes two parameters: a function and a list of elements. The type signature of map is (a -> b) -> [a] -> [b] . The (a -> b) part is the function you pass to map , we will call it f . f takes one value and returns another that may be of a different type.
In many programming languages, map is the name of a higher-order function that applies a given function to each element of a collection, e.g. a list or set, returning the results in a collection of the same type. It is often called apply-to-all when considered in functional form.
in goes along with let to name one or more local expressions in a pure function.
The shows functions return a function that prepends the output String to an existing String . This allows constant-time concatenation of results using function composition.
Using just the core libraries? No. But with the widely used lens
package, yes. Here is what that looks like in your case:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens.TH
import Control.Lens
data Car = Car { _gas :: Int, _licensePlate :: String }
makeLenses ''Car
Now, you can easily get/set/modify fields that are nested in data structures.
runOutOfFuel:: Car -> Car
runOutOfFuel = gas .~ 0
drive:: Car -> Car
drive = gas %~ (`div` 2)
refuel:: Int -> Car -> Car
refuel c = gas +~ c
The magic here is that makeLenses ''Car
generates gas
and licensePlate
functions that are similar (but more powerful) to your mapGas
(in fact, mapGas = (gas %~)
). Getting started with lens
is pretty daunting, but I recommend just reading the examples section.
There is no language feature which does this, but, as with many things in Haskell, the core language is powerful enough that this can be implemented in a simple and elegant way.
The solution for what you are looking for is a kind of value called a lens. A lens does exactly what you want: It allows you to take any kind of abstract data and apply a function on a part of it, getting the entire data value with the modified part included as a result.
There's an introduction to lenses I quite like here. To use the examples included, you'll need the lens
package. (Or this one if you're using Stack)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With