Existe-t-il un moyen de comparer deux fonctions pour l'égalité? Par exemple, (λx.2*x) == (λx.x+x)
devrait retourner vrai, car ceux-ci sont évidemment équivalents.
Il est assez bien connu que l'égalité générale des fonctions est indécidable en général, vous devrez donc choisir un sous-ensemble du problème qui vous intéresse. Vous pourriez envisager certaines de ces solutions partielles:
C'est indécidable en général, mais pour un sous-ensemble approprié, vous pouvez en effet le faire aujourd'hui efficacement en utilisant des solveurs SMT:
$ ghci
GHCi, version 8.0.1: http://www.haskell.org/ghc/ :? for help
Prelude> :m Data.SBV
Prelude Data.SBV> (\x -> 2 * x) === (\x -> x + x :: SInteger)
Q.E.D.
Prelude Data.SBV> (\x -> 2 * x) === (\x -> 1 + x + x :: SInteger)
Falsifiable. Counter-example:
s0 = 0 :: Integer
Pour plus de détails, voir: https://hackage.haskell.org/package/sbv
En plus des exemples pratiques donnés dans l'autre réponse, prenons le sous-ensemble de fonctions exprimables en calcul lambda typé; nous pouvons également autoriser les types de produits et de sommes. Bien que vérifier si deux fonctions sont égales peut être aussi simple que les appliquer à une variable et comparer les résultats , nous ne pouvons pas construire la fonction d'égalité dans le langage de programmation lui-même .
ETA: λProlog est un langage de programmation logique pour la manipulation de fonctions (calcul lambda typé).
2 ans se sont écoulés, mais je veux ajouter une petite remarque à cette question. À l'origine, j'ai demandé s'il y avait un moyen de savoir si (λx.2*x)
est égal à (λx.x+x)
. L'addition et la multiplication sur le λ-calcul peuvent être définies comme:
add = (a b c -> (a b (a b c)))
mul = (a b c -> (a (b c)))
Maintenant, si vous normalisez les termes suivants:
add_x_x = (λx . (add x x))
mul_x_2 = (mul (λf x . (f (f x)))
Vous recevez:
result = (a b c -> (a b (a b c)))
Pour les deux programmes. Puisque leurs formes normales sont égales, les deux programmes sont évidemment égaux. Bien que cela ne fonctionne pas en général, cela fonctionne pour de nombreux termes dans la pratique. (λx.(mul 2 (mul 3 x))
et (λx.(mul 6 x))
les deux ont par exemple les mêmes formes normales.
Dans un langage à calcul symbolique comme Mathematica:
Ou C # avec une bibliothèque d'algèbre informatique :
MathObject f(MathObject x) => x + x;
MathObject g(MathObject x) => 2 * x;
{
var x = new Symbol("x");
Console.WriteLine(f(x) == g(x));
}
Ce qui précède affiche "True" sur la console.
Prouver deux fonctions égales est indécidable en général mais on peut toujours prouver l'égalité fonctionnelle dans des cas particuliers comme dans votre question.
Voici un exemple de preuve en Lean
def foo : (λ x, 2 * x) = (λ x, x + x) :=
begin
apply funext, intro x,
cases x,
{ refl },
{ simp,
dsimp [has_mul.mul, nat.mul],
have zz : ∀ a : nat, 0 + a = a := by simp,
rw zz }
end
On peut faire de même dans d'autres langages typés dépendants tels que Coq, Agda, Idris.
Ce qui précède est une preuve de style tactique. La définition réelle de foo
(la preuve) qui est générée est une bouchée à écrire à la main:
def foo : (λ (x : ℕ), 2 * x) = λ (x : ℕ), x + x :=
funext
(λ (x : ℕ),
nat.cases_on x (eq.refl (2 * 0))
(λ (a : ℕ),
eq.mpr
(id_locked
((λ (a a_1 : ℕ) (e_1 : a = a_1) (a_2 a_3 : ℕ) (e_2 : a_2 = a_3), congr (congr_arg eq e_1) e_2)
(2 * nat.succ a)
(nat.succ a * 2)
(mul_comm 2 (nat.succ a))
(nat.succ a + nat.succ a)
(nat.succ a + nat.succ a)
(eq.refl (nat.succ a + nat.succ a))))
(id_locked
(eq.mpr
(id_locked
(eq.rec (eq.refl (0 + nat.succ a + nat.succ a = nat.succ a + nat.succ a))
(eq.mpr
(id_locked
(eq.trans
(forall_congr_eq
(λ (a : ℕ),
eq.trans
((λ (a a_1 : ℕ) (e_1 : a = a_1) (a_2 a_3 : ℕ) (e_2 : a_2 = a_3),
congr (congr_arg eq e_1) e_2)
(0 + a)
a
(zero_add a)
a
a
(eq.refl a))
(propext (eq_self_iff_true a))))
(propext (implies_true_iff ℕ))))
trivial
(nat.succ a))))
(eq.refl (nat.succ a + nat.succ a))))))