2012-03-27 10 views
2

Ich habe einige Schwierigkeiten, den folgenden Code zu verstehen (in einem ErrorT Monade auf der IO schichtet ausgeführt):

closePort [Port port] = liftIO $ hClose port >> (return $ Bool True) 

>> hat eine höhere precendence als $. So wird Bool True zuerst in IO eingewickelt und dann mit liftIO angehoben oder wird hClose zuerst abgehoben? Mit anderen Worten sind >> und return in der IO Monad oder ErrorT Monad ausgeführt?

Antwort

7

Die gegebene Code entspricht

closePort [Port port] = liftIO (hClose port >> (return (Bool True))) 

so die gesamte (hClose port) >> (return (Bool True)) das Argument liftIO ist. So sind die (>>) und return die von IO, und dann wird die gesamte IO -computation mit liftIO aufgehoben.

1

(Ich gehe davon aus Bool hier ein Daten Konstruktor eines Typs Sie festgelegt haben.)

liftIO $ hClose port >> (return $ Bool True) 

die gleiche wie

ist
liftIO (hClose port >> (return (Bool True))) 

So ist die return und die >> sind beide die IO Versionen, und das Ergebnis der >> wird in die äußere Monade gehoben.

-2

Sie könnten bei der Umsetzung von $ aussehen wollen:

($) :: (a -> b) -> a -> b 
f $ x = f x 

Dies bedeutet, wird diese x ausgewertet werden, wenn durch f erforderlich. Bis dahin haben wir, dass

f $ expression for x = f (expression for x) 

In Ihrem Fall haben wir, dass

x = hClose port >> (return (Bool True)) 
f = liftIO 

was bedeuten würde, dass

f $ expression for x = f (expression for x) 
        = liftIO (expression for x) 
        = liftIO (hClose port >> (return (Bool True))) 

Ich hoffe, dass klärt sie.

+1

„Das bedeutet, dass es bewerten muss 'X', bevor es' f' anwenden können.“Ähm, nein. Semantisch ist '$' eine normale Funktionsanwendung: 'f' muss ausgewertet werden, bevor wir' f' auf 'x' anwenden, aber' x' wird nur ausgewertet, wenn 'f' es erfordert. (Der einzige Unterschied zwischen '$' und normalen Funktionsanwendungen ist syntaktisch.) Vielleicht verwirren Sie '$' mit ['$!'] (Http://www.haskell.org/ghc/docs/latest/html/libraries /base/Prelude.html#v:-36--33-)? – dave4420

+0

Versuchen Sie mit 'f = const 42' und' x = undefiniert', und Sie werden sehen, dass 'x' nicht ausgewertet werden muss. – hammar

+0

Es wurde behoben, sollte jetzt wahr sein. – Undreren

14

Sie müssen über den Vorrang in diesem Fall nicht wirklich Sorge, weil

liftIO (hClose port >> return (Bool True)) 

und

liftIO (hClose port) >> return (Bool True) 

aufgrund der Monade Transformator Gesetze gleichwertig sein müssen, die sagen, dass

  1. Heben return tut nichts.

  2. Das Anheben einer Sequenz von zwei Aktionen ist das gleiche wie das separate Anheben.

    lift (m >>= f) = lift m >>= (lift . f) 
    

liftIO sollte folgen auch diesen Gesetzen, so können wir die

liftIO (hClose port >> return (Bool True)) 
= -- definition of >> 
liftIO (hClose port >>= \_ -> return (Bool True)) 
= -- second monad transformer law 
liftIO (hClose port) >>= \_ -> liftIO (return (Bool True)) 
= -- first monad transformer law 
liftIO (hClose port) >>= \_ -> return (Bool True) 
= -- definition of >> 
liftIO (hClose port) >> return (Bool True) 
+0

Das ist wahr und wichtig zu wissen, aber ich glaube nicht wirklich, dass es die Frage beantwortet, die ich über die lexikalische Syntax gelesen habe. Sie behaupten, dass 'liftIO $ hClose port >> zurück (Bool True) === liftIO (hClose port >> return (Bool True))', aber sage nicht warum, was der Kernpunkt der Frage zu sein scheint. –

+1

@JohnL Er sagt genau warum die gleich sind. Aber Sie haben einen Punkt - er beantwortet nicht die wörtliche Frage nach der Rangfolge der Operatoren. – delnan

+0

@delnan: Wenn Sie nicht wissen, wie man eine Sprache analysiert, wie können Sie sagen, dass zwei Ausdrücke äquivalent sind? –

1

Operatoren mit höherer Priorität bindet stärker als Betreiber mit geringer Priorität sehen.

Um die Reihenfolge zu ermitteln, wenn sich mehrere Operatoren in einem Ausdruck befinden, beginnen Sie mit dem Operator mit der höchsten Priorität und setzen Sie Klammern um die Ausdrücke auf jeder Seite, und setzen Sie dann die Operatorrangfolge fort. Für Operatoren derselben Ebene legen Sie die Reihenfolge anhand ihrer definierten Assoziativität fest. Es ist nicht erlaubt, Operatoren der gleichen Rangstufe, aber unterschiedlicher assoziativer Verhaltensweisen zu mischen, da die Gruppierung dann mehrdeutig ist. Das Verfahren ist wohl bekannt aus der Arbeit mit numerischen Operatoren:

2 + 3 * 5 - 1 + 2 
-- * is infixl 7 
2 + (3 * 5) - 1 + 2 
-- + and - are infixl 6, so apply parens starting at the left 
(2 + (3 * 5) - 1) + 2 

((2 + (3 * 5)) - 1) + 2

Da >> hat eine höhere Priorität als $, die gleiche Behandlung Anwendung zu

liftIO $ hClose port >> (return $ Bool True) 

gibt Ihnen

liftIO $ (hClose port >> (return $ Bool True)) 

Zuerst werden also die hClose und return $ Bool True zu einem Ausdruck mit dem Typ IO (Bool') kombiniert, der dann mit liftIO aufgehoben wird. (Wo Bool' ist welcher Typ Bool True hat).

Die Haskell Report bietet eine gründliche Behandlung der Syntax, insbesondere Kapitel 2, 3 und 9.