Search code examples
haskellpointfree

Multiplication tables via maps in pointfree Haskell


In Haskell I've defined a multiplication-table generator function like so:

multTable :: [[Int]]
multTable=map (\b -> map (b*) [1..12]) [1..12]

Which outputs...

[[1,2,3,4,5,6,7,8,9,10,11,12],
[2,4,6,8,10,12,14,16,18,20,22,24],
...[12,24,36,48,60,72,84,96,108,120,132,144]]

...and if you squint your eyes, this looks like multiplication tables for 1..12. So far so good ... now I've tried to make it point-free, thinking it should be pretty easy with composition. So I'm taking small steps and I believe that I've gotten partway there since I can do this:

map ($ 4) $ map (*) [1..12]

...which gives me:

[4,8,12,16,20,24,28,32,36,40,44,48]

Looking at this, the second map gives [a->a], which specifically here is a list of functions that multiply the values 1 through 12 by some number. The first map evaluates each of these functions providing them with the value 4, in turn producing the [4,8..] line.

I'm not necessarily expecting the pointfree version to be shorter, more concise or even more readable. As a Haskell newbie, I'm just looking to understand how it could be accomplished.


Solution

  • There's a tool called pointfree on hackage, which helps you tremendously (see also the wiki). It creates the following version:

    multTable = map (flip map [1..12] . (*)) [1..12]
    

    How do we get there? Well, lets move operators a little bit and try to apply flip on your function till we get something similar:

    multTable = map (\b -> map (b*) [1..12])           [1..12] 
              = map (\b -> map ((*) b) [1..12])        [1..12] -- change to prefix notation
              = map (\b -> flip map [1..12] ((*) b)    [1..12] -- flip map
              = map (\b -> flip map [1..12] . (*) $ b) [1..12] -- associativity
              = map (flip map [1..12] . (*))           [1..12] -- eta-reduction
    

    Note that we only need to focus on \b -> map (b*) [1..12].

    That being said, the pointfree version feels a little bit contrived. After all, you want a table, and Benjamin's shows a rather nice way to create one: list comprehensions:

    multTable = [[x * y | x <- [1..12]] | y <- [1..12]]
    

    This version is easier to read and more obvious to the reader, but then again, you probably thought that this would happen:

    I'm not necessarily expecting the pointfree version to be shorter, more concise or even more readable.