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

Popular posts from this blog

android - InAppBilling registering BroadcastReceiver in AndroidManifest -

python Tkinter Capturing keyboard events save as one single string -

sql server - Why does Linq-to-SQL add unnecessary COUNT()? -