I am trying to feel my way into dependent types. Based on the logic of the windowl function below, I want to return a list of vectors whose length depend on the size provided.
window : (n : Nat) -> List a -> List (Vect n a)
window size = map fromList loop
where
loop xs = case splitAt size xs of
(ys, []) => if length ys == size then [ys] else []
(ys, _) => ys :: loop (drop 1 xs)
windowl : Nat -> List a -> List (List a)
windowl size = loop
where
loop xs = case List.splitAt size xs of
(ys, []) => if length ys == size then [ys] else []
(ys, _) => ys :: loop (drop 1 xs)
When I attempt to load the function into Idris, I get the following:
When checking argument func to function Prelude.Functor.map:
Type mismatch between
(l : List elem) -> Vect (length l) elem (Type of fromList)
and
a1 -> List (Vect size a) (Expected type)
Specifically:
Type mismatch between
Vect (length v0) elem
and
List (Vect size a)
When reading the documentation on fromList I notice that it says
The length of the list should be statically known.
So I assume that the type error has to do with Idris not knowing that the length of the list is corresponding to the size specified.
I am stuck because I don't even know if it is something impossible I want to do or whether I can specify that the length of the list corresponds to the length of the vector that I want to produce.
Is there a way to do that?
Since in your case it is not possible to know the length statically, we need a function which can fail at run-time:
total
fromListOfLength : (n : Nat) -> (xs : List a) -> Maybe (Vect n a)
fromListOfLength n xs with (decEq (length xs) n)
fromListOfLength n xs | (Yes prf) = rewrite (sym prf) in Just (fromList xs)
fromListOfLength n xs | (No _) = Nothing
fromListOfLength converts a list of length n into a vector of length n or fails. Now let's combine it and windowl to get to window.
total
window : (n : Nat) -> List a -> List (Vect n a)
window n = catMaybes . map (fromListOfLength n) . windowl n
Observe that the window function's type is still an underspecification of what we are doing with the input list, because nothing prevents us from always returning the empty list (this could happen if fromListOfLength returned Nothing all the time).
Related
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
I need some help interpreting an error message regarding implicit arguments in Idris and why a small change fixes it. This is the code:
import Data.Vect
myReverse : Vect n elem -> Vect n elem
myReverse [] = []
myReverse {n} (x :: xs)
= let result = myReverse xs ++ [x] in
?rhs
It results in this error:
When checking left hand side of myReverse:
When checking an application of Main.myReverse:
Type mismatch between
Vect (S len) elem (Type of x :: xs)
and
Vect n elem (Expected type)
Specifically:
Type mismatch between
S len
and
n
However, replacing {n} with {n = S len}, the code type-checks.
I thought that using {n} was simply meant to bring the implicit n argument of the function into scope. Why would this result in an error?
What does the error message mean? The only interpretation I can think of is that the implicit argument n in the type is rewritten due to pattern-matching x::xs into S len, and Idris loses information that these are the same.
How does replacing it with {n = S len} work?
Your best bet in these cases is to use idris to do the programming for you. If you start with
myReverse : Vect n elem -> Vect n elem
myReverse {n} xs = ?myReverse_rhs
and now case split on xs you get
myReverse : Vect n elem -> Vect n elem
myReverse {n = Z} [] = ?myReverse_rhs_1
myReverse {n = (S len)} (x :: xs) = ?myReverse_rhs_2
So not only did idris do a case split on xs, but also on n, since for an empty vector the length must be Z, and for a nonempty vector it must be at least S len. Which also implies that xs is now of length len.
Since n is also on the right hand side of your function, it is obvious that you need to provide something for myReverse_rhs_2 which is of length S len which equals n when you did the pattern matching right.
In the error message idris doesn't know what n is, hence the message.
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)
Let's say we have an infinite list:
data InfList : Type -> Type where
(::) : (value : elem) -> Inf (InfList elem) -> InfList elem
And we want to have finite number of its elements:
getPrefix : (count : Nat) -> InfList a -> List a
getPrefix Z _ = []
getPrefix (S k) (value :: xs) = value :: getPrefix k (?rest)
So, what is left:
a : Type
k : Nat
value : a
xs : InfList a
--------------------------------------
rest : InfList a
It turned out that after pattern matching xs become InfList a instead of Inf (InfList a).
Is there a way to have xs delayed?
It seems to be delayed anyway.
If you execute :x getPrefix 10 one with
one : InfList Int
one = 1 :: one
you get 1 :: getPrefix 9 (1 :: Delay one)
I can't find it anymore in the documentation but idris seems to insert Delay automatically.
Just try to add Delay constructor manually. It's removed implicitly.
getPrefix : (count : Nat) -> InfList a -> List a
getPrefix Z _ = []
getPrefix (S k) (value :: Delay xs) = value :: getPrefix k xs
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.