While one finds a few examples on how to do compositional recursive descent Parsers with f# computation expressions, I tried to use them for the opposite. To create easily readable code to generate (c++) source files from some XML data. Yet, I am stuck and it would be much appreciated if the community could help me find my misunderstandings. For the public benefit, my hope is that soon this post will Showcase how to do code Generators in a cool way by means of f# computation expressions, monadic style.
Here is how far I got so far (simplified, omitting the Input data for the Generation for the purpose of this question):
// in my full fledged application, State type also contains the Input data, used for generating code.
type State() =
let builder = new System.Text.StringBuilder()
let mutable indentLevel : int = 0
member this.Result() = builder.ToString()
member this.Emit (s : string) : unit = builder.Append( s )
// ... Methods allowing to do the indenting right, using indentLevel. And adding Output to the builder instance.
member this.Indent() = indentLevel <- indentLevel + 1
member this.Exdent() = indentLevel <- indentLevel - 1
// The return value of the Formatters is State only to allow for |> pipelining.
type Formatter = State -> State
type FormatterBuilder() =
// Q: Bind() Kind of Looks wrong - should it be a generic, taking one generic first Parameter? See Class function below.
member this.Bind (state,formatter) = formatter state
member this.Return state = state // Q: Not sure if this is the way to go. Maybe some Lambda here?!
let format = new FormatterBuilder()
// Q: Now Comes the part I am stuck in!
// I had the idea to have a "Block" function which
// outputs the "{", increases the indent Level,
// invokes the formatters for the Content of the block,
// then reduces the indent Level, then Closes "}".
// But I have no idea how to write this.
// Here my feeble attempt, not even sure which Parameters this function should take.
let rec Block (formatters : Formatter list) (state : State) : State =
format
{
state.EmitLine("{") // do I Need a "do!" here?
state.Indent()
formatters |> List.iter (fun f -> do! f state) // Q: "state" is not really propagated. How to do this better?
state.Exdent()
state.EmitLine "}"
}
// Functions with "Get" prefix are not shown here. They are supposed to get the Information
// from the Input, stored in State class, which is also not shown here.
let rec Namespace (state : State) : State =
format
{
state.EmitLine(GetNameSpace state)
}
let rec Class (classNode : XmlNode) (state : State) : State =
Format
{
do! TemplateDecl classNode state // TemplateDecl function not shown in sample code
do! ClassDecl classNode state
do! Block [ NestedTypes classNode; Variables classNode; // ... ] // just to give the idea. Q: the list seems wrong here - how to do it better?
}
let GenerateCode() : string =
let state = new State()
format
{
do! Namespace state // Q: Is there a way to get rid of the passing of state here?
do! Block
[ // Q: Maybe a Seq is better than a list here?
for c in State.Classes do // Q: requires override of a few functions in Builder class, I guess?!
do! Class c state
]
}
state.Result()
Obviously the above code at best only shows what I try to achieve. My research did not yield any good examples on how to use computation expressions. Many examples I found stop at showing how the builder is declared or a bit later, but fail to Showcase how to actually write the final expressions.
So, if someone finds the time to post a real sample which does what my gibberish code above tries to do, it would be most instructive and fill a gap of what can be found on the Internet regarding this (at least for me) confusing aspect of f# programming.
In my code sample above, I also fail to see what I get from the builder monad in the first place. The formatter code is not looking cleaner compared to a non-monadic implementation.
It would be great, if someone added the signatures and types to Parameters in answer posts; at least for me it is much more understandable compared to the "let-the-compiler-find-the-types" style.