2014-11-19 11 views
6

Ich prüfe einige Codes und kam über das folgende Juwel, das ich wetten würde, ist eine copy-paste von pointfree Ausgabe:Was sind die Schritte, um diesen Pointfree-Code abzuleiten?

(Ich dachte, die folgende wäre besser geeignet als die üblichen foo/bar für diese spezielle Frage : P)

import Control.Monad (liftM2) 

data Battleship = Battleship { x :: Int 
          , y :: Int 
          } deriving Show 

placeBattleship :: Int -> Int -> Battleship 
placeBattleship x' y' = Battleship { x = x', y = y' } 

coordinates :: Battleship -> (Int, Int) 
coordinates = liftM2 (,) x y 

Wäre jemand so freundlich, von notwendig, um die Schritte zu erklären, zu vereinfachen:

(i) coordinates b = (x b, y b)

zu:

(ii) coordinates = liftM2 (,) x y?

Insbesondere bin ich ein wenig verwirrt in Bezug auf die Verwendung von liftM2, da ich nicht einmal bewusst war, dass eine Monade im Hintergrund lauerte.

Ich weiß, dass (i) kann auch dargestellt werden als: coordinates s = (,) (x s) (y s), aber ich bin mir nicht sicher, wo/wie vorzugehen.


P.S. Hier finden Sie, warum ich vermute, es ist von pointfree (Ausgabe ist von GHCI und :pl ist aliased zu pointfree):

λ: :pl coordinates s = (x s, y s) 
coordinates = liftM2 (,) x y 

Antwort

9

Dieser Vorteil der Monad Instanz nimmt für (->) r, auch die „Leser Monade“ genannt. Dies ist die Monade der Funktionen von ein spezifischer Typ zu a. (Werfen Sie einen Blick here für die Motivation, warum es existiert in dem ersten Platz.)

zu sehen, wie es für verschiedene Funktionen arbeitet, ersetzen m mit (r -> in m a. Zum Beispiel, wenn wir liftM nur tun, erhalten wir:

liftM :: (a -> b) -> (m a -> m b) 
liftM :: (a -> b) -> ((r -> a) -> (r -> b)) 
     :: (a -> b) -> (r -> a) -> (r -> b) -- simplify parentheses 

..., die gerade Funktion Zusammensetzung ist. Ordentlich.

Wir können für liftM2 das gleiche tun:

liftM2 :: (a -> b -> c) -> m a -> m b -> m c 
liftM2 :: (a -> b -> c) -> (r -> a) -> (r -> b) -> (r -> c) 

Also, was wir sehen, ist ein Weg, zwei einargumentigen Funktionen mit zwei Argumenten Funktion zu komponieren. Es ist eine Möglichkeit, die normale Funktionszusammensetzung auf mehr als ein Argument zu verallgemeinern. Die Idee ist, dass wir eine Funktion erstellen, die einen einzigen r durch die Übergabe durch beide der Ein-Argument-Funktionen, erhalten zwei Argumente, um in die Zwei-Argument-Funktion übergeben. Also, wenn wir haben f :: (r -> a), g :: (r -> b) und h :: (a -> b -> c) produzieren wir:

\ r -> h (f r) (h r) 

Nun, wie sich dies auf Ihren Code anwenden? (,) ist die Zwei-Argument-Funktion, und x und y sind One-Argument-Funktionen des Typs Battleship -> Int (weil so funktioniert Feld Accessors). In diesem Sinne:

liftM2 (,) x y = \ r -> (,) (x r) (y r) 
       = \ r -> (x r, y r) 

Sobald Sie die Idee der Multifunktions Zusammensetzung wie dies verinnerlicht haben, pointfree Code wie diesen wird ziemlich viel lesbarer-keine Notwendigkeit, das pointfree Werkzeug zu benutzen! In diesem Fall denke ich, dass die nicht punktfreie Version immer noch besser ist, aber die punktfreie Version ist selbst nicht schrecklich.

5

Die Monade liftM2 arbeitet hier ist die Funktion Monade (->) a. Dies entspricht der Reader Monade, wie Sie vielleicht schon einmal gesehen haben.

Recall die Definition von liftM2:

liftM2 :: Monad m => (a -> b -> r) -> m a -> m b -> m r 
liftM2 f ma mb = do 
    a <- ma 
    b <- mb 
    return $ f a b 

So, jetzt, wenn wir in (,) für f, x für ma ersetzen, und y für mb, erhalten wir

liftM2 (,) x y = do 
    a <- x 
    b <- y 
    return $ (,) a b 

Da x, y :: Battleship -> Int das entspricht zu ((->) Battleship) Int, dann m ~ (->) Battleship. Die Funktion Monade ist definiert als

instance Monad ((->) a) where 
    return x = const x 
    m >>= f = \a -> f (m a) a 

Wesentlichen die Funktion Monade was tut, ist können Sie die Ausgabe von mehreren Funktionen extrahieren, sofern sie alle den gleichen Eingang. Ein anschauliches Beispiel könnte sein, so etwas wie

test = do 
    a <- (^2) 
    b <- (^3) 
    c <- (^4) 
    d <- show 
    return (a, b, c, d) 

> test 2 
(4, 8, 16, "2") 
1

könnten Sie leicht umschreiben

data Battleship = Battleship { x :: Int 
          , y :: Int 
          } deriving Show 

placeBattleship :: Int -> Int -> Battleship 
placeBattleship x y = Battleship x y 

coordinates :: Battleship -> (Int, Int) 
coordinates (Battleship x y) = (x, y) 

Es ist Punkt-freien Stil nicht, aber ganz einfach