SlideShare a Scribd company logo
1 of 59
Functional GUIs with F#
Frank A. Krueger
FRINGE 2015
I ❤️ F#
Simple Domain
module DrawingDomain =
type Shape = Oval | Rectangle
Simple Domain
type Point = { X : float; Y : float }
type Size = { Width : float; Height : float }
type Frame = { Position : Point
Size : Size }
Simple Domain
type Shape =
| Oval of Frame
| Rectangle of Frame
Moar Features!
type Shape =
| Oval of Frame
| Rectangle of Frame
| Path of Point list
| Union of Shape list
| Subtract of Shape list
Common Properties
type Element =
{
Id : Guid
Name : string
Color : Color
Shape : Shape
}
and Shape =
| Oval of Frame
| Rectangle of Frame
| Path of Point list
| Union of Element list
| Subtract of Element list
Document Model
type Document =
{
Elements : Element list
}
module DrawingDomain =
type Color = { R : float; G : float; B : float; A : float }
type Point = { X : float; Y : float }
type Size = { Width : float; Height : float }
type Frame = { Position : Point
Size : Size }
type Shape =
| Oval of Frame
| Rectangle of Frame
| Path of Point list
| Union of Shape list
| Subtract of Shape list
type Element =
{
Id : Guid
Name : string
Color : Color
Shape : Shape
}
type Document =
{
Elements : Element list
}
CRUD Operations
New Document
let newDocument =
{
Elements = []
}
Add Oval
let addOval doc frame =
let gray = rgb 0.5 0.5 0.5
let oval =
{
Id = Guid.NewGuid ()
Name = "Oval"
Color = gray
Shape = Oval frame
}
{ doc with
Elements = oval :: doc.Elements
}
Add Oval
let d1 = newDocument
let d2 = addOval d1 { Position = { X = 0.0; Y = 0.0 }
Size = { Width = 200.0;
Height = 200.0; } }
val d1 : Document = {Elements = [];}
val d2 : Document =
{Elements = [{Id = 9993a910-c487-4b6f-9025-279ce941518f;
Name = "Oval";
Color = {R = 0.5;
G = 0.5;
B = 0.5;
A = 1.0;};
Shape = Oval {Position = {X = 0.0;
Y = 0.0;};
Size = {Width = 200.0;
Height = 200.0;};};}];}
Remove Elements
let removeElement doc id =
...
Mapping
val newData : int list = [0; 1000; 2000; 3000; 4000]
let data = [0; 1; 2; 3; 4]
let times1000 x = x * 1000
let newData = data |> List.map times1000
Map the Document
let rec mapElements f elms =
let mapChildren e =
match e.Shape with
| Union children ->
{ e with Shape = Union (mapElements f children) }
| Subtract children ->
{ e with Shape = Subtract (mapElements f children) }
| _ -> e
let mapElement = mapChildren >> f
elms |> List.choose mapElement
and mapDocument f doc =
{ doc with
Elements = mapElements f doc.Elements
}
Remove Element
et removeElement doc id =
let keep e = if e.Id = id then None else Some e
mapDocument keep doc
Change the Color
let setColor doc ids newColor =
let selected e = Set.contains e.Id ids
let set e =
if selected e then
Some { e with Color = newColor }
else Some e
mapDocument set doc
Mutant Free Zone
GUIs
OOP GUIs
FrankName
Code Behind, View Model, View Controllers,
Reactive, Binding, whatever…
1. User types something
2. Text property changes
3. Some middleman to help you sleep at night
4. Mutate the Model (change properties)
Model
Mutant Love Zone
F# is OOP
type ShapeMutant () =
let mutable color = rgb 0.5 0.5 0.5
member this.Color
with get () = color
and set v = color <- v
F# is OOP
type ShapeMutant () =
let mutable color = rgb 0.5 0.5 0.5
member this.Color
with get () = color
and set v = color <- v
How do I keep my functional
model and still use OOP UI
libraries?
Answer
• Views reference a way to find the data they need,
they don’t reference the data directly
• There is a single Update function on the Document
that:
1. Sets a new document state
2. Signals that the document has changed
OOP Name Editorpublic class ElementNameEditor
{
TextEdit view;
Element model;
void Initialize ()
{
// Display the current data
view.Text = model.Name;
// Handle user changes
view.TextChanged += (s, e) =>
model.Name = view.Text;
// Refresh when the doc changes
model.PropertyChanged += (s, e) => {
if (e.Name == "Text")
view.Text = model.Name;
} ;
}
}
public class ElementNameEditor
{
TextEdit view;
Element model;
void Initialize ()
{
// Display the current data
view.Text = model.Name;
// Handle user changes
view.TextChanged += (s, e) =>
model.Name = view.Text;
// Refresh when the doc changes
model.PropertyChanged += (s, e) => {
if (e.Name == "Text")
view.Text = model.Name;
} ;
}
}
OOP Name Editor
Direct Reference
MAGIC
Two Changes…
1. Instead of a Direct
Reference, use an Indirect
Reference
2. Instead of MAGIC, call a
function and handle the
update event
Functional Name Editor
type ElementNameEditor (view : TextEdit, docc : DocumentController, id) =
member this.Initialize () =
// Handle user changes
view.TextChanged.Add (fun _ ->
let newName = view.Text
let setName e =
if e.Id = id then
Some { e with Name = newName }
else Some e
let newDoc = mapDocument setName docc.Data
docc.Update newDoc)
// Display the current data
let refresh doc =
let model = getElement id doc
view.Text <- model.Name
refresh docc.Data
// Refresh when the doc changes
docc.Updated.Add refresh
Functional Name Editor
type ElementNameEditor (view : TextEdit, docc : DocumentController, id) =
member this.Initialize () =
// Handle user changes
view.TextChanged.Add (fun _ ->
let newName = view.Text
let setName e =
if e.Id = id then
Some { e with Name = newName }
else Some e
let newDoc = mapDocument setName docc.Data
docc.Update newDoc)
// Display the current data
let refresh doc =
let model = getElement id doc
view.Text <- model.Name
refresh docc.Data
// Refresh when the doc changes
docc.Updated.Add refresh
Indirect Reference
Functional Name Editor
type ElementNameEditor (view : TextEdit, docc : DocumentController, id) =
member this.Initialize () =
// Handle user changes
view.TextChanged.Add (fun _ ->
let newName = view.Text
let setName e =
if e.Id = id then
Some { e with Name = newName }
else Some e
let newDoc = mapDocument setName docc.Data
docc.Update newDoc)
// Display the current data
let refresh doc =
let model = getElement id doc
view.Text <- model.Name
refresh docc.Data
// Refresh when the doc changes
docc.Updated.Add refresh
Explicit Update Function
Functional Name Editor
type ElementNameEditor (view : TextEdit, docc : DocumentController, id) =
member this.Initialize () =
// Handle user changes
view.TextChanged.Add (fun _ ->
let newName = view.Text
let setName e =
if e.Id = id then
Some { e with Name = newName }
else Some e
let newDoc = mapDocument setName docc.Data
docc.Update newDoc)
// Display the current data
let refresh doc =
let model = getElement id doc
view.Text <- model.Name
refresh docc.Data
// Refresh when the doc changes
docc.Updated.Add refresh Single Update Event
DocumentController
type DocumentController () =
let updated = Event<_> ()
let mutable data = newDocument
member this.Data = data
member this.Update newData =
data <- newData
updated.Trigger newData
member this.Updated = updated.Publish
DocumentController
type DocumentController () =
let updated = Event<_> ()
let mutable data = newDocument
member this.Data = data
member this.Update newData =
data <- newData
updated.Trigger newData
member this.Updated = updated.Publish
Single Source of Mutation
Why?
public class ElementNameEditor
{
TextEdit view;
Element model;
void Initialize ()
{
// Display the current data
view.Text = model.Name;
// Handle user changes
view.TextChanged += (s, e) =>
model.Name = view.Text;
// Refresh when the doc changes
model.PropertyChanged += (s, e) => {
if (e.Name == "Text")
view.Text = model.Name;
} ;
}
}
type ElementNameEditor (view : TextEdit, docc : DocumentController, id
member this.Initialize () =
// Handle user changes
view.TextChanged.Add (fun _ ->
let newName = view.Text
let setName e =
if e.Id = id then
Some { e with Name = newName }
else Some e
let newDoc = mapDocument setName docc.Data
docc.Update newDoc)
// Display the current data
let refresh doc =
let model = getElement id doc
view.Text <- model.Name
refresh docc.Data
// Refresh when the doc changes
docc.Updated.Add refresh
Five Reasons
1. Undo, Redo, and History
2. Atomic Save and Background Open
3. Background Updates
4. No Dangling References
5. Keep My Functional Model
Document-based
App Requirements
• Undo & Redo
• Background Save &
Open
OOP Undo Architecture
Whenever you change the document,
pass a reference to another function that
can undo all those changes
OOP Undo
view.TextChanged += (s, e) =>
SetName (view.Text);
void SetName (string newName)
{
var oldName = model.Name;
UndoManager.RegisterUndo (() =>
SetName (oldName));
UndoManager.SetAction ("Rename");
model.Name = newName;
}
view.TextChanged += (s, e) =>
model.Name = view.Text;
For every command you write,
you have to write its inverse
😳
F# Undo
docc.Update newDoc
F# Undo
docc.Update newDoc
docc.Update newDoc "Rename"
F# Undo
docc.Update newDoc
docc.Update newDoc "Rename"
No Need to Write an Inverse Function!
type DocumentControllerWithUndo () =
let updated = Event<_> ()
let mutable historyIndex = 0
let mutable history = [(newDocument, "New")]
member this.Data = history.[historyIndex]
member this.Update newData message =
history <- (newData, message) :: history
historyIndex <- 0
updated.Trigger newData
member this.Updated = updated.Publish
member this.Undo () =
historyIndex <- historyIndex + 1
updated.Trigger this.Data
member this.Redo () =
historyIndex <- historyIndex - 1
updated.Trigger this.Data
type DocumentControllerWithUndo () =
let updated = Event<_> ()
let mutable historyIndex = 0
let mutable history = [(newDocument, "New")]
member this.Data = history.[historyIndex]
member this.Update newData message =
history <- (newData, message) :: history
historyIndex <- 0
updated.Trigger newData
member this.Updated = updated.Publish
member this.Undo () =
historyIndex <- historyIndex + 1
updated.Trigger this.Data
member this.Redo () =
historyIndex <- historyIndex - 1
updated.Trigger this.Data
Atomic Save
Because the DocumentController has a
immutable snapshots of the data,
atomic saves are trivial.
Atomic Save
Because the DocumentController has a
immutable snapshots of the data,
atomic saves are trivial.
Background Open
Because the Document is immutable, it
doesn’t matter what thread it came
from.
Background Open
Because the Document is immutable, it
doesn’t matter what thread it came
from.
Background Updates
Because the Document is immutable, it
doesn’t matter what thread was used to
create it or how long it took to generate.
Background Updates
Because the Document is immutable, it
doesn’t matter what thread was used to
create it or how long it took to generate.
No Dangling References
Most of my memory leaks occur
because my model has an event that
has a reference to a UI object.
I never remember to unsubscribe all my
events.
With fewer events, there are fewer
mistakes to be made, and its easier to
track down trouble makers.
No Dangling References
Most of my memory leaks occur
because my model has an event that
has a reference to a UI object.
I never remember to unsubscribe all my
events.
With fewer events, there are fewer
mistakes to be made, and its easier to
track down trouble makers.
For This…
1. Undo, Redo, and History
2. Atomic Save and Background Open
3. Background Updates
4. No Dangling References
5. Keep My Functional Model
I Get…
1. Instead of a Direct Reference,
use an Indirect Reference
2. Call a single Update function
and handle the Updated event
Thank You
Frank A. Krueger
@praeclarum
http://github.com/praeclarum
http://praeclarum.org
fak@praeclarum.org

More Related Content

What's hot

The Ring programming language version 1.2 book - Part 27 of 84
The Ring programming language version 1.2 book - Part 27 of 84The Ring programming language version 1.2 book - Part 27 of 84
The Ring programming language version 1.2 book - Part 27 of 84Mahmoud Samir Fayed
 
The Ring programming language version 1.3 book - Part 29 of 88
The Ring programming language version 1.3 book - Part 29 of 88The Ring programming language version 1.3 book - Part 29 of 88
The Ring programming language version 1.3 book - Part 29 of 88Mahmoud Samir Fayed
 
The Ring programming language version 1.5.2 book - Part 37 of 181
The Ring programming language version 1.5.2 book - Part 37 of 181The Ring programming language version 1.5.2 book - Part 37 of 181
The Ring programming language version 1.5.2 book - Part 37 of 181Mahmoud Samir Fayed
 
The Ring programming language version 1.5.4 book - Part 38 of 185
The Ring programming language version 1.5.4 book - Part 38 of 185The Ring programming language version 1.5.4 book - Part 38 of 185
The Ring programming language version 1.5.4 book - Part 38 of 185Mahmoud Samir Fayed
 
Types in JavaScript: why you should care
Types in JavaScript: why you should careTypes in JavaScript: why you should care
Types in JavaScript: why you should careJean Carlo Emer
 
Pragmatic functional refactoring with java 8 (1)
Pragmatic functional refactoring with java 8 (1)Pragmatic functional refactoring with java 8 (1)
Pragmatic functional refactoring with java 8 (1)RichardWarburton
 
The Ring programming language version 1.5.1 book - Part 36 of 180
The Ring programming language version 1.5.1 book - Part 36 of 180The Ring programming language version 1.5.1 book - Part 36 of 180
The Ring programming language version 1.5.1 book - Part 36 of 180Mahmoud Samir Fayed
 
The Ring programming language version 1.4 book - Part 9 of 30
The Ring programming language version 1.4 book - Part 9 of 30The Ring programming language version 1.4 book - Part 9 of 30
The Ring programming language version 1.4 book - Part 9 of 30Mahmoud Samir Fayed
 
The Ring programming language version 1.6 book - Part 35 of 189
The Ring programming language version 1.6 book - Part 35 of 189The Ring programming language version 1.6 book - Part 35 of 189
The Ring programming language version 1.6 book - Part 35 of 189Mahmoud Samir Fayed
 
The Ring programming language version 1.10 book - Part 48 of 212
The Ring programming language version 1.10 book - Part 48 of 212The Ring programming language version 1.10 book - Part 48 of 212
The Ring programming language version 1.10 book - Part 48 of 212Mahmoud Samir Fayed
 
The Ring programming language version 1.3 book - Part 83 of 88
The Ring programming language version 1.3 book - Part 83 of 88The Ring programming language version 1.3 book - Part 83 of 88
The Ring programming language version 1.3 book - Part 83 of 88Mahmoud Samir Fayed
 
E2D3 ver. 0.2 API Instruction
E2D3 ver. 0.2 API InstructionE2D3 ver. 0.2 API Instruction
E2D3 ver. 0.2 API InstructionE2D3.org
 
Building DSLs with the Spoofax Language Workbench
Building DSLs with the Spoofax Language WorkbenchBuilding DSLs with the Spoofax Language Workbench
Building DSLs with the Spoofax Language WorkbenchEelco Visser
 

What's hot (20)

The Ring programming language version 1.2 book - Part 27 of 84
The Ring programming language version 1.2 book - Part 27 of 84The Ring programming language version 1.2 book - Part 27 of 84
The Ring programming language version 1.2 book - Part 27 of 84
 
The Ring programming language version 1.3 book - Part 29 of 88
The Ring programming language version 1.3 book - Part 29 of 88The Ring programming language version 1.3 book - Part 29 of 88
The Ring programming language version 1.3 book - Part 29 of 88
 
Prototype Framework
Prototype FrameworkPrototype Framework
Prototype Framework
 
The Ring programming language version 1.5.2 book - Part 37 of 181
The Ring programming language version 1.5.2 book - Part 37 of 181The Ring programming language version 1.5.2 book - Part 37 of 181
The Ring programming language version 1.5.2 book - Part 37 of 181
 
Scala best practices
Scala best practicesScala best practices
Scala best practices
 
Introduction to JQuery
Introduction to JQueryIntroduction to JQuery
Introduction to JQuery
 
The Ring programming language version 1.5.4 book - Part 38 of 185
The Ring programming language version 1.5.4 book - Part 38 of 185The Ring programming language version 1.5.4 book - Part 38 of 185
The Ring programming language version 1.5.4 book - Part 38 of 185
 
Solid in practice
Solid in practiceSolid in practice
Solid in practice
 
Types in JavaScript: why you should care
Types in JavaScript: why you should careTypes in JavaScript: why you should care
Types in JavaScript: why you should care
 
Pragmatic functional refactoring with java 8 (1)
Pragmatic functional refactoring with java 8 (1)Pragmatic functional refactoring with java 8 (1)
Pragmatic functional refactoring with java 8 (1)
 
The Ring programming language version 1.5.1 book - Part 36 of 180
The Ring programming language version 1.5.1 book - Part 36 of 180The Ring programming language version 1.5.1 book - Part 36 of 180
The Ring programming language version 1.5.1 book - Part 36 of 180
 
R environment
R environmentR environment
R environment
 
SOLID in Practice
SOLID in PracticeSOLID in Practice
SOLID in Practice
 
The Ring programming language version 1.4 book - Part 9 of 30
The Ring programming language version 1.4 book - Part 9 of 30The Ring programming language version 1.4 book - Part 9 of 30
The Ring programming language version 1.4 book - Part 9 of 30
 
The Ring programming language version 1.6 book - Part 35 of 189
The Ring programming language version 1.6 book - Part 35 of 189The Ring programming language version 1.6 book - Part 35 of 189
The Ring programming language version 1.6 book - Part 35 of 189
 
The Ring programming language version 1.10 book - Part 48 of 212
The Ring programming language version 1.10 book - Part 48 of 212The Ring programming language version 1.10 book - Part 48 of 212
The Ring programming language version 1.10 book - Part 48 of 212
 
The Ring programming language version 1.3 book - Part 83 of 88
The Ring programming language version 1.3 book - Part 83 of 88The Ring programming language version 1.3 book - Part 83 of 88
The Ring programming language version 1.3 book - Part 83 of 88
 
Spsl v unit - final
Spsl v unit - finalSpsl v unit - final
Spsl v unit - final
 
E2D3 ver. 0.2 API Instruction
E2D3 ver. 0.2 API InstructionE2D3 ver. 0.2 API Instruction
E2D3 ver. 0.2 API Instruction
 
Building DSLs with the Spoofax Language Workbench
Building DSLs with the Spoofax Language WorkbenchBuilding DSLs with the Spoofax Language Workbench
Building DSLs with the Spoofax Language Workbench
 

Viewers also liked

Programming iOS in C#
Programming iOS in C#Programming iOS in C#
Programming iOS in C#Frank Krueger
 
Tour of language landscape (code.talks)
Tour of language landscape (code.talks)Tour of language landscape (code.talks)
Tour of language landscape (code.talks)Yan Cui
 
OIS and CSA Discounting
OIS and CSA DiscountingOIS and CSA Discounting
OIS and CSA DiscountingQuantifi
 
Experiences Can be Judged by Their Interest Curves - MkII
Experiences Can be Judged by Their Interest Curves - MkIIExperiences Can be Judged by Their Interest Curves - MkII
Experiences Can be Judged by Their Interest Curves - MkIIMichele Bianchi
 
Discounted measures
Discounted measures Discounted measures
Discounted measures Huma Ilyas
 
Interest Rate Hedging with Swaptions
Interest Rate Hedging with SwaptionsInterest Rate Hedging with Swaptions
Interest Rate Hedging with SwaptionsRedington
 
APAC Webinar: Say Hello To Xamarin.Forms
APAC Webinar: Say Hello To Xamarin.FormsAPAC Webinar: Say Hello To Xamarin.Forms
APAC Webinar: Say Hello To Xamarin.FormsNish Anil
 
How to create calibration curve?
How to create calibration curve?How to create calibration curve?
How to create calibration curve?Mahmood Iqbal
 
Evolution of Interest Rate Curves since the Financial Crisis
Evolution of Interest Rate Curves since the Financial CrisisEvolution of Interest Rate Curves since the Financial Crisis
Evolution of Interest Rate Curves since the Financial CrisisFrançois Choquet
 
An introduction to property based testing
An introduction to property based testingAn introduction to property based testing
An introduction to property based testingScott Wlaschin
 
Multiple Curves, One Price
Multiple Curves, One PriceMultiple Curves, One Price
Multiple Curves, One PriceMarco Bianchetti
 
Gluecon Monitoring Microservices and Containers: A Challenge
Gluecon Monitoring Microservices and Containers: A ChallengeGluecon Monitoring Microservices and Containers: A Challenge
Gluecon Monitoring Microservices and Containers: A ChallengeAdrian Cockcroft
 
Functional Programming Patterns (NDC London 2014)
Functional Programming Patterns (NDC London 2014)Functional Programming Patterns (NDC London 2014)
Functional Programming Patterns (NDC London 2014)Scott Wlaschin
 
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014Scott Wlaschin
 
F# in the Real World (DDD EA)
F# in the Real World (DDD EA)F# in the Real World (DDD EA)
F# in the Real World (DDD EA)Yan Cui
 
Chapter 05_How Do Risk and Term Structure Affect Interest Rate?
Chapter 05_How Do Risk and Term Structure Affect Interest Rate?Chapter 05_How Do Risk and Term Structure Affect Interest Rate?
Chapter 05_How Do Risk and Term Structure Affect Interest Rate?Rusman Mukhlis
 

Viewers also liked (17)

Programming iOS in C#
Programming iOS in C#Programming iOS in C#
Programming iOS in C#
 
Tour of language landscape (code.talks)
Tour of language landscape (code.talks)Tour of language landscape (code.talks)
Tour of language landscape (code.talks)
 
OIS and CSA Discounting
OIS and CSA DiscountingOIS and CSA Discounting
OIS and CSA Discounting
 
Experiences Can be Judged by Their Interest Curves - MkII
Experiences Can be Judged by Their Interest Curves - MkIIExperiences Can be Judged by Their Interest Curves - MkII
Experiences Can be Judged by Their Interest Curves - MkII
 
Discounted measures
Discounted measures Discounted measures
Discounted measures
 
Interest Rate Hedging with Swaptions
Interest Rate Hedging with SwaptionsInterest Rate Hedging with Swaptions
Interest Rate Hedging with Swaptions
 
APAC Webinar: Say Hello To Xamarin.Forms
APAC Webinar: Say Hello To Xamarin.FormsAPAC Webinar: Say Hello To Xamarin.Forms
APAC Webinar: Say Hello To Xamarin.Forms
 
How to create calibration curve?
How to create calibration curve?How to create calibration curve?
How to create calibration curve?
 
Evolution of Interest Rate Curves since the Financial Crisis
Evolution of Interest Rate Curves since the Financial CrisisEvolution of Interest Rate Curves since the Financial Crisis
Evolution of Interest Rate Curves since the Financial Crisis
 
An introduction to property based testing
An introduction to property based testingAn introduction to property based testing
An introduction to property based testing
 
Two Curves, One Price
Two Curves, One PriceTwo Curves, One Price
Two Curves, One Price
 
Multiple Curves, One Price
Multiple Curves, One PriceMultiple Curves, One Price
Multiple Curves, One Price
 
Gluecon Monitoring Microservices and Containers: A Challenge
Gluecon Monitoring Microservices and Containers: A ChallengeGluecon Monitoring Microservices and Containers: A Challenge
Gluecon Monitoring Microservices and Containers: A Challenge
 
Functional Programming Patterns (NDC London 2014)
Functional Programming Patterns (NDC London 2014)Functional Programming Patterns (NDC London 2014)
Functional Programming Patterns (NDC London 2014)
 
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
 
F# in the Real World (DDD EA)
F# in the Real World (DDD EA)F# in the Real World (DDD EA)
F# in the Real World (DDD EA)
 
Chapter 05_How Do Risk and Term Structure Affect Interest Rate?
Chapter 05_How Do Risk and Term Structure Affect Interest Rate?Chapter 05_How Do Risk and Term Structure Affect Interest Rate?
Chapter 05_How Do Risk and Term Structure Affect Interest Rate?
 

Similar to Functional GUIs with F#

Rapid Prototyping with PEAR
Rapid Prototyping with PEARRapid Prototyping with PEAR
Rapid Prototyping with PEARMarkus Wolff
 
JavaScript - Chapter 12 - Document Object Model
  JavaScript - Chapter 12 - Document Object Model  JavaScript - Chapter 12 - Document Object Model
JavaScript - Chapter 12 - Document Object ModelWebStackAcademy
 
CodeMash - Building Rich Apps with Groovy SwingBuilder
CodeMash - Building Rich Apps with Groovy SwingBuilderCodeMash - Building Rich Apps with Groovy SwingBuilder
CodeMash - Building Rich Apps with Groovy SwingBuilderAndres Almiray
 
Pres_python_talakhoury_26_09_2023.pdf
Pres_python_talakhoury_26_09_2023.pdfPres_python_talakhoury_26_09_2023.pdf
Pres_python_talakhoury_26_09_2023.pdfRamziFeghali
 
Web2py tutorial to create db driven application.
Web2py tutorial to create db driven application.Web2py tutorial to create db driven application.
Web2py tutorial to create db driven application.fRui Apps
 
Functions2.pdf
Functions2.pdfFunctions2.pdf
Functions2.pdfprasnt1
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateKiev ALT.NET
 
XPages Workshop: Concepts And Exercises
XPages Workshop:   Concepts And ExercisesXPages Workshop:   Concepts And Exercises
XPages Workshop: Concepts And Exercisesddrschiw
 
Classes and objects1
Classes and objects1Classes and objects1
Classes and objects1Vineeta Garg
 
Lightning talk
Lightning talkLightning talk
Lightning talkGil Gomes
 
Uncommon Design Patterns
Uncommon Design PatternsUncommon Design Patterns
Uncommon Design PatternsStefano Fago
 

Similar to Functional GUIs with F# (20)

Java script
Java scriptJava script
Java script
 
DOM and Events
DOM and EventsDOM and Events
DOM and Events
 
Angular Schematics
Angular SchematicsAngular Schematics
Angular Schematics
 
Rapid Prototyping with PEAR
Rapid Prototyping with PEARRapid Prototyping with PEAR
Rapid Prototyping with PEAR
 
JavaScript - Chapter 12 - Document Object Model
  JavaScript - Chapter 12 - Document Object Model  JavaScript - Chapter 12 - Document Object Model
JavaScript - Chapter 12 - Document Object Model
 
Capstone ms2
Capstone ms2Capstone ms2
Capstone ms2
 
CodeMash - Building Rich Apps with Groovy SwingBuilder
CodeMash - Building Rich Apps with Groovy SwingBuilderCodeMash - Building Rich Apps with Groovy SwingBuilder
CodeMash - Building Rich Apps with Groovy SwingBuilder
 
To-Do App With Flutter: Step By Step Guide
To-Do App With Flutter: Step By Step GuideTo-Do App With Flutter: Step By Step Guide
To-Do App With Flutter: Step By Step Guide
 
Pres_python_talakhoury_26_09_2023.pdf
Pres_python_talakhoury_26_09_2023.pdfPres_python_talakhoury_26_09_2023.pdf
Pres_python_talakhoury_26_09_2023.pdf
 
Web2py tutorial to create db driven application.
Web2py tutorial to create db driven application.Web2py tutorial to create db driven application.
Web2py tutorial to create db driven application.
 
Functions2.pdf
Functions2.pdfFunctions2.pdf
Functions2.pdf
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicate
 
XPages Workshop: Concepts And Exercises
XPages Workshop:   Concepts And ExercisesXPages Workshop:   Concepts And Exercises
XPages Workshop: Concepts And Exercises
 
JavaScript
JavaScriptJavaScript
JavaScript
 
Django crush course
Django crush course Django crush course
Django crush course
 
Classes and objects1
Classes and objects1Classes and objects1
Classes and objects1
 
Lightning talk
Lightning talkLightning talk
Lightning talk
 
Uncommon Design Patterns
Uncommon Design PatternsUncommon Design Patterns
Uncommon Design Patterns
 
Web 6 | JavaScript DOM
Web 6 | JavaScript DOMWeb 6 | JavaScript DOM
Web 6 | JavaScript DOM
 
J Query Public
J Query PublicJ Query Public
J Query Public
 

More from Frank Krueger

Open Source CLRs - Seattle Mobile .NET
Open Source CLRs - Seattle Mobile .NETOpen Source CLRs - Seattle Mobile .NET
Open Source CLRs - Seattle Mobile .NETFrank Krueger
 
Asynchronous Application Patterns in C# - MonkeySpace
Asynchronous Application Patterns in C# - MonkeySpaceAsynchronous Application Patterns in C# - MonkeySpace
Asynchronous Application Patterns in C# - MonkeySpaceFrank Krueger
 
Programming Augmented Reality - Xamarin Evolve
Programming Augmented Reality - Xamarin EvolveProgramming Augmented Reality - Xamarin Evolve
Programming Augmented Reality - Xamarin EvolveFrank Krueger
 
3 Mobile App Dev Problems - Monospace
3 Mobile App Dev Problems - Monospace3 Mobile App Dev Problems - Monospace
3 Mobile App Dev Problems - MonospaceFrank Krueger
 
Algorithms - Future Decoded 2016
Algorithms - Future Decoded 2016Algorithms - Future Decoded 2016
Algorithms - Future Decoded 2016Frank Krueger
 
How I Made Zoom In and Enhance - Seattle Mobile .NET
How I Made Zoom In and Enhance - Seattle Mobile .NETHow I Made Zoom In and Enhance - Seattle Mobile .NET
How I Made Zoom In and Enhance - Seattle Mobile .NETFrank Krueger
 
Overview of iOS 11 - Seattle Mobile .NET
Overview of iOS 11 - Seattle Mobile .NETOverview of iOS 11 - Seattle Mobile .NET
Overview of iOS 11 - Seattle Mobile .NETFrank Krueger
 

More from Frank Krueger (8)

Open Source CLRs - Seattle Mobile .NET
Open Source CLRs - Seattle Mobile .NETOpen Source CLRs - Seattle Mobile .NET
Open Source CLRs - Seattle Mobile .NET
 
Asynchronous Application Patterns in C# - MonkeySpace
Asynchronous Application Patterns in C# - MonkeySpaceAsynchronous Application Patterns in C# - MonkeySpace
Asynchronous Application Patterns in C# - MonkeySpace
 
Programming Augmented Reality - Xamarin Evolve
Programming Augmented Reality - Xamarin EvolveProgramming Augmented Reality - Xamarin Evolve
Programming Augmented Reality - Xamarin Evolve
 
3 Mobile App Dev Problems - Monospace
3 Mobile App Dev Problems - Monospace3 Mobile App Dev Problems - Monospace
3 Mobile App Dev Problems - Monospace
 
Algorithms - Future Decoded 2016
Algorithms - Future Decoded 2016Algorithms - Future Decoded 2016
Algorithms - Future Decoded 2016
 
How I Made Zoom In and Enhance - Seattle Mobile .NET
How I Made Zoom In and Enhance - Seattle Mobile .NETHow I Made Zoom In and Enhance - Seattle Mobile .NET
How I Made Zoom In and Enhance - Seattle Mobile .NET
 
Overview of iOS 11 - Seattle Mobile .NET
Overview of iOS 11 - Seattle Mobile .NETOverview of iOS 11 - Seattle Mobile .NET
Overview of iOS 11 - Seattle Mobile .NET
 
Mocast Postmortem
Mocast PostmortemMocast Postmortem
Mocast Postmortem
 

Recently uploaded

Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)Gáspár Nagy
 
Lessons Learned from Building a Serverless Notifications System.pdf
Lessons Learned from Building a Serverless Notifications System.pdfLessons Learned from Building a Serverless Notifications System.pdf
Lessons Learned from Building a Serverless Notifications System.pdfSrushith Repakula
 
From Theory to Practice: Utilizing SpiraPlan's REST API
From Theory to Practice: Utilizing SpiraPlan's REST APIFrom Theory to Practice: Utilizing SpiraPlan's REST API
From Theory to Practice: Utilizing SpiraPlan's REST APIInflectra
 
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...Optimizing Operations by Aligning Resources with Strategic Objectives Using O...
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...OnePlan Solutions
 
Spring into AI presented by Dan Vega 5/14
Spring into AI presented by Dan Vega 5/14Spring into AI presented by Dan Vega 5/14
Spring into AI presented by Dan Vega 5/14VMware Tanzu
 
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypseTomasz Kowalczewski
 
What is a Recruitment Management Software?
What is a Recruitment Management Software?What is a Recruitment Management Software?
What is a Recruitment Management Software?NYGGS Automation Suite
 
How to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabberHow to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabbereGrabber
 
Workshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit Milan
Workshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit MilanWorkshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit Milan
Workshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit MilanNeo4j
 
Weeding your micro service landscape.pdf
Weeding your micro service landscape.pdfWeeding your micro service landscape.pdf
Weeding your micro service landscape.pdftimtebeek1
 
Evolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI EraEvolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI Eraconfluent
 
Alluxio Monthly Webinar | Simplify Data Access for AI in Multi-Cloud
Alluxio Monthly Webinar | Simplify Data Access for AI in Multi-CloudAlluxio Monthly Webinar | Simplify Data Access for AI in Multi-Cloud
Alluxio Monthly Webinar | Simplify Data Access for AI in Multi-CloudAlluxio, Inc.
 
Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Soroosh Khodami
 
Food Delivery Business App Development Guide 2024
Food Delivery Business App Development Guide 2024Food Delivery Business App Development Guide 2024
Food Delivery Business App Development Guide 2024Chirag Panchal
 
Microsoft365_Dev_Security_2024_05_16.pdf
Microsoft365_Dev_Security_2024_05_16.pdfMicrosoft365_Dev_Security_2024_05_16.pdf
Microsoft365_Dev_Security_2024_05_16.pdfMarkus Moeller
 
Automate your OpenSIPS config tests - OpenSIPS Summit 2024
Automate your OpenSIPS config tests - OpenSIPS Summit 2024Automate your OpenSIPS config tests - OpenSIPS Summit 2024
Automate your OpenSIPS config tests - OpenSIPS Summit 2024Andreas Granig
 
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdfThe Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdfkalichargn70th171
 
Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024
Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024
Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024SimonedeGijt
 
Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...
Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...
Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...Abortion Clinic
 
^Clinic ^%[+27788225528*Abortion Pills For Sale In harare
^Clinic ^%[+27788225528*Abortion Pills For Sale In harare^Clinic ^%[+27788225528*Abortion Pills For Sale In harare
^Clinic ^%[+27788225528*Abortion Pills For Sale In hararekasambamuno
 

Recently uploaded (20)

Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
 
Lessons Learned from Building a Serverless Notifications System.pdf
Lessons Learned from Building a Serverless Notifications System.pdfLessons Learned from Building a Serverless Notifications System.pdf
Lessons Learned from Building a Serverless Notifications System.pdf
 
From Theory to Practice: Utilizing SpiraPlan's REST API
From Theory to Practice: Utilizing SpiraPlan's REST APIFrom Theory to Practice: Utilizing SpiraPlan's REST API
From Theory to Practice: Utilizing SpiraPlan's REST API
 
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...Optimizing Operations by Aligning Resources with Strategic Objectives Using O...
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...
 
Spring into AI presented by Dan Vega 5/14
Spring into AI presented by Dan Vega 5/14Spring into AI presented by Dan Vega 5/14
Spring into AI presented by Dan Vega 5/14
 
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
 
What is a Recruitment Management Software?
What is a Recruitment Management Software?What is a Recruitment Management Software?
What is a Recruitment Management Software?
 
How to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabberHow to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabber
 
Workshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit Milan
Workshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit MilanWorkshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit Milan
Workshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit Milan
 
Weeding your micro service landscape.pdf
Weeding your micro service landscape.pdfWeeding your micro service landscape.pdf
Weeding your micro service landscape.pdf
 
Evolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI EraEvolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI Era
 
Alluxio Monthly Webinar | Simplify Data Access for AI in Multi-Cloud
Alluxio Monthly Webinar | Simplify Data Access for AI in Multi-CloudAlluxio Monthly Webinar | Simplify Data Access for AI in Multi-Cloud
Alluxio Monthly Webinar | Simplify Data Access for AI in Multi-Cloud
 
Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024
 
Food Delivery Business App Development Guide 2024
Food Delivery Business App Development Guide 2024Food Delivery Business App Development Guide 2024
Food Delivery Business App Development Guide 2024
 
Microsoft365_Dev_Security_2024_05_16.pdf
Microsoft365_Dev_Security_2024_05_16.pdfMicrosoft365_Dev_Security_2024_05_16.pdf
Microsoft365_Dev_Security_2024_05_16.pdf
 
Automate your OpenSIPS config tests - OpenSIPS Summit 2024
Automate your OpenSIPS config tests - OpenSIPS Summit 2024Automate your OpenSIPS config tests - OpenSIPS Summit 2024
Automate your OpenSIPS config tests - OpenSIPS Summit 2024
 
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdfThe Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
 
Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024
Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024
Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024
 
Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...
Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...
Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...
 
^Clinic ^%[+27788225528*Abortion Pills For Sale In harare
^Clinic ^%[+27788225528*Abortion Pills For Sale In harare^Clinic ^%[+27788225528*Abortion Pills For Sale In harare
^Clinic ^%[+27788225528*Abortion Pills For Sale In harare
 

Functional GUIs with F#

  • 1. Functional GUIs with F# Frank A. Krueger FRINGE 2015
  • 3.
  • 4. Simple Domain module DrawingDomain = type Shape = Oval | Rectangle
  • 5. Simple Domain type Point = { X : float; Y : float } type Size = { Width : float; Height : float } type Frame = { Position : Point Size : Size }
  • 6. Simple Domain type Shape = | Oval of Frame | Rectangle of Frame
  • 7. Moar Features! type Shape = | Oval of Frame | Rectangle of Frame | Path of Point list | Union of Shape list | Subtract of Shape list
  • 8. Common Properties type Element = { Id : Guid Name : string Color : Color Shape : Shape } and Shape = | Oval of Frame | Rectangle of Frame | Path of Point list | Union of Element list | Subtract of Element list
  • 9. Document Model type Document = { Elements : Element list }
  • 10. module DrawingDomain = type Color = { R : float; G : float; B : float; A : float } type Point = { X : float; Y : float } type Size = { Width : float; Height : float } type Frame = { Position : Point Size : Size } type Shape = | Oval of Frame | Rectangle of Frame | Path of Point list | Union of Shape list | Subtract of Shape list type Element = { Id : Guid Name : string Color : Color Shape : Shape } type Document = { Elements : Element list }
  • 12. New Document let newDocument = { Elements = [] }
  • 13. Add Oval let addOval doc frame = let gray = rgb 0.5 0.5 0.5 let oval = { Id = Guid.NewGuid () Name = "Oval" Color = gray Shape = Oval frame } { doc with Elements = oval :: doc.Elements }
  • 14. Add Oval let d1 = newDocument let d2 = addOval d1 { Position = { X = 0.0; Y = 0.0 } Size = { Width = 200.0; Height = 200.0; } } val d1 : Document = {Elements = [];} val d2 : Document = {Elements = [{Id = 9993a910-c487-4b6f-9025-279ce941518f; Name = "Oval"; Color = {R = 0.5; G = 0.5; B = 0.5; A = 1.0;}; Shape = Oval {Position = {X = 0.0; Y = 0.0;}; Size = {Width = 200.0; Height = 200.0;};};}];}
  • 16. Mapping val newData : int list = [0; 1000; 2000; 3000; 4000] let data = [0; 1; 2; 3; 4] let times1000 x = x * 1000 let newData = data |> List.map times1000
  • 17. Map the Document let rec mapElements f elms = let mapChildren e = match e.Shape with | Union children -> { e with Shape = Union (mapElements f children) } | Subtract children -> { e with Shape = Subtract (mapElements f children) } | _ -> e let mapElement = mapChildren >> f elms |> List.choose mapElement and mapDocument f doc = { doc with Elements = mapElements f doc.Elements }
  • 18. Remove Element et removeElement doc id = let keep e = if e.Id = id then None else Some e mapDocument keep doc
  • 19. Change the Color let setColor doc ids newColor = let selected e = Set.contains e.Id ids let set e = if selected e then Some { e with Color = newColor } else Some e mapDocument set doc
  • 21. GUIs
  • 22. OOP GUIs FrankName Code Behind, View Model, View Controllers, Reactive, Binding, whatever… 1. User types something 2. Text property changes 3. Some middleman to help you sleep at night 4. Mutate the Model (change properties) Model
  • 24. F# is OOP type ShapeMutant () = let mutable color = rgb 0.5 0.5 0.5 member this.Color with get () = color and set v = color <- v
  • 25. F# is OOP type ShapeMutant () = let mutable color = rgb 0.5 0.5 0.5 member this.Color with get () = color and set v = color <- v
  • 26. How do I keep my functional model and still use OOP UI libraries?
  • 27. Answer • Views reference a way to find the data they need, they don’t reference the data directly • There is a single Update function on the Document that: 1. Sets a new document state 2. Signals that the document has changed
  • 28. OOP Name Editorpublic class ElementNameEditor { TextEdit view; Element model; void Initialize () { // Display the current data view.Text = model.Name; // Handle user changes view.TextChanged += (s, e) => model.Name = view.Text; // Refresh when the doc changes model.PropertyChanged += (s, e) => { if (e.Name == "Text") view.Text = model.Name; } ; } }
  • 29. public class ElementNameEditor { TextEdit view; Element model; void Initialize () { // Display the current data view.Text = model.Name; // Handle user changes view.TextChanged += (s, e) => model.Name = view.Text; // Refresh when the doc changes model.PropertyChanged += (s, e) => { if (e.Name == "Text") view.Text = model.Name; } ; } } OOP Name Editor Direct Reference MAGIC
  • 31. 1. Instead of a Direct Reference, use an Indirect Reference
  • 32. 2. Instead of MAGIC, call a function and handle the update event
  • 33. Functional Name Editor type ElementNameEditor (view : TextEdit, docc : DocumentController, id) = member this.Initialize () = // Handle user changes view.TextChanged.Add (fun _ -> let newName = view.Text let setName e = if e.Id = id then Some { e with Name = newName } else Some e let newDoc = mapDocument setName docc.Data docc.Update newDoc) // Display the current data let refresh doc = let model = getElement id doc view.Text <- model.Name refresh docc.Data // Refresh when the doc changes docc.Updated.Add refresh
  • 34. Functional Name Editor type ElementNameEditor (view : TextEdit, docc : DocumentController, id) = member this.Initialize () = // Handle user changes view.TextChanged.Add (fun _ -> let newName = view.Text let setName e = if e.Id = id then Some { e with Name = newName } else Some e let newDoc = mapDocument setName docc.Data docc.Update newDoc) // Display the current data let refresh doc = let model = getElement id doc view.Text <- model.Name refresh docc.Data // Refresh when the doc changes docc.Updated.Add refresh Indirect Reference
  • 35. Functional Name Editor type ElementNameEditor (view : TextEdit, docc : DocumentController, id) = member this.Initialize () = // Handle user changes view.TextChanged.Add (fun _ -> let newName = view.Text let setName e = if e.Id = id then Some { e with Name = newName } else Some e let newDoc = mapDocument setName docc.Data docc.Update newDoc) // Display the current data let refresh doc = let model = getElement id doc view.Text <- model.Name refresh docc.Data // Refresh when the doc changes docc.Updated.Add refresh Explicit Update Function
  • 36. Functional Name Editor type ElementNameEditor (view : TextEdit, docc : DocumentController, id) = member this.Initialize () = // Handle user changes view.TextChanged.Add (fun _ -> let newName = view.Text let setName e = if e.Id = id then Some { e with Name = newName } else Some e let newDoc = mapDocument setName docc.Data docc.Update newDoc) // Display the current data let refresh doc = let model = getElement id doc view.Text <- model.Name refresh docc.Data // Refresh when the doc changes docc.Updated.Add refresh Single Update Event
  • 37. DocumentController type DocumentController () = let updated = Event<_> () let mutable data = newDocument member this.Data = data member this.Update newData = data <- newData updated.Trigger newData member this.Updated = updated.Publish
  • 38. DocumentController type DocumentController () = let updated = Event<_> () let mutable data = newDocument member this.Data = data member this.Update newData = data <- newData updated.Trigger newData member this.Updated = updated.Publish Single Source of Mutation
  • 39. Why? public class ElementNameEditor { TextEdit view; Element model; void Initialize () { // Display the current data view.Text = model.Name; // Handle user changes view.TextChanged += (s, e) => model.Name = view.Text; // Refresh when the doc changes model.PropertyChanged += (s, e) => { if (e.Name == "Text") view.Text = model.Name; } ; } } type ElementNameEditor (view : TextEdit, docc : DocumentController, id member this.Initialize () = // Handle user changes view.TextChanged.Add (fun _ -> let newName = view.Text let setName e = if e.Id = id then Some { e with Name = newName } else Some e let newDoc = mapDocument setName docc.Data docc.Update newDoc) // Display the current data let refresh doc = let model = getElement id doc view.Text <- model.Name refresh docc.Data // Refresh when the doc changes docc.Updated.Add refresh
  • 40. Five Reasons 1. Undo, Redo, and History 2. Atomic Save and Background Open 3. Background Updates 4. No Dangling References 5. Keep My Functional Model
  • 41. Document-based App Requirements • Undo & Redo • Background Save & Open
  • 42. OOP Undo Architecture Whenever you change the document, pass a reference to another function that can undo all those changes
  • 43. OOP Undo view.TextChanged += (s, e) => SetName (view.Text); void SetName (string newName) { var oldName = model.Name; UndoManager.RegisterUndo (() => SetName (oldName)); UndoManager.SetAction ("Rename"); model.Name = newName; } view.TextChanged += (s, e) => model.Name = view.Text;
  • 44. For every command you write, you have to write its inverse 😳
  • 47. F# Undo docc.Update newDoc docc.Update newDoc "Rename" No Need to Write an Inverse Function!
  • 48. type DocumentControllerWithUndo () = let updated = Event<_> () let mutable historyIndex = 0 let mutable history = [(newDocument, "New")] member this.Data = history.[historyIndex] member this.Update newData message = history <- (newData, message) :: history historyIndex <- 0 updated.Trigger newData member this.Updated = updated.Publish member this.Undo () = historyIndex <- historyIndex + 1 updated.Trigger this.Data member this.Redo () = historyIndex <- historyIndex - 1 updated.Trigger this.Data
  • 49. type DocumentControllerWithUndo () = let updated = Event<_> () let mutable historyIndex = 0 let mutable history = [(newDocument, "New")] member this.Data = history.[historyIndex] member this.Update newData message = history <- (newData, message) :: history historyIndex <- 0 updated.Trigger newData member this.Updated = updated.Publish member this.Undo () = historyIndex <- historyIndex + 1 updated.Trigger this.Data member this.Redo () = historyIndex <- historyIndex - 1 updated.Trigger this.Data
  • 50. Atomic Save Because the DocumentController has a immutable snapshots of the data, atomic saves are trivial.
  • 51. Atomic Save Because the DocumentController has a immutable snapshots of the data, atomic saves are trivial.
  • 52. Background Open Because the Document is immutable, it doesn’t matter what thread it came from.
  • 53. Background Open Because the Document is immutable, it doesn’t matter what thread it came from.
  • 54. Background Updates Because the Document is immutable, it doesn’t matter what thread was used to create it or how long it took to generate.
  • 55. Background Updates Because the Document is immutable, it doesn’t matter what thread was used to create it or how long it took to generate.
  • 56. No Dangling References Most of my memory leaks occur because my model has an event that has a reference to a UI object. I never remember to unsubscribe all my events. With fewer events, there are fewer mistakes to be made, and its easier to track down trouble makers.
  • 57. No Dangling References Most of my memory leaks occur because my model has an event that has a reference to a UI object. I never remember to unsubscribe all my events. With fewer events, there are fewer mistakes to be made, and its easier to track down trouble makers.
  • 58. For This… 1. Undo, Redo, and History 2. Atomic Save and Background Open 3. Background Updates 4. No Dangling References 5. Keep My Functional Model I Get… 1. Instead of a Direct Reference, use an Indirect Reference 2. Call a single Update function and handle the Updated event
  • 59. Thank You Frank A. Krueger @praeclarum http://github.com/praeclarum http://praeclarum.org fak@praeclarum.org

Editor's Notes

  1. Primary amongst these is its succinct syntax for modeling the domain of your app.
  2. For a working example through this presentation, I want to discuss a drawing program. Drawing programs let you: draw shapes combine shapes edit the hierarchy edit the graphics themselves property editors
  3. If you're new to F#, then this reads simply as "Shapes are Ovals OR Rectangles". This is called a discriminated union. Enums are very simple discriminated unions.
  4. We need to allow the user to position these shapes. We do this by adding data to the variants - in this case a frame. Let's start by defining the frame. These curly braces denote "records" - objects where all the properties are public. This is safe in F# because everything is immutable by default so no one can mess with your objects.
  5. We then add this frame data to the various shapes.
  6. That's pretty good, but let's also make this an advanced drawing program with boolean operations on the shapes. Note that ships are now recursive
  7. All shapes have a few common properties. We will pull these common things out into an Element record.
  8. Now we need to write all the operations that the user can perform on the document. I’ll show just a few to give you a flavor.
  9. Takes a document and a frame, and gives a NEW DOCUMENT with the oval added to the beginning of the list of elements
  10. Takes a document and a frame, and gives a NEW DOCUMENT with the oval added to the beginning of the list of elements
  11. This is an important function, but it’s also a great opportunity to write a new kind of helper function
  12. Mapping is just applying a function to a bunch of data. In this case we apply a function that multiplies a number by 1000. But you can map a lot more than just normal lists. You can map complicated data like documents.
  13. We never changed the document. We just kept on creating new documents with the new data. For efficiency we also reused objects from the last document.
  14. GUIs are tangled webs of state and events. This goes against the simplicity of my Model.
  15. Since GUI libraries work on the premise of mutating state, they force that concept onto your model.
  16. This is fine because F# is an object oriented language
  17. But oh. This is so not F#. How do we get out of this mutant love zone?
  18. Make this compatible with my Functional Model.
  19. Am I just holding onto an ideology?
  20. I love to write document-based apps.
  21. That’s a lot of code just for a property set, imagine a complicated function!
  22. Now every Update is undoable. We even have a full history so we can be advanced like Photoshop.
  23. Now every Update is undoable