2015-04-06 7 views
5

Я отвод вокруг с бесплатными монадами и объективом, с помощью бесплатной монады, чтобы создать свою собственную версию монады IO:Трудности с зумом и свободными монадами

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

Я штабелирование это на вершине государственной монады как так: FreeT MyIO (State GameState) a где GameState является:

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

Теперь, что я хотел бы иметь способ «зум-в» а PlayerState из GameState контекста. Что-то вроде этого:

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

Но я получаю эту ошибку:

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

Эта ошибка кажется, связанная с тем, что players . element i является обходом; если я удалю список из _players и использую обычный объектив, тогда код работает.

Любые идеи о том, как написать эту функцию?

+5

Не будет ли проще иметь 'StateT GameState (Free MyIO) a'? Если вы хотите позже поменять «Free MyIO» на «IO», тогда это будет обычным способом, так как нет трансформаторной версии 'IO'. – bheklilr

+1

Кроме того, проблема связана с тем, что индексирование списка не является безопасной. Библиотека объективов хочет, чтобы вы возвращали тип, который можно использовать по умолчанию, а стандартным классом для этого является «Monoid». Если вы просто добавите 'Monoid a =>' в подпись типа 'zoomPlayer', тогда вы будете установлены. Это, конечно, ограничивает то, что вы можете вернуть, но вам придется, если вы не хотите писать больше кода. – bheklilr

+0

@bheklir Плохо, «ошибка» была опечаткой; это было фактически 'a1'. Прошу прощения за путаницу. Кажется, что 'FreeF' - это то, что он ожидает быть моноидом, но у меня нет способа написать экземпляр для этого. Возможно, я мог бы временно обернуть его в список, а затем развернуть. – Pubby

ответ

2

Если вы уверены, что вы никогда не индекс в несуществующий игрок и не против немного небезопасности, вы можете использовать unsafeSingular комбинатора превратить Traversal в Lens, как это:

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

(также, возможно, я хотел бы использовать ix вместо element, но это не связанно с проблемой.)

Мы также можем построить безопасную индексацию линзу для всегда бесконечных последовательностей, как потоки, определенных с использованием Cofree из free упаковка:

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' 

Но игра вряд ли будет иметь бесконечных игроков.

 Смежные вопросы

  • Нет связанных вопросов^_^