haskellghcghcirank-n-types

RankNTypes in haskell. List comprehension works but map does not


I'd like to know specifically why map does not work in the following:

{-# Language RankNTypes #-}
module Demo where
import Numeric.AD

newtype Fun = Fun (forall a. Num a => [a] -> a)

test1 :: Fun 
test1 = Fun $ \[u, v] -> (v - (u * u * u)) 

test2 :: Fun 
test2 = Fun $ \ [u, v] -> ((u * u) + (v * v) - 1)

works :: (Ord a, Num a) => [[a] -> [a]]
works = [ grad f | Fun f <- fList ]

fList :: [Fun]
fList = [test1, test2]

pulling this into GHCI I get the following:

*Demo> w = [ grad f | Fun f <- fList ]
*Demo> map (\f -> f [1, 1]) w
[[-3,1],[2,2]]

which works nicely, however the following, which as far as I can tell should do the same thing, does not work. Why not? What is the issue here?

*Demo> map (\f -> grad (Fun f)) fList
<interactive>:31:18: error:
• Couldn't match expected type ‘f (Numeric.AD.Internal.Reverse.Reverse
                                     s a)
                                -> Numeric.AD.Internal.Reverse.Reverse s a’
              with actual type ‘Fun’
• Possible cause: ‘Fun’ is applied to too many arguments
  In the first argument of ‘grad’, namely ‘(Fun f)’
  In the expression: grad (Fun f)
  In the first argument of ‘map’, namely ‘(\ f -> grad (Fun f))’
• Relevant bindings include
    it :: [f a -> f a] (bound at <interactive>:31:1)

<interactive>:31:22: error:
• Couldn't match expected type ‘[a1] -> a1’ with actual type ‘Fun’
• In the first argument of ‘Fun’, namely ‘f’
  In the first argument of ‘grad’, namely ‘(Fun f)’
  In the expression: grad (Fun f)

the library I'm using is the haskell ad library: https://hackage.haskell.org/package/ad-4.3.4

Cheers!


Solution

  • These are not equivalent:

    *Demo> [ grad f | Fun f <- fList ]
    *Demo> map (\f -> grad (Fun f)) fList
    

    The first one, roughly, extracts a value from fList, say x, chooses f so that Fun f = x, and then calls grad f.

    The second one extracts a value from fList calling it f (!), and computes Fun f, and passes it to grad.

    So, the first removes the Fun wrapper from the list elements, the second adds the wrapper instead.

    Compare it to:

    *Demo> map (\ (Fun f) -> grad f) fList
    

    which would remove the wrapper as the list comprehension does.