f# - Partially deferred computation builder -
i'm trying work out how use computation builder represent deferred, nested set of steps.
i've got following far:
type entry =     | leaf of string * (unit -> unit)     | node of string * entry list * (unit -> unit)  type stepbuilder(desc:string) =     member this.zero() = leaf(desc,id)     member this.bind(v:string, f:unit->string) =         node(f(), [leaf(v,id)], id)     member this.bind(v:entry, f:unit->entry) =         match f()         | node(label,children,a) -> node(label, v :: children, a)         | leaf(label,a) -> node(label, [v], a)   let step desc = stepbuilder(desc)  let = step "a" {     do! step "b" {         do! step "c" {             do! step "c.1" {                 // todo: still evals goes; need find way defer                 // inner contents...                 printfn "test"             }         }     }     do! step "d" {         printfn "d"     } } this produces desired structure:
a(b(c(c.1)), d) my issue in building structure up, printfn calls made.
ideally want able retrieve tree structure, able call returned function/s execute inner blocks.
i realise means if have 2 nested steps "normal" code between them, need able read step declarations, , invoke on (if makes sense?).
i know delay , run things used in deferred execution computation expressions, i'm not sure if me here, unfortunately evaluate everything.
i'm missing glaringly obvious , "functional-y" can't seem make want.
clarification
i'm using id demonstration, they're part of puzzle, , imagine how might surface "invokable" parts of expression want.
as mentioned in other answer, free monads provide useful theoretical framework thinking kind of problems - however, think not need them answer specific question asking here.
first, had add return computation builder make code compile. never return anything, added overload taking unit equivalent zero:
member this.return( () ) = this.zero() now, answer question - think need modify discriminated union allow delaying of computations produce entry - have functions unit -> unit in domain model, that's not quite enough delay computation produce new entry. so, think need extend type:
type entry =   | leaf of string * (unit -> unit)   | node of string * entry list * (unit -> unit)   | delayed of (unit -> entry) when evaluating entry, need handle delayed case - contains function might perform side-effect such printing "test".
now can add delay computation builder , implement missing case delayed in bind this:
member this.delay(f) = delayed(f) member this.bind(v:entry, f:unit->entry) = delayed(fun () ->     let rec loop = function       | delayed f -> loop (f())       | node(label,children,a) -> node(label, v :: children, a)       | leaf(label,a) -> node(label, [v], a)     loop (f()) ) essentially, bind create new delayed computation that, when called, evaluates entry v until finds node or leaf (collapsing other delayed nodes) , same thing code did before.
i think answers question - i'd bit careful here. think computation expressions useful syntactic sugar, harmful if think them more think domain of problem solving - in question, did not actual problem. if did, answer might different.
Comments
Post a Comment