Здравствуйте люди,Создание монады к аналогичной IO Монады с цепью, состояние
Я довольно новыми для Haskell снова в этом году (после того, как использовать его в начале 1990-х годов, а затем в начале 00-х). Я пытаюсь написать код, который использует шаблон, который почти непосредственно к аналогичным example IO monad shown on the Haskell Wiki:
type IO a = RealWorld -> (a, RealWorld)
(Да, я знаю, что это не реализация GHC ИО, а лишь средство для ее понимания .) Причина в том, что в моем приложении (игре) у меня теперь есть два шаблона, делающих это с двумя различными заменами RealWorld
здесь. Во-первых, это состояние игры, а в другом - это просто случайное число из StdGen
. Я, конечно, теперь есть две пар типов, как это:
-- | Easily return a specified value as well as the new random number generator
type ReturnRNG a = (a, StdGen)
-- | Take an RNG and return it and another value.
-- (This is basically like the IO type but with the StdGen instead of RealWorld.)
type WithRNG a = StdGen -> ReturnRNG a
-- | Easily return a specified value as well as the new GameState
type ReturnGS a = (a, GameState)
-- | Use a GameState and return a value with the updated GameState.
-- (This is like IO.)
type WithGS a = GameState -> ReturnGS a
(Да, я мог бы абстрактные их в одну пару с двумя параметрами, но я не удосужился к нему.) Вы можете видеть, конечно, что мои типы WithGS a
и WithRNG a
(синонимы типов) в точности аналогичны IO a
выше.
Итак, вот простой пример фактического рабочего кода, который у меня сейчас:
-- | Returns a random position for the given size.
randomPos :: (Int, Int) --^The size
-> WithRNG (Int, Int) --^The result (0 up to 1 less than the size) and new RNG seed
randomPos (w, h) r0 = ((x, y), r2)
where
(x, r1) = randomR (0, w - 1) r0
(y, r2) = randomR (0, h - 1) r1
Это создает случайные пары в заданном диапазоне, и возвращает окончательное семени ГСЧ. Большой процент моих методов подобен этому (используя WithRNG
или WithGS
), используя прикованное состояние, иногда даже поднимаясь до r4
или r6
(или gs4
и т. Д.). Я бы предпочел написать этот пример, чтобы выглядеть следующим образом:
-- (Not working example)
randomPosSt (w, h) = do
x <- randomR (0, w - 1)
y <- randomR (0, h - 1)
return (x, y)
... все же имеют точный метод подписи и семантики. Кажется, что это должно быть возможно после вышеупомянутого учебника, который дает этот пример:
(>>=) :: IO a -> (a -> IO b) -> IO b
(action1 >>= action2) world0 =
let (a, world1) = action1 world0
(b, world2) = action2 a world1
in (b, world2)
Это, как вы можете видеть, это почти то, что я делаю выше (как только вы заменить «let
» за «where
» обозначения).
Однако я не могу создать Monad из синонима типа. (Я пробовал TypeSynonymInstances, но он, похоже, не работает ни с «instance Monad WithRNG where
», ни с использованием параметра. Использование newtype
также, похоже, добавит бесполезный уродливый синтаксис.) Я не смог хорошо определить состояние штата Монада достаточно, чтобы использовать эквивалентный метод. Однако, даже если бы я преуспел, реализация государственной Монады показала бы уродливые «get
» и «put
» (и «runState
» и т. Д.) И сделать код менее читаемым, не более.
-- THIS DOES NOT WORK
-- | Make a State Monad with random number generator - like WithRNG above
type RandomState = State StdGen
-- | Returns a random position for the given size.
randomPosSt :: (Int, Int) --^The size
-> RandomState (Int, Int) --^The result (0 up to 1 less than the size) and new RNG seed
После всего этого я сделал вывод, что я либо делаю что-то неправильно, то недоразумение, или просто не может делать то, что я хочу сделать. Я как раз собирался сказать «ну, вам действительно не нужно выяснять, как изменить свой код, чтобы заставить государство переноситься, чтобы оно обрабатывалось автоматически, так как оно отлично работает» и сдаться, а затем я подумал, что спросите здесь (мой дебютный разговор). Я предпочел бы более элегантное решение.
Я также рисунок более элегантное решение дало бы мне эту функцию я использую «бесплатно:»
-- | Maps the specified method, which must take a RNG as the last parameter,
-- over all the elements of the list, propagating the RNG and returning it.
-- TODO: Implement this without using recursion? Using a fold?
mapRandom :: (a -> WithRNG b) --^The function to map (that takes a RNG)
-> [a] --^The input list
-> WithRNG [b] --^The RNG to return
mapRandom func [] r0 = ([], r0)
mapRandom func (x:xs) r0 = (mapped : rest, r2)
where
(mapped, r1) = func x r0
(rest, r2) = mapRandom func xs r1
Спасибо за любые мысли, предложения, ссылки и ваше время!
Прежде чем я отвечу, вы знаете о монаде «Государство»? –
@GabrielGonzalez Действительно, я слышал о государственной монаде: «Я не смог правильно определить государственную Монаду, чтобы использовать эквивалентный метод, используя это. Даже если бы мне удалось, однако, реализация государственной монады показалась бы использовать уродливые «get» и «put» s (и «runStates» и т. д.) и сделать код менее читаемым, а не больше ». –
Прежде чем я отвечу, вы знаете о монад-трансформаторе ['RandT'] (http://hackage.haskell.org/packages/archive/MonadRandom/0.1.8/doc/html/Control-Monad-Random.html) ? –