4

I am reading the book https://www.packtpub.com/application-development/haskell-high-performance-programming and trying to figure out, what is the difference between those two functions:

This functions does memoize the intermediate numbers:

fib_mem :: Int -> Integer
fib_mem = (map fib [0..] !!)
            where fib 0 = 1
                  fib 1 = 1
                  fib n = fib_mem (n-2) + fib_mem (n-1) 

and this not:

fib_mem_arg :: Int -> Integer
fib_mem_arg x = map fib [0..] !! x
                where fib 0 = 1
                      fib 1 = 1
                      fib n = fib_mem_arg (n-2) + fib_mem_arg (n-1) 

The author tries to explain as following:

Running fib_mem_arg with anything but very small arguments, one can confirm it does no memoization. Even though we can see that map fib [0..] does not depend on the argument number and could be memorized, it will not be, because applying an argument to a function will create a new expression that cannot implicitly have pointers to expressions from previous function applications.

What does he mean with the sentence, that is bold marked? Could someone provide me a simple example?

Why fib_mem is a constant applicative form?

softshipper
  • 32,463
  • 51
  • 192
  • 400
  • But the bold sentence is wrong! After all, `fib_mem 30000` and `fib_mem 30001` are applications of functions to arguments, but the latter implicitly has pointers to the former and so whichever one you do first takes *significantly* longer. They're pointing their blaming finger at the wrong problem! – Daniel Wagner Feb 19 '19 at 14:06
  • 1
    See also: [What are constant applicative forms?](https://stackoverflow.com/q/8330756/791604). – Daniel Wagner Feb 19 '19 at 14:19
  • @DanielWagner Maybe a simple example would be not bad. – softshipper Feb 19 '19 at 14:20

1 Answers1

3

Why fib_mem is a constant applicative form?

Not fib_mem, but (map fib [0..] !!). It is a CAF because it is a partially applied function (!!). As such it is subject to memory retention.

(see also: What are super combinators and constant applicative forms?)

Since the type is monomorphic, it is retained in memory even between calls to fib_mem, in effect as if having map fib [0..] "floated" to the top level, as if defined as

fib_mem_m :: Int -> Integer
fib_mem_m = (the_list !!)
            where fib 0 = 1
                  fib 1 = 1
                  fib n = (the_list !! (n-2)) + (the_list !! (n-1))
the_list = map fib [0..]

If the type were polymorphic, the floating to top level wouldn't be possible, but it would still be retained for the duration of each call to fib_mem, as if defined as

fib_mem_p :: Num a => Int -> a
fib_mem_p = (the_list !!)
            where fib 0 = 1
                  fib 1 = 1
                  fib n = (the_list !! (n-2)) + (the_list !! (n-1))
                  the_list = map fib [0..]

To see the difference, evaluate fib_mem_m 10000 twice, at the GHCi propt. The second attempt will take 0 seconds. But fib_mem_p 10000 will take same amount of time each time it is called. It will still be as fast as the first one, so there is still memoization going on there, it's just not retained between calls.

With this style of definition, the full application as in fib_mem_arg will too be memoized -- and just as the one above, not between the calls to fib_mem_arg, but only during each call.

fib_mem_arg :: Num a => Int -> Integer  -- or polymorphic, makes no difference
fib_mem_arg x = the_list !! x
            where fib 0 = 1
                  fib 1 = 1
                  fib n = (the_list !! (n-2)) + (the_list !! (n-1))
                  the_list = map fib [0..]
Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • Why this is a CAF `z = (+) 4` and this not `z = \x -> 4 + x`? What is the different? – softshipper Feb 19 '19 at 15:06
  • by definition, lambda expression is not a CAF. that's what the wiki page is saying. – Will Ness Feb 19 '19 at 15:06
  • Super combinators look like for me functions composition, for instance `h(g (f x) )`. Is it right? – softshipper Feb 19 '19 at 15:24
  • and why `z n = [1..n]` is not CAF? `n` is a bound variable. – softshipper Feb 19 '19 at 15:26
  • `h(g (f x) )` is an expression with a lot of free variables. You need to show the left hand side of the equation (definition). ---- in `z n = [1..n]` the `n` on the RHS depends on `n` on the LHS, thus `z` is not CAF. indeed it can be called as `z 5` first, and then `z 3`. these will be two different values, not one. – Will Ness Feb 19 '19 at 16:32
  • `z n = [1..n]` is a lambda abstration? – softshipper Feb 19 '19 at 21:00
  • it is equivalent to `z = \n -> [1..n]`, yes. Not *eta*-equivalent like `((+) 4) =~= (\x -> ((+) 4) x) = (\x -> 4 + x)`, but *directly* equivalent. – Will Ness Feb 20 '19 at 08:21
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/188733/discussion-between-will-ness-and-zero-coding). – Will Ness Feb 20 '19 at 08:22