Elm Online Meetup
October 6th 2021
Jeroen Engels
Elm Online Meetup
October 6th 2021
Jeroen Engels
(extensible records)
(extensible records)
2
1
3
Elm Online Meetup
October 6th 2021
Jeroen Engels
1
view : Html Msg
view =
Button.new
|> Button.withText "Submit"
|> Button.withPrimaryStyle
|> Button.withOnClick UserClickedSubmitButton
|> Button.toHtml
button : Html Msg
button =
Button.new
|> Button.withText "Submit"
|> Button.withPrimaryStyle
|> Button.withOnClick UserClickedSubmitButton
|> Button.toHtml
button : Html Msg
button =
Button.button
[ Button.text "Submit"
, Button.primaryStyle
, Button.onClick UserClickedSubmitButton
]
module Button exposing
( Button, new
, withOnClick, withIcon {- and more -}
, toHtml)
type Button msg =
Button
{ text : String
, onClick : Maybe msg
-- ...
}
withOnClick : msg -> Button msg -> Button msg
withOnClick onClick (Button button) =
Button { button | onClick = Just onClick }
view : Html Msg
view =
Button.new
|> Button.withText "Click me!"
-- We don't want a button that is both
-- clickable
|> Button.withOnClick ButtonHasBeenClicked
-- AND disabled
|> Button.withDisabled
|> Button.toHtml
type Maybe a
= Just a
| Nothing
name : Maybe String
name = Just "Jeroen"
age : Maybe Int
age = Just 31
-- a is not used
type Currency a
= Currency Int
type Dollar = Dollar
type Euro = Euro
dollars : Currency Dollar
dollars = Currency 100
euros : Currency Euro
euros = Currency 200
-- TYPE MISMATCH ❌
euros === dollars
2
clickableButton : msg -> Button msg
clickableButton onClick =
Button.new
|> Button.withOnClick onClick
disabledButton : Button msg
disabledButton =
Button.new
|> Button.withDisabled
type Button constraints msg =
Button
{ text : String
-- ...
}
type Not_DisabledOrClickable
= Not_DisabledOrClickable Never
new : Button Not_DisabledOrClickable msg
new =
-- Unchanged compared to before
Button
{ text = ""
-- ...
}
type DisabledOrClickable
= DisabledOrClickable Never
withDisabled :
Button Not_DisabledOrClickable msg
-> Button DisabledOrClickable msg
withDisabled (Button button) =
-- Unchanged compared to before
Button { button | disabled = True }
withOnClick :
msg
-> Button Not_DisabledOrClickable msg
-> Button DisabledOrClickable msg
withOnClick onClick (Button button) = -- ...
new : Button Not_DisabledOrClickable msg
withDisabled :
Button Not_DisabledOrClickable msg
-> Button DisabledOrClickable msg
Button DisabledOrClickable msg -> Html msg
Button constraints msg -> Html msg
view =
Button.new
-- UNFORTUNATELY COMPILES ❌
-- |> Button.withText "Click me!"
|> Button.toHtml
view =
Button.new { text = "Click me!" }
|> Button.toHtml
buttonWithIcon =
Button.new { text = "" }
|> Button.withoutText -- 😢
|> Button.withIcon Icons.SaveIcon
|> Button.toHtml
type NoTextOrIcon = NoTextOrIcon Never
type HasTextOrIcon = HasTextOrIcon Never
new : Button NoTextOrIcon msg
withText : Button constraints msg -> Button HasTextOrIcon msg
withIcon : Button constraints msg -> Button HasTextOrIcon msg
toHtml : Button HasTextOrIcon msg -> Html msg
incrementX : { a | x : Int } -> { a | x : Int }
incrementX record =
{ record | x = record.x + 1 }
doSomething : { a | x : Int } -> { a | notX : Int }
doSomething record =
-- Impossible implementation in Elm
3
with... : Button { a | x : Int } msg -> Button { a | notX : Int } msg
with... (Button button) =
-- No problem ✅
Button button
-- Remove a field
Button { a | x : () } -> Button a
-- Add a field
Button a -> Button { a | x : () }
-- Change the type of a field
Button { a | x : Y } -> Button { a | x : Z }
-- Reset the record
Button a -> Button {}
new : Button { needsInteractivity : () } msg
withDisabled :
Button { constraints | needsInteractivity : () } msg
-> Button { constraints | hasInteractivity : () } msg
withText :
String
-> Button constraints msg
-> Button { constraints | hasTextOrIcon : () } msg
toHtml :
Button { constraints | hasInteractivity : (), hasTextOrIcon : () } msg
-> Html msg
extensible records
🎉
🎉
button : Html Msg
button =
Button.new
|> Button.withText "Submit"
|> Button.withPrimaryStyle
|> Button.withOnClick UserClickedSubmitButton
|> Button.toHtml
listOfButtons : List (Button ??? msg)
listOfButtons =
[ Button.new
, Button.new
|> Button.withDisabled
]
listOfButtons : List (Html msg)
listOfButtons =
[ Button.new
|> Button.toHtml
, Button.new
|> Button.disabled
|> Button.toHtml
]
createButton disabled =
Button.new
|> ( -- TYPE MISMATCH
if disabled then
-- Button { a | interactive : () } msg
-- -> Button a msg
Button.withDisabled
else
-- a -> a
identity
)
createButton disabled =
Button.new
|> ( -- TYPE MATCH ✅
if disabled then
Button.withDisabled
else
Button.withOnClick ButtonClicked
)
This is a MAJOR change.
---- Button - MAJOR ----
Changed:
- withOnClick :
msg
-> Button { constraint | x : () } msg
-> Button constraint msg
- withOnClick :
msg
-> Button constraint msg
-> Button constraint msg
Design needs quite some thought
@jfmengels
Jeroen Engels