2013-07-09 10 views
8

Sto usando il codice seguente su un record che ha un campo "_scene" di tipo SceneGraph. Ho creato degli obiettivi usando i makeLenses.Perché il mio tipo di dati necessita di un'istanza di Monoid per utilizzare questo obiettivo?

inputGame :: Input -> Game -> Game 
inputGame i g = flip execState g $ do 
    let es = g ^. userInput . events 
     sg = g ^. scene 
    userInput .= i 
    scene .= foldl (flip inputEvent) sg es 

inputEvent :: InputEvent -> SceneGraph -> SceneGraph 
inputEvent (WindowSizeChangedTo (w,h)) (SceneRoot _ _ sg) = SceneRoot w h sg 
inputEvent _ sg = sg 

sto ottenendo l'errore:

No instance for (Monoid SceneGraph) arising from a use of `scene' 
Possible fix: add an instance declaration for (Monoid SceneGraph) 
In the second argument of `(^.)', namely `scene' 
In the expression: g ^. scene 
In an equation for `sg': sg = g ^. scene 

Ma io non capisco perché scenegraph deve essere un'istanza di Monoide al fine di utilizzare questo obiettivo.

risposta

14

probabilmente si desidera sia (^?), o forse (^..) (i nomi non operatore: preview, toListOf).

Quando hai un Lens (o un Getter, Iso, Equality, ecc), si riferisce sempre ad esattamente un elemento. Quindi puoi usare semplicemente il vecchio (^.) (nome non-operatore: view). Se hai uno Traversal (o uno Fold, Prism, ecc.), Può fare riferimento a 0 o più elementi.

Pertanto deve esserci un modo per combinarli se ce n'è più di uno, o un valore predefinito da fornire se non ce ne sono. Questo viene fatto con il vincolo Monoid. toListOf ti fornisce un elenco di tutti i valori; preview fornisce Nothing o Just il primo valore.

Non hai fornito i tipi per nessuna delle funzioni che stai utilizzando, quindi non posso davvero dire cosa intendi. La mia ipotesi sarebbe che forse scene può fallire perché hai usato makeLenses con un tipo di somma che non definisce scene in ogni summand. In questo caso probabilmente vorrai usare (^?) e gestire il caso Nothing. Ma potrebbe essere qualcos'altro.

Vedere anche la mia risposta a this question (e this question da ieri! Questo sembra essere un argomento popolare).

+0

Ah! Ecco perché 'makeLenses' creerebbe un prisma. –

+1

Ok, penso di averlo capito. È perché il mio tipo di dati sembra dati Game = GameLoad | Gioco {_scene :: SceneGraph} | GameOver quindi è possibile che 'scene' possa fallire perché il gioco è uno degli altri due costruttori ... – schellsan

+0

Aha. Sì. Devi gestire i casi 'GameLoad' /' GameOver'. – shachaf

1

Suppongo che si stia utilizzando la libreria di obiettivi di Ed Kmett, sarebbe utile se si potesse anche pubblicare la versione che si sta utilizzando e le importazioni. Sembra anche che ci siano due versioni di (^.) Supportate in quella libreria di obiettivi, una con Getter e una con Fold, la versione fold che richiede un'istanza di Monoid: (^.) :: Monoid r => s - > Fold sr -> R

Edit: sto scommettendo che accidentalmente importato Control.Lens.Fold

+3

No, c'è solo un '(^.)'. Se usato con una piega/attraversamento viene aggiunto il vincolo 'Monoid'. – shachaf

+0

Qual è il vantaggio di richiedere che il risultato sia un Monoid? – schellsan

+2

Consente il ritorno di un singolo risultato anche quando l'obiettivo è effettivamente focalizzato su valori 0 ('mempty') o multipli (' mconcat'). –

Problemi correlati