open System let decorateSequence concatnator xs mapper decorator = xs |> List.map mapper |> List.fold concatnator "" |> decorator let decorateAttributes xs mapper decorator = decorateSequence (fun x y -> x + " " + y) xs mapper decorator let decorateNodes xs mapper decorator = decorateSequence (fun x y -> x + y) xs mapper decorator let decorateNode (tag : string) s = tag + s + (tag.Replace ("<", " "Http-Equiv=\"" + s + "\"" | Content s -> "Content=\"" + s + "\"" type HeaderNode = Meta of (MetaNode list) | Title of string let markupHeaderNode = function | Meta mls -> decorateAttributes mls markupMetaNode <| fun s -> "" | Title s -> decorateNode "" s type Header = Head of (HeaderNode list) let markupHeader = function | Head hls -> decorateNodes hls markupHeaderNode <| decorateNode "<head>" type BodyNode = | Div of (BodyNode list) | Span of (BodyNode list) | P of (BodyNode list) | Text of string | H1 of string | H2 of string | H3 of string | Br | Hr let rec markupBodyNode = function | Br -> "<br />" | Hr -> "<hr>" | H1 s -> decorateNode "<h1>" s | H2 s -> decorateNode "<h2>" s | H3 s -> decorateNode "<h3>" s | Text s -> s | Div bls -> decorateNodes bls markupBodyNode <| decorateNode "<div>" | Span bls -> decorateNodes bls markupBodyNode <| decorateNode "<span>" | P bls -> decorateNodes bls markupBodyNode <| decorateNode "<p>" type Body = Body of (BodyNode list) let markupBody = function | Body bls -> decorateNodes bls markupBodyNode <| decorateNode "<body>" type HtmlNode = Html of Header * Body let markupHtmlNode = function | Html (h, b) -> (markupHeader h) + (markupBody b) |> decorateNode "<html>" type DocType = DocType of string let markupDocType = function | DocType s -> "<!DOCTYPE " + s + ">" type HtmlFile = HtmlFile of (DocType * HtmlNode) let markupHtmlFile = function | HtmlFile (d, h) -> (markupDocType d) + (markupHtmlNode h) HtmlFile ( DocType ("html"), Html ( Head ( [Meta ([HttpEquiv ("Content-Type"); Content ("text/html; charset='utf-8'")]); Title ("Html File")] ), Body ( [H1 ("This"); H2 ("is"); H3 ("it"); Hr; Div ( [Span ([Text ("a span node")]); P ([Text ("a paragraph")]); Br; Text ("plain text")] )] ) ) ) |> markupHtmlFile |> printfn "%s" // You will see like below: // <!DOCTYPE html><html><head><meta Http-Equiv="Content-Type" Content="text/html; c // harset='utf-8'" /><title>HtmlBuilder

This

is

it


a span node

a paragraph


plain text< // /div>