Sie tatsächlich Schließungen in Haskell simulieren kann, aber nicht so, wie Sie vielleicht denken. Zunächst werde ich einen Verschluss definieren:
data Closure i o = Respond (i -> (o, Closure i o))
Dies definiert einen Typen, der bei jedem „Schritt“ einen Wert vom Typ nimmt i
, die eine Antwort vom Typ o
zu berechnen ist.
Also, lassen Sie uns einen „closure“ definieren, die leeren Eingaben und Antworten mit ganzen Zahlen akzeptiert, d.h .:
incrementer :: Closure() Int
dieses Verhalten Schließung von Anfrage zu verlangen variieren. Ich werde es einfach halten und machen es so, dass es mit 0 zur ersten Reaktion reagiert und erhöht dann seine Antwort für jede folgende Anfrage:
incrementer = go 0 where
go n = Respond $ \() -> (n, go (n + 1))
Wir können dann die Schließung wiederholt abfragen, die ein Ergebnis ergibt und ein neuer Verschluss:
query :: i -> Closure i o -> (o, Closure i o)
query i (Respond f) = f i
bemerkt, dass die zweite Hälfte des oben genannten Typs ein gemeinsames Muster in Haskell ähnelt, das der State
monadisch:
newtype State s a = State { runState :: s -> (a, s) }
Es kann von Control.Monad.State
importiert werden. So können wir query
in diesem State
Monade wickeln:
query :: i -> State (Closure i o) o
query i = state $ \(Respond f) -> f i
... und jetzt haben wir eine generische Art und Weise jede Schließung abzufragen, um die State
Monade mit:
someQuery :: State (Closure() Int) (Int, Int)
someQuery = do
n1 <- query()
n2 <- query()
return (n1, n2)
Sagen wir es unseren Schließung passieren und sehen was passiert:
>>> evalState someQuery incrementer
(0, 1)
Lassen Sie uns einen anderen Verschluss schreiben, die etwas willkürliche Muster zurückgibt:
weirdClosure :: Closure() Int
weirdClosure = Respond (\() -> (42, Respond (\() -> (666, weirdClosure))))
...und testen Sie es:
>>> evalState someQuery weirdClosure
(42, 666)
Jetzt, Verschlüsse von Hand zu schreiben ziemlich umständlich erscheint. Wäre es nicht schön, wenn wir do
Notation verwenden könnten, um die Schließung zu schreiben? Nun, wir können! Wir haben nur eine Änderung unserer Verschlussart zu machen:
data Closure i o r = Done r | Respond (i -> (o, Closure i o r))
Jetzt können wir eine Monad
Instanz definieren (von Control.Monad
) für Closure i o
:
instance Monad (Closure i o) where
return = Done
(Done r) >>= f = f r
(Respond k) >>= f = Respond $ \i -> let (o, c) = k i in (o, c >>= f)
Und wir können eine Komfortfunktion schreiben, die dem entspricht, Wartung einer einzigen Anfrage:
answer :: (i -> o) -> Closure i o()
answer f = Respond $ \i -> (f i, Done())
... die wir alle unsere alten Verschlüsse umschreiben können:
incrementer :: Closure() Int()
incrementer = forM_ [1..] $ \n -> answer (\() -> n)
weirdClosure :: Closure() Int r
weirdClosure = forever $ do
answer (\() -> 42)
answer (\() -> 666)
Jetzt brauchen Sie nur unsere Abfragefunktion ändern wir:
query :: i -> StateT (Closure i o r) (Either r) o
query i = StateT $ \x -> case x of
Respond f -> Right (f i)
Done r -> Left r
... und es verwenden, Abfragen zu schreiben:
someQuery :: StateT (Closure() Int()) (Either()) (Int, Int)
someQuery = do
n1 <- query()
n2 <- query()
return (n1, n2)
es jetzt testen!
>>> evalStateT someQuery incrementer
Right (1, 2)
>>> evalStateT someQuery weirdClosure
Right (42, 666)
>>> evalStateT someQuery (return())
Left()
Allerdings habe ich immer noch nicht berücksichtigen, dass eine wirklich elegante Methode, also werde ich durch schamlos Aufstecken meine Proxy Typ in meinem pipes
als viel allgemeiner und strukturierter Art und Weise des Schreibens Schließungen und ihre abschließen Verbraucher. Der Server
Typ repräsentiert einen verallgemeinerten Verschluss und der Client
repräsentiert einen generalisierten Verbraucher eines Verschlusses.
Einfach, Sie können den geschlossenen Zustand oder andere nicht ändern :) – is7s
Ähnliche Fragen: http://stackoverflow.com/questions/9419175/are-closures-a-violation-of-the-functional- Programmier-Paradigma/ – amindfv
@ Is7s, aber Sie können verursachen, dass zwischen Anrufen weiter instanziiert wird, wenn es sich um nicht-atomare Daten handelt. –