2014-09-10 11 views
12

Ich versuche, etwas über Objektive zu lernen, indem ich es in Haskell implementiere. Ich habe die view combinator wie folgt umgesetzt:Haskell-Objektive: wie man mit Traverse schön spielen kann?

{-# LANGUAGE RankNTypes #-} 

import Control.Applicative 
import Data.Traversable 

type Lens s a = Functor f => (a -> f a) -> s -> f s 

view :: Lens s a -> s -> a 
view lens = getConst . lens Const 

aber wenn ich versuche, es mit traverse in Verbindung zu verwenden, erhalte ich die folgende Fehlermeldung:

Prelude> :load Lens.hs 
[1 of 1] Compiling Main    (Lens.hs, interpreted) 
Ok, modules loaded: Main. 
*Main> :t view traverse 

<interactive>:1:6: 
    Could not deduce (Applicative f) arising from a use of ‘traverse’ 
    from the context (Traversable t) 
     bound by the inferred type of it :: Traversable t => t a -> a 
     at Top level 
    or from (Functor f) 
     bound by a type expected by the context: 
       Functor f => (a -> f a) -> t a -> f (t a) 
     at <interactive>:1:1-13 
    Possible fix: 
     add (Applicative f) to the context of 
     a type expected by the context: 
      Functor f => (a -> f a) -> t a -> f (t a) 
     or the inferred type of it :: Traversable t => t a -> a 
    In the first argument of ‘view’, namely ‘traverse’ 
    In the expression: view traverse 

Leider verstehe ich nicht diesen Fehler Botschaft. Bitte erläutern Sie, was es bedeutet und wie ich es beheben kann.

Antwort

10

Wie die anderen Antworten bereits erklären, das Problem ist, dass view etwas erwartet, die für jede Functor f funktioniert, aber traverse funktioniert nur, wenn f auch ist Applicative (und es gibt functors, die nicht applicative sind).

In lens, das Problem, indem sie die Art des view nicht nehmen Rank2 Argument gelöst wird (in der Tat, die meisten Funktionen in Objektiv verwenden Sie nicht das Synonym Objektivtyp, verwenden sie immer etwas schwächer). Beachten Sie für Ihre Funktion, dass view nur f ~ Const verwendet. Aus diesem Grund ist der Typ Signatur ändern können:

view :: ((a -> Const a a) -> s -> Const a s) -> s -> a 

Die Implementierung kann gleich bleiben, aber jetzt view funktioniert auch auf traverse:

view traverse :: (Traversable t, Monoid a) => t a -> a 

Beachten Sie die zusätzliche Monoid Einschränkung. Diese Einschränkung wird angezeigt, wenn Sie f ~ Const a in traverse :: (Traversable t, Applicative f) => (a -> f a) -> t a -> f (t a) setzen, benötigen Sie eine Instanz Applicative (Const a). Diese Instanz hat jedoch eine Monoid Beschränkung auf a. Und das ist auch sinnvoll, weil der Traversable leer sein oder mehr als ein Element enthalten kann, also benötigen Sie mempty und mappend.

+1

Große Antwort. Vielen Dank für eine kluge Lösung des Problems. Ich habe allerdings eine Frage: Würde die Typ-Signatur von 'view' nicht' ((a -> Const a a) -> s -> Const a s) -> s -> a '? –

+0

@AaditMShah oh, du hast Recht. Ich werde das korrigieren – bennofs

3

traverse hat diese Art:

traverse :: (Applicative f, Traversable t) => (x -> f y) -> t x -> f (t y) 

Wenn wir die freie Variable f in der Typdefinition von Lens explizit, seine Definition ist eigentlich

type Lens s a = forall f . Functor f => (a -> f a) -> s -> f s 

So view will eine Funktion machen, die arbeiten können auf beliebigFunctor, während traverse nur auf einem Applicative arbeiten kann.

Sie können die Fehler beheben einfach durch Functor in Applicative in der Definition von Lens zu ändern, aber ich bin nicht sicher, ob das ist genau das, was Sie hier erreichen möchten.

+2

Wenn Sie 'Functor' in' Applicative' umändern, erhalten Sie einen 'Traversal', oder? Ich denke, das gibt eine gewisse Intuition dafür, woher der Name "Traversal" kommt. –

3

tl; dr - Nach Ihrer Definition von Lens kann ein traverse nicht ein Lens weil traverse nicht für alle Functor s nicht.


Lassen Sie uns einen Blick auf Ihre Arten nehmen:

λ :set -XRankNTypes 
λ :m +Control.Applicative Data.Traversable 
λ type Lens s a = Functor f => (a -> f a) -> s -> f s 
λ :t traverse 
traverse 
    :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b) 

An diesem Punkt können wir sehen, dass traverse ist, in einer Art und Weise, etwas allgemeiner als unsere Lens Art - es ist eine Funktion übernehmen kann von a -> f b, wo unser Objektiv nur Funktionen von a -> f a übernehmen kann.

es zu diesem Fall Beschränkung ist kein Problem, so können wir also jetzt

λ :t traverse :: (Traversable t, Applicative f) => (a -> f a) -> t a -> f (t a) 
traverse :: (Traversable t, Applicative f) => (a -> f a) -> t a -> f (t a) 
    :: (Applicative f, Traversable t) => (a -> f a) -> t a -> f (t a) 

sagen, es ist offensichtlich, dass, wenn traverse ein Lens sein, es ist ein Lens (t a) a sein muss, denn das ist der einzige Weg zu machen Die Typvariablen stehen in einer Linie.

Also lasst uns das ausprobieren.

λ :t traverse :: Lens (t a) a 

<interactive>:1:1: 
    Could not deduce (Traversable t1) arising from a use of `traverse' 
    from the context (Functor f) 
     bound by the inferred type of 
       it :: Functor f => (a -> f a) -> t a -> f (t a) 
     at Top level 
    or from (Functor f1) 
     bound by an expression type signature: 
       Functor f1 => (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1) 
     at <interactive>:1:1-24 
    Possible fix: 
     add (Traversable t1) to the context of 
     an expression type signature: 
      Functor f1 => (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1) 
     or the inferred type of 
      it :: Functor f => (a -> f a) -> t a -> f (t a) 
    In the expression: traverse :: Lens (t a) a 

Oof, es mochte das nicht. Oh, warte, um traverse zu verwenden, muss unser Typ tTraversable sein, also fügen wir diese Einschränkung hinzu.(Genau wie die „Mögliche fix“) schlägt vor:

λ :t traverse :: Traversable t => Lens (t a) a 

<interactive>:1:1: 
    Could not deduce (Applicative f1) arising from a use of `traverse' 
    from the context (Functor f, Traversable t) 
     bound by the inferred type of 
       it :: (Functor f, Traversable t) => (a -> f a) -> t a -> f (t a) 
     at Top level 
    or from (Traversable t1, Functor f1) 
     bound by an expression type signature: 
       (Traversable t1, Functor f1) => 
       (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1) 
     at <interactive>:1:1-41 
    Possible fix: 
     add (Applicative f1) to the context of 
     an expression type signature: 
      (Traversable t1, Functor f1) => 
      (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1) 
     or the inferred type of 
      it :: (Functor f, Traversable t) => (a -> f a) -> t a -> f (t a) 
    In the expression: traverse :: Traversable t => Lens (t a) a 

Ok, so dass das Problem ist jetzt, dass es nicht ableiten kann, dass f ist Applicative nur (was auch erforderlich ist traverse zu verwenden), dass es ein Functor ist (was es von der Definition von Lens erhält).

Wir können Applicative f jedoch nicht zum Kontext hinzufügen - die f ist ausgeblendet. Wenn wir type Lens s a = Functor f => (a -> f a) -> s -> f s sagen, sagen wir, dass die Lensfür alleFunctor s arbeiten muss.

Aber traverse funktioniert nur für die Teilmenge von Functor s, die auch Applicative sind. Auf diese Weise ist der Typ traversegenauer als für Lens es zulässig ist.