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)