Comment puis-je comprendre": t ((==) )" à Haskell?

Je suis nouveau à Haskell, ici avoir des ennuis avec <*>:

((==) <*>) :: Eq a => (a -> a) -> a -> Bool

Comment puis-je comprendre cela et comment cela peut être déduit?

27
demandé sur Will Ness 2013-09-15 18:21:09

1 réponses

Avertissement: Ce n'est pas du code Haskell idiomatique.

La première chose qui prime est la "section opérateur" de <*>. Lorsque vous voyez un opérateur appliqué uniquement à un argument appelé section. Voici un exemple d'une section d'opérateur plus commune:

(1 +) :: Int -> Int

C'est une fonction qui applique partiellement + à 1, laissant place à un dernier argument. C'est équivalent à:

\x -> 1 + x

Donc, dans votre exemple de code, le <*> est partiellement appliqué à (==), donc nous allons étendre cela à:

((==) <*>)

= \g -> (==) <*> g

Ensuite, vous devez comprendre ce que fait le <*>. C'est un membre de la classe de type Applicative:

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

Cela signifie que <*> est surchargé pour fonctionner sur tout type qui implémente Applicative. Une instance du type Applicative est ((->) r):

instance Applicative ((->) r) where
    pure  :: a -> ((->) r) a
    (<*>) :: (->) r (a -> b) -> (->) r a -> (->) r b

Les parenthèses autour du (->) signifient qu'il est utilisé sous forme de préfixe (ce qui est nécessaire pour des raisons syntaxiques lors de la définition d'instances de classe comme celle-ci). Si vous développez pour infix forme que vous obtenez:

instance Applicative ((->) r) where
    pure  :: a -> r -> a
    (<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)

Dans votre exemple spécifique, le premier argument de <*> est l'opérateur (==), qui a le type suivant:

(==) :: Eq e => e -> e -> Bool

Par conséquent, si nous le passons au (<*>) le compilateur peut en déduire plus sur les types de r, a, et b dans la signature pour (<*>):

 (<*>) ::        (r -> a -> b   ) -> (r -> a) -> (r -> b)
 (==)  :: Eq e => e -> e -> Bool
                  |    |    |
                  |    |    +-> `b` must be `Bool`
                  |    |
                  |    +------> `a` must be `e`
                  |
                  +-----------> `r` must also be `e`

Ainsi, lorsque nous fournissons (==) comme premier argument de (<*>) nous obtenons ce type déduit:

((==) <*>) :: Eq e => (e -> e) -> (e -> Bool)

Vous pouvez laisser les parenthèses droites parce que (->) est droit-associatif, et changez e en a pour donner la signature finale que vous avez obtenue:

((==) <*>) :: Eq a => (a -> a) -> a -> Bool

, Mais qu'est-ce que cela fait? Pour comprendre que nous devons voir comment (<*>) est défini pour l'instance Applicative de ((->) r):

(f <*> g) r = f r (g r)

Si nous remplaçons f par (==) nous obtenons:

((==) <*> g) r = (==) r (g r)

Lorsque nous entourons (==) de parenthèses, cela signifie que nous l'utilisons en notation de préfixe. Cela signifie que si nous supprimons les parenthèses et les changeons en notation infixe nous obtenir:

((==) <*> g) r = r == (g r)

, Ceci est équivalent à:

((==) <*>) = \g r -> r == g r

Cela signifie Donc que votre fonction prend deux paramètres: g et r, puis voit si r est égal à g r.

56
répondu Gabriel Gonzalez 2013-09-15 15:16:50