Lambda-Ausdrücke ohne Parameter in der Haskell- und / oder Lambda-Rechnung


9

In eifrigen Sprachen wie Scheme und Python können Sie einen Lambda-Ausdruck ohne Parameter verwenden, um die Auswertung zu verzögern, z. B. in Scheme (Chicken Scheme):

#;1> (define (make-thunk x) (lambda () (+ x 1)))
#;2> (define t (make-thunk 1))
#;3> (t)
2

In Zeile 2 twird an den nicht bewerteten Ausdruck gebunden, der (lambda () (+ 1 1))dann 2in Zeile 3 ausgewertet wird .

Ähnlich in Python:

>>> def make_thunk(x): return lambda: x + 1
... 
>>> t = make_thunk(1)
>>> t()
2

Mit dieser Technik kann man eine verzögerte Auswertung in einer eifrigen Sprache implementieren.

Ich hatte also erwartet, dass Haskell keine Lambda-Ausdrücke ohne Parameter haben würde, da die Sprache bereits faul ist und keine verzögerten Ausdrücke erstellt werden müssen. Zu meiner Überraschung fand ich heraus, dass es in Haskell möglich ist, den Lambda-Ausdruck zu schreiben

\() -> "s"

was nur auf den ()Wert wie folgt angewendet werden kann :

(\() -> "s") ()

das Ergebnis geben

"s"

Das Anwenden dieser Funktion auf ein anderes Argument als das ()Auslösen einer Ausnahme (zumindest soweit ich dies während meiner Tests sehen konnte). Dies scheint sich von der verzögerten Auswertung in Scheme und Python zu unterscheiden, da der Ausdruck noch ein zu bewertendes Argument benötigt. Was bedeutet ein Lambda-Ausdruck ohne Variablen (wie \() -> "s") in Haskell und wofür kann er nützlich sein?

Ich wäre auch neugierig zu wissen, ob ähnliche parameterlose Lambda-Ausdrücke in (einer Vielzahl von) Lambda-Berechnungen existieren.


"Anwenden dieser Funktion auf ein anderes Argument als das ()Auslösen einer Ausnahme ..." Verursacht das Programm eine Ausnahme oder beschwert sich der Compiler darüber, dass der Code keine Prüfung eingibt?
Fried Brice

Antworten:


12

Nun, die anderen Antworten decken ab, was \() -> "something"in Haskell bedeutet: eine unäre Funktion, die ()als Argument dient.

  • Was ist eine Funktion ohne Argumente? - Ein Wert. Tatsächlich kann es gelegentlich nützlich sein, sich Variablen als Nullfunktionen vorzustellen, die auf ihren Wert ausgewertet werden. Die let-syntax für eine Funktion ohne Argumente (die eigentlich nicht existiert) gibt Ihnen eine Variablenbindung:let x = 42 in ...

  • Hat der Lambda-Kalkül Nullfunktionen? - Nein. Jede Funktion benötigt genau ein Argument. Dieses Argument kann jedoch eine Liste sein, oder die Funktion kann eine andere Funktion zurückgeben, die das nächste Argument verwendet. Haskell bevorzugt die letztere Lösung, das a b csind also eigentlich zwei Funktionsaufrufe ((a b) c). Um Nullfunktionen zu simulieren, müssen Sie einen nicht verwendeten Platzhalterwert übergeben.


1
+1 für "Was ist eine Funktion ohne Argumente? - Ein Wert" und im weiteren Sinne auch die Nullfunktionsansicht. Ich denke, es ist nützlich zu sehen, wie Funktionen und Werte zusammenhängen, und eine Funktion kann als Verallgemeinerung eines Wertes angesehen werden.
KChaloux

@amon: Eine Nullfunktion ist also nur in einer Sprache wie Scheme sinnvoll, in der Sie explizit eine Auswertung auslösen können und in der Funktionen (Prozeduren) sowohl für ihren Wert als auch für ihre Nebenwirkungen nützlich sein können.
Giorgio

@Giorgio Lisp ist eine direkte Codierung von Lambda-Kalkül mit viel syntaktischem Zucker, aber wenn es Lambda-Kalkül ist, kann es keine Nullfunktionen haben. Alles ist eine Liste, daher ist der Funktionsanwendungsausdruck (f)konzeptionell eine Einzelelementliste mit einem Kopfwert von 'fund einem Null-Schwanz - also können conswir ihn als schreiben (cons 'f '()). Dieser Schwanz ist die Liste, die (konzeptionell) als Argument verwendet wird, und es spielt keine Rolle, dass nil die leere Liste darstellt. Der Unterschied zwischen Lisp und Haskell besteht darin, dass letzteres implizites Currying hat, so dass der Ausdruck (f)verschiedene Dinge bedeutet
amon

2
Aber ja, Funktionen ohne Argumente sind in Haskell im Allgemeinen nicht nützlich, weil sie faul und rein sind. Strenge Sprachen können argumentlose Funktionen verwenden, um Faulheit zu simulieren, oder für allgemeine Rückrufe. Ob Sie ein Dummy-Argument wie ()(wie es bei MLs der Fall ist) angeben müssen oder ob Sie dies nicht wissentlich tun (wie dies bei Lisps oder C-ähnlichen Sprachen der Fall ist), spielt keine Rolle.
Amon

9

Sie interpretieren falsch, was ()in Haskell bedeutet. Es ist nicht das Fehlen eines Wertes, sondern der einzige Wert des Einheitentyps (der Typ selbst, auf den durch einen leeren Satz von Klammern verwiesen wird ()).

Da Lambdas so konstruiert werden können, dass sie den Mustervergleich verwenden, \() -> "s"sagt der Lambda-Ausdruck explizit "Erstellen Sie eine anonyme Funktion und erwarten Sie eine Eingabe, die dem ()Muster entspricht". Es macht nicht viel Sinn, es zu tun, aber es ist auf jeden Fall erlaubt.

Sie können den Mustervergleich mit Lambdas auch auf andere Weise verwenden, zum Beispiel:

map (\(a, b) -> a + b) [(1,2), (3,4), (5,6)] -- uses pattern matching to destructured tuples

map (\(Name first _) -> first) [Name "John" "Smith", Name "Jane" "Doe"] -- matches a "Name" data type and its first field

map (\(x:_) -> x) [[1,2,3], [4,5,6]] -- matches the head of a list

Außer das Unitwird auch ()in Haskell geschrieben.

@delnan Ich beherrsche Scala = P Ich werde die Antwort aktualisieren, um in diesem Punkt etwas genauer zu sein.
KChaloux

Ich stelle mir also vor, dass dieser Mustervergleichsmechanismus ein Haskell-Merkmal ist, das im Lambda-Kalkül (?) Nicht vorhanden ist
Giorgio

@Giorgio Ich bin nicht besonders gut in meinem Lambda-Grundkalkül, aber ich verstehe, dass Sie richtig sind; Der Mustervergleich ist ein Sprachmerkmal und dem Lambda-Kalkül nicht eigen.
KChaloux

1
@Giorgio Klingt ungefähr richtig. Ich denke, Sie können dies darauf zurückführen, wie jede Sprache mit Nebenwirkungen umgeht. Eine parameterlose Funktion ist entweder A) ein unveränderlicher Wert oder B) etwas, das einen Nebeneffekt hat. Haskell versucht, Nebenwirkungen stärker als Python (oder in geringerem Maße Scheme) zu vermeiden, und geht daher davon aus, dass eine parameterlose Funktion ein Wert ist. Es macht nicht viel Sinn machen eine anonyme Funktion zur Verfügung zu stellen , die keine Eingabe nimmt und gibt den gleichen Wert ( im Gegensatz zu const, die nimmt jede Eingabe und wandelt sie alle in den gleichen Wert).
KChaloux

1

() -> "s"ist eine Lambda-Funktion, die ein Argument akzeptiert:

ghci> :t (\() -> "s")
(\() -> "s") :: () -> [Char]

(), das leere Tupel (manchmal auch als Einheit bekannt), ist ein vollwertiger Typ in Haskell. Es hat nur ein Mitglied (ignoriert _|_*), das auch als geschrieben ist (). Schauen Sie sich die Definition von ():

data () = ()

Dies bedeutet, dass das Formular auf der linken Seite Ihres Lambda-Ausdrucks syntaktisch eine übereinstimmende Musterübereinstimmung ist (), das einzige Mitglied des Typs (). Es gibt nur eine gültige Möglichkeit, es aufzurufen (nämlich ()als Argument anzugeben), da es nur ein gültiges Mitglied des Typs gibt ().

Eine solche Funktion ist in Haskell nicht sehr nützlich, da Begriffe standardmäßig ohnehin träge ausgewertet werden, wie Sie in Ihrer Frage festgestellt haben.

Eine ausführlichere Erklärung finden Sie in dieser Antwort .

* _|_heißt "bottom" oder "undefined". Es prüft immer die Typen, stürzt aber Ihr Programm ab. Das klassische Beispiel, wie man ein bekommt, _|_ist let x = x in x.


"Eine solche Funktion ist in Haskell nicht sehr nützlich, da Begriffe standardmäßig ohnehin träge ausgewertet werden, wie Sie in Ihrer Frage festgestellt haben.": Nicht nur das: Ich muss ein Argument angeben, das ich zB in Schema nicht tun muss .
Giorgio
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.