I'm having little success wrapping my head around the basic plumbing of the types involved in the ad package. For example, the following works perfectly:
import Numeric.AD
ex :: Num a => [a] -> a
ex [x, y] = x + 2*y
> grad ex [1.0, 1.0]
[1.0, 2.0]
where grad has the type:
grad
:: (Num a, Traversable f) =>
(forall (s :: * -> *). Mode s => f (AD s a) -> AD s a)
-> f a -> f a
If I change the type signature of ex to [Double] -> Double and try the same thing, I get
Couldn't match expected type `AD s a0' with actual type `Double'
Expected type: f0 (AD s a0) -> AD s a0
Actual type: [Double] -> Double
The same behaviour occurs when replacing Double with seemingly any type constructor with kind * that instantiates Num.
When the Traversable f is a list, the first argument of grad must have type [AD s a] -> AD s a for some acceptable Mode - e.g., Reverse. But clearly the user of grad doesn't have to deal with the AD constructor or the Mode directly. Peeking into these internals have left me a bit confused; specifically, I can't follow the kind/type trail to the difference between using Num a => [a] -> a and [Double] -> Double.
Why does the type signature [Double] -> Double cause problems with grad? And in terms of plain old library use: is there any way to use the [Double] -> Double version of ex, or is a polymorphic version necessary?
(title inspired by this similar question)