The Author Online Book Forums are Moving

The Author Online Book Forums will soon redirect to Manning's liveBook and liveVideo. All book forum content will migrate to liveBook's discussion forum and all video forum content will migrate to liveVideo. Log in to liveBook or liveVideo with your Manning credentials to join the discussion!

Thank you for your engagement in the AoF over the years! We look forward to offering you a more enhanced forum experience.

449049 (17) [Avatar] Offline

Can someone walk me through the closure on organCounts (line 23-24)? Can't seem to wrap my head around it.

data Organ = Heart | Brain | Kidney | Spleen deriving (Show, Eq, Ord, Enum)

organs :: [Organ]
organs = [Heart,Heart,Brain,Spleen,Spleen,Kidney]

ids :: [Int]
ids = [2,7,13,14,21,24]

organPairs :: [(Int,Organ)]
organPairs = zip ids organs

organCatalog :: Map.Map Int Organ
organCatalog = Map.fromList organPairs

values :: [Organ]
values = map snd (Map.toList organCatalog)

allOrgans :: [Organ]
allOrgans = [Heart .. Spleen]

organCounts :: [Int]
organCounts = map countOrgan allOrgans
  where countOrgan = (\organ ->
                        (length . filter (== organ)) values)
dre (42) [Avatar] Offline
Gday 449049,

Let me try my best to give you an explanation.

The goal is to get the count of each organ present in the organs list. Look through the code to see that the values list is derived from the organs list. The functions organPairs, organCatalog are just playing around with the organs list.

In words, for each value that the Organ type can be, scan through the 'organs' list (equivalent to the values list) to see how many times each one appears.


It trick to understanding this code is to really focus on line 22:

organCounts = map countOrgan allOrgans

Handing 'allOrgans' to the map function makes Haskell step through each
value in allOrgans, passing in one value at a time to the 'countOrgan' function.

And 'allOrgans' is just a list of all the different values the Organ type can be:

allOrgans = [Heart, Brain, Kidney, Spleen]

Because there's 4 values in 'allOrgans', that means the 'countOrgan' function is called 4 times by map:

1. 'countOrgan' is given Heart as its argument, then it runs its code in the body of the lambda function.
2. 'countOrgan' is given Brain as its argument, then it runs its code in the body of the lambda function.
3. 'countOrgan' is given Kidney as its argument, then it runs its code in the body of the lambda function.
4. 'countOrgan' is given Spleen as its argument, then it runs its code in the body of the lambda function.

Let's focus on only understanding step 1 because, as you can see, steps 2,3, and 4, are similar.

The body of the lambda function in line 24 looks like this when Heart is substituted in:

(length . filter (== Heart)) values)

What does this do? For now forget about the length function because that acts last due to the function composition operator '.'

We're left with the filter function. It looks through the values list and returns all the Heart elements. Because the values list is [Heart, Heart, Brain,Spleen,Spleen,Kidney], that means after filtering we get a list like [Heart,Heart]. Finally the length function acts on [Heart,Heart] and we get the result of 2.

This is done for steps 2, 3, and 4, and we get the desired result, [2,1,1,2].

You can read this as "yo 449049, there are 2 Hearts, 1 Brain, 1 Kidney, and 2 Spleens in the organs list. Yours Truly, Haskell"
449049 (17) [Avatar] Offline
Thank you for taking the time to break this down. It cleared it all up. The function composition had me confused.