Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Functional Programming in a Stateful World

Functional Programming in a Stateful World

Video: https://yow.eventer.com/yow-lambda-jam-2015-1305/functional-programming-in-a-stateful-world-by-manuel-chakravarty-1883

Today, functional programming is mostly used in the development of server-side software and command line applications. This is changing with Swift, a language with strong support for functional programming that is pitched as the successor of Objective-C for desktop and mobile applications. Unsurprisingly, there are obstacles. Any attempt to use Swift’s support for functional programming to its full potential leads to architectural challenges, especially in the separation of stateful and purely functional computations. In desktop and especially in mobile applications, a superb user experience is a central aim of application design. This typically leads to a ubiquitous use of application frameworks, such as Cocoa, which tend to encourage a tangled web of stateful, mutually mutating objects — very much the anti-thesis of modern functional programming.

Established approaches to disentangling stateful from pure computations, while necessary, are not sufficient for desktop and mobile applications. A careful separation of the view layer (implementing the user interface) from the application logic (forming the computational heart of the application) requires us to rethink application architecture and depends on adopting an interaction paradigm focusing on the data flow between components rather than on object mutation.

This talk summarises my experience with building a desktop application including a complex user interface in Swift and Haskell. The talk will provide answers to the following questions. In which way is functional programming useful in desktop and mobile applications? What are the advantages of stateless, pure functions? What software architecture maximises the impact of functional programming in that context? How can we propagate changes from user interactions, file system & network events, and so on in a manner that keeps an application responsive, while facilitating stateless computations.

Although, the talk mostly revolves around Swift, no previous knowledge of Swift is required and the main ideas translate to writing desktop and mobile applications in other functional languages, such as Scala, F#, or Haskell.

This talk was presented at the YOW! Lambda Jam 2015 conference, in Brisbane http://lambdajam.yowconference.com.au as well as the Sydney CocoaHeads user group.

Manuel Chakravarty

May 21, 2015
Tweet

More Decks by Manuel Chakravarty

Other Decks in Programming

Transcript

  1. Manuel M T Chakravarty Applicative & UNSW Australia Functional Programming

    in a Stateful World mchakravarty TacticalGrace justtesting.org 1 30 minute time slot [10min The Problem; 6min for each of the three principles]
  2. λ Abstract 2 * FP strives for pure, compositional code

    (human reasoning) — benefits have been discussed elsewhere * The Real World™ is stateful and full of interference (machine execution) » How do we resolve this tension?
  3. λ Abstract Concrete 2 * FP strives for pure, compositional

    code (human reasoning) — benefits have been discussed elsewhere * The Real World™ is stateful and full of interference (machine execution) » How do we resolve this tension?
  4. λ Abstract Concrete 2 * FP strives for pure, compositional

    code (human reasoning) — benefits have been discussed elsewhere * The Real World™ is stateful and full of interference (machine execution) » How do we resolve this tension?
  5. λ Abstract Concrete Entangled 2 * FP strives for pure,

    compositional code (human reasoning) — benefits have been discussed elsewhere * The Real World™ is stateful and full of interference (machine execution) » How do we resolve this tension?
  6. λ Abstract Concrete Compositional Entangled 2 * FP strives for

    pure, compositional code (human reasoning) — benefits have been discussed elsewhere * The Real World™ is stateful and full of interference (machine execution) » How do we resolve this tension?
  7. λ Abstract Concrete Compositional Entangled 2 * FP strives for

    pure, compositional code (human reasoning) — benefits have been discussed elsewhere * The Real World™ is stateful and full of interference (machine execution) » How do we resolve this tension?
  8. Anatomy of a Haskell Program 3 * The Haskell type

    systems enforces this architecture: functional core with an imperative shell. * The imperative shell performs I/O etc » Proven successful for…
  9. Anatomy of a Haskell Program λ purely functional core 3

    * The Haskell type systems enforces this architecture: functional core with an imperative shell. * The imperative shell performs I/O etc » Proven successful for…
  10. Anatomy of a Haskell Program λ purely functional core imperative

    shell 3 * The Haskell type systems enforces this architecture: functional core with an imperative shell. * The imperative shell performs I/O etc » Proven successful for…
  11. Anatomy of a Haskell Program λ purely functional core imperative

    shell encapsulated state 3 * The Haskell type systems enforces this architecture: functional core with an imperative shell. * The imperative shell performs I/O etc » Proven successful for…
  12. Pipeline Applications 4 * Today, most FP applications are in

    server side code, or command line applications, etc * Pipeline applications: work well with functional-core & imperative-shell architecture * Does this transfer to client-side (desktop or mobile) applications emphasising user interaction? We have very little experience with that. » There are some Elm and F# applications, but the big change is now with…
  13. Pipeline Applications Read request or data 4 * Today, most

    FP applications are in server side code, or command line applications, etc * Pipeline applications: work well with functional-core & imperative-shell architecture * Does this transfer to client-side (desktop or mobile) applications emphasising user interaction? We have very little experience with that. » There are some Elm and F# applications, but the big change is now with…
  14. Pipeline Applications Read request or data Compute database 4 *

    Today, most FP applications are in server side code, or command line applications, etc * Pipeline applications: work well with functional-core & imperative-shell architecture * Does this transfer to client-side (desktop or mobile) applications emphasising user interaction? We have very little experience with that. » There are some Elm and F# applications, but the big change is now with…
  15. Pipeline Applications Read request or data Compute Emit result database

    4 * Today, most FP applications are in server side code, or command line applications, etc * Pipeline applications: work well with functional-core & imperative-shell architecture * Does this transfer to client-side (desktop or mobile) applications emphasising user interaction? We have very little experience with that. » There are some Elm and F# applications, but the big change is now with…
  16. Pipeline Applications Read request or data Compute Emit result Server-side

    apps Command-line apps database Clojure Erlang Haskell Scala 4 * Today, most FP applications are in server side code, or command line applications, etc * Pipeline applications: work well with functional-core & imperative-shell architecture * Does this transfer to client-side (desktop or mobile) applications emphasising user interaction? We have very little experience with that. » There are some Elm and F# applications, but the big change is now with…
  17. Pipeline Applications Read request or data Compute Emit result Server-side

    apps Command-line apps database Clojure Erlang Haskell Scala What about user-centric & mobile applications? 4 * Today, most FP applications are in server side code, or command line applications, etc * Pipeline applications: work well with functional-core & imperative-shell architecture * Does this transfer to client-side (desktop or mobile) applications emphasising user interaction? We have very little experience with that. » There are some Elm and F# applications, but the big change is now with…
  18. Swift 5 * Unlike Scala & F#, designated successor to

    Objective-C by platform vendor (it is like MS deprecating C# in favour of F#) * Probably largest scale adoption of a major language in the history of PL so far * Swift is for writing *end-user* apps with a heavy UI/UX focus (mainly for mobile devices) » This is a totally different game — can we make pipeline applications fit?
  19. Swift 5 * Unlike Scala & F#, designated successor to

    Objective-C by platform vendor (it is like MS deprecating C# in favour of F#) * Probably largest scale adoption of a major language in the history of PL so far * Swift is for writing *end-user* apps with a heavy UI/UX focus (mainly for mobile devices) » This is a totally different game — can we make pipeline applications fit?
  20. Swift Can we extend pipeline applications? 5 * Unlike Scala

    & F#, designated successor to Objective-C by platform vendor (it is like MS deprecating C# in favour of F#) * Probably largest scale adoption of a major language in the history of PL so far * Swift is for writing *end-user* apps with a heavy UI/UX focus (mainly for mobile devices) » This is a totally different game — can we make pipeline applications fit?
  21. 6 * GUI code looks more likes this than a

    pipeline… » How can we use FP to its full advantage in this context?
  22. GUI elements have their own derived state 6 * GUI

    code looks more likes this than a pipeline… » How can we use FP to its full advantage in this context?
  23. GUI elements have their own derived state Must use OO-based

    platform libraries 6 * GUI code looks more likes this than a pipeline… » How can we use FP to its full advantage in this context?
  24. Frequent asynchronous events GUI elements have their own derived state

    Must use OO-based platform libraries 6 * GUI code looks more likes this than a pipeline… » How can we use FP to its full advantage in this context?
  25. Frequent asynchronous events Must be responsive GUI elements have their

    own derived state Must use OO-based platform libraries 6 * GUI code looks more likes this than a pipeline… » How can we use FP to its full advantage in this context?
  26. A GUI-centric Mac app in Swift & Haskell 8 *

    Like any fearless scientist would do, I conducted a self experiment. * I wrote a Haskell development environment (Haskell for Mac), trying to use FP where I can (with the caveat that I had to learn Swift & AppKit on-the-fly) » Here is what I learnt…
  27. A GUI-centric Mac app in Swift & Haskell 8 *

    Like any fearless scientist would do, I conducted a self experiment. * I wrote a Haskell development environment (Haskell for Mac), trying to use FP where I can (with the caveat that I had to learn Swift & AppKit on-the-fly) » Here is what I learnt…
  28. Three Methods to Facilitate FP 9 * MVVM 㱺 view

    model 㱺 enables an immutable model * Immutable values 㱺 minimise mutable state 㱺 need a way to deal with changes * Time series 㱺 replaces mutating variables by a stream of immutable values
  29. Three Methods to Facilitate FP Application architecture Isolate side effects

    with a view model 9 * MVVM 㱺 view model 㱺 enables an immutable model * Immutable values 㱺 minimise mutable state 㱺 need a way to deal with changes * Time series 㱺 replaces mutating variables by a stream of immutable values
  30. Three Methods to Facilitate FP Application architecture Isolate side effects

    with a view model Immutable values Structs & enums over classes; and use let (not var) 9 * MVVM 㱺 view model 㱺 enables an immutable model * Immutable values 㱺 minimise mutable state 㱺 need a way to deal with changes * Time series 㱺 replaces mutating variables by a stream of immutable values
  31. Three Methods to Facilitate FP Application architecture Isolate side effects

    with a view model Immutable values Structs & enums over classes; and use let (not var) Time series Streams of values over time replace mutable state 9 * MVVM 㱺 view model 㱺 enables an immutable model * Immutable values 㱺 minimise mutable state 㱺 need a way to deal with changes * Time series 㱺 replaces mutating variables by a stream of immutable values
  32. What is a View Model? Controller View Model 11 *

    C owns and updates V & M; V & M send events to the C * VM adapts the M for the view/presentation; it isolates the M from the V & C * Good for portability & testing, but also for separating purely functional from state-based * Intro to MMVM in general, see objc.io, Issue #13
  33. What is a View Model? View C Model View Model

    11 * C owns and updates V & M; V & M send events to the C * VM adapts the M for the view/presentation; it isolates the M from the V & C * Good for portability & testing, but also for separating purely functional from state-based * Intro to MMVM in general, see objc.io, Issue #13
  34. What is a View Model? View C Model View Model

    Immutable λ Hybrid Mutable 11 * C owns and updates V & M; V & M send events to the C * VM adapts the M for the view/presentation; it isolates the M from the V & C * Good for portability & testing, but also for separating purely functional from state-based * Intro to MMVM in general, see objc.io, Issue #13
  35. Project Descriptions in HfM 12 » Let’s look at an

    example of how this plays out… * In some cases, the mapping is straight forward… * …in others it isn’t. * This conversion (in both directions) is the responsibility of the view model.
  36. Project Descriptions in HfM Model Serialised Dat 12 » Let’s

    look at an example of how this plays out… * In some cases, the mapping is straight forward… * …in others it isn’t. * This conversion (in both directions) is the responsibility of the view model.
  37. Project Descriptions in HfM View Model Serialised Dat 12 »

    Let’s look at an example of how this plays out… * In some cases, the mapping is straight forward… * …in others it isn’t. * This conversion (in both directions) is the responsibility of the view model.
  38. Project Descriptions in HfM View Model Serialised Dat 12 »

    Let’s look at an example of how this plays out… * In some cases, the mapping is straight forward… * …in others it isn’t. * This conversion (in both directions) is the responsibility of the view model.
  39. Project Descriptions in HfM View Model Serialised Dat 12 »

    Let’s look at an example of how this plays out… * In some cases, the mapping is straight forward… * …in others it isn’t. * This conversion (in both directions) is the responsibility of the view model.
  40. 13 * Model: immutable in-memory representation (Haskell & Swift provide

    strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  41. Model Immutable 13 * Model: immutable in-memory representation (Haskell &

    Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  42. Model Immutable View Model 13 * Model: immutable in-memory representation

    (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  43. Model Immutable View Model C View 13 * Model: immutable

    in-memory representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  44. Model View Model C View 14 * Model: immutable in-memory

    representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability — let versus var) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  45. Model View Model C View data PackageDescription = PackageDescription {

    package :: PackageIdentifier, license :: License, licenseFiles :: [FilePath], copyright :: String, maintainer :: String, author :: String, stability :: String, testedWith :: [(CompilerFlavor,VersionRange)], homepage :: String, pkgUrl :: String, bugReports :: String, sourceRepos :: [SourceRepo], synopsis :: String, description :: String, category :: String, … 14 * Model: immutable in-memory representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability — let versus var) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  46. Model View Model C View data PackageDescription = PackageDescription {

    package :: PackageIdentifier, license :: License, licenseFiles :: [FilePath], copyright :: String, maintainer :: String, author :: String, stability :: String, testedWith :: [(CompilerFlavor,VersionRange)], homepage :: String, pkgUrl :: String, bugReports :: String, sourceRepos :: [SourceRepo], synopsis :: String, description :: String, category :: String, … 14 * Model: immutable in-memory representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability — let versus var) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  47. Model View Model C View data PackageDescription = PackageDescription {

    package :: PackageIdentifier, license :: License, licenseFiles :: [FilePath], copyright :: String, maintainer :: String, author :: String, stability :: String, testedWith :: [(CompilerFlavor,VersionRange)], homepage :: String, pkgUrl :: String, bugReports :: String, sourceRepos :: [SourceRepo], synopsis :: String, description :: String, category :: String, … struct Package { private var modelPackage: CBLPackage … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … 14 * Model: immutable in-memory representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability — let versus var) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  48. Model View Model C View data PackageDescription = PackageDescription {

    package :: PackageIdentifier, license :: License, licenseFiles :: [FilePath], copyright :: String, maintainer :: String, author :: String, stability :: String, testedWith :: [(CompilerFlavor,VersionRange)], homepage :: String, pkgUrl :: String, bugReports :: String, sourceRepos :: [SourceRepo], synopsis :: String, description :: String, category :: String, … struct Package { private var modelPackage: CBLPackage … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … 14 * Model: immutable in-memory representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability — let versus var) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  49. Model View Model C View data PackageDescription = PackageDescription {

    package :: PackageIdentifier, license :: License, licenseFiles :: [FilePath], copyright :: String, maintainer :: String, author :: String, stability :: String, testedWith :: [(CompilerFlavor,VersionRange)], homepage :: String, pkgUrl :: String, bugReports :: String, sourceRepos :: [SourceRepo], synopsis :: String, description :: String, category :: String, … struct Package { private var modelPackage: CBLPackage … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … 14 * Model: immutable in-memory representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability — let versus var) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  50. Model View Model C View data PackageDescription = PackageDescription {

    package :: PackageIdentifier, license :: License, licenseFiles :: [FilePath], copyright :: String, maintainer :: String, author :: String, stability :: String, testedWith :: [(CompilerFlavor,VersionRange)], homepage :: String, pkgUrl :: String, bugReports :: String, sourceRepos :: [SourceRepo], synopsis :: String, description :: String, category :: String, … struct Package { private var modelPackage: CBLPackage … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … 14 * Model: immutable in-memory representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability — let versus var) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  51. Model View Model C View data PackageDescription = PackageDescription {

    package :: PackageIdentifier, license :: License, licenseFiles :: [FilePath], copyright :: String, maintainer :: String, author :: String, stability :: String, testedWith :: [(CompilerFlavor,VersionRange)], homepage :: String, pkgUrl :: String, bugReports :: String, sourceRepos :: [SourceRepo], synopsis :: String, description :: String, category :: String, … struct Package { private var modelPackage: CBLPackage … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … 14 * Model: immutable in-memory representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability — let versus var) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  52. Model View Model C View data PackageDescription = PackageDescription {

    package :: PackageIdentifier, license :: License, licenseFiles :: [FilePath], copyright :: String, maintainer :: String, author :: String, stability :: String, testedWith :: [(CompilerFlavor,VersionRange)], homepage :: String, pkgUrl :: String, bugReports :: String, sourceRepos :: [SourceRepo], synopsis :: String, description :: String, category :: String, … struct Package { private var modelPackage: CBLPackage … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … class ProjectItem: NSObject { let category: ProjectItemCategory var identifier: String … weak var parent: ProjectItem? var children: [ProjectItem] var fileWrapper: NSFileWrapper? … } 14 * Model: immutable in-memory representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability — let versus var) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  53. Model View Model C View data PackageDescription = PackageDescription {

    package :: PackageIdentifier, license :: License, licenseFiles :: [FilePath], copyright :: String, maintainer :: String, author :: String, stability :: String, testedWith :: [(CompilerFlavor,VersionRange)], homepage :: String, pkgUrl :: String, bugReports :: String, sourceRepos :: [SourceRepo], synopsis :: String, description :: String, category :: String, … struct Package { private var modelPackage: CBLPackage … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … class ProjectItem: NSObject { let category: ProjectItemCategory var identifier: String … weak var parent: ProjectItem? var children: [ProjectItem] var fileWrapper: NSFileWrapper? … } 14 * Model: immutable in-memory representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability — let versus var) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  54. Model View Model C View data PackageDescription = PackageDescription {

    package :: PackageIdentifier, license :: License, licenseFiles :: [FilePath], copyright :: String, maintainer :: String, author :: String, stability :: String, testedWith :: [(CompilerFlavor,VersionRange)], homepage :: String, pkgUrl :: String, bugReports :: String, sourceRepos :: [SourceRepo], synopsis :: String, description :: String, category :: String, … struct Package { private var modelPackage: CBLPackage … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … class ProjectItem: NSObject { let category: ProjectItemCategory var identifier: String … weak var parent: ProjectItem? var children: [ProjectItem] var fileWrapper: NSFileWrapper? … } 14 * Model: immutable in-memory representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability — let versus var) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  55. Model View Model C View data PackageDescription = PackageDescription {

    package :: PackageIdentifier, license :: License, licenseFiles :: [FilePath], copyright :: String, maintainer :: String, author :: String, stability :: String, testedWith :: [(CompilerFlavor,VersionRange)], homepage :: String, pkgUrl :: String, bugReports :: String, sourceRepos :: [SourceRepo], synopsis :: String, description :: String, category :: String, … struct Package { private var modelPackage: CBLPackage … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … class ProjectItem: NSObject { let category: ProjectItemCategory var identifier: String … weak var parent: ProjectItem? var children: [ProjectItem] var fileWrapper: NSFileWrapper? … } class HeaderEditorController: NSViewController { @IBOutlet weak var nameTextField: NSTextField! … @IBOutlet var fullDescriptionTextView: NSTextView! … let packageItem: ProjectItem … } 14 * Model: immutable in-memory representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability — let versus var) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  56. Model View Model C View data PackageDescription = PackageDescription {

    package :: PackageIdentifier, license :: License, licenseFiles :: [FilePath], copyright :: String, maintainer :: String, author :: String, stability :: String, testedWith :: [(CompilerFlavor,VersionRange)], homepage :: String, pkgUrl :: String, bugReports :: String, sourceRepos :: [SourceRepo], synopsis :: String, description :: String, category :: String, … struct Package { private var modelPackage: CBLPackage … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … class ProjectItem: NSObject { let category: ProjectItemCategory var identifier: String … weak var parent: ProjectItem? var children: [ProjectItem] var fileWrapper: NSFileWrapper? … } class HeaderEditorController: NSViewController { @IBOutlet weak var nameTextField: NSTextField! … @IBOutlet var fullDescriptionTextView: NSTextView! … let packageItem: ProjectItem … } 14 * Model: immutable in-memory representation (Haskell & Swift provide strong guarantees) * View Model: partially mutable representation (Swift provides fine-grained choice of mutability — let versus var) * View: Mostly mutable view & controller object graph (instances of Apple’s Cocoa classes or subclasses)
  57. Immutable Values Minimise Mutable State 15 » A core reason

    to for using a view model is to facilitate an immutable model.
  58. Immutable by Default 16 * At first approximation, mutability makes

    everything more difficult: reasoning, tests, etc * Hence, we need a good reason to justify every mutable structure. » With this in minds, let’s go back to our model layer…
  59. Immutable by Default Sometimes performance requires mutability 16 * At

    first approximation, mutability makes everything more difficult: reasoning, tests, etc * Hence, we need a good reason to justify every mutable structure. » With this in minds, let’s go back to our model layer…
  60. Immutable by Default Sometimes performance requires mutability Sometimes an existing

    API is based on mutability 16 * At first approximation, mutability makes everything more difficult: reasoning, tests, etc * Hence, we need a good reason to justify every mutable structure. » With this in minds, let’s go back to our model layer…
  61. Immutable by Default Sometimes performance requires mutability Sometimes an existing

    API is based on mutability Beware of premature optimisation! 16 * At first approximation, mutability makes everything more difficult: reasoning, tests, etc * Hence, we need a good reason to justify every mutable structure. » With this in minds, let’s go back to our model layer…
  62. Immutable by Default Sometimes performance requires mutability Sometimes an existing

    API is based on mutability Beware of premature optimisation! Can you wrap the mutable API? 16 * At first approximation, mutability makes everything more difficult: reasoning, tests, etc * Hence, we need a good reason to justify every mutable structure. » With this in minds, let’s go back to our model layer…
  63. Model Immutable 17 * In HfM, it was fairly straight

    forward to keep the model immutable. * NSFileWrappers for directories are mutable, but making them explicit in the model would have been awkward with Cabal’s representation anyway. * If you can’t make the model immutable, at least provide an immutable API (as, e.g., Swift arrays, dictionaries, and sets)
  64. Model Immutable ✓ Cabal package in Haskell ✓ Source files

    as NSFileWrapper 17 * In HfM, it was fairly straight forward to keep the model immutable. * NSFileWrappers for directories are mutable, but making them explicit in the model would have been awkward with Cabal’s representation anyway. * If you can’t make the model immutable, at least provide an immutable API (as, e.g., Swift arrays, dictionaries, and sets)
  65. Model Immutable ✓ Cabal package in Haskell ✓ Source files

    as NSFileWrapper Directories are not explicit in the model 17 * In HfM, it was fairly straight forward to keep the model immutable. * NSFileWrappers for directories are mutable, but making them explicit in the model would have been awkward with Cabal’s representation anyway. * If you can’t make the model immutable, at least provide an immutable API (as, e.g., Swift arrays, dictionaries, and sets)
  66. Model Immutable ✓ Cabal package in Haskell ✓ Source files

    as NSFileWrapper Directories are not explicit in the model At least provide an immutable interface 17 * In HfM, it was fairly straight forward to keep the model immutable. * NSFileWrappers for directories are mutable, but making them explicit in the model would have been awkward with Cabal’s representation anyway. * If you can’t make the model immutable, at least provide an immutable API (as, e.g., Swift arrays, dictionaries, and sets)
  67. View Model Hybrid 18 * Don’t copy model information —

    just reference it. It can’t change after all! * Swift: mutable methods in structs are like record update in Haskell (with builtin Lens-like syntax) * Transform to/from view format when information passes through.
  68. View Model Hybrid struct Package { private var modelPackage: CBLPackage

    … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … 18 * Don’t copy model information — just reference it. It can’t change after all! * Swift: mutable methods in structs are like record update in Haskell (with builtin Lens-like syntax) * Transform to/from view format when information passes through.
  69. View Model Hybrid struct Package { private var modelPackage: CBLPackage

    … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … Don’t copy 18 * Don’t copy model information — just reference it. It can’t change after all! * Swift: mutable methods in structs are like record update in Haskell (with builtin Lens-like syntax) * Transform to/from view format when information passes through.
  70. View Model Hybrid struct Package { private var modelPackage: CBLPackage

    … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … Don’t copy Use lets & structs 18 * Don’t copy model information — just reference it. It can’t change after all! * Swift: mutable methods in structs are like record update in Haskell (with builtin Lens-like syntax) * Transform to/from view format when information passes through.
  71. View Model Hybrid struct Package { private var modelPackage: CBLPackage

    … var name: String { get { return modelPackage.name } set { modelPackage = CBLPackage(modelPackage, withName: newValue) } } … var richTextDesc: NSAttributedString{ get { return NSAttributedString(string: modelPackage.fullDescription) } set { modelPackage = CBLPackage(modelPackage, withFullDescription: newValue.string) } } … Don’t copy Use lets & structs class ProjectItem: NSObject { let category: ProjectItemCategory var identifier: String … weak var parent: ProjectItem? var children: [ProjectItem] var fileWrapper: NSFileWrapper? … } 18 * Don’t copy model information — just reference it. It can’t change after all! * Swift: mutable methods in structs are like record update in Haskell (with builtin Lens-like syntax) * Transform to/from view format when information passes through.
  72. Time Series Changes as Streams of Values 19 » We

    need glue to (1) bind view state to view model state, but (2) without mutable variables.
  73. 42 Δt: 4s Mutable Variable 24 * The contents of

    the variable changes… » …in contrast to the value of a time series…
  74. Δt 0 1 2 3 4 42 73 11 7

    25 25 * The time series includes all values at once — it reifies time. * Can be regarded as a function over time, or an unbound stream of values. * FRP — popular ObjC/Swift library: ReactiveCocoa https://github.com/ReactiveCocoa/ ReactiveCocoa » Initially, no Swift version, so I wrote my own simple library ’Changes’…
  75. Δt 0 1 2 3 4 42 73 11 7

    25 ✓ Transform and compose (map, fold, etc) 25 * The time series includes all values at once — it reifies time. * Can be regarded as a function over time, or an unbound stream of values. * FRP — popular ObjC/Swift library: ReactiveCocoa https://github.com/ReactiveCocoa/ ReactiveCocoa » Initially, no Swift version, so I wrote my own simple library ’Changes’…
  76. Δt 0 1 2 3 4 42 73 11 7

    25 ✓ Transform and compose (map, fold, etc) ✓ Observe stream values (changes) 25 * The time series includes all values at once — it reifies time. * Can be regarded as a function over time, or an unbound stream of values. * FRP — popular ObjC/Swift library: ReactiveCocoa https://github.com/ReactiveCocoa/ ReactiveCocoa » Initially, no Swift version, so I wrote my own simple library ’Changes’…
  77. Δt 0 1 2 3 4 42 73 11 7

    25 ✓ Transform and compose (map, fold, etc) ✓ Observe stream values (changes) Related YLJ’15 talk FRP in Swift with ReactiveCocoa — Sebastian Grail 25 * The time series includes all values at once — it reifies time. * Can be regarded as a function over time, or an unbound stream of values. * FRP — popular ObjC/Swift library: ReactiveCocoa https://github.com/ReactiveCocoa/ ReactiveCocoa » Initially, no Swift version, so I wrote my own simple library ’Changes’…
  78. protocol Observable: class { typealias Value // observed value typealias

    Observer = Value -> () // observer func func observeWithContext<Context: AnyObject> (context: Context, // determines lifetime observer: Context -> Observer) -> Observation<Observer> … } 26 * Observable pairs observable values with matching observers * Changes — stream of observable, but ephemeral Changes * Variable — stream of values, where the last one is always available
  79. protocol Observable: class { typealias Value // observed value typealias

    Observer = Value -> () // observer func func observeWithContext<Context: AnyObject> (context: Context, // determines lifetime observer: Context -> Observer) -> Observation<Observer> … } class Changes<Change>: Observable { // ephemeral typealias Value = Change func announce(change: Change) … } class Variable<T>: Observable { // last one persists typealias Value = T var value: T { didSet { …announce… } } … } 26 * Observable pairs observable values with matching observers * Changes — stream of observable, but ephemeral Changes * Variable — stream of values, where the last one is always available
  80. protocol Observable: class { typealias Value // observed value typealias

    Observer = Value -> () // observer func func observeWithContext<Context: AnyObject> (context: Context, // determines lifetime observer: Context -> Observer) -> Observation<Observer> … } class Changes<Change>: Observable { // ephemeral typealias Value = Change func announce(change: Change) … } class Variable<T>: Observable { // last one persists typealias Value = T var value: T { didSet { …announce… } } … } class Changes<Change>: Observable { // ephemeral … } class Variable<T>: Observable { // last one persists … } func map<Observed: Observable, Change> (source: Observed, f: Observed.Value -> Change) -> Changes<Change> 26 * Observable pairs observable values with matching observers * Changes — stream of observable, but ephemeral Changes * Variable — stream of values, where the last one is always available
  81. public class ProjectViewModel: NSObject { var package: Variable<Package> = Variable(initialValue:

    …) var groupItems: ProjectItemGroups { get { return theGroupItems } } private lazy var theGroupItems: ProjectItemGroups = … … } stream of Package values 27 * Stream of Package values connects the view model with the view layer.