[Haskell-cafe] I just don't get it (data structures and OO)
Phlex at telenet.be
Sun Jun 3 07:48:00 EDT 2007
Tillmann Rendel wrote:
> Phlex wrote:
>> changePlanetAge universe galaxy planet age = ...lots of code,
>> returning a new universe
>> And the same code for all functions updating any of the properties of
>> my planet ...
>> And the same code for all functions updating properties of a country
>> on this planet...
> In functional programming, problems of the kind "and the same code for
> all functions doing something related" are most often solved by
> introducing higher order functions. Let's try to decompose the problem
> as follows:
> (1) change an attribute
> (2) change a planet by changing one of it's attributes
> (3) change a galaxy by changing one of it's planets
> (4) change an universe by changing one of it's galaxies.
> (1) is done by functions of type (Attribute -> Attribute). We will
> construct them on the fly.
> For (2), we need functions for each attribute of a planet.
> type Name = String
> type Age = Integer
> data Planet = Planet Name Age
> -- lift name changer to planet changer
> changeName :: (Name -> Name) -> Planet -> Planet
> changeName f (Planet n a) = Planet (f n) a
> -- lift age changer to planet changer
> changeAge :: (Age -> Age) -> Planet -> Planet
> changeAge f (Planet n a) = Planet n (f a)
> we need one of these functions for each attribute of each object. they
> correspond to setter-Methods in oop.
> For (3), we have to select one of the planets in a galaxy to be
> changed. Let's assume integer indices for all planets.
> type Galaxy = [Planet]
> -- lift planet changer to galaxy changer
> changePlanet :: Integer -> (Planet -> Planet) -> Galaxy -> Galaxy
> changePlanet 0 f (p:ps) = f p : ps
> changePlanet n f (p:ps) = p : changePlanet (pred n) f ps
> For (4), we have to select one of the galaxies in a universe to be
> changed. Let's assume integer indices again.
> type Universe = [Galaxy]
> -- lift galaxy changer to universe changer
> changeGalaxy :: Integer -> (Galaxy -> Galaxy) -> Universe -> Universe
> changeGalaxy 0 f (g:gs) = f g : gs
> changeGalaxy n f (g:gs) = g : changeGalaxy (pred n) f gs
> Oups, that's the same as (3), up to renaming of types and variables.
> Let's refactor it to
> -- lift element changer to list changer
> changeElement :: Integer -> (a -> a) -> [a] -> [a]
> changeElement f 0 (x:xs) = f x : xs
> changeElement f n (x:xs) = x : changeListElement f (pred n) xs
> -- provide nicer names
> changePlanet = changeElement
> changeGalaxy = changeElement
> Let's see how we can use this:
> To set the name of the second planet of the third galaxy to "earth":
> (changeGalaxy 2 $ changePlanet 1 $ changeName $ const "earth") univ
> To increase the age of the same planet by 1:
> (changeGalaxy 2 $ changePlanet 1 $ changeAge $ succ) univ
> Using map instead of changeElement, we can change all galaxies or
> planets at once:
> -- provide nicer names
> changeGalaxies = map
> changePlanets = map
> To set the name of the first planet in all galaxies to "first":
> (changeGalaxies $ changePlanet 0 $ changeName $ const "first") univ
> To increase the age of all planets by one:
> (changeGalaxies $ changePlanets $ changeAge $ succ) univ
> A possible next step is to use typeclasses as supposed by apfelmus to
> access elements of different structures in a uniform way.
This is very informative, and easier to grasp for such a newbie as me.
So the idea is to take the "changing function" down the chain, i knew
this couldn't be that hard !
Still this requires indeed to think different, I guess i'm up for quite
a few exercises in order to wrap my mind around this.
That's the kind of information that's missing from all these tutorials i
found around the web.
More information about the Haskell-Cafe