2015-04-06 21 views
5

ja mucking z bezpłatnych monad i obiektyw, korzystając z bezpłatnego monady stworzyć własną wersję monady IO:trudności z zoomem i wolnych monad

data MyIO next 
    = LogMsg String next 
    | GetInput (String -> next) 
    deriving (Functor) 

jestem układania to na szczycie monady państwowej tak: FreeT MyIO (State GameState) a gdzie GameState jest:

data GameState = GameState { _players :: [PlayerState] } 

teraz, co chciałbym mieć to sposób na "zoom-do" a PlayerState od kontekstu GameState. Coś takiego:

zoomPlayer :: Int -> FreeT MyIO (State PlayerState) a -> FreeT MyIO (State GameState) a 
zoomPlayer i prog = hoistFreeT (zoom (players . element i)) prog 

Ale ja dostaję ten błąd:

No instance for (Data.Monoid.Monoid a1) 
    arising from a use of ‘_head’ 

Błąd ten wydaje się mieć związek z faktem, że players . element i jest przechodzenie; jeśli usunę ten aspekt listy z _players i użyję zwykłego obiektywu, wtedy kod działa.

Wszelkie pomysły na temat napisania tej funkcji?

+5

Czy nie byłoby łatwiej zamiast tego mieć 'StateT GameState (Free MyIO) a'? Jeśli chcesz później zamienić 'Free MyIO' na' IO', to będzie to zwykły sposób, ponieważ nie ma wersji transformatora 'IO'. – bheklilr

+1

Problem wynika również z faktu, że indeksowanie listy nie jest bezpieczną operacją. Biblioteka obiektywów chce, abyś zwrócił typ, który może być ustawiony domyślnie, a standardową czcionką jest "Monoid". Jeśli po prostu dodasz 'Monoid a =>' do sygnatury typu 'zoomPlayer', to zostaniesz ustawiony. To ogranicza oczywiście to, co możesz zwrócić, ale będziesz musiał, chyba że chcesz napisać więcej kodu. – bheklilr

+0

@bheklir Mój zły, "a" w błędzie był literówka; faktycznie było to "a1". Przepraszam za zamieszanie. Wygląda na to, że 'FreeF' jest tym, czego oczekuje się od monoidu, ale nie mam sposobu na napisanie takiej instancji. Może mógłbym tymczasowo zawinąć ją na listę, a następnie ją rozwinąć. – Pubby

Odpowiedz

2

Jeżeli jesteś pewien, że nigdy nie wskaźnik do nieistniejącego odtwarzacza i nie przeszkadza trochę unsafety, można użyć unsafeSingular COMBINATOR obrócić Traversal do Lens, tak:

zoomPlayer :: Int -> FreeT MyIO (State PlayerState) a -> FreeT MyIO (State GameState) a 
zoomPlayer i prog = hoistFreeT (zoom (players . unsafeSingular (element i))) prog 

(również, może użyłbym ix zamiast element, ale to bez związku z problemem).

Możemy również konstruować bezpieczne soczewki indeksowania zawsze nieskończonych sekwencji, jak strumienie zdefiniowane za pomocą Cofree odPakiet:

import Control.Lens (Lens', _Wrapped') 
import Control.Comonad.Cofree (Cofree, telescoped) 
import Data.Functor.Identity 
import Control 

sureIx :: Int -> Lens' (Cofree Identity a) a 
sureIx i = telescoped $ replicate i _Wrapped' 

Jednak jest mało prawdopodobne, aby gra zawierała nieskończoną liczbę graczy.