hackett
hackett copied to clipboard
A 'State' monad, and corresponding additions to the code by 'iitalic'
Maybe I've reinvented the wheel, but as an exercise here is an implemention of the State monad (state.rkt
) with the relevant get
, put
and modify
operations.
Also, based on that, I've added some relevant functions to random.rkt
for drawing random numbers through State PRNG
:
draw-double : (State PRNG Double)
draw-range : {Integer -> Integer -> (State PRNG Integer)}
draw-below : {Integer -> (State PRNG Integer)}
Demo of (State Integer)
:
(def stateInteger : (State Integer (List Integer))
(do
[i1 <- get] ; query the initial state
(modify (+ 10)) ; modify the internal state, adding 10 to it
[i2 <- get] ; query again
(modify (+ 10)) ; modify again
(pure {i1 :: i2 :: nil}))))) ; return the two intermediate internal states
(main {(runState stateInteger 42) & show & println})
This should demo this new functionality for random:
(def example-of-the-draw-functions : (State PRNG (List Double))
(do
[d0 <- draw-double]
[d1 <- draw-double]
(put (prng/seeded 1337))
[d2 <- draw-double]
[d3 <- draw-double]
(put (prng/seeded 1337))
[d4 <- draw-double]
[d5 <- draw-double]
(pure {d0 :: d1 :: d2 :: d3 :: d4 :: d5 :: nil })))
(main
(do
; In IO, get a PRNG seeded by the current time:
[prng <- make-io-prng]
(println " First two numbers in the following are different, and different on each run of this program, confirming the IO-seed PRNG is \"proceeding\",")
(println " and the third and fifth are the same, as as the fourth and sixth, to show the 'put' has reseeded deterministically")
; run an example State computation with the prng we just got from IO
{(runState example-of-the-draw-functions prng) & fst & show & println}
; And finally, with a deterministally-seeded PRNG (i.e. not created in IO)
{(runState example-of-the-draw-functions (prng/seeded 1337)) & fst & show & println}))
By the way, I'm relatively new to working with others on github. I've just realised that maybe I should have sent a pull request to @iitalics instead, as this extends their code. I don't really know the etiquette around these things
In an ideal world, I think the right thing to do here would be to define a StateT
transformer, then define a RandomT
transformer as a newtype-ish thing around StateT
, with a variety of instances derived using something like GeneralizedNewtypeDeriving
. However, Hackett has neither newtypes nor GND, so just writing StateT
for now seems reasonable. I would put it in hackett/monad/state
, however, much like how ErrorT
and ReaderT
are already defined.
I haven't had any time for Hackett in recent weeks, partly because of a C++ conference I attended (Meeting C++ 2017, it was fantastic!). But maybe I will again in a few weeks. I'm going to Nepal on Thursday for three weeks trekking up to 5000m. In future, I may be able to follow up on this, if nobody else does so first. Thanks again for the excellent feedback :-)