In Idris, what's the type of a list of lists of doubles, where all the lengths are known? - idris

An example value: [[1, 2], [1]]
Here, I would know that there are 2 lists, and that the first list has length 2, while the second one has length 1. Ideally, this function would compute those types:
func : (n ** Vect n Nat) -> Type
But I don't know how to write it. I'm pretty sure it's something to do with dependent pairs, but I'm not sure how to write it.
To clarify, I know it'd be possible to simply use (n ** Vect n (p ** Vect p Double)) as the type of the example value. However, n only constrains the number of lists, not the number of their elements, because inside the list, p could be anything. I would most likely need something where the first element of the dependent pair is a vector of lengths, not just the number of lists. So something like (Vect n Nat ** Vect n (Vect m Double))--where each m is the corresponding element of the first vector.

You could define a new vector type which contains possibly differently indexed elements of an indexed type at each position:
import Prelude
import Data.Vect
-- heterogeneously indexed vector
data IVect : Vect n ix -> (ix -> Type) -> Type where
Nil : IVect Nil b
(::) : b i -> IVect is b -> IVect (i :: is) b
-- of which a special case is a vector of vectors
VVect : Vect n Nat -> Type -> Type
VVect is a = IVect is (flip Vect a)
test1 : VVect [2, 2, 2] Nat
test1 = [[1, 2], [3, 4], [5, 6]]
test2 : VVect [0, 1, 2] Bool
test2 = [[], [True], [False, True]]
Alternatively, you can define VVect using dependent pairs and map, but this is more cumbersome to use:
VVect' : Vect n Nat -> Type -> Type
VVect' {n = n} is a = (xs : Vect n (m ** Vect m a) ** (map fst xs = is))
test3 : VVect' [0, 1, 2] Bool
test3 = ([(_ ** []), (_ ** [True]), (_ ** [False, False])] ** Refl)
You have some choice though whether to use lists or vectors. With lists as the inner container, values look more compact:
VVect'' : Vect n Nat -> Type -> Type
VVect'' {n = n} is a = (xs : Vect n (List a) ** (map length xs = is))
test4 : VVect'' [0, 1, 2] Bool
test4 = ([[], [True], [False, True]] ** Refl)

Related

Rewiring my own Vect but encountering issues

I was trying to re-implement the Vect data type to become more familiar with the dependent types. This is what I wrote:
data Vect : (len : Nat) -> (elem : Type) -> Type where
Nil : Vect Z elem
(::) : (x : elem) -> (xs : Vect len elem) -> Vect (S len) elem
append : Vect n elem -> Vect m elem -> Vect (n + m) elem
append [] y = y
append (x :: xs) y = x :: append xs y
I can define, for example Vect 4 Nat and others as well. But if I try append (Vect 4 Nat) (Vect 3 Nat) I get an error that I am not able to parse:
When checking an application of function Main.append:
Type mismatch between
Type (Type of Vect len elem)
and
Vect n elem (Expected type)
Clearly there is something wrong in the way I think about this.
Any suggestion?
Also when I try to create Vect 4 [1,2,3,4] I get an error:
When checking argument elem to type constructor Main.Vect:
Can't disambiguate since no name has a suitable type:
Prelude.List.::, Main.::, Prelude.Stream.::
So I guess I am quite lost ...
Your definition of Vect and append look fine to me, but it's how you're creating values that's the problem. You're mixing up the type constructor Vect with the data constructors Nil and ::. You should create values of type Vect len elem with calls to Nil and ::.
In particular, Vect 4 Nat is a type, but append expects a value of that type, like 1 :: 2 :: 3 :: 4 :: Nil (or [1,2,3,4] which is just syntactic sugar for the former).
And Vect 4 [1,2,3,4] isn't possible since [1,2,3,4] is a value not a Type

splitEvery function for Vect in Idris

In Idris I am trying to write a type-safe(r) version of a splitEvery function for Vect, that on Lists looks like this:
splitEvery : Nat -> List a -> List (List a)
splitEvery _ [] = []
splitEvery n xs = (take n xs) ++ splitEvery n (drop n xs)
So far I've got:
splitEvery : {k : Nat} -> (n : Nat) -> Vect (k * n) a -> Vect k (Vect n a)
splitEvery {k = Z} _ [] = []
splitEvery {k = (S j)} n xs = (take n xs) :: splitEvery {k = j} n (drop n xs)
which will type-check and load in the REPL alright, but when I try to invoke it, it will fail:
*Main> splitEvery 2 (fromList [1..10])
(input):1:1-31:When checking an application of function Main.splitEvery:
Type mismatch between
Vect (length (enumFromTo 1 10))
Integer (Type of fromList (enumFromTo 1 10))
and
Vect (k * 2) Integer (Expected type)
Specifically:
Type mismatch between
length (enumFromTo 1 10)
and
mult k 2
So obviously Idris isn't seeing that a valid choice of k here would be 5. A way to make it work would be to explicitly give it the implicit parameter k, but that's ugly:
*Main> splitEvery {k = 5} 2 (fromList [1..10])
[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] : Vect 5 (Vect 2 Integer)
So my question is: is there a way to make this work that isn't ugly, or is more idiomatic than what I've produced so far?
Idris is only failing to infer the value of k in the REPL for some reason. If you amend your code with
result: Vect 5 (Vect 2 Nat)
result = splitEvery 2 (fromList [1..10])
everything typechecks - as the type is already known and k can be infered. You can achieve the same on the REPL by providing a type hint using the:
the (Vect 5 (Vect 2 Nat)) (splitEvery 2 (fromList [1..10])) works as well.

Idris - map an operation on a n-dimensional vector

I defined n-dimensional vectors in Idris as follows:
import Data.Vect
NDVect : (Num t) => (rank : Nat) -> (shape : Vect rank Nat) -> (t : Type) -> Type
NDVect Z [] t = t
NDVect (S n) (x::xs) t = Vect x (NDVect n xs t)
Then I defined the following function which maps a function f to every entry in the tensor.
iterateT : (f : t -> t') -> (v : NDVect r s t) -> NDVect r s t'
iterateT {r = Z} {s = []} f v = f v
iterateT {r = S n} {s = x::xs} f v = map (iterateT f) v
But when I try to call iteratorT in the following function:
scale : Num t => (c : t) -> (v : NDVect rank shape t) -> NDVect rank shape t
scale c v = iterateT (*c) v
I get the following error message saying there is a type mismatched, which seems pretty fine to me
When checking right hand side of scale with expected type
NDVect rank shape t
When checking argument v to function Main.iterateT:
Type mismatch between
NDVect rank shape t (Type of v)
and
NDVect r s t (Expected type)
Specifically:
Type mismatch between
NDVect rank shape t
and
NDVect r s t
Specifically:
Type mismatch between
NDVect rank shape t
and
NDVect r s t
I have also been wondering how to express n-dimensional vectors (i.e. tensors) in Idris. I had a play with the type definition in the question, but encountered various issues, so I expressed the NDVect function as a data type:
data NDVect : (rank : Nat) -> (shape : Vect rank Nat) -> Type -> Type where
NDVZ : (value : t) -> NDVect Z [] t
NDV : (values : Vect n (NDVect r s t)) -> NDVect (S r) (n::s) t
And implemented map as follows:
nmap : (t -> u) -> (NDVect r s t) -> NDVect r s u
nmap f (NDVZ value) = NDVZ (f value)
nmap f (NDV values) = NDV (map (nmap f) values)
The following now works:
*Main> NDVZ 5
NDVZ 5 : NDVect 0 [] Integer
*Main> nmap (+4) (NDVZ 5)
NDVZ 9 : NDVect 0 [] Integer
*Main> NDV [NDVZ 1, NDVZ 2, NDVZ 3]
NDV [NDVZ 1, NDVZ 2, NDVZ 3] : NDVect 1 [3] Integer
*Main> nmap (+4) (NDV [NDVZ 1, NDVZ 2, NDVZ 3])
NDV [NDVZ 5, NDVZ 6, NDVZ 7] : NDVect 1 [3] Integer
Unfortunately, having all the type constructors makes things a bit ugly. I'd love to know if there's a cleaner way to solve this.
Edit:
Here's a slightly shorter type signature that doesn't explicitly encode the tensor rank in the type:
data NDVect : (shape : List Nat) -> Type -> Type where
NDVZ : (value : t) -> NDVect [] t
NDV : (values : Vect n (NDVect s t)) -> NDVect (n::s) t
nmap : (t -> u) -> (NDVect s t) -> NDVect s u
nmap f (NDVZ value) = NDVZ (f value)
nmap f (NDV values) = NDV (map (nmap f) values)

Dependent types: enforcing global properties in inductive types

I have the following inductive type MyVec:
import Data.Vect
data MyVec: {k: Nat} -> Vect k Nat -> Type where
Nil: MyVec []
(::): {k, n: Nat} -> {v: Vect k Nat} -> Vect n Nat -> MyVec v -> MyVec (n :: v)
-- example:
val: MyVec [3,2,3]
val = [[2,1,2], [0,2], [1,1,0]]
That is, the type specifies the lengths of all vectors inside a MyVec.
The problem is, val will have k = 3 (k is the number of vectors inside a MyVec), but the ctor :: does not know this fact. It will first build a MyVec with k = 1, then with 2, and finally with 3. This makes it impossible to define constraints based on the final shape of the value.
For example, I cannot constrain the values to be strictly less than k. Accepting Vects of Fin (S k) instead of Vects of Nat would rule out some valid values, because the last vectors (the first inserted by the ctor) would "know" a smaller value of k, and thus a stricter contraint.
Or, another example, I cannot enforce the following constraint: the vector at position i cannot contain the number i. Because the final position of a vector in the container is not known to the ctor (it would be automatically known if the final value of k was known).
So the question is, how can I enforce such global properties?
There are (at least) two ways to do it, both of which may require tracking additional information in order to check the property.
Enforcing properties in the data definition
Enforcing all elements < k
I cannot constrain the values to be strictly less than k. Accepting Vects of Fin (S k) instead of Vects of Nat would rule out some valid values...
You are right that simply changing the definition of MyVect to have Vect n (Fin (S k)) in it would not be correct.
However, it is not too hard to fix this by generalizing MyVect to be polymorphic, as follows.
data MyVec: (A : Type) -> {k: Nat} -> Vect k Nat -> Type where
Nil: {A : Type} -> MyVec A []
(::): {A : Type} -> {k, n: Nat} -> {v: Vect k Nat} -> Vect n A -> MyVec A v -> MyVec A (n :: v)
val : MyVec (Fin 3) [3,2,3]
val = [[2,1,2], [0,2], [1,1,0]]
The key to this solution is separating the type of the vector from k in the definition of MyVec, and then, at top level, using the "global value of k to constrain the type of vector elements.
Enforcing vector at position i does not contain i
I cannot enforce that the vector at position i cannot contain the number i because the final position of a vector in the container is not known to the constructor.
Again, the solution is to generalize the data definition to keep track of the necessary information. In this case, we'd like to keep track of what the current position in the final value will be. I call this index. I then generalize A to be passed the current index. Finally, at top level, I pass in a predicate enforcing that the value does not equal the index.
data MyVec': (A : Nat -> Type) -> (index : Nat) -> {k: Nat} -> Vect k Nat -> Type where
Nil: {A : Nat -> Type} -> {index : Nat} -> MyVec' A index []
(::): {A : Nat -> Type} -> {k, n, index: Nat} -> {v: Vect k Nat} ->
Vect n (A index) -> MyVec' A (S index) v -> MyVec' A index (n :: v)
val : MyVec' (\n => (m : Nat ** (n == m = False))) 0 [3,2,3]
val = [[(2 ** Refl),(1 ** Refl),(2 ** Refl)], [(0 ** Refl),(2 ** Refl)], [(1 ** Refl),(1 ** Refl),(0 ** Refl)]]
Enforcing properties after the fact
Another, sometimes simpler way to do it, is to not enforce the property immediately in the data definition, but to write a predicate after the fact.
Enforcing all elements < k
For example, we can write a predicate that checks whether all elements of all vectors are < k, and then assert that our value has this property.
wf : (final_length : Nat) -> {k : Nat} -> {v : Vect k Nat} -> MyVec v -> Bool
wf final_length [] = True
wf final_length (v :: mv) = isNothing (find (\x => x >= final_length) v) && wf final_length mv
val : (mv : MyVec [3,2,3] ** wf 3 mv = True)
val = ([[2,1,2], [0,2], [1,1,0]] ** Refl)
Enforcing vector at position i does not contain i
Again, we can express the property by checking it, and then asserting that the value has the property.
wf : (index : Nat) -> {k : Nat} -> {v : Vect k Nat} -> MyVec v -> Bool
wf index [] = True
wf index (v :: mv) = isNothing (find (\x => x == index) v) && wf (S index) mv
val : (mv : MyVec [3,2,3] ** wf 0 mv = True)
val = ([[2,1,2], [0,2], [1,1,0]] ** Refl)

Idris non-trivial type computation for tensor indexing

I've been messing around with a simple tensor library, in which I have defined the following type.
data Tensor : Vect n Nat -> Type -> Type where
Scalar : a -> Tensor [] a
Dimension : Vect n (Tensor d a) -> Tensor (n :: d) a
The vector parameter of the type describes the tensor's "dimensions" or "shape". I am currently trying to define a function to safely index into a Tensor. I had planned to do this using Fins but I ran into an issue. Because the Tensor is of unknown order, I could need any number of indices, each of which requiring a different upper bound. This means that a Vect of indices would be insufficient, because each index would have a different type. That drove me to look at using tuples (called "pairs" in Idris?) instead. I wrote the following function to compute the necessary type.
TensorIndex : Vect n Nat -> Type
TensorIndex [] = ()
TensorIndex (d::[]) = Fin d
TensorIndex (d::ds) = (Fin d, TensorIndex ds)
This function worked as I expected, calculating the appropriate index type from a dimension vector.
> TensorIndex [4,4,3] -- (Fin 4, Fin 4, Fin 3)
> TensorIndex [2] -- Fin 2
> TensorIndex [] -- ()
But when I tried to define the actual index function...
index : {d : Vect n Nat} -> TensorIndex d -> Tensor d a -> a
index () (Scalar x) = x
index (a,as) (Dimension xs) = index as $ index a xs
index a (Dimension xs) with (index a xs) | Tensor x = x
...Idris raised the following error on the second case (oddly enough it seemed perfectly okay with the first).
Type mismatch between
(A, B) (Type of (a,as))
and
TensorIndex (n :: d) (Expected type)
The error seems to imply that instead of treating TensorIndex as an extremely convoluted type synonym and evaluating it like I had hoped it would, it treated it as though it were defined with a data declaration; a "black-box type" so to speak. Where does Idris draw the line on this? Is there some way for me to rewrite TensorIndex so that it works the way I want it to? If not, can you think of any other way to write the index function?
Your definitions will be cleaner if you define Tensor by induction over the list of dimensions whilst the Index is defined as a datatype.
Indeed, at the moment you are forced to pattern-match on the implicit argument of type Vect n Nat to see what shape the index has. But if the index is defined directly as a piece of data, it then constrains the shape of the structure it indexes into and everything falls into place: the right piece of information arrives at the right time for the typechecker to be happy.
module Tensor
import Data.Fin
import Data.Vect
tensor : Vect n Nat -> Type -> Type
tensor [] a = a
tensor (m :: ms) a = Vect m (tensor ms a)
data Index : Vect n Nat -> Type where
Here : Index []
At : Fin m -> Index ms -> Index (m :: ms)
index : Index ms -> tensor ms a -> a
index Here a = a
index (At k i) v = index i $ index k v
Your life becomes so much easier if you allow for a trailing () in your TensorIndex, since then you can just do
TensorIndex : Vect n Nat -> Type
TensorIndex [] = ()
TensorIndex (d::ds) = (Fin d, TensorIndex ds)
index : {ds : Vect n Nat} -> TensorIndex ds -> Tensor ds a -> a
index {ds = []} () (Scalar x) = x
index {ds = _ :: ds} (i, is) (Dimension xs) = index is (index i xs)
If you want to keep your definition of TensorIndex, you'll need to have separate cases for ds = [_] and ds = _::_::_ to match the structure of TensorIndex:
TensorIndex : Vect n Nat -> Type
TensorIndex [] = ()
TensorIndex (d::[]) = Fin d
TensorIndex (d::ds) = (Fin d, TensorIndex ds)
index : {ds : Vect n Nat} -> TensorIndex ds -> Tensor ds a -> a
index {ds = []} () (Scalar x) = x
index {ds = _ :: []} i (Dimension xs) with (index i xs) | (Scalar x) = x
index {ds = _ :: _ :: _} (i, is) (Dimension xs) = index is (index i xs)
The reason this works and yours didn't is because here, each case of index corresponds exactly to one TensorIndex case, and so TensorIndex ds can be reduced.