What's the meaning of this (a, b, c: Nat) argument in:
g : (a, b, c: Nat) -> Int
g (a,b,c) = 42
?
Evidently the first argument is a triple, i.e. 3-tuple.
g: (a,b,c: Nat) -> Int
is just a short cut for
g: (a: Nat) -> (b: Nat) -> (c: Nat) -> Int
If you expand on g: (a,b,c: Nat) -> Int you will get
g: (a, b, c: Nat) -> Int
g a b c = ?g_rhs
A named tuple parameter (AFAIK idris does not have built-in triples) would be specified as
g: (a: (Nat, Nat)) -> Int
g a = ?g_rhs
Related
I would like to define a function of the following type
pairEquality :
(a, b : (obj1, obj2))
-> (fst a) = (fst b)
-> (snd a) = (snd b)
-> a = b
but I am a bit at loss with the implementation.
I know I can write
pairEquality' :
a1 = b1
-> a2 = b2
-> (a1, a2) = (b1, b2)
but that doesn't seem to compile where I need to use it (that's another question: what's the big difference between the two functions?)
The first implementation is pretty straight-forward. Once you split the tuples to get pairEquality (a, b) (x, y) prf1 prf2 = ?t and inspect the the hole, you already see, that the compiler infers that prf1 : a = x, prf2: b = y, alas:
pairEquality :
(a, b : (obj1, obj2))
-> (fst a) = (fst b)
-> (snd a) = (snd b)
-> a = b
pairEquality (x, y) (x, y) Refl Refl = Refl
In pairEquality you de-construct the tuples and in pairEquality' you construct the tuples. Latter is usually a better approach, and I guess you can change something in your caller function so it can be used.
I actually find out I can define pairEquality in terms of pairEquality' as
pairEquality :
(a, b : (obj1, obj2))
-> (fst a) = (fst b)
-> (snd a) = (snd b)
-> a = b
pairEquality (a1, a2) (b1, b2) = pairEquality' {a1} {a2} {b1} {b2}
This fails:
> the ({A : Type} -> A -> {B : Type} -> B -> (A, B)) MkPair
(input):1:5:When checking argument value to function Prelude.Basics.the:
Type mismatch between
A -> B1 -> (A, B1) (Type of MkPair)
and
A1 -> B -> (A1, B) (Expected type)
Specifically:
Type mismatch between
Pair A
and
\uv => uv -> uv
This works:
> ({A : Type} -> {B : Type} -> A -> B -> (A, B)) MkPair
\A1, B1 => MkPair : A -> B -> (A, B)
Oddly:
q : {A : Type} -> A -> {B : Type} -> B -> (A, B)
q a b = MkPair a b
> :t q
q : A -> B -> (A, B)
> :t MkPair
MkPair : A -> B -> (A, B)
Why do q and MkPair appear to have the same type? Do they really have the same type? Why does the order of implicit arguments matter?
In some sense implicit arguments are no different from non-implicit ones. The compiler infers them for you most of the time, but they are still arguments and must be present because at the level of the core language there are no implicit arguments. You can ask REPL to show implicits for you:
λΠ> :set showimplicits
λΠ> :t MkPair
Builtins.MkPair : {A : Type} -> {B : Type} -> (a : A) -> (b : B) -> (A, B)
λΠ> :t q
Main.q : {A : Type} -> A -> {B : Type} -> B -> (A, B)
If you substitute ordinary parentheses for curly braces in the above types, you'll see that the types of MkPair and q are different because of the different order of their parameters.
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)
Inspired by this blog post and this code I thought I'd try some category theory in Idris using its interfaces (type-classes).
I defined Category as follows, which works fine:
interface Category (obj : Type) (hom : obj -> obj -> Type) where
id : {a : obj} -> hom a a
comp : {a, b, c : obj} -> hom b c -> hom a b -> hom a c
Then, in the spirit of the Verified module, I thought I'd define a verified category:
interface Category obj hom =>
VerifiedCategory (obj : Type) (hom : obj -> obj -> Type) where
categoryAssociativity : {a, b, c, d : obj} ->
(f : hom c d) -> (g : hom b c) -> (h : hom a b) ->
(comp (comp f g) h = comp f (comp g h))
categoryLeftIdentity : {a, b : obj} -> (f : hom a b) -> (comp id f = f)
categoryRightIdentity : {a, b : obj} -> (f : hom a b) -> (comp f id = f)
Unfortunately, Idris rejects that code with the following error message:
When checking type of constructor of CategoryTheory.VerifiedCategory:
Can't find implementation for Category obj hom
Am I doing something wrong, or am I trying to do something with multi-parameter sub-classes that Idris cannot do?
All this code is in its own module, called CategoryTheory, without any imports.
I'm working with Idris v0.12.
I don't know why (and would be very curious to find out!) but it works if you specify all the implicit arguments to id and comp in VerifiedCategory explicitly. It is pretty ugly and tedious to figure out, but this typechecks:
interface Category obj hom => VerifiedCategory (obj : Type) (hom : obj -> obj -> Type) where
categoryAssociativity : {a, b, c, d : obj} ->
(f : hom c d) ->
(g : hom b c) ->
(h : hom a b) ->
(comp {a = a} {b = b} {c = d} (comp {a = b} {b = c} {c = d} f g) h = comp {a = a} {b = c} {c = d} f (comp {a = a} {b = b} {c = c} g h))
categoryLeftIdentity : {a, b : obj} ->
(f : hom a b) ->
(comp {a = a} {b = b} {c = b} (id {a = b}) f = f)
categoryRightIdentity : {a, b : obj} ->
(f : hom a b) ->
(comp {a = a} {b = a} {c = b} f (id {a = a}) = f)
Edit: Another way I just found is to explicitly designate hom as a determining parameter, i.e. the parameter of the type class that is sufficient to find an implementation. This has to happen in Category as well as in VerifiedCategory (I'm not sure, why), like so:
interface Category (obj : Type) (hom : obj -> obj -> Type) | hom where
-- ...
interface Category obj hom => VerifiedCategory (obj : Type) (hom : obj -> obj -> Type) where
-- ...
i.e. by putting a | hom before the where.
One thing you have to do in addition to that is qualify the usage of id in VerifiedCategory, because otherwise it is interpreted as implicit parameter for whatever reason:
categoryLeftIdentity : {a, b : obj} ->
(f : hom a b) ->
comp CategoryTheory.id f = f
categoryRightIdentity : {a, b : obj} ->
(f : hom a b) ->
comp f CategoryTheory.id = f
See also this reddit thread that might be illuminating in the future.
In order for my question to be meaningful, I must provide some background.
I think it would be useful to have a dependently typed language that can infer the existence and type of an argument a for a function whose other parameters and/or return value have types that depend on a. Consider the following snippet in a language I am designing:
(* Backticks are used for infix functions *)
def Cat (`~>` : ob -> ob -> Type) :=
sig
exs id : a ~> a
exs `.` : b ~> c -> a ~> b -> a ~> c
exs lid : id . f = f
exs rid : f . id = f
exs asso : (h . g) . f = h . (g . f)
end
If we make two (admittedly, unwarranted) assumptions:
No dependencies must exist that cannot be inferred from explicitly provided information.
Every free variable must be converted into an implicit argument of the last identifier introduced using def or exs.
We can interpret the above snippet as being equivalent to the following one:
def Cat {ob} (`~>` : ob -> ob -> Type) :=
sig
exs id : all {a} -> a ~> a
exs `.` : all {a b c} -> b ~> c -> a ~> b -> a ~> c
exs lid : all {a b} {f : a ~> b} -> id . f = f
exs rid : all {a b} {f : a ~> b} -> f . id = f
exs asso : all {a b c d} {f : a ~> b} {g} {h : c ~> d}
-> (h . g) . f = h . (g . f)
end
Which is more or less the same as the following Agda snippet:
record Cat {ob : Set} (_⇒_ : ob → ob → Set) : Set₁ where
field
id : ∀ {a} → a ⇒ a
_∙_ : ∀ {a b c} → b ⇒ c → a ⇒ b → a ⇒ c
lid : ∀ {a b} {f : a ⇒ b} → id ∙ f ≡ f
rid : ∀ {a b} {f : a ⇒ b} → f ∙ id ≡ f
asso : ∀ {a b c d} {f : a ⇒ b} {g} {h : c ⇒ d} → (h ∙ g) ∙ f ≡ h ∙ (g ∙ f)
Clearly, two unwarranted assumptions have saved us a lot of typing!
Note: Of course, this mechanism only works as long as the original assumptions hold. For example, we cannot correctly infer the implicit arguments of the dependent function composition operator:
(* Only infers (?2 -> ?3) -> (?1 -> ?2) -> (?1 -> ?3) *)
def `.` g f x := g (f x)
In this case, we have to explicitly provide some additional information:
(* If we omitted {x}, it would become an implicit argument of `.` *)
def `.` (g : all {x} (y : B x) -> C y) (f : all x -> B x) x := g (f x)
Which can be expanded into the following:
def `.` {A} {B : A -> Type} {C : all {x} -> B x -> Type}
(g : all {x} (y : B x) -> C y) (f : all x -> B x) x := g (f x)
Here is the equivalent Agda definition, for comparison:
_∘_ : ∀ {A : Set} {B : A → Set} {C : ∀ {x} → B x → Set}
(g : ∀ {x} (y : B x) → C y) (f : ∀ x → B x) x → C (f x)
(g ∘ f) x = g (f x)
End of Note
Is the mechanism described above feasible? Even better, is there any language that implements something resembling this mechanism?
This sounds like implicit generalization in Coq.