Je veux comprendre comment laissez
les liaisons fonctionnent dans Haskell (ou peut-être le calcul lambda, si l'implémentation Haskell diffère?)
Je comprends en lisant Écrivez-vous un Haskell que cela est valide pour une seule liaison let
.
(\square -> square 5) ((\times x -> times x x) (\x -> x * x))
Cela a du sens pour moi, car cela est cohérent avec le fonctionnement des liaisons dans le calcul lambda. Là où je suis confus, c'est en utilisant plusieurs liaisons let
, où une liaison peut référencer les liaisons ci-dessus. Je vais donner un exemple trivial.
Code d'origine:
(\square times -> square 5) (\x -> times x x) (\x -> x * x)
Ma conjecture sur l'implémentation:
let times x y = x * y square x = times x x in square 5
Cela ne semble pas fonctionner car times
n'est pas défini lorsque square est appelé par le lambda. Cependant, cela peut être résolu par cette implémentation:
let x = y in e == (\x -> e) y
Est-ce la bonne façon d'implémenter cette liaison, au moins dans le calcul lambda?
3 Réponses :
L'exemple times
/ square
peut être exprimé en termes de fonctions lambda en utilisant la portée:
(\ones -> take 5 ones)((\f -> (\x -> f (x x))(\x -> f (x x)))(\ones -> 1 : ones)) (\evenodd -> evenodd (\x y -> x) 7)((\f -> (\x -> f (x x))(\x -> f (x x)))(\evenodd c -> c (\n -> n == 0 || evenodd (\x y -> y) (abs n - 1)) (\n -> n /= 0 && evenodd (\x y -> x) (abs n - 1))))
Mais la portée ne suffit pas pour les liaisons let récursives ou mutuellement récursives comme
(\f -> (\x -> f (x x))(\x -> f (x x)))
Dans le calcul lambda, vous pouvez définir le y-combinator pour la récursivité en tant que
let ones = 1 : ones in take 5 ones let even n = n == 0 || odd (abs n - 1) odd n = n /= 0 && even (abs n - 1) in even 7
Cela vous permet de définir des fonctions et des valeurs en fonction d'elles-mêmes. Cette formulation n'est pas légale en raison de contraintes de frappe mais il existe des moyens de contourner cela . p>
L'utilisation du combinateur y nous permet d'exprimer les liaisons let ci-dessus en utilisant le calcul lambda:
(\times -> (\square -> square 5)(\x -> times x x))(\x y -> x * y)
Je vous remercie! J'étais déjà conscient de l'utilisation de fix
avec le lambda supplémentaire pour faire de la récursivité, mais je n'avais pas vu de liaisons mutuellement récursives, alors merci! C'est vraiment cool. Avec votre exemple de portée, je vois que c'est comme mon implémentation échouée, mais avec des crochets supplémentaires. Si je comprends actuellement, l.c. est juste associative, et donc ces deux expressions devraient évaluer la même chose, oui? Votre exemple fonctionne dans GHCi. C'est toujours cool de voir que tant de choses peuvent être faites avec juste le calcul lambda typé.
Oh, je vois la différence. J'ai fait \ square times ->
au lieu de \ times -> (\ square
. Cela a plus de sens. Je ne comprends toujours pas vraiment pourquoi les crochets sont nécessaires. Peut-être vous pourriez clarifier?
Tout ce dont vous avez besoin est U : Y = U. (. U)
. :) (enfin, B aussi).
Notez que plusieurs liaisons let
peuvent être réduites à une seule, définissant une paire (tuple, dans le cas général). Par exemple. nous pouvons réécrire
(\pair -> snd pair 5) (fix (\pair -> (\x y -> x * y, \x -> fst pair x x)))
comme
let pair = (\x y -> x * y, \x -> fst pair x x) in snd pair 5
puis
let (times, square) = (\x y -> x * y, \x -> times x x) in square 5
puis, si on le souhaite ,
let times = \x y -> x * y square = \x -> times x x in square 5
Après cela, nous pouvons appliquer la traduction habituelle du calcul lambda. Si la définition de paire finit par être récursive, comme dans le cas ci-dessus, nous avons besoin d'un combinateur à virgule fixe.
let times x y = x * y square x = times x x in square 5
Notez que cette traduction ne joue pas avec les algorithmes d'inférence de type, qui gèrent let
d'une manière spéciale, introduisant le polymorphisme. Ce n’est pas important si nous ne nous soucions que des aspects dynamiques de notre programme.
Je vais répondre à ma propre question pour peut-être fournir une perspective utile à ceux qui la consultent.
Nous voulons implémenter le programme suivant avec deux liaisons let:
(\times -> (\square -> square 5) (\x -> times x x)) (\a b -> a * b) => (\square -> square 5) (\x -> (\a b -> a * b) x x) => (\square -> square 5) (\x -> (\b -> x * b) x) => (\square -> square 5) (\x -> x * x) => (\x -> x * x) 5 => 5 * 5 => 25
Pour commencer, simplifions ceci à l'essence de ce que nous voulons:
(\times -> (\square -> square 5) (\x -> times x x)) (\a b -> a * b)
Assez simple. Cependant, square
dans ce cas n'est pas défini! Eh bien, nous pouvons le lier en utilisant le mécanisme que notre langage nous fournit - un lambda. Cela nous donne (\ square -> square 5) (\ x -> times x x)
. Maintenant square
est défini, mais son cousin times
ne l'est pas ... Eh bien, nous avons besoin d'un autre lambda! Notre programme final devrait ressembler à ceci:
square 5
Notez que le (\ times -> ...)
englobe complètement notre dernière étape, de sorte que fois
sera dans la portée car il est lié. Ceci est cohérent avec la réponse donnée par @rampion, et se réduit comme suit:
let times a b = a * b square x = times x x in square 5
Si la fonction square
n’avait pas dépendu de fois
, on aurait pu facilement écrire (\ times square -> ....
. La dépendance signifie qu'il faut imbriquer ces deux environnements, l'un contenant heures
, et un autre à l'intérieur de ce qui peut utiliser sa définition.
Merci pour toute votre aide! Je suis époustouflé par la simplicité et la puissance du calcul lambda.
vous devez probablement définir
let times x y = x * y ...
sinon cela n'a pas beaucoup de sens.@karakfa Merci! Petite erreur mentale de ma part. J'ai pris de l'avance moi-même! Je vais éditer. :)
Le
let
de Haskell n'est paslet
, c'estletrec
.@WillNess Si
let
est en faitletrec
, cela signifie-t-il quefix
est appliqué à toutes les liaisons, ou uniquement celles récursives?chaque et toujours. du moins en théorie; un compilateur peut réaliser qu'il n'y a pas de récursivité et optimiser. et nous n'avons pas à utiliser de correctif. la récursivité peut être mise en œuvre directement par l'implémentation, sous le capot.