Ist eine Funktion sofort unrein, wenn sie eine Funktion als Parameter übernimmt?

17

Da die Reinheit eines Eingabeparameters bis zur Laufzeit unbekannt ist, wird eine Funktion sofort als unrein angesehen, wenn sie eine Funktion als Eingabeparameter verwendet?

Verwandte: Wenn eine Funktion eine reine Funktion anwendet, die außerhalb der Funktion definiert, aber nicht als Parameter übergeben wird, ist sie dann immer noch rein, wenn sie die Kriterien erfüllt, keine Nebenwirkungen zu haben, und die Ausgabe ausschließlich von der Eingabe abhängt?

Für den Kontext schreibe ich funktionalen Code in JavaScript.

Dancrumb
quelle
Betrachten Sie als einfaches Gegenbeispiel:foo = function(function bar){ print(bar.toString()) }
David sagt Reinstate Monica
1
@DavidGrinberg Das ist meines Erachtens kein Gegenbeispiel und zeigt tatsächlich ein größeres Problem auf. Wenn Sie Funktionen haben, die überschrieben werden können und nicht garantieren können, dass Implementierungen frei von Nebenwirkungen sind, können Sie auch nicht garantieren, dass die meisten Funktionen, die Objekte verwenden und ihre Methoden aufrufen, rein sind. Vielleicht löscht toString () von bar einige Dateien von der Festplatte?
Joshua Taylor
3
@ DavidGrinberg Aber ich denke, Sie denken in eine gute Richtung. foo = function(function bar) { return 3; } ist rein und nimmt eine Funktion als Argument an.
Joshua Taylor
@JoshuaTaylor Fair point, daran habe ich nicht gedacht. Aber Sie haben das Problem bereits im Wesentlichen gelöst. Als alternative Lösung rufen Sie einfach den 'root' auf toString()(dh den, den Sie auf Javas Objekt finden würden).
David sagt Reinstate Monica

Antworten:

22

Solange alle in der Funktion verwendeten Werte ausschließlich durch ihre Parameter definiert sind, handelt es sich um eine reine Funktion.

Die Facette, dass die Ausgabe jedes Mal für dieselbe Eingabe dieselbe ist, wird dadurch gesteuert, ob die Parameter rein sind. Wenn Sie annehmen, dass die Parameter (wie ein Funktionsargument) ebenfalls rein sind, dann ist es rein.

In einer Sprache wie Javascript, in der Reinheit nicht erzwungen wird, bedeutet dies, dass es möglich ist, eine ansonsten reine Funktion unrein zu machen, indem eine unreine Funktion aufgerufen wird, die als Parameter übergeben wird.

Dies bedeutet effektiv, dass es für Sprachen, die keine (dh fast alle) Reinheit erzwingen, unmöglich ist, eine reine Funktion zu definieren, die als Argumente übergebene Funktionen aufruft. Es ist immer noch nützlich, sie so rein wie möglich zu schreiben und sie als reine Funktionen zu betrachten, aber Sie müssen vorsichtig sein, denn die Annahme, dass sie rein ist, wird durch falsche Argumente zerstört.

Nach meiner Erfahrung in der Praxis ist dies normalerweise keine große Sache - ich finde es selten, dass unreine Funktionen als Funktionsargumente für reine Funktionen verwendet werden.

Daenyth
quelle
In Bezug auf Ihre Aussage: "Solange alle in der Funktion verwendeten Werte ausschließlich durch ihre Parameter definiert sind, handelt es sich um eine reine Funktion." Was passiert bei Konstanten? Wenn ich eine Funktion habe areaOfCircle r => Math.Pi * r * r, wird sie areaOfCirclenicht rein, da sie nicht nur Parameter verwendet?
David Arno
2
@DavidArno Das ist ein fairer Punkt. Laut referentieller Transparenz ist die Bezugnahme auf einen statischen Außenwert nicht anders als eine Hardcodierung, sodass er immer noch rein ist.
Daenyth
1
"Dies bedeutet, dass es möglich ist, eine reine Funktion zu einem unreinen Verhalten zu machen" - per definitionem kann eine reine Funktion kein unreines Verhalten haben. Sie machen den Fehler zu denken, dass eine Funktion f(f2), die aufgerufen f2wird, nicht transitiv auf irgendetwas f2angewiesen ist. Eine Funktion, die möglicherweise beliebige übergebene Funktionen aufruft, ist nicht rein.
user2357112 unterstützt Monica
2
@Daenyth: Besser, aber es wird immer noch davon ausgegangen, dass die Funktion die übergebene Funktion aufrufen muss. Es könnte so etwas sein function compose(f, g) {return function h(x) {return f(g(x));};}, das rein ist, obwohl es Funktionen als Argumente nimmt.
user2357112 unterstützt Monica
1
"Ich finde es selten, dass unreine Funktionen als Funktionsargumente für reine Funktionen verwendet werden." - keine funktionale Sprache, aber bestimmte C ++ - Bibliotheksfunktionen haben spezifische Warnungen, dass Prädikatargumente rein sein müssen (eine Annäherung an). In gewissem Sinne bedeutet dies, dass es nicht nur selten ist, sondern auch nie wirklich passiert. Aber in einem anderen Sinne müssen sie es verbieten, weil die Leute es manchmal wollen . Zum Beispiel möchten sie einer findRoutine ein unreines Prädikat übergeben, das für das dritte übereinstimmende Element, auf das sie trifft, "true" zurückgibt, oder einen solchen Unsinn.
Steve Jessop
19

Da die Reinheit eines Eingabeparameters bis zur Laufzeit unbekannt ist, wird eine Funktion sofort als unrein angesehen, wenn sie eine Funktion als Eingabeparameter verwendet?

Gegenbeispiel:

function pure(other_function) {
    return 1;
}

Es spielt keine Rolle, ob other_functiones sich um eine reine Funktion, eine unreine Funktion oder gar keine Funktion handelt. Die pureFunktion ist rein.

Anderes Gegenbeispiel:

function identity(x) {
    return x;
}

Diese Funktion ist rein, auch wenn xes sich um eine unreine Funktion handelt. identity(impure_function)wird immer zurückkehren impure_function, egal wie oft Sie den Anruf wiederholen. Es spielt keine Rolle, ob identity(impure_function)()immer dasselbe zurückgegeben wird. Der Rückgabewert einer Funktion hat keinen Einfluss auf ihre Reinheit.


Wenn eine Funktion eine Funktion aufruft, die als Argument übergeben wurde, ist sie im Allgemeinen nicht rein. Zum Beispiel ist eine Funktion function call(f) {f();}nicht rein, weil sie, obwohl sie keinen globalen oder veränderlichen Zustand erwähnt, fso etwas wie alertsichtbare Nebenwirkungen hervorrufen kann.

Wenn eine Funktion Funktionen als Argumente verwendet, diese aber nicht aufruft oder aufruft, kann sie rein sein. Es könnte immer noch unrein sein, wenn es eine andere unreine Sache tut. Zum Beispiel function f(ignored_function) {alert('This isn't pure.');}ist unrein, obwohl es nie anruft ignored_function.

user2357112 unterstützt Monica
quelle
4
Diese Reaktion scheint zu umständlich. Wir können aus der Frage schließen, dass es sich um den Funktionsparameter handelt, der aufgerufen wird. Die Existenz von Funktionen, die andere Funktionen als Parameter annehmen können / können, ohne sie aufzurufen, hat keinen Einfluss auf diese Frage.
Walpen
13
@walpen: In der Frage wird das Argument nicht erwähnt. Es gibt keinen Grund anzunehmen, dass der Fragesteller sogar erkannt hat, dass eine Funktion eine andere Funktion als Eingabe übernimmt, ohne sie aufzurufen. Es ist wichtig, versteckte Annahmen wie diese aufzuzeigen, anstatt nur anzunehmen, dass Sie sie annehmen sollten.
user2357112 unterstützt Monica
12

Da die Reinheit eines Eingabeparameters bis zur Laufzeit unbekannt ist, wird eine Funktion sofort als unrein angesehen, wenn sie eine Funktion als Eingabeparameter verwendet?

Technisch gesehen ja, es sei denn, es gibt in Ihrer Sprache Möglichkeiten, um sicherzustellen, dass die Eingabefunktion auch rein ist.

Wenn eine Funktion eine reine Funktion anwendet, die außerhalb der Funktion definiert, aber nicht als Parameter übergeben wird, ist sie dann immer noch rein, wenn sie die Kriterien erfüllt, keine Nebenwirkungen zu haben, und die Ausgabe ausschließlich von der Eingabe abhängt?

Ja. Konzentrieren wir uns also darauf, worauf es ankommt. Eine Funktion pur aufzurufen oder nicht, ist an und für sich nicht sinnvoll. Reine Funktionen sind nützlich, da das Erzeugen der gleichen Ausgabe für jede Eingabe und unabhängig vom Status oder mit Nebenwirkungen eine sehr nützliche Eigenschaftsgruppe ist. Sobald Ihre Funktion ausgeführt wurde, können Sie sich die Antwort für diese Eingabe "merken" und sie ist immer wahr. Sie müssen die Funktion auch nicht erneut ausführen, um Nebenwirkungen zu erzeugen. Und Sie können diese Funktion parallel (oder außer Betrieb) mit anderen Funktionen ausführen und wissen, dass sie keine versteckten Interaktionen haben, die sich schlecht verhalten.

Diese nützlichen Eigenschaften bleiben erhalten, wenn die Funktion andere reine Nur-Lese-Funktionen verwendet, unabhängig davon, wie auf sie verwiesen wird.

Telastyn
quelle
5

Wie Telastyn sagte: Technisch gesehen ja, es sei denn, es gibt einen Weg in Ihrer Sprache, um sicherzustellen, dass die Eingabefunktion auch rein ist.

Das ist nicht hypothetisch, es gibt in der Tat gute Möglichkeiten, dies zu garantieren. Zumindest in einer stark typisierten Sprache.

So eine reine ~ Funktion würden Sie in JavaScript als schreiben

function foo(f) {
   return f(1) + 2;
}

kann direkt nach Haskell übersetzt werden:

foo :: (Int -> Int) -> Int
foo f = f 1 + 2

Jetzt können Sie in JavaScript böse Dinge tun wie

js> foo (function(x) {console.log("muharhar"); return 0})
muharhar
2

Dies ist in Haskell nicht möglich . Der Grund dafür ist, dass etwas nebenwirkungsreiches console.log()immer einen Ergebnistyp haben muss IO something, nicht nur somethingalleine.

GHCi> foo (\x -> print "muarhar" >> return 0)

<interactive>:7:12:
    Couldn't match expected type ‘Int’ with actual type ‘IO b0’
    In the expression: print "muarhar" >> return 0
    In the first argument of ‘foo’, namely
      ‘(\ x -> print "muarhar" >> return 0)’
    In the expression: foo (\ x -> print "muarhar" >> return 0)

Für diesen Ausdruck typecheck, müssten wir geben foodie Art Unterschrift

foo :: (Int -> IO Int) -> Int

Es stellt sich aber heraus, dass ich es dann nicht mehr implementieren kann: Da die Argumentfunktion IOdas Ergebnis hat, kann ich es nicht innerhalb verwenden foo.

<interactive>:8:44:
    Couldn't match expected type ‘Int’ with actual type ‘IO Int’
    In the first argument of ‘(+)’, namely ‘f 1’
    In the expression: f 1 + 2

Die einzige Möglichkeit, in der ich eine IOAktion verwenden könnte, fooist, wenn das Ergebnis von fooden Typ IO Intselbst hat:

foo :: (Int -> IO Int) -> IO Int
foo f = do
   f1 <- f 1
   return (f1 + 2)

Aber an dieser Stelle wird aus der Signatur deutlich, foodass es sich auch nicht um eine reine Funktion handelt.

links herum
quelle
1
Bevor Sie "unmöglich" sagen, werfen Sie einen Blick auf unsafeIO:-)
Bergi
2
@Bergi: Das ist eigentlich kein Teil von Haskell, sondern von dessen fremder Funktionsschnittstelle: um behaupten zu können, dass eine in einer anderen Sprache definierte Funktion rein ist, was der Haskell-Compiler offensichtlich nicht aus der Typensignatur ableiten kann, da andere Sprachen im Allgemeinen keine haben so etwas wie IO. Übrigens kann es auch verwendet werden, um Chaos zu verursachen, indem Nebenwirkungen in einer "reinen" Funktion ausgeblendet werden. Dies ist in Haskell jedoch sehr unsicher, da es keinen wirklich zuverlässigen Weg gibt, die Auswertungsreihenfolge von reinen Funktionen anzugeben.
links um 24.10.15 Uhr
Ja, das ist wahr. Ich denke jedoch, ich habe gesehen, dass es verwendet wird, um vorteilhafte Nebenwirkungen auch in einer "reinen" Funktion zu "verbergen" , in der die Sicherheit des Ansatzes vorher festgelegt werden musste.
Bergi
@Bergi Du solltest meistens nicht verwenden unsafeIO; Dies ist eine Notausstiegsluke, die das Typsystem garantiert umgeht, und daher ist Ihre keine gute Sache.
Andres F.
0

Nein ist es nicht.

Wenn die übergebene Funktion unrein ist UND Ihre Funktion die übergebene Funktion aufruft, wird Ihre Funktion als unrein betrachtet.

Die reine / unreine Beziehung ist ein bisschen wie Sync / Async in JS. Sie können reinen Code von unreinen frei verwenden, aber nicht umgekehrt.

Bobby Marinoff
quelle
Diese Antwort fügt nichts hinzu, was in dieser nicht bereits erklärt wurde ... Bitte überprüfen Sie frühere Antworten, bevor Sie die Dinge neu formulieren :)
Andres F.
Was ist mit der Sync / Async-Analogie?
Bobby Marinoff