2016-01-05 8 views
19

Dort war ich, eine Funktion zu schreiben, die einen Wert als Eingabe nimmt, ruft eine Funktion für diese Eingabe auf, und wenn das Ergebnis davon Just x ist, sollte es x zurückgeben; Andernfalls sollte die ursprüngliche Eingabe zurückgegeben werden.Wie kann ap aus Maybe komponieren?

Mit anderen Worten, diese Funktion (die ich nicht wusste, was zu nennen):

foo :: (a -> Maybe a) -> a -> a 
foo f x = fromMaybe x (f x) 

Da es wie eine Allzweck-Funktion scheint, fragte ich mich, wenn es nicht bereits definiert wurde, so I asked on Twitter und Chris Allen replied, dass es ap fromMaybe ist.

Das vielversprechend klang, so feuerte ich GHCI und begann zu experimentieren:

Prelude Control.Monad Data.Maybe> :type ap 
ap :: Monad m => m (a -> b) -> m a -> m b 
Prelude Control.Monad Data.Maybe> :type fromMaybe 
fromMaybe :: a -> Maybe a -> a 
Prelude Control.Monad Data.Maybe> :type ap fromMaybe 
ap fromMaybe :: (b -> Maybe b) -> b -> b 

Die Art der ap fromMaybe sicher richtig aussieht, und ein paar Experimente scheinen zu zeigen, dass sie das Verhalten als auch gewünscht hat.

Aber wie funktioniert es?

Die fromMaybe Funktion scheint mir klar, und in Isolation, ich glaube, ich verstehe, was ap tut - zumindest im Rahmen der Maybe. Wenn mMaybe ist, hat es den Typ Maybe (a -> b) -> Maybe a -> Maybe b.

Was ich nicht verstehe ist, wie ap fromMaybe sogar kompiliert. Dieser Ausdruck sieht für mich wie eine partielle Anwendung aus, aber ich kann das falsch verstehen. Wenn dies der Fall ist, verstehe ich nicht, wie die Typen zusammenpassen. Das erste Argument zu ap ist m (a -> b), aber fromMaybe hat den Typ a -> Maybe a -> a Wie passt das zusammen? Welche Monad Instanz schließt der Compiler, dass m ist? Wie wird fromMaybe, die zwei (curried) Argumente benötigt, zu einer Funktion, die ein einzelnes Argument benötigt?

Kann mir jemand helfen, die Punkte zu verbinden?

+5

Ich bin froh, dass Sie gefragt, weil dies mich auch verrückt machte x.x – dcastro

+1

Übrigens ist "ap von Maybe" Code verschleiert, dass hoffentlich niemand jemals in einem echten Programm schreiben würde. –

Antwort

16

Entschuldigung für lakonische und mechanische Antwort. Ich mag es nicht, Dinge wie Applicative oder Monad auszusuchen, aber ich weiß nicht, wo du bist. Dies ist not my usual approach to teaching Haskell.

Erstens, ap ist wirklich (<*>) unter der Haube.

Prelude> import Control.Monad 
Prelude> import Data.Maybe 
Prelude> import Control.Applicative 
Prelude> :t ap 
ap :: Monad m => m (a -> b) -> m a -> m b 
Prelude> :t (<*>) 
(<*>) :: Applicative f => f (a -> b) -> f a -> f b 

Was bedeutet das? Es bedeutet, dass wir nicht etwas so "Starkes" wie Monad brauchen, um zu beschreiben, was wir tun. Anwendung reicht aus. Functor tut das aber nicht.

Prelude> :info Applicative 
class Functor f => Applicative (f :: * -> *) where 
    pure :: a -> f a 
    (<*>) :: f (a -> b) -> f a -> f b 
Prelude> :info Functor 
class Functor (f :: * -> *) where 
    fmap :: (a -> b) -> f a -> f b 

Hier ap/(<*>) mit dem Vielleicht Monad/Applicative:

Prelude> ap (Just (+1)) (Just 1) 
Just 2 
Prelude> (<*>) (Just (+1)) (Just 1) 
Just 2 

Das erste, was, um herauszufinden, welcher Instanz der Applicative typeclass reden wir?

Prelude> :t fromMaybe 
fromMaybe :: a -> Maybe a -> a 

Entzuckern fromMaybe der Typ etwas gibt uns:

(->) a (Maybe a -> a) 

So ist der Typkonstruktor wir hier sind besorgt (->) ist. Was sagt uns GHCi über (->), auch bekannt als Funktionstypen?

Prelude> :info (->) 
data (->) a b -- Defined in ‘GHC.Prim’ 
instance Monad ((->) r) -- Defined in ‘GHC.Base’ 
instance Functor ((->) r) -- Defined in ‘GHC.Base’ 
instance Applicative ((->) a) -- Defined in ‘GHC.Base’ 

Hrm. Was ist mit Vielleicht?

Prelude> :info Maybe 
data Maybe a = Nothing | Just a  -- Defined in ‘GHC.Base’ 
instance Monad Maybe -- Defined in ‘GHC.Base’ 
instance Functor Maybe -- Defined in ‘GHC.Base’ 
instance Applicative Maybe -- Defined in ‘GHC.Base’ 

Was mit dem Einsatz von (<*>) geschah Vielleicht war:

Prelude> (+1) 1 
2 
Prelude> (+1) `fmap` Just 1 
Just 2 
Prelude> Just (+1) <*> Just 1 
Just 2 
Prelude> :t fmap 
fmap :: Functor f => (a -> b) -> f a -> f b 
Prelude> let mFmap = fmap :: (a -> b) -> Maybe a -> Maybe b 
Prelude> (+1) `mFmap` Just 1 
Just 2 
Prelude> :t (<*>) 
(<*>) :: Applicative f => f (a -> b) -> f a -> f b 
Prelude> let mAp = (<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b 
Prelude> :t (+1) 
(+1) :: Num a => a -> a 
Prelude> :t Just (+1) 
Just (+1) :: Num a => Maybe (a -> a) 
Prelude> Just (+1) `mAp` Just 1 
Just 2 

Okay, was ist mit dem Functor und Applicative des Funktionstypen? Einer der kniffligen Teile hier ist, dass (->) teilweise im Typ als ein Functor/Applicative/Monad angewendet werden muss. Ihr f wird also (->) a des gesamten (->) a b, wobei a ein Argumenttyp ist und b das Ergebnis ist.

Prelude> (fmap (+1) (+2)) 0 
3 
Prelude> (fmap (+1) (+2)) 0 
3 
Prelude> :t fmap 
fmap :: Functor f => (a -> b) -> f a -> f b 
Prelude> let funcMap = fmap :: (a -> b) -> (c -> a) -> c -> b 
Prelude> -- f ~ (->) c 
Prelude> (funcMap (+1) (+2)) 0 
3 

Prelude> :t (<*>) 
(<*>) :: Applicative f => f (a -> b) -> f a -> f b 
Prelude> let funcAp = (<*>) :: (c -> a -> b) -> (c -> a) -> (c -> b) 
Prelude> :t fromMaybe 
fromMaybe :: a -> Maybe a -> a 
Prelude> :t funcAp fromMaybe 
funcAp fromMaybe :: (b -> Maybe b) -> b -> b 
Prelude> :t const 
const :: a -> b -> a 
Prelude> :t funcAp const 
funcAp const :: (b -> b1) -> b -> b 

Nicht garantiert, um nützlich zu sein. Sie können sagen, funcAp const ist nicht nur von der Art interessant und wissen, wie parametrisch funktioniert.

Edit: Sprechen von komponieren, der Functor für (->) a ist nur (.). Anwendbar ist das, aber mit einem zusätzlichen Argument. Monad ist der Applicative, aber mit Argumenten gewendet.

Weitere whattery: Applicative <*> für (->) a) ist S und pure ist K der SKI Kombinator Kalkül. (Sie kann ich von K und S. ableiten Eigentlich können Sie jedes Programm aus K und S. ableiten)

Prelude> :t pure 
pure :: Applicative f => a -> f a 
Prelude> :t const 
const :: a -> b -> a 
Prelude> :t const 
const :: a -> b -> a 
Prelude> let k = pure :: a -> b -> a 
Prelude> k 1 2 
1 
Prelude> const 1 2 
1 
+4

Diese Antwort ist weder lakonisch noch mechanisch. Es ist lebendig und detailliert! –

+3

Danke, das war sehr hilfreich. Ich fand insbesondere die Definitionen von 'mFmap',' funcMap' und 'funcAp' nützlich, um mein Verständnis zu fördern. –

19

Aber diese Verwendung von ap ist nicht im Zusammenhang mit Maybe. Wir verwenden es mit einer Funktion, fromMaybe, so ist es im Rahmen der Funktionen, wo

ap f g x = f x (g x) 

Unter den verschiedenen Monad Fällen haben wir

instance Monad ((->) r) 

so ist es

ap :: Monad m => m (a  -> b) -> m a -> m b 
fromMaybe  :: r -> (Maybe r -> r) 
ap   :: (r -> (a  -> b)) -> (r -> a) -> (r -> b) 
ap     f      g  x :: b 
ap fromMaybe ::       (r -> a) -> (r -> b) , a ~ Maybe r , b ~ r 

, weil -> in Typen Associates auf der rechten Seite: a -> b -> c ~ a -> (b -> c). Wenn wir versuchen, die Typen zusammenzufügen, können wir nur mit dieser Definition oben enden.

Und mit (<*>) :: Applicative f => f (a -> b) -> f a -> f b, können wir es als (fromMaybe <*>) schreiben, wenn Sie diese Art von Graffiti mögen:

#> :t (fromMaybe <*>) 
(fromMaybe <*>) :: (r -> Maybe r) -> r -> r 

Wie zu Recht hier in einer anderen Antwort erwähnt, wenn sie mit Funktionen verwendet, <*> ist nur Ihre gute alte 'S combinator. Wir können nicht sehr gut die Funktion S in Haskell haben, also ist <*> nur ein Teil des Standardrepertoires des Point-Free-Stils der Codierung. Monadischen bind (um so mehr, gekippt), =<< kann noch mysteriöser, aber ein pointfree Coder gerade interessiert nicht und es glücklich ein anderes, ähnliches Muster zu kodieren verwenden,

(f =<< g) x = f (g x) x 

in combinatory Funktionsaufrufe, Geheimnis oder kein Geheimnis (zipWith (-) =<< drop 1 kommt in den Sinn).

8

Ich werde die Typargumente aus Gründen der Übersichtlichkeit umbenennen.

ap :: Monad m => m (a -> b) -> m a -> m b 
fromMaybe :: c -> Maybe c -> c 

Welche Monad Instanz funktioniert der Compiler daraus schließen, dass m?

((->) r) ist ein Monad. Dies sind alle Funktionen, die vom Typ r als Argument für einige spezifischer haben.

So in der Art:

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

m ~ (c ->), a ~ Maybe c und b ~ c.

Der Rückgabetyp m a -> m b wird auf (c -> Maybe c) -> c -> c erweitert. Dies ist der Typ ap fromMaybe.

6

Die Monade Sie suchen, ist (->) r oder r -> _ wenn Sie Infix Syntax bevorzugen.

Dann wird die Signatur von ap expandiert nach:

m (a -> b) -> m a -> m b = 
(r -> (a -> b)) -> (r -> a) -> r -> b = -- now we use the signature of fromMaybe 
(b -> (Maybe b -> b)) -> (b -> Maybe b) -> b -> b 

Nun, wenn Sie ap fromMaybe als teilweise angewandten Funktion betrachten und Sie voila das gewünschte Ergebnis erhalten.