7

I'm trying to use ekmett's linear library, and I'm having some trouble with variable length vectors, in Linear.V. How do I use the dim function to get the size of a vector? How do I use trace on a large square matrix made of nested Vs ? I get errors in both of these cases.

Minimal code:

import qualified Data.Vector as Vector
import Linear.V (V(V), dim)
import Linear.Vector (outer)
import Linear.Matrix (trace)

v, w :: V n Double -- What do I do here?
v = V $ Vector.fromList [1..5]
w = V $ Vector.fromList [2, 3, 5, 7, 11]

d = dim v
m = outer v w
t = trace m

It gives these errors which I don't understand:

• Ambiguous type variable ‘n0’ arising from a use of ‘dim’
  prevents the constraint ‘(Linear.V.Dim n0)’ from being solved.
  Probable fix: use a type annotation to specify what ‘n0’ should be.
  These potential instances exist:
    two instances involving out-of-scope types
    (use -fprint-potential-instances to see them all)
• In the expression: dim v
  In an equation for ‘d’: d = dim v

• Ambiguous type variable ‘n1’ arising from a use of ‘trace’
  prevents the constraint ‘(Linear.V.Dim n1)’ from being solved.
  Probable fix: use a type annotation to specify what ‘n1’ should be.
  These potential instances exist:
    two instances involving out-of-scope types
    (use -fprint-potential-instances to see them all)
• In the expression: trace m
  In an equation for ‘t’: t = trace m
duplode
  • 33,731
  • 7
  • 79
  • 150
Christian Oudard
  • 48,140
  • 25
  • 66
  • 69
  • You shouldn't use the `V` constructor directly, as you can easily violate the invariant that the vector length and the type level index indicating the length do not match. Use `Linear.V.fromVector (fromList [1..5]) :: V 5 Double` - note that here if you specify the wrong length at the type level then this function gives you `Nothing`, and with this type `dim` will be able to satisfy the `Dim n` (`Dim 5` in this case) constraint. If you actually want the type `exists length . V length X` then that type is precisely `Vector X`. – user2407038 Feb 15 '17 at 01:37
  • 1
    Hmm. @user2407038 is right, but there is something fishy about `trace` - for some reason I can't seem to convince GHC that the `Trace` constraint is satisfied... – Alec Feb 15 '17 at 02:06

1 Answers1

5

Because Haskell is not dependently typed, it can't lift to the type level the length of a list it might be getting only at runtime. With that said, the purpose of the n is that you can make code that is polymorphic over the size of the vectors (for example you can make sure your aren't taking the dot product of vector that have different lengths). But you still need to explicitly specify the length of your actual vectors at compile time, if you want to use that information.

What linear does give you is fromVector which performs at runtime a check that the vector you gave matches the type you specified. For example,

ghci> :set +t -XDataKinds -XOverloadedLists
ghci> import Linear
ghci> import Linear.V
ghci> fromVector [1,2,3] :: Maybe (V 3 Int)
Just (V {toVector = [1,2,3]})
it :: Maybe (V 3 Int)
ghci> fromVector [1,2,3] :: Maybe (V 2 Int)
Nothing
it :: Maybe (V 3 Int)

So, in your case, you should probably be doing something like the following:

ghci> Just v = fromVector [1..5]           :: Maybe (V 5 Double)
v :: V 5 Double
ghci> Just w = fromVector [2, 3, 5, 7, 11] :: Maybe (V 5 Double)
w :: V 5 Double
ghci> dim v
5
it :: Int
ghci> m = outer v w
m :: V 5 (V 5 Double)
ghci> trace m
<interactive>:44:1: error:
   • No instance for (Trace (V 5)) arising from a use of ‘trace’
   • In the expression: trace m
     In an equation for ‘it’: it = trace m

...annnnd yeah - I think the last interaction is a bug (unless someone can see something I'm missing). The Trace (V 5) variant should be satisfiable though the Dim n => Trace (V n) instance but for some reason it isn't.

EDIT

As @user2407038 has pointed out, the problem is that the Dim n => Trace (V n) I mentioned above is not polykinded - it only works for n :: * while we would like it to work for any kind (specifically n :: Nat in this case). There isn't really any reason for this restriction, so we can go ahead and define our own version of the instance

ghci> :set -XPolyKinds
ghci> instance Dim n => Trace (V n)
ghci> trace m
106.0

I've opened an issue.

EDIT 2

The issue is now fixed. I reckon it should probably make it into the next release of linear.


As a side note, I use -XDataKinds so that I am allowed to write type-level literals (which have kind GHC.TypeLits.Nat - they are special and hardwired into GHC) and -XOverloadedLists so I can write [1..5] :: Vector Int.

Alec
  • 31,829
  • 7
  • 67
  • 114
  • 2
    `Trace (V 5)` isn't satisfied because the kinds are wrong, the instance is actually `Dim * n => Trace (V * n)` but you would need `Dim Nat n => Trace (V Nat n)` or more generally `Dim k n => Trace (V k n)`. Presumably this is just a bug, but you could still declare the appropriate instance yourself as a quick fix. – user2407038 Feb 15 '17 at 02:18
  • 1
    @user2407038 Ah yes. Nice catch - I suspected something of the sort but hadn't quite gotten there. I'll edit the answer and maybe also open a bug. – Alec Feb 15 '17 at 02:29
  • I'm shocked I actually stumbled on a bug in this. I was sure I just was doing it wrong. Thanks so much! – Christian Oudard Feb 15 '17 at 05:41