$30 off During Our Annual Pro Sale. View Details »

Don't Worry About Monads

Don't Worry About Monads

Many of us have struggled to pick up functional languages. We’ve been scared off by the terminology: monads, monoids, functors. My goal is to show those afraid to jump in that there is room in the functional world for all of us, no matter your background or skills. I will show how you can not only learn functional languages without understanding those scary terms but that you can even ship production code. Like many of you, I approached functional programming from an imperative background without much math education. I found that sitting down with the goal of understanding monads got me me nowhere. It wasn’t until I started building applications that I started making progress. We will start with Elm and show how you can get up and running with a project even more quickly than you can with many of the popular imperative languages. I’ll show how those coming from Python, Ruby, and JavaScript can feel right at home with Elm. Once we are comfortable with the basics of the syntax, we’ll then walk through the Elm architecture and how structuring functional systems are often simpler than it is in object-oriented systems. To finish, we’ll quickly show how, all along, we were using monads, monoids, and functors without once using the terms. I’ll leave you with quick examples of how Elm can lead you right into strongly-typed functional languages such as Haskell and PureScript.

Beau Lyddon

June 04, 2018
Tweet

More Decks by Beau Lyddon

Other Decks in Programming

Transcript

  1. realkinetic.com | @real_kinetic Don’t Worry About Monads Just build it

    already
  2. realkinetic.com | @real_kinetic Beau Lyddon Managing Partner at Real Kinetic

    We mentor engineering teams to unleash your full potential. @lyddonb linkedin.com/in/beau-lyddon github.com/lyddonb
  3. realkinetic.com | @real_kinetic We’re going to focus on Strongly-Typed Functional

    Languages in the ML family
  4. realkinetic.com | @real_kinetic Haskell, Purescript, Elm, OCaml, Idris, F#

  5. realkinetic.com | @real_kinetic Although many of the concepts apply to

    other functional languages: Scala, lisp family, Rust, etc
  6. realkinetic.com | @real_kinetic So why am I doing this?

  7. realkinetic.com | @real_kinetic Because the world (especially our industry) will

    be better if we get more to embrace functional programming principles
  8. realkinetic.com | @real_kinetic And for that we need to start

    shipping code based around functional programming
  9. realkinetic.com | @real_kinetic This is not a direct “teach you

    the language” presentation
  10. realkinetic.com | @real_kinetic It’s more of a “get comfortable with

    syntax, structure and terms” presentation
  11. realkinetic.com | @real_kinetic It’s also a kill the FUD (Fear,

    Uncertainty, Doubt) Presentation
  12. realkinetic.com | @real_kinetic I want to remove the barriers that

    may be keeping you from diving in
  13. realkinetic.com | @real_kinetic This comes from my own experience attempting

    to learn these languages
  14. realkinetic.com | @real_kinetic I have/had imposter syndrome from a lacking

    mathematics background
  15. realkinetic.com | @real_kinetic And I struggle to learn programming from

    books
  16. realkinetic.com | @real_kinetic Much of the code and documentation seemed

    quite advanced
  17. realkinetic.com | @real_kinetic Difficult to separate struggles from lack of

    familiarity of syntax and complex/ advanced constructs
  18. realkinetic.com | @real_kinetic But …

  19. realkinetic.com | @real_kinetic One thing I want to make clear

  20. realkinetic.com | @real_kinetic This is not going to be an

    anti-terminology presentation
  21. realkinetic.com | @real_kinetic I was that person, I was wrong

  22. realkinetic.com | @real_kinetic The terminology can be quite helpful as

    you learn it
  23. realkinetic.com | @real_kinetic Instead I hope to show …

  24. realkinetic.com | @real_kinetic You don’t need to understand all of

    the terminology (While still appreciating it’s value)
  25. realkinetic.com | @real_kinetic You don’t need to be great at

    math
  26. realkinetic.com | @real_kinetic Or even go through books

  27. realkinetic.com | @real_kinetic To be productive in functional languages today

  28. realkinetic.com | @real_kinetic And still get many of the advantages

    these languages provide
  29. realkinetic.com | @real_kinetic This is why we will mostly focus

    on Elm
  30. realkinetic.com | @real_kinetic Elm is a compile to Javascript mostly,

    front-end (in the browser) focused language (Yes, it can be used in other environments)
  31. realkinetic.com | @real_kinetic Elm is meant to be the more

    accessible strongly- typed (ML family) language
  32. realkinetic.com | @real_kinetic It is designed for you to get

    work done and shipped to production
  33. realkinetic.com | @real_kinetic If you leave this presentation excited you

    can go spend a couple of hours doing the getting started with Elm
  34. realkinetic.com | @real_kinetic And build something real by the time

    your done
  35. realkinetic.com | @real_kinetic Elm sacrifices some power to allow more

    access
  36. realkinetic.com | @real_kinetic They purposely avoid non-common mathematical terms, etc

  37. realkinetic.com | @real_kinetic But hopefully once you’ve built some stuff

    in Elm you’ll be excited to learn about this magic that’s available in these other languages
  38. realkinetic.com | @real_kinetic And then you can dig as deep

    into terminology, formalism, etc as you would prefer
  39. realkinetic.com | @real_kinetic And you’ll get even more benefits appearing

    in your programs
  40. realkinetic.com | @real_kinetic Fair warning: Once you start, you may

    not be able to stop :)
  41. realkinetic.com | @real_kinetic Now, let’s get started

  42. realkinetic.com | @real_kinetic The first hurdle is the syntax

  43. realkinetic.com | @real_kinetic We’re going to go very fast through

    the syntax
  44. realkinetic.com | @real_kinetic I want to get to the fun

    stuff at the end
  45. realkinetic.com | @real_kinetic This is all taken from the Elm

    documentation and starter material (tutorials, etc)
  46. realkinetic.com | @real_kinetic Let’s make a quick counter

  47. realkinetic.com | @real_kinetic In Python

  48. realkinetic.com | @real_kinetic from jinja2 import Template def main(): return

    SomeProcessor(model, view, update) class Msg: Increment = "INCREMENT" Decrement = "DECREMENT" class Model(object): def __init__(self, count): self.count = count def model(): return Model(0) def update(msg, model): if msg == Msg.Increment: model.count += 1 elif msg == Msg.Decrement: model.count -= 1 return model TEMPLATE = """ <div> <button onClick="decrement()">-</button> <div>{{ count }} <button onClick="increment()">+</button> </div> """ def view(model): template = Template(TEMPLATE) return template.render(count=model.count)
  49. realkinetic.com | @real_kinetic Now, Elm http://elm-lang.org/examples/buttons

  50. realkinetic.com | @real_kinetic import Html exposing (Html, button, div, text)

    import Html.Events exposing (onClick) main = Html.beginnerProgram { model = model, view = view, update = update } model = 0 type Msg = Increment | Decrement update msg model = case msg of Increment -> model + 1 Decrement -> model - 1 view model = div [] [ button [ onClick Decrement ] [ text "-" ] , div [] [ text (toString model) ] , button [ onClick Increment ] [ text "+" ] ]
  51. realkinetic.com | @real_kinetic from jinja2 import Template def main(): return

    SomeProcessor(model, view, update) class Msg: Increment = "INCREMENT" Decrement = "DECREMENT" class Model(object): def __init__(self, count): self.count = count def model(): return Model(0) def update(msg, model): if msg == Msg.Increment: model.count += 1 elif msg == Msg.Decrement: model.count -= 1 return model TEMPLATE = """ <div> <button onClick="decrement()">-</button> <div>{{ count }} <button onClick="increment()">+</button> </div> """ def view(model): template = Template(TEMPLATE) return template.render(count=model.count)
  52. realkinetic.com | @real_kinetic Where are the types?

  53. realkinetic.com | @real_kinetic They are not required as they are

    inferred.
  54. realkinetic.com | @real_kinetic But it is recommend to annotate your

    code
  55. realkinetic.com | @real_kinetic Let’s start with our Python example

  56. realkinetic.com | @real_kinetic class Model(object): def __init__(self, count): """Initialize a

    counter. args: count (Int) """ self.count = count def model(): """Initializes a new model. returns: Model """ return Model(0) def update(msg, model): """Updates a model based off the message and existing model state. args: msg (Msg) model (Model) returns: Model """ if msg == Msg.Increment: model.count += 1 elif msg == Msg.Decrement: model.count -= 1 return model def view(model): """Renders our view with our template and the passed in model state. args: model (Model) returns: String """ template = Template(TEMPLATE) return template.render(count=model.count)
  57. realkinetic.com | @real_kinetic And now Elm

  58. realkinetic.com | @real_kinetic import Html exposing (Html, button, div, text)

    import Html.Events exposing (onClick) Program Never Model Msg main = Html.beginnerProgram { model = model, view = view, update = update } type alias Model = Int model : Model model = 0 type Msg = Increment | Decrement update : Msg -> Model -> Model update msg model = case msg of Increment -> model + 1 Decrement -> model - 1 view : Model -> Html Msg view model = div [] [ button [ onClick Decrement ] [ text "-" ] , div [] [ text (toString model) ] , button [ onClick Increment ] [ text "+" ] ]
  59. realkinetic.com | @real_kinetic import Html exposing (Html, button, div, text)

    import Html.Events exposing (onClick) main = Html.beginnerProgram { model = model, view = view, update = update } model = 0 type Msg = Increment | Decrement update msg model = case msg of Increment -> model + 1 Decrement -> model - 1 view model = div [] [ button [ onClick Decrement ] [ text "-" ] , div [] [ text (toString model) ] , button [ onClick Increment ] [ text "+" ] ]
  60. realkinetic.com | @real_kinetic Let’s dive in and step through so

    we can get more comfortable with the syntax
  61. realkinetic.com | @real_kinetic With type definitions the last type is

    the return type
  62. realkinetic.com | @real_kinetic -- A single type means it takes

    0 arguments thing : Int thing = 0 -- This takes 2 arguments each of int and -— returns and int add : Int -> Int -> Int add x y = x + y
  63. realkinetic.com | @real_kinetic -- You can alias a type type

    alias Things = Int -- This allows us to make more readable types. thing : Things thing = 0
  64. realkinetic.com | @real_kinetic -- This is a sum or union

    type (specifically a boolean type in this case). -- You may also see: tagged or disjoint —- unions or variant types type Msg = Increment | Decrement
  65. realkinetic.com | @real_kinetic -- Sum Types can have as many

    options as you'd like type Cars = Mustang | Camero | Taurus | Fit | Focus —- With Boolean there are 2 possible options type Bool = False | True
  66. realkinetic.com | @real_kinetic Why called Sum Type?

  67. realkinetic.com | @real_kinetic You can find the total number of

    possible values by adding them
  68. realkinetic.com | @real_kinetic Bool has 2 possible options: (False +

    True)
  69. realkinetic.com | @real_kinetic Cars has 5 possible options: (Mustang +

    Camero + Taurus + Fit + Focus)
  70. realkinetic.com | @real_kinetic Sum Types are a bit like Enums

    in other languages
  71. realkinetic.com | @real_kinetic Enums in Python

  72. realkinetic.com | @real_kinetic from enum import Enum class Color(Enum): RED

    = 1 GREEN = 2 BLUE = 3 >>> print(Color.RED) Color.RED >>> type(Color.RED) <enum 'Color'> >>> isinstance(Color.GREEN, Color) True >>> print(Color.RED.name) RED
  73. realkinetic.com | @real_kinetic Enums in Javascript Kinda

  74. realkinetic.com | @real_kinetic // Enum var DaysEnum = Object.freeze({"monday":1, "tuesday":2,

    "wednesday":3 }); DaysEnum.monday = 33; // Throws an error in strict mode console.log(DaysEnum.tuesday); // expected output: 2
  75. realkinetic.com | @real_kinetic But not quite. It usually takes quite

    a bit of code to make a proper Sum type in other languages
  76. realkinetic.com | @real_kinetic Full Sum Type in Javascript https://medium.com/fullstack-academy/better-js-cases-with-sum-types-92876e48fd9f

  77. realkinetic.com | @real_kinetic const PointTag = Symbol('Point') const Point =

    (x, y) => { if (typeof x !== 'number') throw TypeError('x must be a Number') if (typeof y !== 'number') throw TypeError('y must be a Number') return { x, y, tag: PointTag } } const CircleTag = Symbol('Circle') const RectangleTag = Symbol('Rectangle') const Shape = { Circle: (center, radius) => { if (center.tag !== PointTag) throw TypeError('center must be a Point') if (typeof radius !== 'number') throw TypeError('radius must be a Number') return { center, radius, tag: CircleTag } }, Rectangle: (corner1, corner2) => { if (corner1.tag !== PointTag) throw TypeError('corner1 must be a Point') if (corner2.tag !== PointTag) throw TypeError('corner2 must be a Point') return { corner1, corner2, tag: RectangleTag } } }
  78. realkinetic.com | @real_kinetic const circ1 = Shape.Circle(Point(2, 3), 6.5) const

    circ2 = Shape.Circle(Point(5, 1), 3) const rect1 = Shape.Rectangle(Point(1.5, 9), Point(7, 7)) const rect2 = Shape.Rectangle(Point(0, 3), Point(3, 0)) console.log('Is circ1 a circle?', circ1.tag === CircleTag) // true console.log('Is circ2 a circle?', circ2.tag === CircleTag) // true console.log('Is rect1 a rectangle?', rect1.tag === RectangleTag) // true console.log('Is rect2 a rectangle?', rect2.tag === RectangleTag) // true const rect3 = Shape.Rectangle(Point(1, 2), 9) // ERROR: corner2 must be a Point
  79. realkinetic.com | @real_kinetic Pattern Matching

  80. realkinetic.com | @real_kinetic type MyBool = MyFalse | MyTrue handleBool

    : MyBool -> String handleBool myBool = case myBool of MyTrue -> "It's my true" MyFalse -> "It's my false” handleBool MyTrue > "It's My True" handleBool MyFalse > "It's My False"
  81. realkinetic.com | @real_kinetic class MyBoo(Enum): MyTrue = 1 MyFalse =

    0 def handle_bool(my_bool): if my_bool == MyBoo.MyTrue: return "It's my true" elif my_bool == MyBoo.MyFalse: return "It's my false” handle_bool(MyBoo.MyTrue) > "It's my true" handle_bool(MyBoo.MyFalse) > "It's my false"
  82. realkinetic.com | @real_kinetic But what about …

  83. realkinetic.com | @real_kinetic handle_bool(“THIS CAN BE ANYTHING”) > None

  84. realkinetic.com | @real_kinetic def handle_bool_all(my_bool): if my_bool == MyBoo.MyTrue: return

    "It's my true" elif my_bool == MyBoo.MyFalse: return "It's my false” else: return “Oops” handle_bool_all(“THIS CAN BE ANYTHING!") > "Oops"
  85. realkinetic.com | @real_kinetic var MyBool = Object.freeze({"myTrue": 1, "myFalse": 0});

    const handleMyBool = (myBool) => { switch (myBool) { case MyBool.myTrue: return "It's my true" case MyBool.myFalse: return "It's my false" default: "Oops!" } }
  86. realkinetic.com | @real_kinetic Btw, there’s nothing stopping us from doing

  87. realkinetic.com | @real_kinetic def handle_bool_missing(my_bool): if my_bool == MyBoo.MyTrue: return

    "It's my true”
  88. realkinetic.com | @real_kinetic handleBool : MyBool -> String handleBool myBool

    = case myBool of MyTrue -> "It's my true"
  89. realkinetic.com | @real_kinetic Missing Patterns This `case` does not have

    branches for all possibilities. You need to account for the following values: Main.MyFalse Add a branch to cover this pattern! If you are seeing this error for the first time, check out these hints: <https://github.com/elm-lang/elm-compiler/blob/0.18.0/hints/missing- patterns.md> The recommendations about wildcard patterns and `Debug.crash` are important!
  90. realkinetic.com | @real_kinetic handleBool : MyBool -> String handleBool myBool

    = case myBool of MyTrue -> "It's my true" MyFalse -> "It's my false” Default -> "The default"
  91. realkinetic.com | @real_kinetic Naming Error Cannot find pattern `Default`

  92. realkinetic.com | @real_kinetic handleBool : MyBool -> String handleBool myBool

    = case myBool of MyTrue -> "It's my true" _ -> "The default” —- WORKS!
  93. realkinetic.com | @real_kinetic handleBool : MyBool -> String handleBool myBool

    = case myBool of MyTrue -> "It's my true" MyFalse -> "It's my false” _ -> "The default"
  94. realkinetic.com | @real_kinetic Redundant Pattern The following pattern is redundant.

    Remove it. Any value with this shape will be handled by a previous pattern.
  95. realkinetic.com | @real_kinetic Functions, Arrows, Partial Application, and Currying

  96. realkinetic.com | @real_kinetic Sounds harder than it is

  97. realkinetic.com | @real_kinetic Javascript, Python, Ruby support partial application and

    currying
  98. realkinetic.com | @real_kinetic -- This takes 2 integers and returns

    and Int add2Things : Int -> Int -> Int add2Things x y = x + y
  99. realkinetic.com | @real_kinetic -- But it doesn't have to. —-

    You can pass only one argument add2Things 1
  100. realkinetic.com | @real_kinetic This is called: partial application (You are

    partially applying arguments to a function)
  101. realkinetic.com | @real_kinetic -- This means when you don't pass

    all of the —- arguments in you will get back a function. -- So: add2Things 1 -- Returns a function (Int -> Int)
  102. realkinetic.com | @real_kinetic —- You can read that like: add2ThingsPartial

    : Int -> (Int -> Int) —- This is now a function that takes a single: Int —- And returns a function of: (Int -> Int)
  103. realkinetic.com | @real_kinetic -- Let's set that to a variable

    let addTo1 = add2Things 1
  104. realkinetic.com | @real_kinetic -- Now addTo1 is a function that

    takes a single argument. It looks like addTo1 : Int -> Int addTo1 x = add2Things 1 -- or addTo1 x = 1 + x -- So if we call it addTo1 2 == 3
  105. realkinetic.com | @real_kinetic Python

  106. realkinetic.com | @real_kinetic def add2Things(x): return lambda y: x +

    y addTo1 = add2Things(1) >>> addTo1(2) 3
  107. realkinetic.com | @real_kinetic Comes with built-in for partial application

  108. realkinetic.com | @real_kinetic from functools import partial def add2Things(x, y):

    return x + y addTo1 = partial(add2Things, 1) >>> addTo1(2) 3
  109. realkinetic.com | @real_kinetic Javascript

  110. realkinetic.com | @real_kinetic // As mentioned this can be done

    in Javascript var add2Things = function (x, y){ return function (y){ x + y; } } > add2Things(1); function (y){ x + y; } > var addTo1 = add2Things(1); > addTo1(2); > 3
  111. realkinetic.com | @real_kinetic You now have seen partial application, currying

    and what are called:
  112. realkinetic.com | @real_kinetic Higher Order Functions

  113. realkinetic.com | @real_kinetic A function that does at least one

    of the following:
  114. realkinetic.com | @real_kinetic Takes one or more functions as arguments

  115. realkinetic.com | @real_kinetic Or returns a function as its result

  116. realkinetic.com | @real_kinetic That’s all higher order functions are

  117. realkinetic.com | @real_kinetic Functions that take and/or return functions themselves

  118. realkinetic.com | @real_kinetic We will come back to that later

    and use it often.
  119. realkinetic.com | @real_kinetic It’s important in functional languages to get

    comfortable with treating functions like data that can be passed around.
  120. realkinetic.com | @real_kinetic Let’s go through more to keep getting

    familiar with syntax
  121. realkinetic.com | @real_kinetic type Msg = Increment | Decrement --

    This takes 2 arguments, —- a Msg and a Model and then returns a Model update : Msg -> Model -> Model update msg model = case msg of Increment -> model + 1 Decrement -> model - 1
  122. realkinetic.com | @real_kinetic const helloWorldReducer = (state=0, action) => {

    switch(action.type){ case PLUS: return Object.assign({}, state, state + 1) case MINUS: return Object.assign({}, state, state - 1) default: return state } }
  123. realkinetic.com | @real_kinetic type Msg = Increment | Decrement --

    This takes a single type of Model —- and returns a single type of Html Msg view : Model -> Html Msg view model = div [] [ button [ onClick Decrement ] [ text "-" ] , div [] [ text (toString model) ] , button [ onClick Increment ] [ text "+" ] ]
  124. realkinetic.com | @real_kinetic -- Html is another type alias for:

    type alias Html msg = VirtualDom.Node msg -- This type as a "generic" type. -- The msg could be anything: type alias Html a = VirtualDom.Node a // Java Generics: public interface Html<A> {} Html<Msg> myHtml = new MyHtml(myMSG)
  125. realkinetic.com | @real_kinetic import React from 'react' const Hello =

    ( {onClickPlus, onClickMinus, message} ) => { return( <div> <h2>{message}</h2> <button onClick={onClickPlus}>+</button> <button onClick={onClickMinus}>-</button> </div> ) } export default Hello
  126. realkinetic.com | @real_kinetic What if we want to store multiple

    values
  127. realkinetic.com | @real_kinetic -- Records! type alias Counter = {

    value : Int , count : Int }
  128. realkinetic.com | @real_kinetic Records are often called Product Types or

    Tuples
  129. realkinetic.com | @real_kinetic Why? It's a compound type that is

    formed by a sequence of types and is commonly denoted:
  130. realkinetic.com | @real_kinetic (T1, T2, ..., Tn) or T1 x

    T2 x ... x Tn
  131. realkinetic.com | @real_kinetic They correspond to cartesian products thus products

    types
  132. realkinetic.com | @real_kinetic By allowing you to be named they

    become records
  133. realkinetic.com | @real_kinetic Or potentially in other languages ... structs,

    classes, etc
  134. realkinetic.com | @real_kinetic So you find the total number of

    options by multiplying the maximum value of each option. https://www.stephanboyer.com/post/18/algebraic-data-types
  135. realkinetic.com | @real_kinetic type alias Counter = { value :

    Int , count : Int } counter : Counter counter = { value = 0 , count = 0 }
  136. realkinetic.com | @real_kinetic -- Accessing properties getValue : Counter ->

    Int getValue counter = counter.value getValue counter > 0 getValue { value = 2, count = 1} > 2
  137. realkinetic.com | @real_kinetic -- Shorthand accessors getValueShort : Counter ->

    Int getValueShort counter = .value counter getValueShort counter > 0 getValueShort { value = 2, count = 1} > 2
  138. realkinetic.com | @real_kinetic -- Updating Single Property updateValue : Int

    -> Counter -> Counter updateValue newValue existingCounter = { existingCounter | value = newValue } updateValue 10 counter > { value = 10, count 0 }
  139. realkinetic.com | @real_kinetic -- Updating Multiple Properties updateValueWithCount : Int

    -> Counter -> Counter updateValueWithCount newValue existingCounter = { existingCounter | value = newValue , count = existingCounter.count + 1 } updateValueWithCount 10 counter > { value = 10, count 1 }
  140. realkinetic.com | @real_kinetic Python

  141. realkinetic.com | @real_kinetic dict_counter = {"value": 1, "count": 1} >>>

    dict_counter["value"] 1 >>> dict_counter["value"] = 2 >>> dict_counter["value"] 2 >>> dict_counter["count"] 1 def update(val, rec): cnt["value"] = val cnt["count"] = int(cnt("count", 0)) + 1 return cut >>> update(33, dict_counter) >>> dict_counter["value"] 33 >>> dict_counter["count"] 2
  142. realkinetic.com | @real_kinetic class Counter(object): def __init__(self, val): self._value =

    val self._count = 0 @property def count(self): return self._count @property def value(self): return self._value @value.setter def value(self, value): self._value = value self._count += 1 >>> cnt = Counter(1) >>> cnt.value 1 >>> cnt.count 1 >>> cnt.value = 22 >>> cnt.value 22 >>> cnt.count 2
  143. realkinetic.com | @real_kinetic Javascript

  144. realkinetic.com | @real_kinetic var counter = { _count: 0, _value:

    0}; Object.defineProperty(counter, "count", { get: function() { return this._count; } }) Object.defineProperty(counter, "value", { get: function() { return this._value; }, set: function(v) { this._count++; return this._value = val; } }) >>> counter.value; 0 >>> counter.count; 0 >>> counter.value = 44; >>> counter.value; 44 >>> counter.count; 1
  145. realkinetic.com | @real_kinetic Extensible Records in Elm

  146. realkinetic.com | @real_kinetic type alias Record2 = { value :

    Int , count : Int , foo : String } type alias Record3 = { value : Int , count : Int , bar : String } type alias Record4 = { value : Int , foobar : String }
  147. realkinetic.com | @real_kinetic -- Extensible record alias type alias ValueRecord

    a = { a | value : Int } getValue2 : ValueRecord a -> Int getValue2 valRec = valRec.value -- COMPILES: getValue2 record2 -- COMPILES: getValue2 record3 -- COMPILES: getValue2 record4 -- COMPILES: getValue2 { value = 0 } -- FAILURE: getValue2 { foo = 0 } -- FAILURE: getValue2 {}
  148. realkinetic.com | @real_kinetic We can make our update function more

    generic and readable
  149. realkinetic.com | @real_kinetic updateRecord : Int -> { a |

    value : Int, count : Int } -> { a | value : Int, count : Int } updateRecord rec newValue = { rec | value = newValue , count = count + 1 } -- COMPILES: updateRecord 1 record2 -- COMPILES: updateRecord 1 record3 -- DOESN'T COMPILE: updateRecord 1 record4
  150. realkinetic.com | @real_kinetic type alias ValueRecord a b = {

    a | value : b, count : Int } updateRecord : Int -> ValueRecord a b -> ValueRecord a b updateRecord rec newValue = { rec | value = newValue , count = count + 1 } -- COMPILES: updateRecord 1 record2 -- COMPILES: updateRecord 1 record3 -- COMPILES: updateRecord “a” { value = “b”, count = 0 }
  151. realkinetic.com | @real_kinetic Nulls?

  152. realkinetic.com | @real_kinetic Do NOT exist

  153. realkinetic.com | @real_kinetic Yay!

  154. realkinetic.com | @real_kinetic If we can't have a null we

    need something to help us
  155. realkinetic.com | @real_kinetic What does a NULL mean?

  156. realkinetic.com | @real_kinetic It means we have “nothing” when we

    might have been expecting “something”
  157. realkinetic.com | @real_kinetic So what about something called “either”?

  158. realkinetic.com | @real_kinetic type Either a b = Left a

    | Right b
  159. realkinetic.com | @real_kinetic This is something we could use (and

    do use often) but our need is less generic.
  160. realkinetic.com | @real_kinetic Each side isn’t some general structure. One

    means something very specific.
  161. realkinetic.com | @real_kinetic So what about?

  162. realkinetic.com | @real_kinetic type Result error value = Ok value

    | Err error
  163. realkinetic.com | @real_kinetic This is closer but our result isn’t

    really an error it’s “Nothing”
  164. realkinetic.com | @real_kinetic So maybe we could go with:

  165. realkinetic.com | @real_kinetic type Maybe a = Just a |

    Nothing
  166. realkinetic.com | @real_kinetic Now we’re on to something.

  167. realkinetic.com | @real_kinetic Maybe we Just have a value (of

    type a) or we have Nothing
  168. realkinetic.com | @real_kinetic giveMeIfGreatherThan0 : Int -> Maybe Int giveMeIfGreatherThan0

    val = if val > 0 then Just val else Nothing giveMeIfGreatherThan0 10 > Just 10 giveMeIfGreatherThan0 -23 > Nothing
  169. realkinetic.com | @real_kinetic But now we have this structure that

    we have to deal with
  170. realkinetic.com | @real_kinetic And we don’t want our code to

    look like Go’s error handling code
  171. realkinetic.com | @real_kinetic Where you have this same code everywhere

  172. realkinetic.com | @real_kinetic f, err := os.Open("filename.ext") if err !=

    nil { log.Fatal(err) }
  173. realkinetic.com | @real_kinetic Thankfully there are functions in the maybe

    package to help us out
  174. realkinetic.com | @real_kinetic withDefault : a -> Maybe a ->

    a withDefault default maybe = case maybe of Just value -> value Nothing -> default withDefault 10 (Just 15) -- 15 withDefault 10 (Nothing) -- 10 withDefault "foo" (Just "bar") -- "bar" withDefault "foo" (Nothing) -- “foo”
  175. realkinetic.com | @real_kinetic -- Map map : (a -> b)

    -> Maybe a -> Maybe b map f maybe = case maybe of Just value -> Just (f value) Nothing -> Nothing
  176. realkinetic.com | @real_kinetic So we take in a function of

    (a -> b), Maybe a and return Maybe b
  177. realkinetic.com | @real_kinetic Let’s break this down

  178. realkinetic.com | @real_kinetic We may have an “a” and if

    do we want to get back a “b”
  179. realkinetic.com | @real_kinetic So the function we give needs to

    take an “a” and give back a “b”
  180. realkinetic.com | @real_kinetic The map will take care of the

    actual application
  181. realkinetic.com | @real_kinetic If you give it a “Just a”

    it will take the “a” out of the “Just” and apply your function
  182. realkinetic.com | @real_kinetic It will take the result of that

    function and put that inside a “Just”
  183. realkinetic.com | @real_kinetic If you give it “Nothing” it will

    skip applying the function and will just give you “Nothing”
  184. realkinetic.com | @real_kinetic Let’s create an function of (a ->

    b) and walk through
  185. realkinetic.com | @real_kinetic add1 : Int -> Int add1 val

    = val + 1 -- In this case we use the same type. —- It doesn't have to be 2 different types. -- but we can do that positiveMessage : Int -> String positiveMessage val = if val > 0 then "I'm a positive message!" else "I'm not so postivie :("
  186. realkinetic.com | @real_kinetic And now when we “run” it:

  187. realkinetic.com | @real_kinetic map add1 (Just 10) > Just 11

    map add1 Nothing > Nothing map positiveMessage (Just 10) > Just "I'm a positive message!” map positiveMessage (Just -23) > Just "I'm not so positive :(“ map positiveMessage Nothing > Nothing
  188. realkinetic.com | @real_kinetic Javascript

  189. realkinetic.com | @real_kinetic We’re going to cheat and use a

    javascript maybe library https://github.com/alexanderjarvis/maybe
  190. realkinetic.com | @real_kinetic import { maybe } from 'maybes' import

    { maybe, just, nothing } from 'maybes' const value = maybe(1) // Just(1) value.isJust() // true value.isNothing() // false value.just() // 1 (or could error since JS is not safe) value.map(v => v + 1) // Just(2) const empty = maybe(null) empty.isJust() // false empty.isNothing() // true empty.just() // throws error empty.map(v => v + 1) // noop (No Operation) empty.map(v => v.toUpperCase()).orJust('hello') // 'hello'
  191. realkinetic.com | @real_kinetic Lists

  192. realkinetic.com | @real_kinetic simpleList : List Int simpleList = [1,

    2, 3, 4] insertIntoSimpleList : Int -> List Int insertIntoSimpleList num = num :: simpleList insertIntoSimpleList 10 > [10, 1, 2, 3, 4] insertIntoSimpleList 333 > [333, 1, 2, 3, 4]
  193. realkinetic.com | @real_kinetic Loops

  194. realkinetic.com | @real_kinetic Elm doesn’t have them

  195. realkinetic.com | @real_kinetic We instead use functions (Remember that passing

    functions around like data thing)
  196. realkinetic.com | @real_kinetic We’ll start with a “fold” (AKA: Reduce)

  197. realkinetic.com | @real_kinetic -- This is a fold left of

    Ints -- The left means we reduce (or traverse) from the left foldlInt : (Int -> List Int -> List Int) -> Int -> List Int -> Int foldlInt func aggVal list = case list of [] -> aggVal x :: xs -> foldl func (func x aggVal) xs
  198. realkinetic.com | @real_kinetic Ooh, we’ve got that “higher order functions”

    thing again (A function that takes a function in this case)
  199. realkinetic.com | @real_kinetic It’s first argument is a function of

    two values that returns one (Int -> List Int -> List Int)
  200. realkinetic.com | @real_kinetic It then takes some value to accumulate

    into. Something that can accumulate like a list, a string, a number.
  201. realkinetic.com | @real_kinetic It also takes a starting value

  202. realkinetic.com | @real_kinetic foldlInt (::) [] [1,2,3] —- (::) is

    called `cons` (::) : a -> List a -> List a
  203. realkinetic.com | @real_kinetic So this takes a function of “cons”

    or “insert at 0 index” (insert into the 0 index of a list)
  204. realkinetic.com | @real_kinetic As well as an empty list to

    accumulate into
  205. realkinetic.com | @real_kinetic And of course our starting list

  206. realkinetic.com | @real_kinetic Let’s step through our fold

  207. realkinetic.com | @real_kinetic Thus starting from the left of [1,2,3]

    we prepend 1 to our starting empty list []. We now have [1]
  208. realkinetic.com | @real_kinetic And then inserting 2 into the 0

    index of [1] We now have [2, 1]
  209. realkinetic.com | @real_kinetic And then we finish by prepending the

    3 to [2,1]: [3,2,1]
  210. realkinetic.com | @real_kinetic -- We also can fold from the

    right foldrInt : (Int -> List Int -> List Int) -> Int -> List Int -> Int foldrInt (::) [] [1,2,3] == [1,2,3] -- Using the same arguments as before we end —- up with the same list we started -- Since we started from the right we prepend 3 —- we have [3] -- And the inserting 2 into the 0 index we have [2, 3] -- And then we finish by prepending the 1: [1,2,3]
  211. realkinetic.com | @real_kinetic Python

  212. realkinetic.com | @real_kinetic from functools import reduce def insert(arr, val):

    arr.insert(0, val) return arr arr = [1, 2, 3] >>> reduce(insert, [], arr) [3, 2, 1]
  213. realkinetic.com | @real_kinetic Javascript

  214. realkinetic.com | @real_kinetic const arr = [1, 2, 3]; const

    reducer = (accumulator, currentValue) => { accumulator.push(currentValue); return accumulator }; > arr.reduce(reducer, []); [ 1, 2, 3] const array1 = [1, 2, 3, 4]; const reducer = (accumulator, currentValue) => accumulator + currentValue; // 1 + 2 + 3 + 4 > array1.reduce(reducer); 10 // 5 + 1 + 2 + 3 + 4 > array1.reduce(reducer, 5); 15
  215. realkinetic.com | @real_kinetic Back to Elm

  216. realkinetic.com | @real_kinetic We of course can pass in different

    types of functions
  217. realkinetic.com | @real_kinetic What if we use the max function?

  218. realkinetic.com | @real_kinetic foldlInt max 0 [1,2,3] == 3 --

    We give it the builtin max function —- that is a more generic version of: max : Int -> Int -> Int max x y = if x > y then x else y
  219. realkinetic.com | @real_kinetic We can then make a nice function

    to get the maximum integer
  220. realkinetic.com | @real_kinetic maximumInt : List Int -> Int maximumInt

    list = case list of [] -> 0 [x] -> x x :: xs -> foldl max x xs
  221. realkinetic.com | @real_kinetic But this kinda sucks. If we give

    an empty list we get back a 0.
  222. realkinetic.com | @real_kinetic maximumInt [1,2] == 2 maximumInt [1] ==

    1 maximumInt [] == 0 -- Blech!
  223. realkinetic.com | @real_kinetic This is standard in other types of

    languages but we’re using strong-type FP for a reason
  224. realkinetic.com | @real_kinetic What if we introduce a “Maybe”?

  225. realkinetic.com | @real_kinetic maximumInt : List Int -> Maybe Int

    maximumInt list = case list of x :: xs -> Just (foldl max x xs) _ -> Nothing
  226. realkinetic.com | @real_kinetic Now it’s obvious to us when our

    result has no maximum value as we gave it no values
  227. realkinetic.com | @real_kinetic maximumInt [1,2] == Just 2 maximumInt [1]

    == Just 1 maximumInt [] == Nothing
  228. realkinetic.com | @real_kinetic And if we want to get a

    0 value when the list is empty we just use what we already have:
  229. realkinetic.com | @real_kinetic withDefault 0 (maximumInt [1,2]) == 2 withDefault

    0 (maximumInt [1]) == 1 withDefault 0 (maximumInt []) == 0
  230. realkinetic.com | @real_kinetic While we're here, we do this function

    chaining quite a bit.
  231. realkinetic.com | @real_kinetic And we're not a lisp so we'd

    like to avoid all of the parentheses. (foo (bar 1 (another “a” “b”)) 3)
  232. realkinetic.com | @real_kinetic Thankfully elm gives us some nice symbols

    to use (These symbols are actually functions known as infix operators)
  233. realkinetic.com | @real_kinetic withDefault 0 <| maximumInt [1,2] > 2

    withDefault 0 <| maximumInt [1] > 1 withDefault 0 <| maximumInt [] > 0
  234. realkinetic.com | @real_kinetic This allows us to do nice, readable

    chaining when we have many functions
  235. realkinetic.com | @real_kinetic withDefault 0 <| maximumInt <| range 0

    10 > 10 -- other direction: [1,2,3] |> maximumInt |> withDefault 0 > 3
  236. realkinetic.com | @real_kinetic Let’s go back and tweak our fold

    functions to make them more generic
  237. realkinetic.com | @real_kinetic foldl : (a -> b -> b)

    -> b -> List a -> b foldl func acc list = case list of [] -> acc x :: xs -> foldl func (func x acc) xs
  238. realkinetic.com | @real_kinetic Now our fold function can work on

    many different data types.
  239. realkinetic.com | @real_kinetic With generic fold functions what can we

    do?
  240. realkinetic.com | @real_kinetic It turns out, quite a lot.

  241. realkinetic.com | @real_kinetic map : (a -> b) -> List

    a -> List b map f xs = foldr (\x acc -> f x :: acc) [] xs map (\x -> x + 1) [1,2,3] > [2,3,4] add1 : Int -> Int add1 x = x + 1 map add1 [1,2,3] > [2,3,4]
  242. realkinetic.com | @real_kinetic Javascript & Python

  243. realkinetic.com | @real_kinetic // Javascript var arr = [1, 2,

    3] > arr.map(x => x + 1) [2, 3, 4] # Python arr = [1, 2, 3] >>> map(lambda x: x + 1, arr) [2, 3, 4]
  244. realkinetic.com | @real_kinetic There are all kinds of functions for

    working with lists in the List.elm module in the standard library
  245. realkinetic.com | @real_kinetic Many of these leverage fold (and each

    other)
  246. realkinetic.com | @real_kinetic We end up with a library of

    functions that will look very similar to what you find in Python, Javascript, Ruby, etc
  247. realkinetic.com | @real_kinetic isEmpty, length, reverse, member, head, tail, filter,

    take, drop, sum, all, etc
  248. realkinetic.com | @real_kinetic Immutability

  249. realkinetic.com | @real_kinetic Elm is functional and immutable ... so

    can we store and mutate state?
  250. realkinetic.com | @real_kinetic Yes, but we need to leverage “let”

    expressions
  251. realkinetic.com | @real_kinetic forty : Int forty = let twentyFour

    = 3 * 8 sixteen = 4 ^ 2 in twentyFour + sixteen
  252. realkinetic.com | @real_kinetic You just can't reassign the same variable

  253. realkinetic.com | @real_kinetic bad : Int bad = let twentyFour

    = 3 * 8 twentyFour = 20 + 4 in twentyFour
  254. realkinetic.com | @real_kinetic This will not compile. You will get

    this message:
  255. realkinetic.com | @real_kinetic There are multiple values named `twentyFour` in

    this let-expression. Search through this let-expression, find all the values named `twentyFour`, and give each of them a unique name.
  256. realkinetic.com | @real_kinetic good : Int good = let twentyFour

    = 3 * 8 newTwentyFour = 20 + 4 in newTwentyFour -- This will compile.
  257. realkinetic.com | @real_kinetic 2 Other Notes on Let expressions

  258. realkinetic.com | @real_kinetic You can assign functions

  259. realkinetic.com | @real_kinetic And you can provide type annotations

  260. realkinetic.com | @real_kinetic letFunctions : Int letFunctions = let hypotenuse

    a b = sqrt (a^2 + b^2) name : String name = "Hermann" increment : Int -> Int increment n = n + 1 in increment 10
  261. realkinetic.com | @real_kinetic Those are the basics you need to

    be successful in Elm
  262. realkinetic.com | @real_kinetic But if you’re still nervous don’t worry.

  263. realkinetic.com | @real_kinetic You have another tool available to you

  264. realkinetic.com | @real_kinetic The compiler

  265. realkinetic.com | @real_kinetic Don’t fight the compiler. Let it help

    you.
  266. realkinetic.com | @real_kinetic You don’t even have to provide the

    types
  267. realkinetic.com | @real_kinetic The compiler will give you the types!!!

  268. realkinetic.com | @real_kinetic update msg model = case msg of

    Increment -> model + 1 Decrement -> model - 1
  269. realkinetic.com | @real_kinetic ============= WARNINGS =============== -- missing type annotation

    - /…/elm-counter/src/elm/Main.elm Top-level value `update` does not have a type annotation. 29| update msg model = ^^^^^^ I inferred the type annotation so you can copy it into your code: update : Msg -> Model -> Model
  270. realkinetic.com | @real_kinetic update : Msg -> Model -> Model

    update msg model = case msg of Increment -> model + 1 Decrement -> model - 1
  271. realkinetic.com | @real_kinetic And it will even let you know

    when you might be overly strict
  272. realkinetic.com | @real_kinetic ============= WARNINGS =============== -- missing type annotation

    - /…/elm-counter/src/elm/Main.elm Top-level value `update` does not have a type annotation. 29| update msg model = ^^^^^^ I inferred the type annotation so you can copy it into your code: update : Msg -> { a | value : Int } -> ( { a | value : Int }, Cmd Msg )
  273. realkinetic.com | @real_kinetic The Elm compiler is a really helpful

    tool.
  274. realkinetic.com | @real_kinetic Don’t fight it.

  275. realkinetic.com | @real_kinetic Don’t attempt to out smart it and

    write the perfect code if you’re unsure.
  276. realkinetic.com | @real_kinetic Just get something in and compile.

  277. realkinetic.com | @real_kinetic One last syntax thing before we move

    into architecture and building applications
  278. realkinetic.com | @real_kinetic Debugging

  279. realkinetic.com | @real_kinetic In the standard library (core) we are

    provided some very helpful debugging functions
  280. realkinetic.com | @real_kinetic Debug.log log : String -> a ->

    a
  281. realkinetic.com | @real_kinetic Log a tagged value on the developer

    console, and then return the value.
  282. realkinetic.com | @real_kinetic 1 + log "number" 1 -- equals

    2, logs "number: 1" length (log "start" []) -- equals 0, logs "start: []"
  283. realkinetic.com | @real_kinetic Notice that log is not a pure

    function! It should only be used for investigating bugs or performance problems.
  284. realkinetic.com | @real_kinetic myFunc : Action -> String myFunc action

    = case action of act1 -> "It's act 1" act2 -> "It's act 2"
  285. realkinetic.com | @real_kinetic myFunc : Action -> String myFunc action

    = let _ = Debug.log "Action: " action in case action of act1 -> "It's act 1" act2 -> "It's act 2"
  286. realkinetic.com | @real_kinetic myFunc : Action -> String myFunc action

    = case (Debug.log "Action: " action) of act1 -> "It's act 1" act2 -> "It's act 2"
  287. realkinetic.com | @real_kinetic Debug.crash crash : String -> a

  288. realkinetic.com | @real_kinetic Crash the program with an error message.

    This is an uncatchable error, intended for code that is soon-to-be-implemented.
  289. realkinetic.com | @real_kinetic USE THIS if you want to do

    some testing while you are partway through writing a function.
  290. realkinetic.com | @real_kinetic DO NOT USE THIS IF you want

    to do some typical try- catch exception handling. Use the Maybe or Result libraries instead.
  291. realkinetic.com | @real_kinetic The Elm Architecture

  292. realkinetic.com | @real_kinetic Model Update View

  293. realkinetic.com | @real_kinetic Model The state of your application

  294. realkinetic.com | @real_kinetic Update A way to update your state

  295. realkinetic.com | @real_kinetic View A way to view your state

    as HTML
  296. realkinetic.com | @real_kinetic http://elmprogramming.com/subscriptions.html

  297. realkinetic.com | @real_kinetic This architecture is very important as it

    helps us with …
  298. realkinetic.com | @real_kinetic Side Effects

  299. realkinetic.com | @real_kinetic Historical FUD: “You can’t build anything in

    real in functional programming because it’s ‘pure’”
  300. realkinetic.com | @real_kinetic Then came along Philip Wadler

  301. realkinetic.com | @real_kinetic Monads for Functional Programming Wadler, 1992 http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf

  302. realkinetic.com | @real_kinetic We finally mentioned Monads

  303. realkinetic.com | @real_kinetic When people are freaked out by these

    languages it’s often around side effects and those specific MONADS
  304. realkinetic.com | @real_kinetic IO ()

  305. realkinetic.com | @real_kinetic getMeetupNameAndVenues :: GroupId -> IO [(Text, Text)]

    getMeetupNameAndVenues groupId = getWith (eventsOptions groupId) meetupEventsUrl >>= asValue >>= ((^.. responseBody . key "results" . _Array . traverse) >>> map ((^. key "name" . _String) &&& (^. key "venue" . key "name" . _String) ) >>> return
  306. realkinetic.com | @real_kinetic Purescript

  307. realkinetic.com | @real_kinetic Eff () Aff ()

  308. realkinetic.com | @real_kinetic forall eff. Eff (console :: CONSOLE, random

    :: RANDOM | eff) Unit
  309. realkinetic.com | @real_kinetic eval :: Query ~> H.ComponentDSL State Query

    Void (Aff (ajax :: AX.AJAX | eff)) eval = case _ of SetUsername username next -> do H.modify (_ { username = username, result = Nothing :: Maybe String }) pure next MakeRequest next -> do username <- H.gets _.username H.modify (_ { loading = true }) response <- H.liftAff $ AX.get ("https://api.github.com/users/" <> username) H.modify (_ { loading = false, result = Just response.response }) pure next
  310. realkinetic.com | @real_kinetic Elm Doesn’t Support This

  311. realkinetic.com | @real_kinetic All Side Effects are handled by the

    architecture via tasks and “effect managers”
  312. realkinetic.com | @real_kinetic You likely will not need to write

    an effect manager (and only occasionally will you write tasks)
  313. realkinetic.com | @real_kinetic Elm’s goal is to provide all necessary

    effect managers as part of the standard library or as provided libraries
  314. realkinetic.com | @real_kinetic But if you need to write them

    you can
  315. realkinetic.com | @real_kinetic Side Effects in Elm

  316. realkinetic.com | @real_kinetic Elm uses commands for side effects

  317. realkinetic.com | @real_kinetic You trigger a command which will then

    trigger actions as part of it’s effects
  318. realkinetic.com | @real_kinetic http://elmprogramming.com/subscriptions.html

  319. realkinetic.com | @real_kinetic Http http://elm-lang.org/examples/http

  320. realkinetic.com | @real_kinetic Example: Making HTTP Requests (Loading gifs)

  321. realkinetic.com | @real_kinetic type alias Model = { topic :

    String , gifUrl : String } init : (Model, Cmd Msg) init = (Model "cats" "waiting.gif", Cmd.none)
  322. realkinetic.com | @real_kinetic view : Model -> Html Msg view

    model = div [] [ h2 [] [text model.topic] , img [src model.gifUrl] [] , button [ onClick MorePlease ] [ text "More Please!" ] ]
  323. realkinetic.com | @real_kinetic type Msg = MorePlease | NewGif (Result

    Http.Error String) update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of MorePlease -> (model, getRandomGif model.topic) NewGif (Ok newUrl) -> ( { model | gifUrl = newUrl }, Cmd.none) NewGif (Err _) -> (model, Cmd.none)
  324. realkinetic.com | @real_kinetic getRandomGif : String -> Cmd Msg getRandomGif

    topic = let url = "https://api.giphy.com/v1/gifs/random?" ++ "api_key=dc6zaTOxFJmzC&tag=" ++ topic request = Http.get url decodeGifUrl in Http.send NewGif request decodeGifUrl : Decode.Decoder String decodeGifUrl = Decode.at ["data", "image_url"] Decode.string
  325. realkinetic.com | @real_kinetic Http.get : String -> Decode.Decoder value ->

    Http.Request value Http.send : (Result Error value -> msg) -> Http.Request value -> Cmd msg —- MSG: —- NewGif (Result Http.Error String) —- UPDATE: —- NewGif (Ok newUrl) -> —- ( { model | gifUrl = newUrl }, Cmd.none)
  326. realkinetic.com | @real_kinetic Time http://elm-lang.org/examples/time

  327. realkinetic.com | @real_kinetic Example: “Subscribing” to Time (Making a Clock)

  328. realkinetic.com | @real_kinetic main = Html.program { init = init

    , view = view , update = update , subscriptions = subscriptions }
  329. realkinetic.com | @real_kinetic type alias Model = Time init :

    (Model, Cmd Msg) init = (0, Cmd.none)
  330. realkinetic.com | @real_kinetic view : Model -> Html Msg view

    model = let angle = turns (Time.inMinutes model) handX = toString (50 + 40 * cos angle) handY = toString (50 + 40 * sin angle) in svg [ viewBox "0 0 100 100", width "300px" ] [ circle [ cx "50", cy "50", r "45", fill "#0B79CE" ] [] , line [ x1 "50", y1 "50", x2 handX, y2 handY, stroke "#023963" ] [] ]
  331. realkinetic.com | @real_kinetic type Msg = Tick Time update :

    Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of Tick newTime -> (newTime, Cmd.none)
  332. realkinetic.com | @real_kinetic main = Html.program { init = init

    , view = view , update = update , subscriptions = subscriptions }
  333. realkinetic.com | @real_kinetic import Time exposing (Time, second) subscriptions :

    Model -> Sub Msg subscriptions model = Time.every second Tick —- ‘Every' Type Signature every : Time -> (Time -> msg) -> Sub msg —- ‘second' Type Signature second : Time —- ‘Time’ MSG type Msg = Tick Time
  334. realkinetic.com | @real_kinetic http://elmprogramming.com/subscriptions.html

  335. realkinetic.com | @real_kinetic Javascript Interop

  336. realkinetic.com | @real_kinetic Example: Calling into Javascript from Elm (A

    Spellchecker)
  337. realkinetic.com | @real_kinetic This is how you inject your Elm

    app into your browser via Javascript …
  338. realkinetic.com | @real_kinetic <div id="spelling"></div> <script src="spelling.js"></script> <script> var app

    = Elm.Spelling.fullscreen(); </script> ### NOTE: spelling.js is the Javascript generated by the Elm compiler
  339. realkinetic.com | @real_kinetic Let’s add our javascript functions

  340. realkinetic.com | @real_kinetic <div id="spelling"></div> <script src="spelling.js"></script> <script> var app

    = Elm.Spelling.fullscreen(); app.ports.check.subscribe(function(word) { var suggestions = spellCheck(word); app.ports.suggestions.send(suggestions); }); fruits = [] function spellCheck(word) { // You can check on the js console if fruits // was updated by elm typing fruits fruits.push(word); return fruits; } </script>
  341. realkinetic.com | @real_kinetic main = program { init = init

    , view = view , update = update , subscriptions = subscriptions } type alias Model = { word : String , suggestions : List String } init : ( Model, Cmd Msg ) init = ( Model "" [], Cmd.none )
  342. realkinetic.com | @real_kinetic view : Model -> Html Msg view

    model = div [] [ input [ onInput Change ] [] , button [ onClick Check ] [ text "Check" ] , div [] [ text (String.join ", " model.suggestions) ] ]
  343. realkinetic.com | @real_kinetic type Msg = Change String | Check

    | Suggest (List String) update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of Change newWord -> ( Model newWord [], Cmd.none ) Check -> ( model, check model.word ) Suggest newSuggestions -> ( Model model.word newSuggestions, Cmd.none ) port check : String -> Cmd msg
  344. realkinetic.com | @real_kinetic // Javascript Function app.ports.check.subscribe(function(word) { var suggestions

    = spellCheck(word); app.ports.suggestions.send(suggestions); }); -- Elm Wrapper port check : String -> Cmd msg
  345. realkinetic.com | @real_kinetic port suggestions : (List String -> msg)

    -> Sub msg subscriptions : Model -> Sub Msg subscriptions model = suggestions Suggest
  346. realkinetic.com | @real_kinetic app.ports.check.subscribe(function(word) { var suggestions = spellCheck(word); //

    Javascript Function app.ports.suggestions.send(suggestions); }); -- Elm Wrapper port suggestions : (List String -> msg) -> Sub msg
  347. realkinetic.com | @real_kinetic And now you can call javascript functions

    as well as subscribe to javascript functions!
  348. realkinetic.com | @real_kinetic Terminology

  349. realkinetic.com | @real_kinetic We’ve seen functions like `map` and the

    concept of applying functions to data (Instead of looping through data)
  350. realkinetic.com | @real_kinetic We often call data structures that can

    be mapped over: mappable (Look at us making up words)
  351. realkinetic.com | @real_kinetic But there is precise term from mathematics

    to describe this concept: (Wait for it)
  352. realkinetic.com | @real_kinetic Functor

  353. realkinetic.com | @real_kinetic In mathematics, a functor is a type

    of mapping (a homomorphism) between categories arising in category theory. In the category of small categories, functors can be thought of more generally as morphisms. (https://en.wikipedia.org/wiki/Functor)
  354. realkinetic.com | @real_kinetic In non category theory …

  355. realkinetic.com | @real_kinetic A functor is simply something that can

    be mapped over.
  356. realkinetic.com | @real_kinetic In Haskell and Purescript we have a

    “typeclass” (This is something like an “interface” or “abstract class” in other languages)
  357. realkinetic.com | @real_kinetic These allows us to define rules for

    data structures
  358. realkinetic.com | @real_kinetic class Functor f where fmap :: (a

    -> b) -> f a -> f b
  359. realkinetic.com | @real_kinetic -- List Functor Instance instance Functor []

    where fmap = map -- Maybe Functor Instance instance Functor Maybe where fmap _ Nothing = Nothing fmap f (Just a) = Just (f a) -- Either Functor Instance instance Functor (Either a) where fmap _ (Left x) = Left x fmap f (Right y) = Right (f y)
  360. realkinetic.com | @real_kinetic With those classes defined you can now

    fmap over those structures.
  361. realkinetic.com | @real_kinetic So in Haskell and Purescript when you

    define new structures you can implement classes for those methods as well
  362. realkinetic.com | @real_kinetic Elm does not support this concept so

    we accomplish it a different way
  363. realkinetic.com | @real_kinetic We just create the functions in the

    modules and use them directly
  364. realkinetic.com | @real_kinetic So instead of having one `map` function

    that works over all structures that implement the class
  365. realkinetic.com | @real_kinetic We create a function per structure: List.map

    Maybe.map Result.map
  366. realkinetic.com | @real_kinetic Btw the next step up beyond Functor

    is Applicative Functor which then leads to Monads (Oh no!)
  367. realkinetic.com | @real_kinetic We’re not going to dive into those

    today :)
  368. realkinetic.com | @real_kinetic But we have been using them throughout

    the presentation! (Wut!)
  369. realkinetic.com | @real_kinetic Let’s look at another common pattern

  370. realkinetic.com | @real_kinetic We’ll start with multiplication

  371. realkinetic.com | @real_kinetic When we multiply something by 1 we

    always get the that value back
  372. realkinetic.com | @real_kinetic x * 1 == x 1 *

    x == x
  373. realkinetic.com | @real_kinetic We have similar with addition

  374. realkinetic.com | @real_kinetic x + 0 == x 0 +

    x == x
  375. realkinetic.com | @real_kinetic A similar when combining lists

  376. realkinetic.com | @real_kinetic Let x = [1,2] x ++ []

    == x [] ++ x == x append x [] == x append [] x == x
  377. realkinetic.com | @real_kinetic These all share the same properties

  378. realkinetic.com | @real_kinetic The function takes 2 parameters

  379. realkinetic.com | @real_kinetic The parameters and the returned value are

    the same type
  380. realkinetic.com | @real_kinetic There exists a value that doesn’t change

    other values when used with the binary function
  381. realkinetic.com | @real_kinetic And another property that we haven’t shown

    yet
  382. realkinetic.com | @real_kinetic They are associative

  383. realkinetic.com | @real_kinetic (2 + 7) + 4 == 2

    + (7 + 4)
  384. realkinetic.com | @real_kinetic Notice this kills things like subtraction and

    division
  385. realkinetic.com | @real_kinetic (2 - 7) - 4 != 2

    - (7 - 4) (2 / 7) / 4 != 2 / (7 / 4)
  386. realkinetic.com | @real_kinetic Structures that adhere to these properties (laws)

    are known as:
  387. realkinetic.com | @real_kinetic Monoids

  388. realkinetic.com | @real_kinetic In abstract algebra, a branch of mathematics,

    a monoid is an algebraic structure with a single associative binary operation and an identity element. (https://en.wikipedia.org/wiki/Monoid)
  389. realkinetic.com | @real_kinetic class Monoid m where mempty :: m

    mappend :: m -> m -> m mconcat :: [m] -> m mconcat = foldr mappend mempty
  390. realkinetic.com | @real_kinetic Why do we care about Monoids?

  391. realkinetic.com | @real_kinetic There are tons of reasons.

  392. realkinetic.com | @real_kinetic But here is one example …

  393. realkinetic.com | @real_kinetic Our hint was in the last line

    of the Monoid type class and we showed it earlier
  394. realkinetic.com | @real_kinetic Folds

  395. realkinetic.com | @real_kinetic The fold type definition: foldr :: (a

    -> b -> b) -> b -> [a] -> b
  396. realkinetic.com | @real_kinetic For it’s arguments it requires:

  397. realkinetic.com | @real_kinetic A function that takes 2 arguments

  398. realkinetic.com | @real_kinetic An initial value that when applied to

    the binary function doesn’t change (This gives us our base case or starting point)
  399. realkinetic.com | @real_kinetic And of course the item(s) we’ll apply

    the function to and start with the initial value
  400. realkinetic.com | @real_kinetic foldr (*) 1 [1,2,3] > 6 foldr

    (+) 0 [2,3,4] > 9 foldr (++) [] [[1,2,3], [20, 30, 40]] > [1,2,3,20,30,40]
  401. realkinetic.com | @real_kinetic This means that if we have a

    monoid or if we can make a structure become a monoid …
  402. realkinetic.com | @real_kinetic Then we get all of this free

    code
  403. realkinetic.com | @real_kinetic Free code that follow mathematical laws

  404. realkinetic.com | @real_kinetic Which means that free code his highly

    unlikely to change (especially the API) and thus …
  405. realkinetic.com | @real_kinetic It’s free code without many of the

    pains that come with dependent code and dealing with changes, versions, etc (The best and really, only kind of free code)
  406. realkinetic.com | @real_kinetic This is part of the reason folks

    in these worlds get so excited about precise terms and laws
  407. realkinetic.com | @real_kinetic These things aren’t random or context specific.

    They are precise.
  408. realkinetic.com | @real_kinetic When you say monoid it means that

    it has those specific characteristics
  409. realkinetic.com | @real_kinetic It can have more than those characteristics

    of course but at minimum it must have those to be a monoid
  410. realkinetic.com | @real_kinetic From functor we get to applicative to

    monad and from there the ability to implement real programs
  411. realkinetic.com | @real_kinetic And more keeps coming out like things

    like “Free” which allow us to think about the entire structure/architecture of our program
  412. realkinetic.com | @real_kinetic This is why people around you this

    week will be super excited.
  413. realkinetic.com | @real_kinetic You should dive in.

  414. realkinetic.com | @real_kinetic Expose yourself to this world.

  415. realkinetic.com | @real_kinetic Open your mind and expect to feel

    “dumb”
  416. realkinetic.com | @real_kinetic It’s ok.

  417. realkinetic.com | @real_kinetic We all do.

  418. realkinetic.com | @real_kinetic Moving Forward

  419. realkinetic.com | @real_kinetic I highly recommend you give Elm a

    shot
  420. realkinetic.com | @real_kinetic And then give OCaml, F- Sharp, Haskell,

    Idris and Purescript a look.
  421. realkinetic.com | @real_kinetic Especially coming from Elm I’d give Purescript

    a try.
  422. realkinetic.com | @real_kinetic There is a great set of libraries

    from `rgempel` on GitHub that is a bunch of Elm’s modules implement in Purescript
  423. realkinetic.com | @real_kinetic This will give you a good mapping

    of how the Elm concepts and terminology map into Purescript
  424. realkinetic.com | @real_kinetic -- | Equivalent to Purescript's `show`. toString

    :: ∀ a. (Show a) => a -> String toString = show -- | Equivalent to Purescript's `<<<`. compose :: ∀ a b c. (b -> c) -> (a -> b) -> (a -> c) compose = (<<<) -- | Equivalent to Purescript's `$`. applyFn :: ∀ a b. (a -> b) -> a -> b applyFn = ($) -- | The Purescript equivalent is `id`. identity :: ∀ a. a -> a identity = id -- | The Purescript equivalent is `const`. always :: ∀ a b. a -> b -> a always = const -- | The Purescript equivalent is `Void`. type Never = Void
  425. realkinetic.com | @real_kinetic And if you have or you’re already

    wanting more then here a few resources.
  426. realkinetic.com | @real_kinetic First, if you’re going to buy a

    single book then buy “Haskell Programming from first principles” aka “The Haskell Book” even if you’re wanting to learn Elm
  427. realkinetic.com | @real_kinetic And it’s not yet finished but Richard

    Feldman’s “Elm in Action” will almost certainly be one of the best resources specific to Elm
  428. realkinetic.com | @real_kinetic Resources •Elm Website :: Your best resource

    for Elm • http://elm-lang.org/ •Elm to Purescript • https://github.com/pselm •Elm in Action • https://www.manning.com/books/elm-in-action •Haskell Programming from first principles (Haskell Book) • http://haskellbook.com/ •Type Driven Development Book • https://www.manning.com/books/type-driven-development-with-idris •PureScript Conf 2018 • June 6th … here! • https://github.com/lambdaconf/lambdaconf-2018/wiki/PureScript-Conf-2018#schedule •Elm-Conf 2018 • September 26, 2018 in St. Louis, MO as a Strange Loop pre-conference event • https://www.elm-conf.us/
  429. realkinetic.com | @real_kinetic Thank you! (And please come find me

    if you have questions.)
  430. realkinetic.com | @real_kinetic Beau Lyddon Managing Partner at Real Kinetic

    We mentor engineering teams to unleash your full potential. @lyddonb linkedin.com/in/beau-lyddon github.com/lyddonb