I want to define a function f w/ 2 explicit arguments. The types of the arguments and value of f are applications of some g. Suppose the arguments' types are g x y and g z w. The tricky part is that f's value must depend on the unifiability of x and z. Below is a naive attempt at defining f that fails. How should I tackle this?
Inductive A := a | a0 | fa.
Inductive B := b | b0.
Parameter C: Type.
Parameter g: A -> B -> C.
Parameter CT:> C -> Type.
Parameter gab: g a b.
Parameter ga0b: g a0 b.
Definition f {x y z w}(n: g x y)(m: g z w) :=
ltac:(match x with z => exact (g z b) | _ => exact (g fa b) end).
Compute f gab ga0b. (*= g fa b: C*)
Compute f gab gab. (*! = g fa b: C !*)
f's value must depend on the unifiability of x and z
It is impossible to write such a definition. Suppose you could write a function that could tell whether or not two natural numbers unified, call it unify_nat : nat -> nat -> bool. Consider now the functions
F := fun x y : nat => if unify_nat (x + y) (y + x) then True else False
G := fun x y : nat => if unify_nat (x + y) (x + y) then True else False
We can prove, because addition is commutative, that forall x y, F x y = G x y. But then unify_nat (x + y) (y + x) must return true, even though x + y and y + x do not unify.
There are two things you can do: (1) you can ask not if the terms unify, but if they are propositionally equal; and (2) you can write a notation, or an alias, which is like syntactic sugar for a definition.
1.
The command Scheme Equality will generate an equality decision function for most inductive types:
Scheme Equality for A.
Definition f {x y z w}(n: g x y)(m: g z w) :=
if A_beq x z then g z b else g fa b.
Compute f gab ga0b. (*= g fa b: C*)
Compute f gab gab. (*= g a b: C*)
2.
We can create evars with open_constr:(_) and use unification to infer the types of n and m:
Ltac f_tac n m :=
let x := open_constr:(_) in
let y := open_constr:(_) in
let z := open_constr:(_) in
let w := open_constr:(_) in
let n := constr:(n : g x y) in
let m := constr:(m : g z w) in
match x with z => exact (g z b) | _ => exact (g fa b) end.
Notation f n m := (ltac:(f_tac n m)) (only parsing).
Compute f gab ga0b. (*= g fa b: C*)
Compute f gab gab. (*= g a b: C*)
Related
I'm trying to define a simple Haskell function with the type (m -> n -> l) -> (m -> n) -> m -> l.
I thought that it needs be to defined as f g h x = f g (h x), but apparently that's not true. How could I correct this function?
Based on the signature, the only sensical implementation is:
f :: (m -> n -> l) -> (m -> n) -> m -> l
f g h x = g x (h x)
This makes sense since we are given two functions g :: m -> n -> l and h :: m -> n, and a value x :: m. We should construct a value with type l. The only way to do this is to make use of the function g. For the parameter with type m we can work with x, for the second parameter we need a value of type n, we do not have such value, but we can construct one by applying h on x. Since h x has type h x :: n, we thus can use this as second parameter for g.
This function is already defined: it is a special case of (<*>) :: Applicative f => f (n -> l) -> f n -> f l with f ~ (->) m.
Djinn is a tool that reasons about types and thus is generating function definitions based on its signature. If one queries with f :: (m -> n -> l) -> (m -> n) -> m -> l, we get:
f :: (m -> n -> l) -> (m -> n) -> m -> l
f a b c = a c (b c)
which is the same function (except that it uses other variable names).
f :: (m -> n -> l) -> (m -> n) -> m -> l
f g h x = _
Now you need to use the arguments in some way.
g (_ :: m) (_ :: n) :: l
h (_ :: m) :: n
x :: m
Both g and h need a value of type m as their first argument. Well, luckily we have exactly one such value, so it's easy to see what to do.
g x (_ :: n) :: l
h x :: n
x :: m
So now g still needs a value of type n. Again we're lucky, because applying h to x has produced such a value.
g x (h x) :: l
h x :: n
x :: m
Ok, and there we now have something of type l, which is what we needed!
f g h x = g x (h x)
f :: (m -> n -> l) -> (m -> n) -> m -> l
f g h x = l
where
l =
what can produce an l for us? only g:
g
which takes two parameters, an m and an n,
m n
but where can we get those? Well, m we've already got,
m = x
and n we can get from h,
n = h
which needs an m
m
and where do we get an m? We've already got an m!
I know that (.) f g x = f (g x). Suppose f has type Int -> Int, g has type Int -> Int -> Int. Now let h be defined by h x y = f (g x y). Which of the following statements are true and why (why not)?
a. h = f . g
b. h x = f . (g x)
c. h x y = (f . g) x y
Supposedly, only b. is true, while the others are false. I would think a. and b. are equivalent... a. is saying two functions are equal. Two functions are equal only iff when I add an argument to the end of both sides, it will still be equal. So I get h x = f . g x. Now (.) is an operator, so functional application takes precedence over it, so f . g x = f . (g x), which is b.
This looks like a homework, so I won't give the answer of which one is correct.
You consider a and b as identical incorrectly. If f . g is applied to x, you get (from the definition of (.))
(f . g) x = f (g x)
But b is f . (g x), which doesn't expand to f (g x). If you follow b through using the definition of (.), you will see the sense in the comments of the others.
Function composition's initial definition is a bit confusing, so I'll write it a different way:
f . g = \a -> f (g a)
This means that f . g returns a function which first applies the argument to g, then applies the result of that to f. This is also clear in the type signature:
(.) :: (b -> c) -> (a -> b) -> (a -> c)
Now for your function h, it has a type of something like h :: a -> a -> a. Remember that -> is right-associative, so the type could be written as h :: a -> (a -> a). Essentially, h :: a -> b, though this type would cause an error because b and a are uninferrable. (.) will only allow one argument to be applied to the first function, so:
-- One could write,
h x y = f (g x y)
-- But One could also write
h x = f . g x
-- Or, more clearly:
h x = (f) . (g x)
This is because Haskell functions are curried, so we can apply some arguments to g without fully evaluating it.
If we imagine what would happen if we applied (.) visually, then simplify, we can see how it works:
h x = \a -> f ((g x) a)
h x = \a -> f (g x a)
h x a = f (g x a)
So yes, b is the answer. This is because (.) only allows one argument to be applied to the first function before moving to the next.
Now you're job can be tackling the other incorrect solutions by simplifying as I have. It's not too difficult.
flip' :: (a -> b -> c) -> (b -> a -> c)
flip' f = g
where g x y = f y x
I want to ask that in the above flip' function , it seems that flip' will return a function named g and g x y share the same value with f y x
however, in " where g x y = f y x " , f y x is a function call and will return a value, g x y will aslo return a value, so does it make sense that " where v2 = v1 "?
I know that the code will work but I want to know more about the way it make this happen.
does anybody has an idea? thank you so much
In where g x y = f y x, g x y is not a function call g with parameters x and y. It is a declaration of the function g as a function taking 2 arguments x and y and evaluation to f y x.
So it means flip' given a function f taking 2 arguments will evaluate to g. g itself being defined as swapping two arguments to call f.
Suppose I have two functions, f:X->Y and g:Y*Y->Z.
I want to make a third function, h(a, b) = g(f(a), f(b)).
h a b = g (f a) (f b)
Is there any way to write it like h(a, b) = g*f (a, b)?
And what if h(a,b,c,d) = g2*g1*f2*f1 (a,b,c,d), where g_i takes 2 args?
Searching Hoogle for functions with the right signature reveals on from Data.Function. According to its documentation,
g `on` f
seems to be what you want.
The on combinator (in Data.Function, as pointed out by gspr in another answer) is defined by
g `on` f = \x y -> g (f x) (f y)
Which would allow you to write
h = g `on` f
You can make higher-dimensional generalizations of this, for example
g `on3` f = \x y z -> g (f x) (f y) (f z)
g `on4` f = \w x y z -> g (f w) (f x) (f y) (f z)
So that you could write
h = g `on3` f
There may be a way to write on3 and on4 in terms of on, but if there is I can't see it at the moment.
You may also find Arrows interesting. Here's one way to do it:
h g f a b = uncurry g ((f *** f) (a, b))
Which is equivalent to your example (except that g and f are not free) and on. Using:
definition of *** for functions:
(***) f g ~(x,y) = (f x, g y)
definition of uncurry:
uncurry f p = f (fst p) (snd p)
And substituting them into the original equation:
h g f a b = uncurry g (f a, f b) (used *** definition)
h g f a b = g (f a) (f b) (used uncurry definition)
Assuming the following definitions (the first two are taken from http://www.cis.upenn.edu/~bcpierce/sf/Basics.html):
Fixpoint beq_nat (n m : nat) : bool :=
match n with
| O => match m with
| O => true
| S m' => false
end
| S n' => match m with
| O => false
| S m' => beq_nat n' m'
end
end.
Fixpoint ble_nat (n m : nat) : bool :=
match n with
| O => true
| S n' =>
match m with
| O => false
| S m' => ble_nat n' m'
end
end.
Definition blt_nat (n m : nat) : bool :=
if andb (ble_nat n m) (negb (beq_nat n m)) then true else false.
I would like to prove the following:
Lemma blt_nat_flip0 : forall (x y : nat),
blt_nat x y = false -> ble_nat y x = true.
Lemma blt_nat_flip : forall (x y : nat),
blt_nat x y = false -> beq_nat x y = false -> blt_nat y x = true.
The furthest I was able to get to is to prove blt_nat_flip assuming blt_nat_flip0. I spent a lot of time and I am almost there but overall it seems more complex than it should be. Anybody has a better idea on how to prove the two lemmas?
Here is my attempt so far:
Lemma beq_nat_symmetric : forall (x y : nat),
beq_nat x y = beq_nat y x.
Proof.
intros x. induction x.
intros y. simpl. destruct y.
reflexivity. reflexivity.
intros y. simpl. destruct y.
reflexivity.
simpl. apply IHx.
Qed.
Lemma and_negb_false : forall (b1 b2 : bool),
b2 = false -> andb b1 (negb b2) = b1.
Proof.
intros. rewrite -> H. unfold negb. destruct b1.
simpl. reflexivity.
simpl. reflexivity.
Qed.
Lemma blt_nat_flip0 : forall (x y : nat),
blt_nat x y = false -> ble_nat y x = true.
Proof.
intros x.
induction x.
intros. destruct y.
simpl. reflexivity.
simpl. inversion H.
intros. destruct y. simpl. reflexivity.
simpl. rewrite -> IHx. reflexivity.
(* I am giving up for now at this point ... *)
Admitted.
Lemma blt_nat_flip : forall (x y : nat),
blt_nat x y = false -> beq_nat x y = false ->
blt_nat y x = true.
Proof.
intros.
unfold blt_nat.
rewrite -> beq_nat_symmetric. rewrite -> H0.
rewrite -> and_negb_false.
replace (ble_nat y x) with true.
reflexivity.
rewrite -> blt_nat_flip0. reflexivity. apply H. reflexivity.
Qed.
coq seems to have trouble doing an inversion on H in the last case of your induction, but if you unfold blt_nat before, it seems to work as intended.