Skip to content

Instantly share code, notes, and snippets.

@srdjan
Forked from sgoguen/_about.md
Created September 15, 2017 18:31
Show Gist options
  • Save srdjan/f14d1db5deef1578b4fb1efc9a534661 to your computer and use it in GitHub Desktop.
Save srdjan/f14d1db5deef1578b4fb1efc9a534661 to your computer and use it in GitHub Desktop.

Revisions

  1. @sgoguen sgoguen revised this gist Aug 31, 2017. 1 changed file with 8 additions and 3 deletions.
    11 changes: 8 additions & 3 deletions _about.md
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@
    [![A Small Elm-Like DSL in F#](https://img.youtube.com/vi/9jCP8-wtnIo/0.jpg)](https://www.youtube.com/watch?v=9jCP8-wtnIo "A Small Elm-Like DSL in F#")

    I've been working on a talk about the virtues of building toy examples for the purpose
    of communicating ideas with simply interactive examples.
    of communicating ideas with simple interactive examples.

    The toys I talk about in my presentation are based my interest in tools that allow
    programmers to quickly build web applications that allow them to explore their
    @@ -41,8 +41,10 @@ let myFanPage =

    ## Cleaning up our F# Flavored Html

    Right off the bat, we can agree this example is not only a little ugly, it's also a little verbose.
    While I'd love to be able to use an HTML like syntax in my F# ala React, I simply don't know how at this point.
    Right off the bat, we can agree this example is not only a little ugly, it's verbose.
    While I'd love to be able to use an HTML like syntax in my F# ala React's JSX, I don't
    see that happening anytime soon.

    However, we can make our document look a little like Elm with a few helpful functions:

    ````fsharp
    @@ -63,6 +65,7 @@ let myFanPage =
    p [] [Text("She's simply amazing...")]
    ]
    ````
    Now that's something we post with pride.

    ## Using Partial Application to Create Specialized Functions

    @@ -73,6 +76,8 @@ In F#, we can call a multiparameter function with less than the expected paramet
    new function that expects the remaining parameters. So in our example, we create three specialized functions for creating
    HTML tags with each one specialized for each tag.

    That feature is called partial application, because we are providing a partial list of arguments to our function rather than all of them.

    ## Printing our Page

    All this is nice, but we have a fan page to publish so we're going to need some code to convert our fan page to HTML.
  2. @sgoguen sgoguen revised this gist Aug 31, 2017. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions _about.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,7 @@
    # Making Toys with F# - A Small Elm-like DSL in F#

    [![A Small Elm-Like DSL in F#](https://img.youtube.com/vi/9jCP8-wtnIo/0.jpg)](https://www.youtube.com/watch?v=9jCP8-wtnIo "A Small Elm-Like DSL in F#")

    I've been working on a talk about the virtues of building toy examples for the purpose
    of communicating ideas with simply interactive examples.

  3. @sgoguen sgoguen revised this gist Aug 30, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion example.fsx
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    // ******************** 1. DEFINE OUR HtmlTag Type ******************
    // ******************** 1. Define out HtmlTag Type ******************

    type TagName = string
    type Attributes = Map<string, string>
  4. @sgoguen sgoguen revised this gist Aug 30, 2017. 1 changed file with 21 additions and 19 deletions.
    40 changes: 21 additions & 19 deletions example.fsx
    Original file line number Diff line number Diff line change
    @@ -4,23 +4,24 @@ type TagName = string
    type Attributes = Map<string, string>

    type HtmlTag =
    | HtmlTag of Name:TagName * Attributes:Attributes * Body:Tag list
    | HtmlTag of Name:TagName * Attributes:Attributes * Body:HtmlTag list
    | HtmlText of string

    // ******************** 2. A Simple Page to Test It ******************

    let myFanPage =
    HtmlTag("div", Map.empty, [
    HtmlTag("h1", Map.empty, [HtmlText("My Taylor Swift Fan Page")])
    HtmlTag("p", Map.empty, [HtmlText("She's simply amazing...")])
    ])
    let myFanPage1 =
    HtmlTag("div", Map.empty,
    [
    HtmlTag("h1", Map.empty, [HtmlText("My Taylor Swift Fan Page")])
    HtmlTag("p", Map.empty, [HtmlText("She's simply amazing...")])
    ])

    // *************** 3. Let's create a teeny tiny DSL ******************

    // Can we even really call it a DSL?

    let makeTag name attributes body =
    Tag(name, (Map.ofList attributes), body)
    HtmlTag(name, (Map.ofList attributes), body)

    let div = makeTag "div"
    let h1 = makeTag "h1"
    @@ -30,8 +31,8 @@ let p = makeTag "p"

    let myFanPage =
    div [] [
    h1 [] [Text("My Taylor Swift Fan Page")]
    p [] [Text("She's simply amazing...")]
    h1 [] [HtmlText("My Taylor Swift Fan Page")]
    p [] [HtmlText("She's simply amazing...")]
    ]

    // **************** 5. Let's add code to print it ************************
    @@ -43,13 +44,15 @@ module Encoders = begin
    let inline url(s:string) = HttpUtility.UrlEncode(s)
    end

    let writeToTextWriter (w:TextWriter) (t:Tag) = begin
    open System.IO

    let rec writeToTextWriter (w:TextWriter) (t:HtmlTag) = begin
    match t with
    | Text(s) ->
    | HtmlText(s) ->
    w.Write("<span>")
    w.WriteLine(Encoders.html(s))
    w.Write("</span>")
    | Tag(name, attributes, body) ->
    | HtmlTag(name, attributes, body) ->
    w.Write(sprintf "<%s" name)
    // Print the attributes
    for (name,value) in attributes |> Map.toList do
    @@ -61,13 +64,12 @@ let writeToTextWriter (w:TextWriter) (t:Tag) = begin
    w.WriteLine(sprintf "</%s>" name)
    end

    type Tag with
    override this.ToString() =
    use sw = new StringWriter()
    this.Write(sw)
    sw.ToString()
    let toString(t:HtmlTag) =
    use sw = new StringWriter()
    t |> writeToTextWriter sw
    sw.ToString()


    // ******************** Let's print it *************************

    printfn(myFanPage.ToString())

    myFanPage |> toString |> printfn "%s"
  5. @sgoguen sgoguen revised this gist Aug 30, 2017. 1 changed file with 73 additions and 0 deletions.
    73 changes: 73 additions & 0 deletions example.fsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,73 @@
    // ******************** 1. DEFINE OUR HtmlTag Type ******************

    type TagName = string
    type Attributes = Map<string, string>

    type HtmlTag =
    | HtmlTag of Name:TagName * Attributes:Attributes * Body:Tag list
    | HtmlText of string

    // ******************** 2. A Simple Page to Test It ******************

    let myFanPage =
    HtmlTag("div", Map.empty, [
    HtmlTag("h1", Map.empty, [HtmlText("My Taylor Swift Fan Page")])
    HtmlTag("p", Map.empty, [HtmlText("She's simply amazing...")])
    ])

    // *************** 3. Let's create a teeny tiny DSL ******************

    // Can we even really call it a DSL?

    let makeTag name attributes body =
    Tag(name, (Map.ofList attributes), body)

    let div = makeTag "div"
    let h1 = makeTag "h1"
    let p = makeTag "p"

    // *************** 4. Test out the cleaned up version ******************

    let myFanPage =
    div [] [
    h1 [] [Text("My Taylor Swift Fan Page")]
    p [] [Text("She's simply amazing...")]
    ]

    // **************** 5. Let's add code to print it ************************

    module Encoders = begin
    open System.Web
    let inline html(s:string) = HttpUtility.HtmlEncode(s)
    let inline attribute(s:string) = HttpUtility.HtmlAttributeEncode(s)
    let inline url(s:string) = HttpUtility.UrlEncode(s)
    end

    let writeToTextWriter (w:TextWriter) (t:Tag) = begin
    match t with
    | Text(s) ->
    w.Write("<span>")
    w.WriteLine(Encoders.html(s))
    w.Write("</span>")
    | Tag(name, attributes, body) ->
    w.Write(sprintf "<%s" name)
    // Print the attributes
    for (name,value) in attributes |> Map.toList do
    w.Write(sprintf " %s=\"%s\"" (Encoders.attribute name) (Encoders.attribute value))
    w.WriteLine(">")
    // Print the body of our tag
    for child in body do
    child |> writeToTextWriter w
    w.WriteLine(sprintf "</%s>" name)
    end

    type Tag with
    override this.ToString() =
    use sw = new StringWriter()
    this.Write(sw)
    sw.ToString()

    // ******************** Let's print it *************************

    printfn(myFanPage.ToString())

  6. @sgoguen sgoguen created this gist Aug 30, 2017.
    142 changes: 142 additions & 0 deletions _about.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,142 @@
    # Making Toys with F# - A Small Elm-like DSL in F#

    I've been working on a talk about the virtues of building toy examples for the purpose
    of communicating ideas with simply interactive examples.

    The toys I talk about in my presentation are based my interest in tools that allow
    programmers to quickly build web applications that allow them to explore their
    architecture. So to kickstart this series off, I want to introduce a simple
    component we can use to build other ideas.

    ## Encoding HTML

    HTML tags can be encoded simply with F# if we limit ourselves to basic tag features.

    1. A tag name (Let's ignore namespaces)
    2. A set of attributes that are basically key-value pairs
    3. A list of subtags or text.

    We can encode it like this:

    ````fsharp
    type TagName = string
    type Attributes = Map<string, string>
    type HtmlTag =
    | HtmlTag of Name:TagName * Attributes:Attributes * Body:Tag list
    | HtmlText of string
    ````

    If we wanted to make a page, an example page might look like this:

    ````fsharp
    let myFanPage =
    HtmlTag("div", Map.empty, [
    HtmlTag("h1", Map.empty, [HtmlText("My Taylor Swift Fan Page")])
    HtmlTag("p", Map.empty, [HtmlText("She's simply amazing...")])
    ])
    ````

    ## Cleaning up our F# Flavored Html

    Right off the bat, we can agree this example is not only a little ugly, it's also a little verbose.
    While I'd love to be able to use an HTML like syntax in my F# ala React, I simply don't know how at this point.
    However, we can make our document look a little like Elm with a few helpful functions:

    ````fsharp
    let makeTag name attributes body =
    Tag(name, (Map.ofList attributes), body)
    let div = makeTag "div"
    let h1 = makeTag "h1"
    let p = makeTag "p"
    ````

    Adding this little bit of code allows use to change our Taylor Swift fan page to look much more respectful:

    ````fsharp
    let myFanPage =
    div [] [
    h1 [] [Text("My Taylor Swift Fan Page")]
    p [] [Text("She's simply amazing...")]
    ]
    ````

    ## Using Partial Application to Create Specialized Functions

    What's important to note is the makeTag function takes three parameters, but our example only shows us passing
    in one parameter when we called it in our example. What gives?

    In F#, we can call a multiparameter function with less than the expected parameters. When we do that, we get back a
    new function that expects the remaining parameters. So in our example, we create three specialized functions for creating
    HTML tags with each one specialized for each tag.

    ## Printing our Page

    All this is nice, but we have a fan page to publish so we're going to need some code to convert our fan page to HTML.

    First, we'll need a few helper functions to encode string into proper HTML.

    ````fsharp
    module Encoders =
    open System.Web
    let inline html(s:string) = HttpUtility.HtmlEncode(s)
    let inline attribute(s:string) = HttpUtility.HtmlAttributeEncode(s)
    let inline url(s:string) = HttpUtility.UrlEncode(s)
    ````

    Next, I want a simple toy function that lets me write my HTML object to a TextWriter class.

    ````fsharp
    let writeToTextWriter (w:TextWriter) (t:Tag) =
    match t with
    | Text(s) ->
    w.Write("<span>")
    w.WriteLine(Encoders.html(s))
    w.Write("</span>")
    | Tag(name, attributes, body) ->
    w.Write(sprintf "<%s" name)
    // Print the attributes
    for (name,value) in attributes |> Map.toList do
    w.Write(sprintf " %s=\"%s\"" (Encoders.attribute name) (Encoders.attribute value))
    w.WriteLine(">")
    // Print the body of our tag
    for child in body do
    child |> writeToTextWriter w
    w.WriteLine(sprintf "</%s>" name)
    ````

    Finally, if we want to convert our Tag into a string, we can write a method against our Tag type like so:

    ````fsharp
    type Tag with
    override this.ToString() =
    use sw = new StringWriter()
    this.Write(sw)
    sw.ToString()
    ````

    That should be enough to get us started. If we were to take our example and run it:

    ````fsharp
    let myFanPage =
    div [] [
    h1 [] [Text("My Taylor Swift Fan Page")]
    p [] [Text("She's simply amazing...")]
    ]
    printfn(myFanPage.ToString())
    ````

    We would expect an output that loosely resembles:

    ````html
    <div>
    <h1>My Taylor Swift Fan Page</h1>
    <p>She's simply amazing...</p>
    </div>
    ````

    There's nothing particularly exciting about this example, but stay tuned because
    I'm going to show you how we use this as a building block to create some useful
    components.