Я использую StdGen
внутри большей структуры состояний и хочу реализовать класс RandomGen
в структуре состояний.Используя линзы, я придумал следующую реализацию:
module Test
( StateData(..)
, randomGen
) where
import Lens.Micro.Platform
import System.Random
data StateData = StateData
{ _randomGen :: StdGen
} deriving (Show)
randomGen :: Lens' StateData StdGen
randomGen = lens _randomGen (\ s x -> s { _randomGen = x })
instance RandomGen StateData where
next s = r & _2 .~ (s & randomGen .~ (r ^. _2))
where r = (s ^. randomGen ^. to next)
split s = r & _1 .~ (s & randomGen .~ (r ^. _1))
& _2 .~ (s & randomGen .~ (r ^. _2))
where r = (s ^. randomGen ^. to split)
Чтобы упростить это определение (и будущие определения, подобные ему), я хотел бы обобщить шаблон следующим образом:
reinsert :: (a -> b) -> Lens' s a -> [Lens b b' a s] -> a -> b'
reinsert f a bs s
= foldr (&) r [b .~ (s & a .~ (r ^. b)) | b <- bs]
where r = (s ^. a ^. to f)
instance RandomGen StateData where
next = reinsert next randomGen [_2]
split = reinsert split randomGen [_1, _2]
Тампроблема с этим подходом, хотяОбъявление типа reinsert
является «Недопустимым полиморфным типом».Я понимаю это как тип сложного для Хаскелла.Если я удаляю объявление типа, первое использование объектива a
превращает его в класс Getting
, что делает второе использование как ASetter
недопустимым;то же самое происходит с b
в понимании списка.
Есть ли способ исправить это?Или, в качестве альтернативы, есть ли лучший способ реализовать экземпляр RandomGen
на StateData
?
Cheers, Johan
Редактировать: немного проще, но не решить основную проблему:
instance RandomGen StateData where
next s = (s ^. randomGen ^. to next)
& _2 %~ (\ x -> s & randomGen .~ x)
split s = (s ^. randomGen ^. to split)
& _1 %~ (\ x -> s & randomGen .~ x)
& _2 %~ (\ x -> s & randomGen .~ x)