2016-04-10 20 views
2

Ich habe gerade etwas über Monaden gelernt, und ich habe versucht, viele der Funktionen in Control.Monad zu implementieren. Ich habe gerade ap, aber ich kann es nicht funktionieren. Ich machte eine Funktion almostAp :: Monad m => m (a -> b) -> m a -> m (m b), und ich habe versucht, es mit einer anderen Funktion, die ich machte, flatten :: Monad m => m (m b) -> m b zu komponieren. Das Problem ist, wenn ich versuche, ap = flatten . almostAp zu verwenden, ichFehler beim Versuch, monadische Funktionen zu erstellen

bekommen
Occurs check: cannot construct the infinite type: m ~ (->) (m a) 
    Expected type: m (a -> b) -> m a -> m a -> m b 
    Actual type: m (a -> b) -> m a -> m (m b) 
In the second argument of ‘(.)’, namely ‘almostAp’ 
In the expression: (flatten . almostAp)` 

Aber (flatten .) Typ Monad m => (a -> m (m b)) -> a -> m b hat nach GHCI, also warum geschieht dies?

Hier sind die Funktionsdefinitionen (ich weiß, ich sie mit =<< reinigen kann und die Funktors Gesetze):

almostAp :: Monad m => m (a -> b) -> m a -> m (m b) 
almostAp = (flip (\x -> fmap ($x))) . (fmap (flip (>>=))) . (fmap (return .)) 

flatten :: Monad m => m (m a) -> m a 
flatten = (>>= id) 
+4

Sie scheinen sehr auf Point-Free-Stil, der Ihren Code ziemlich schwer zu lesen macht (und ich kann mir vorstellen, ziemlich schwer zu schreiben, auch!) Haben Sie keine Angst vor "Let' Bindings" oder "Do" Notation. –

+2

"Lass uns vom punktfreien Stil gehen: benutze es nur' wo' es macht Sinn, und du wirst besser "tun" – Cactus

Antwort

5

Sie haben Ihre Art Fehler bekommen, weil Sie eine einargumentigen zu komponieren versuchten Funktion flatten (die übrigens normalerweise mit dem Namen join geht) mit einer zwei-Argument Funktion almostAp. (.) :: (b -> c) -> (a -> b) -> (a -> c) ist gemeint, wenn die Funktion auf der rechten Seite eine Ein-Argument-Funktion ist. Leider war die Fehlermeldung nicht sehr hilfreich.

(.).(.) (ausgesprochen "Punkt -dot- Punkt" oder "Brüste") tut, was Sie wollen:

ghci> let ap = ((.).(.)) flatten almostAp 
ghci> :t ap 
ap :: Monad m => m (a -> b) -> m a -> m b 

Aber ap kann viel einfacher mit do Notation umgesetzt werden. Ist deine Version wirklich einfacher zu verstehen?

ap mf mx = do 
    f <- mf 
    x <- mx 
    return (f x) 

do Notation ist nur syntaktischer Zucker für >>=. Hier ist, wie es ohne es aussieht (obwohl ich viel die do Version bevorzugen):

ap mf mx = mf >>= \f -> fmap f mx 
+2

'(.). (.)' Ist "Eule Augen" in meinem Kopf, die scheint eher mehr ... familienfreundlich. – dfeuer

3

Aber (flatten .) hat Typ Monad m => (a -> m (m b)) -> a -> m b nach GHCI, also warum geschieht dies?

Tatsächlich tut es. Bedenken Sie:

flatten :: Monad m => m (m a) -> m a 

almostAp :: Monad m => m (a -> b) -> m a -> m (m b) 

Somit ist die Art von (flatten .) ist:

flatten  :: Monad m => m (m a) -> m a -- renaming a to b 
          |  | | | 
          ------- --- 
          |  | 
(.)   ::    (b -> c) -> (a -> b) -> a -> c 
                |    | 
                -------   --- 
                |  |   | | 
(flatten .) :: Monad m =>     (a -> m (m b)) -> a -> m b 

jedoch nicht (flatten .) zu almostAp weil die Typen nicht kompatibel sind anwenden können:

diese
almostAp :: Monad m => m (a -> b) -> m a -> m (m b) 
          |  | |   | 
          ---------- -------------- 
           |    | 
           |   ------- 
           |   |  | 
(flatten .) :: Monad m => ( a  ->  m (m b)) -> a -> m b 

Sie erwartet :

almostAp :: Monad m => m (a -> b) -> m a -> m (m b) 
          |    | |  | 
          ----------------- ------- 
            |    | 
            |   ------- 
            |   |  | 
(flatten .) :: Monad m => (  a   -> m (m b)) -> a -> m b 

Aber das ist nicht der Weg currying funktioniert. Eine Funktion des Typs a -> b -> c bedeutet a -> (b -> c) und nicht (a -> b) -> c.Die erste Funktion a -> (b -> c) benötigt zwei Argumente , a und b und gibt c zurück. Die zweite Funktion (a -> b) -> c benötigt ein Argument a -> b und gibt c zurück.

Also, wie komponieren Sie flatten und almostAp? Sie können es nicht normale Funktion Zusammensetzung verwendet, weil almostAp zwei Argumente erfordert:

(.) :: (b -> c) -> (a -> b) -> a -> c 
     |  | |  | 
     -------- -------- 
      |   | 
     flatten  +-- almostAp can't be used because it needs two arguments 
         -- but (.) only gives it one argument (the `a` in a -> b) 

Wir brauchen eine spezielle Zusammensetzung Betreiber sie zu komponieren:

(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d 
     |  | |   | 
     -------- ------------- 
      |    | 
     flatten  almostAp 

(.:) f g x y = f (g x y) 

Jetzt können wir einfach flatten .: almostAp schreiben. Eine andere Möglichkeit, es zu schreiben wäre (flatten .) . almostAp. Dies liegt daran, (.:) = (.) . (.). Lesen Sie die folgenden für weitere Details:

What does (f .) . g mean in Haskell?


eigentlich eine Funktion des Typs a -> (b -> c) nimmt nur ein Argument a und gibt eine weitere Funktion b -> c, die das zweite Argument nimmt b und gibt c.

Verwandte Themen