-
-
Save srdjan/f14d1db5deef1578b4fb1efc9a534661 to your computer and use it in GitHub Desktop.
Revisions
-
sgoguen revised this gist
Aug 31, 2017 . 1 changed file with 8 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,7 +3,7 @@ [](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 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 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. -
sgoguen revised this gist
Aug 31, 2017 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,7 @@ # Making Toys with F# - A Small Elm-like DSL in F# [](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. -
sgoguen revised this gist
Aug 30, 2017 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,4 @@ // ******************** 1. Define out HtmlTag Type ****************** type TagName = string type Attributes = Map<string, string> -
sgoguen revised this gist
Aug 30, 2017 . 1 changed file with 21 additions and 19 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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:HtmlTag list | HtmlText of string // ******************** 2. A Simple Page to Test It ****************** 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 = 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 [] [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 open System.IO let rec writeToTextWriter (w:TextWriter) (t:HtmlTag) = begin match t with | HtmlText(s) -> w.Write("<span>") w.WriteLine(Encoders.html(s)) w.Write("</span>") | 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 let toString(t:HtmlTag) = use sw = new StringWriter() t |> writeToTextWriter sw sw.ToString() // ******************** Let's print it ************************* myFanPage |> toString |> printfn "%s" -
sgoguen revised this gist
Aug 30, 2017 . 1 changed file with 73 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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())
-
sgoguen created this gist
Aug 30, 2017 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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.