i'm trying wrap head around mon-, err, workflows in f# , while think have pretty solid understanding of basic "maybe" workflow, trying implement state workflow generate random numbers has got me stumped.
my non-completed attempt can seen here:
let randomint state = let random = system.random(state) // generate random number , new state random.next(0,1000), random.next() type randomwf (initstate) = member this.bind(rnd,rest) = let value, newstate = rnd initstate // how feed "newstate" "rest"?? value |> rest member this.return = // should maybe feed "initstate" computation here? randomwf(0) { let! = randomint let! b = randomint let! c = randomint return [a; b; c] } |> printfn "%a" edit: got work! not sure how works though, if wants lay out in answer, it's still grabs. here's working code:
type randomwf (initstate) = member this.bind(rnd,rest) = fun state -> let value, nextstate = rnd state rest value nextstate member this.return = fun _ -> member this.run x = x initstate
there 2 things make harder see workflow doing:
- you're using function type type of monad,
- your workflow not builds computation, runs it.
i think it's clearer follow once see how without 2 impediments. here's workflow defined using du wrapper type:
type random<'a> = comp of (int -> 'a * int) let run init (comp f) = f init type random<'a> member this.run(state) = fst <| run state type randombuilder() = member this.bind(comp m, f: 'a -> random<_>) = comp <| fun state -> let value, nextstate = m state let comp = f value run nextstate comp member this.return(a) = comp (fun s -> a, s) let random = randombuilder() and here how use it:
let randomint = comp <| fun state -> let rnd = system.random(state) rnd.next(0,1000), rnd.next() let rand = random { let! = randomint let! b = randomint let! c = randomint return [a; b; c ] } rand.run(0) |> printfn "%a" in version separately build computation (and store inside random type), , run passing in initial state. @ how types on builder methods inferred , compare them msdn documentation describes.
edit: constructing builder object once , using binding alias of sorts convention, it's justified in makes sense builders stateless. can see why having parameterized builders seems useful feature, can't imagine convincing use case it.
the key selling point of monads separation of definition , execution of computation.
in case - want able take representation of computation , able run state - perhaps 0, perhaps 42. don't need know initial state define computation use it. passing in state builder, end blurring line between definition , execution, , makes workflow less useful.
compare async workflow - when write async block, don't make code run asynchronously. create async<'a> object representing computation produce object of 'a when run - how it, you. builder doesn't need know.
Comments
Post a Comment