I am following the Functional Programming in Scala lecture on Coursera and at the end of the video 5.7, Martin Odersky asks to prove by induction the correctness of the following equation :
(xs ++ ys) map f = (xs map f) ++ (ys map f)
How to handle proof by induction when there are multiple lists involved ?
I have checked the base cases of xs being Nil and ys being Nil.
I have proven by induction that the equation holds when xs is replaced by x::xs, but do we also need to check the equation with ys replaced by y::ys ?
And in that case (without spoiling the exercise too much...which is not graded anyway) how do you handle : (xs ++ (y::ys)) map f ?
This is the approach I have used on a similar example, to prove that
(xs ++ ys).reverse = ys.reverse ++ xs.reverse
Proof (omitting the base case, and easy x::xs case) :
(xs ++ (y::ys)).reverse
= (xs ++ (List(y) ++ ys)).reverse //y::ys = List(y) ++ ys
= ((xs ++ List(y)) ++ ys).reverse //concat associativity
= ys.reverse ++ (xs ++ List(y)).reverse //by induction hypothesis (proven with x::xs)
= ys.reverse ++ List(y).reverse ++ xs.reverse //by induction hypothesis
= ys.reverse ++ (y::Nil).reverse ++ xs.reverse //List(y) = y :: Nil
= ys.reverse ++ Nil.reverse ++ List(y) ++ xs.reverse //reverse definition
= (ys.reverse ++ List(y)) ++ xs.reverse //reverse on Nil (base case)
= (y :: ys).reverse ++ xs.reverse //reverse definition
Is this right ?
The property involves multiple lists, but ++ only recurses on its left argument. That's a hint that you can prove by induction on that left argument. In general, when proving a proposition about some recursive function, the first thing you try is inducting on the same argument that function recurses on.
I'll do this one for you as an example:
Claim: (xs ++ ys) map f = (xs map f) ++ (ys map f)
Proof: by induction on xs.
Base case: xs = Nil
lhs = (Nil ++ ys) map f = ys map f
(by ++'s definition)
rhs = (Nil map f) ++ (ys map f) = Nil ++ ys map f = ys map f
(by map's, then ++'s definitions)
Hence lhs = rhs
Inductive case: xs = z :: zs
hypothesis: (zs ++ ys) map f = (zs map f) ++ (ys map f)
goal: ((z :: zs) ++ ys) map f = ((z :: zs) map f) ++ (ys map f)
lhs = (z :: (zs ++ ys)) map f = f(z) :: ((zs ++ ys) map f) (1)
(by map's definition)
rhs = ((z :: zs) map f) ++ (ys map f) = (f(z) :: (zs map f)) ++ (ys map f)
(by map's definition)
in turn, rhs = f(z) :: ((zs map f) ++ (ys map f)) (2)
(by ++'s definition)
From hypothesis, (1) and (2), we have proven goal.
Therefore, we have proven the claim to be true reguardless of xs, ys, and f.
As the comment of #Phil says, first is a good understaning of what the methods ++ and :: are doing on the lists the better way is the documentation
How can we prove properties of list programs?
The answer is by Structural induction!
Proof rule for proving a list property P(xs) via structural induction:
P(Nil) (base case)
for all x,xs : P(xs) => P(x::xs) (induction step)
for all xs : P(xs) (consequence)
P(xs) in induction step is called induction hypothesis
for as the only important thing is xs, ys is fix proper List with lenght l, after proving for xs you can proof for ys, or see that is commutative
So let's apply induction and the definitions of the functions
P(xs): (xs ++ ys) map f = (xs map f) ++ (ys map f)
Base case we substitue xs by nil
(nil ++ ys) map f [definition of ++ ]
ys map f on the other hand
(xs map f) ++ (ys map p) [apply map over NIL]
(NIL) ++ (ys map p) [definition pf ++]
ys map p
Induction Step
((x::xs) ++ ys) map f [definition ++]
(x:: (xs ++ ys)) map f [definition map]
f(x) :: ((xs ++ ys) map f) [induction hypothesis]
f(x) :: ((xs map f) ++ (ys map f)) [definition ++]
(f(x) :: (xs map f)) ++ (ys map f) [definition map]
(x::xs) map f ++ ys map f
q.e.d
for example another case in a scala work sheet
import scala.util.Random
// P : length ( append(as,bs) )) = length ( as ) + length (bs)
def length[T](as: List[T]): Int = as match {
case Nil => 0
case _::xs => 1 + length(xs)
}
def append[T](as: List[T], bs: List[T]): List[T] = as match {
case Nil => bs
case x :: xs => x :: append(xs, bs)
}
// base case we substitute Nil for as in P
val a:List[Int] = Nil
val n = 10
val b:List[Int] = Seq.fill(n)(Random.nextInt).toList
length((append(a,b)))
length(a)
length(b)
import scala.util.Random
length: length[T](val as: List[T]) => Int
append: append[T](val as: List[T],val bs: List[T]) => List[T]
a: List[Int] = List()
n: Int = 10
b: List[Int] = List(1168053950, 922397949, -1884264936, 869558369, -165728826, -1052466354, -1696038881, 246666877, 1673332480, -975585734)
res0: Int = 10
res1: Int = 0
res2: Int = 10
here you can find more examples
Related
I have a heterogenous list as described in CPDT:
Section hlist.
Variable A : Type.
Variable B : A -> Type.
Inductive hlist : list A -> Type :=
| HNil : hlist nil
| HCons : forall (x : A) (ls : list A), B x -> hlist ls -> hlist (x :: ls)
.
End hlist.
and am trying to define a 'pointwise membership' predicate between a list of Ensembles and a list of elements:
Definition hlist_in_ensemble_hlist {A : Type}{B : A -> Type}(types : list A)
(sets : hlist A (fun a => Ensemble (B a)) types) (elems : hlist A B types) : Prop :=
match sets with
| HNil _ _ => True
| HCons _ _ a1 a1s b1 b1s =>
match elems with
| HNil _ _ => False
| HCons _ _ a2 a2s b2 b2s =>
Ensembles.In (B a1) b1 b2 (* /\ recursion (TODO) *)
end
end.
However, Coq complains about the Ensembles.In (B a1) b1 b2 part:
The term "b2" has type "B a2" while it is expected to have type "B a1"
Intuitively, a1 and a2 are the same, since they are the heads of the same types list. How do I communicate that to Coq? I tried matching elems with cons x xs and changing the offending line to Ensembles.In (B x) b1 b2, but that results in a similar error. I also read about the Convoy pattern, but am not sure how to apply it in this context.
This is a classic application of the convoy pattern of CPDT: when you need to argue that two indices are equal after pattern matching, you need to change the match so that it returns a function. In this case, I find it easier to perform the recursion on the hlist indices:
Require Import Coq.Lists.List.
Import ListNotations.
Section hlist.
Variable A : Type.
Variable B : A -> Type.
Inductive hlist : list A -> Type :=
| HNil : hlist nil
| HCons : forall (x : A) (ls : list A), B x -> hlist ls -> hlist (x :: ls)
.
Definition head x xs (l : hlist (x :: xs)) : B x :=
match l with
| HCons _ _ b _ => b
end.
Definition tail x xs (l : hlist (x :: xs)) : hlist xs :=
match l with
| HCons _ _ _ l => l
end.
End hlist.
Fixpoint hlist_in_ensemble_hlist {A : Type}{B : A -> Type}(types : list A)
: hlist A (fun a => B a -> Prop) types -> hlist A B types -> Prop :=
match types with
| [] => fun _ _ => True
| x :: xs => fun sets elems =>
head _ _ _ _ sets (head _ _ _ _ elems) /\
hlist_in_ensemble_hlist _ (tail _ _ _ _ sets) (tail _ _ _ _ elems)
end.
(Note that I have inlined the definition of Ensemble.)
My task is to implement an instance of equality type for the seq datatype. To do this I need to create a comparison function and a proof that this function is correct:
From mathcomp Require Import ssreflect ssrfun ssrbool eqtype ssrnat div seq.
Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.
Fixpoint eq_seq {T: eqType} (x y: seq T) : bool :=
match x, y with
| [::],[::] => true
| cons x' xs, [::] => false
| [::], cons y' ys => false
| cons x' xs, cons y' ys => (x' == y') && eq_seq xs ys
end.
Arguments eq_seq T x y : simpl nomatch.
Lemma eq_seq_correct : forall T: eqType, Equality.axiom (#eq_seq T).
Proof.
move=> T x. elim: x=> [|x' xs].
- move=> y. case: y=> //=; by constructor.
- move=> IH y. case: y=> [| y' ys].
+ rewrite /(eq_seq (x' :: xs) [::]). constructor. done.
+ move=> /=. case E: (x' == y').
* move: E. case: eqP=> E _ //=. rewrite <- E.
Result:
2 subgoals (ID 290)
T : eqType
x' : T
xs : seq T
IH : forall y : seq T, reflect (xs = y) (eq_seq xs y)
y' : T
ys : seq T
E : x' = y'
============================
reflect (x' :: xs = x' :: ys) (eq_seq xs ys)
subgoal 2 (ID 199) is:
reflect (x' :: xs = y' :: ys) (false && eq_seq xs ys)
How to get rid of x' in both sides of equality: (x' :: xs = x' :: ys)?
I tried case: (x' :: xs = x' :: ys), but it didn't help.
(Just in case you are not aware, this lemma is already proved in the seq library. Look for eqseqP.)
The key step in completing the proof is the iffP lemma:
iffP : forall P Q b, reflect P b -> (P -> Q) -> (Q -> P) -> reflect Q b.
Thus, by calling apply/(iffP (IH ys)), we reduce the current goal to proving that xs = ys -> x' :: xs = x' :: ys and x' :: xs = x' :: ys -> xs = ys. You can discharge both subgoals by applying the congruence tactic.
I want to remove all instances of an element from a list, like this:
def remove(x: Char, xs: List[Char]) = xs match {
case Nil => Nil
case x :: ys => remove(x, ys)
case y :: ys => y :: remove(x, ys)
}
However, I get an error on the line case y :: ys => y :: remove(x, ys) saying Cannot resolve symbol ::
Am I pattern matching correctly?
I got a couple of different compile errors, first: "error: recursive method remove needs result type" (fixed by adding return type of List[Char]), then (as I expected): "warning: unreachable code" on the line with y :: ys. This latter warning comes because the x used in the line with case x :: ys is not recognised as the same x from the function's arguments - it is shadowing the argument variable, and so matched any character.
To be sure to match the function argument, add backticks:
def remove(x: Char, xs: List[Char]): List[Char] = xs match {
case Nil => Nil
case `x` :: ys => remove(x, ys)
case y :: ys => y :: remove(x, ys)
}
or you can capitalise the argument name (capitalised variables in a match case are treated specially this way):
def remove(X: Char, xs: List[Char]): List[Char] = xs match {
case Nil => Nil
case X :: ys => remove(X, ys)
case y :: ys => y :: remove(X, ys)
}
If using this style, don't forget to capitalise the argument everywhere it is referenced!
You need to specify the return type if you are using recursive call. This will work:
def remove(x: Char, xs: List[Char]):List[Char] = xs match {
case Nil => Nil
case x :: ys => remove(x, ys)
case y :: ys => y :: remove(x, ys)
}
I agree, the error is misleading
The first problem is that a recursive method needs to declare its return type.
def remove(x: Char, xs: List[Char]): List[Char] = xs match {
case Nil => Nil
case x :: ys => remove(x, ys)
case y :: ys => y :: remove(x, ys)
}
The second problem is that both cases are the same so the second one is unreachable.
I've been trying to encode GADTs in PureScript using rank-2 types, as described here for Haskell
My code looks like:
data Z
data S n
data List a n
= Nil (Z -> n)
| Cons forall m. a (List a m) (S m -> n)
fw :: forall f a. (Functor f) => (forall b . (a -> b) -> f b) -> f a
fw f = f id
bw :: forall f a. (Functor f) => f a -> (forall b . (a -> b) -> f b)
bw x f = map f x
nil :: forall a. List a Z
nil = fw Nil
cons :: forall a n. a -> List a (S n)
cons a as = fw (Cons a as)
instance listFunctor :: Functor (List a) where
map f (Nil k) = Nil (f <<< k)
map f (Cons x xs k) = Cons x xs (f <<< k)
The compiler complains Wrong number of arguments to constructor Main.Cons, referring to the LHS pattern match in the Functor instance.
What is going wrong here?
Regards,
Michael
The syntax used for existential types in Haskell is not present in PureScript. What you've written for Cons is a data constructor with a single universally-quantified argument.
You might like to try using purescript-exists to encode the existential type instead.
Another option is to use a final-tagless encoding of the GADT:
class Listy l where
nil :: forall a. l Z a
cons :: forall a n. a -> l n a -> l (S n) a
You can write terms for any valid Listy instance:
myList :: forall l. (Listy l) => l (S (S Z)) Int
myList = cons 1 (cons 2 nil)
and interpret them by writing instances
newtype Length n a = Length Int
instance lengthListy :: Listy Length where
nil = Length 0
cons _ (Length n) = Length (n + 1)
newtype AsList n a = AsList (List a)
instance asListListy :: Listy AsList where
nil = AsList Nil
cons x (AsList xs) = AsList (Cons x xs)
Using :i Map, I don't see a Monad instance for it.
ghci> import Data.Map
ghci> :i Map
type role Map nominal representational
data Map k a
= containers-0.5.5.1:Data.Map.Base.Bin {-# UNPACK #-} !containers-0.5.5.1:Data.Map.Base.Size
!k
a
!(Map k a)
!(Map k a)
| containers-0.5.5.1:Data.Map.Base.Tip
-- Defined in ‘containers-0.5.5.1:Data.Map.Base’
instance (Eq k, Eq a) => Eq (Map k a)
-- Defined in ‘containers-0.5.5.1:Data.Map.Base’
instance Functor (Map k)
-- Defined in ‘containers-0.5.5.1:Data.Map.Base’
instance (Ord k, Ord v) => Ord (Map k v)
-- Defined in ‘containers-0.5.5.1:Data.Map.Base’
instance (Ord k, Read k, Read e) => Read (Map k e)
-- Defined in ‘containers-0.5.5.1:Data.Map.Base’
instance (Show k, Show a) => Show (Map k a)
-- Defined in ‘containers-0.5.5.1:Data.Map.Base
However, I see that Scala's Map implements flatMap.
I do not know if Map if obeys the Monad Laws.
If my observation on Data.Map is correct, then why isn't there an instance Monad (Map) in Haskell?
I looked at this answer, but it looks like it uses Monad Transformers.
It's hard to reason what Scala's flatMap is supposed to do:
trait Map[A, B+] extends Iterable[(A, B)] {
def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): Map[B]
}
It takes a key, value pair of map (because flatMap comes from Iterable, where A is (A,B)):
scala> val m = Map("one" -> 1, "two" -> 2)
m: scala.collection.immutable.Map[String,Int] = Map(one -> 1, two -> 2)
scala> m.flatMap (p => p match { case (_, v) => List(v, v + 3) })
res1: scala.collection.immutable.Iterable[Int] = List(1, 4, 2, 5)
This isn't monadic bind, it's more closer to Foldable's foldMap
λ > import Data.Map
λ > import Data.Monoid
λ > import Data.Foldable
λ > let m = fromList [("one", 1), ("two", 2)]
λ > (\v -> [v, v + 3]) `foldMap` m
[1,4,2,5]
Map is lawful Ord k => Apply (Map k v) and Ord k => Bind (Map k v):
-- | A Map is not 'Applicative', but it is an instance of 'Apply'
instance Ord k => Apply (Map k) where
(<.>) = Map.intersectionWith id
(<. ) = Map.intersectionWith const
( .>) = Map.intersectionWith (const id)
-- | A 'Map' is not a 'Monad', but it is an instance of 'Bind'
instance Ord k => Bind (Map k) where
m >>- f = Map.mapMaybeWithKey (\k -> Map.lookup k . f) m
Which is a bit like ZipList instance could be, zipping elements by key. Note: ZipList isn't Bind (only Apply) because you cannot remove elements from between the range.
And you cannot make it Applicative or Monad, because there are no way to make lawful pure / return, which should have a value at all keys. Or it might be possible if some Finite type class is constraining k (because Map is strict in it's spine, so you cannot create infinite maps).
EDIT: pointed out in the comments. If we think properly, the above tries to make a concrete (inspectable) representation of MaybeT (Reader k) v = k -> Maybe v with Map k v. But we fail, as we cannot represent pure x = const x. But we can try to do that by explicitly representing that case:
module MMap (main) where
import Data.Map (Map)
import qualified Data.Map as Map
import Test.QuickCheck
import Test.QuickCheck.Function
import Control.Applicative
import Control.Monad
-- [[ MMap k v ]] ≅ k -> Maybe v
data MMap k v = MConstant v
| MPartial (Map k v)
deriving (Eq, Ord, Show)
-- Morphism
lookup :: Ord k => k -> MMap k v -> Maybe v
lookup _ (MConstant x) = Just x
lookup k (MPartial m) = Map.lookup k m
instance Functor (MMap k) where
fmap f (MConstant v) = MConstant (f v)
fmap f (MPartial m) = MPartial (fmap f m)
instance Ord k => Applicative (MMap k) where
pure = MConstant
(MConstant f) <*> (MConstant x) = MConstant (f x)
(MConstant f) <*> (MPartial x) = MPartial (fmap f x)
(MPartial f) <*> (MConstant x) = MPartial (fmap ($x) f)
(MPartial f) <*> (MPartial x) = MPartial (Map.intersectionWith ($) f x)
instance Ord k => Monad (MMap k) where
return = MConstant
(MConstant x) >>= f = f x
(MPartial m) >>= f = MPartial $ Map.mapMaybeWithKey (\k -> MMap.lookup k . f) m
instance (Ord k, Arbitrary k, Arbitrary v) => Arbitrary (MMap k v) where
arbitrary = oneof [ MConstant <$> arbitrary
, MPartial . Map.fromList <$> arbitrary
]
prop1 :: Int -> Fun Int (MMap Int Int) -> Property
prop1 x (Fun _ f) = (return x >>= f) === f x
prop2 :: MMap Int Int -> Property
prop2 x = (x >>= return) === x
prop3 :: MMap Int Int -> Fun Int (MMap Int Int) -> Fun Int (MMap Int Int) -> Property
prop3 m (Fun _ f) (Fun _ g) = ((m >>= f) >>= g) === (m >>= (\x -> f x >>= g))
main :: IO ()
main = do
quickCheck prop1
quickCheck prop2
quickCheck prop3
It indeed works! Yet this a bit fishy definition, as we cannot define semantically correct Eq instance:
m1 = MConstant 'a'
m2 = MPartial (Map.fromList [(True, 'a'), (False, 'a')])
The m1 are m2 are semantically equivalent (lookup k has same results), but structurally different. And we can't know when MPartial have all key-values defined.
Spine refers to, uh, data structure spine. For example list defined as
data List a = Nil | Cons a (List a)
ins't strict in the spine, but
data SList a = SNil | SCons a !(SList a)
is.
You can define infinite List, but SLists:
λ Prelude > let l = Cons 'a' l
λ Prelude > let sl = SCons 'a' sl
λ Prelude > l `seq` ()
()
λ Prelude > sl `seq` () -- goes into infinite loop
As Map is also strict in it's spine
data Map k a = Bin {-# UNPACK #-} !Size !k a !(Map k a) !(Map k a)
| Tip
we cannot construct infinite Map, even we had means to get all values of k type. But we can construct infinite ordinary Haskell list: [] to make pure for Applicative ZipList.
No, there is no Monad instance for Map indeed.
I see that Scala's Map implements flatMap.
I assume you notice that alone doesn't make it a monad?
But we can try nonetheless to make Haskell's Map a Monad. How would it intuitively work? We'd map over the values of the map, return a new map for each, and then join together all those maps by using unions. That should work!
Indeed, if we take a closer look at the classes that Map implements we see something very similar:
import Data.Map
import Data.Traversable
import Data.Foldable
import Data.Monoid
where Monoid.mconcat takes the role of our unions, and Traversable offers a foldMapDefault that does exactly what we want (and could be used for >>=)!
However, when we want to implement return we have a problem - there's no key! We get a value, but we cannot make a Map from that! That's the same problem Scala has avoided by making flatMap more generic than a monad. We could solve this by getting a default value for the key, e.g. by requiring the key type to be a Monoid instance, and make an instance (Ord k, Monoid k) => Monad (Map k) with that - but it will fail to satisfy the monad laws because of the limited return.
Still, all the use cases of the overloaded flatMap in Scala are covered by equivalent methods on Haskell Maps. You'll want to have a closer look at mapMaybe/mapMaybeWithkey and foldMap/foldMapWithKey.
How would you implement return for Data.Map? Presumably return x would have x as a value, but with what key(s)?