Slay
Internet Explorer
SERVER
Is the browser IE?
Request
YES
REJECT THE REQUEST
SERVE NORMAL CONTENT
NO
app.get("/", (req, res) => {
if(isUsingIE(req)) {
res.status(400)
.send("Use a true browser!")
} else { res.render("index.ejs"); }
});
app.get("/", (req, res) => {
if(isUsingIE(req)) {
res.status(400)
.send("Use a true browser!")
} else { res.render("index.ejs"); }
});
app.get("/haskell", (req, res) => {
if(isUsingIE(req)) {
res.status(400)
.send("Use a true browser!")
} else { res.render("haskell.ejs"); }
});
/haskell
/
Welcome to my amazing website!
Haskell is amazing!
USER CODE
USER CODE
/haskell
/
Welcome to my amazing website!
Haskell is amazing!
USER CODE
USER CODE
FRAMEWORK
/haskell
/
Welcome to my amazing website!
Haskell is amazing!
USER CODE
USER CODE
FRAMEWORK
USER CODE
MIDDLEWARE
/haskell
/
Welcome to my amazing website!
Haskell is amazing!
USER CODE
USER CODE
FRAMEWORK
USER CODE
MIDDLEWARE
REJECT THE REQUEST
/haskell
/
Welcome to my amazing website!
Haskell is amazing!
USER CODE
USER CODE
FRAMEWORK
USER CODE
MIDDLEWARE
REJECT THE REQUEST
SERVER
Is the browser IE?
Request
YES
REJECT THE REQUEST
SERVE NORMAL CONTENT
NO
/haskell
/
Welcome to my amazing website!
Haskell is amazing!
USER CODE
USER CODE
FRAMEWORK
USER CODE
MIDDLEWARE
REJECT THE REQUEST
/haskell
/
Welcome to my amazing website!
Haskell is amazing!
USER CODE
USER CODE
REJECT THE REQUEST
USER CODE
If the browser is IE
Is the browser IE?
Request
YES
REJECT THE REQUEST
NO
Is there an \(\texttt{auth}\) cookie?
NO
LOGIN PAGE
Is the browser IE?
Request
YES
REJECT THE REQUEST
NO
Is there an \(\texttt{auth}\) cookie?
NO
LOGIN PAGE
YES: User
ROUTER
Just Home
/
Just Haskell
/haskell
POST /haskell
referer: lambda.org
{ "message": "hello",
"dest": "Evan" }
Just ( SendMessage
{ to = "Evan"
, content = "Hello"
, sentFrom ="lambda.org"
} )
Nothing
Is the browser IE?
Request
YES
REJECT THE REQUEST
NO
Is there an \(\texttt{auth}\) cookie?
NO
LOGIN PAGE
YES: User
ROUTER
Just Home
/
Just Haskell
/haskell
POST /haskell
referer: lambda.org
{ "message": "hello",
"dest": "Evan" }
Just ( SendMessage
{ to = "Evan"
, content = "Hello"
, sentFrom ="lambda.org"
} )
Nothing
serveStatic "Welcome!"
serveStatic "Haskell!"
send { to = "Evan", from = User
, content = "Hello" };
log "Message sent from: lambda.org"
notFound
Server:
\(\texttt{Request} \to \texttt{Response}\)
Server:
\(\texttt{Request} \to \texttt{IO Response}\)
Elm, a functional language?
Sébastien Besnier
@_sebbes_
What is elm?
Programming language
designed for front end development
17k LoC
200k LoC
A language with no runtime exception
- total functions
- immutable data
- strong typing
And a Lot More!
Check it out:
Some cool libs
WebGL support
3D Physics Engine
What about performance?
Real World App
- Signup/Signin
- Content creation
- Routing
694
Is it hard to achieve?
Follow the guide:
elm
init
reactor
install
make
publish
repl
a tiny dev server
add new dependencies
download deps, compile, build really quickly
packages
Testimonials
We have newcomers go through the official Elm guide, we sit down with them to answer questions and do concrete work, and after a couple of weeks, we don’t get requests for help anymore.
https://www.humio.com/whats-new/blog/why-we-chose-elm-for-humio-s-web-ui
We have newcomers go through the official Elm guide, we sit down with them to answer questions and do concrete work, and after a couple of weeks, we don’t get requests for help anymore.
https://www.humio.com/whats-new/blog/why-we-chose-elm-for-humio-s-web-ui
A beginner Elm developer, in our experience, can be productive in a couple of weeks and can master the language in a couple of months.
Testimonials
We have newcomers go through the official Elm guide, we sit down with them to answer questions and do concrete work, and after a couple of weeks, we don’t get requests for help anymore.
https://www.humio.com/whats-new/blog/why-we-chose-elm-for-humio-s-web-ui
A beginner Elm developer, in our experience, can be productive in a couple of weeks and can master the language in a couple of months.
Several rounds of summer interns have also proven that it is possible to learn Elm and our systems, and become productive in a matter of days.
Testimonials
Amazing compiler error messages
REPL demo!
Rendering HTML
text : String -> Html
node :
String
-> List Attributes
-> List Html
-> Html
node tag attributes children =
...
TWO BUILDING BLOCKS
node "p" [class "btn"] [ text "hello"]
<p class="btn">Hello</p>
text : String -> Html
node :
String
-> List Attributes
-> List Html
-> Html
node tag attributes children =
...
p = node "p"
ul = node "ul"
div = node "div"
...
p [class "btn"] [ text "hello"]
<p class="btn">Hello</p>
viewKids kids =
ul []
(List.map
(\kid -> li [] [ text kid ])
kids
)
Displaying Lists
It's just a map!
Hey, I'm used to write HTML, what about some "templating" language?
Hey, I'm used to write HTML, what about some "templating" language?
Templating language demands:
- defining & learning new syntax
- reinventing conditionals, loops, "context", "inheritance", "functions"
- building tools (coloration, completion, ...)
- bridging types from "host" language
(hi Ivy!)
- deciding where the code should live
Hey, I'm used to write HTML, what about some "templating" language?
Templating language demands:
- defining & learning new syntax
- reinventing conditionals, loops, "context", "inheritance", "functions"
- building tools (coloration, completion, ...)
- bridging types from "host" language
(hi Ivy!)
- deciding where the code should live
Free/useless if you directly use your language with text and node!
Rendering HTML
What about CSS?
External CSS
<html><head>
<link href="/my-style.css">
<script src="main.js"></script>
</head>
<body>
<div id="myapp"></div>
<script>
var app = Elm.Main.init({
node: document.getElementById('myapp')
});
</script>
</body></html>
External CSS
<html><head>
<link href="/my-style.css">
<script src="main.js"></script>
</head>
<body>
<div id="myapp"></div>
<script>
var app = Elm.Main.init({
node: document.getElementById('myapp')
});
</script>
</body></html>
-- Main.elm
p [ class "my-btn"
, style "color" "red"
]
[ text "hello"]
Some Issues when dealing with External CSS:
- is CSS valid?
- is it correctly loaded?
- CSS code maintenance & refactor
elm-css
Some Issues when dealing with External CSS:
- is CSS valid?
- is it correctly loaded?
- CSS code maintenance & refactor
Let's use our existing compiler tackle all of that!
elm-css
elm-css
-- Main.elm
div []
[ p [ css
[ backgroundColor (rgb 255 0 255)
, padding (px 30) ]
]
[ text "hello" ]
]
elm-css
<div>
<style>._72f9689 {
background-color:rgb(255, 0, 255);
padding:30px;
}
</style>
<p class="_72f9689">hello</p>
</div>
-- Main.elm
div []
[ p [ css
[ backgroundColor (rgb 255 0 255)
, padding (px 30) ]
]
[ text "hello" ]
]
elm-css
-- Main.elm
div []
[ p [ buttonStyle ] [ text "hello" ]
, p [ buttonStyle ] [ text "good bye" ]
]
buttonStyle =
css
[ backgroundColor (rgb 255 0 255)
, padding (px 30)
]
Some Issues when dealing with External CSS:
- is CSS valid? ✓
- is it correctly loaded?✓
- CSS code maintenance & refactor
Let's use our existing compiler tackle all of that!
elm-css
Organize the style as normal code!
elm-ui
Some Issues when dealing with CSS:
- Yet Another Complex Language to learn
- Context sensitive
- How to vertically center an element?
elm-ui
Some Issues when dealing with CSS:
- Yet Another Complex Language to learn
- Context sensitive
- How to vertically center an element?
elm-ui
- "If it compiles, it works" applied to the layout
- no "cascading"
- simple primitives
elm-ui
Building blocks
text : String -> Element
row : List Attribute
-> List Element
-> Element
column : List Attribute
-> List Element
-> Element
layout : List Attribute
-> List Element
-> Html
elm-ui
Building blocks
text : String -> Element
row : List Attribute
-> List Element
-> Element
column : List Attribute
-> List Element
-> Element
layout : List Attribute
-> List Element
-> Html
button :
List Attribute
->
{ onPress : Msg
, label : Element
}
-> Element
elm-ui
Example
row [ centerY, centerX, width fill ]
[ text "Home"
, el [ centerX ] (text "Lambda Lille")
, el [ alignRight ] (text "Connexion")
]
Text
Lambda Lille
Connexion
Home
Side Note: Records
type alias Person =
{ name : String
, age: Int
}
view : Person -> Html
view p =
text p.name
-- FUNCTION CALL:
view { name = "Seb", age = 42 }
Side Note: Records
List.map .age
[{ name = "Seb", age = 42 }
,{ name = "Bob", age = 53 } ]
-- RESULT:
[ 42, 53 ]
-- SIGNATURE:
.age : { anyRecord | age : value } -> value
elm-ui
Responsiveness
Displaying depends on screen size.
elm-ui
Responsiveness
view model =
case model.screenWidth of
Phone -> viewPhone model.page
Tablet -> viewTable model.page
Desktop -> viewDesktop model.page
BigDesktop -> viewBigDesktop model.page
Displaying depends on screen size.
Let's make that explicit!
What's a pure functional programming language?
What's a pure functional programming language?
Side effects are totally "controlled"
What's a pure functional programming language?
Side effects are totally "controlled"
What's a pure functional programming language?
Side effects are totally "controlled"
What's a pure functional programming language?
Side effects are totally "controlled"
IO
What's a pure functional programming language?
Side effects are totally "controlled"
IO
List.head []
Binding to C (it is even "easy")
What's a pure functional programming language?
Side effects are totally "controlled"
IO
List.head []
Binding to C (it is even "easy")
init : Model
view : Model -> Html Msg
update : Msg -> Model -> Model
init : Model
view : Model -> Html Msg
update : Msg -> Model -> (Model, Cmd Msg)
Commands (Cmd) are "requests" to (e.g):
- HTTP
activateCmd : Cmd Msg
activateCmd = post
{ url = "/activate"
, expect =
expectWhatever GotActivationResult
, body = emptyBody
}
Commands (Cmd) are "requests" to (e.g):
- HTTP
activateCmd : Cmd Msg
activateCmd = post
{ url = "/activate"
, expect =
expectWhatever GotActivationResult
, body = emptyBody
}
Msg generated when response received
type Msg
= ButtonClicked
| GotActivationResult (Result Error ())
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
ButtonClicked ->
(model, activateCmd)
GotActivationResult (Ok ()) ->
({model | message = "Activated!"}
, Cmd.none
)
GotActivationResult (Err _) ->
({model | message = "Error :/"}
, Cmd.none
)
Commands (Cmd) are "requests" to (e.g):
- HTTP
- Time
Time.now : (Posix -> msg) -> Cmd msg
Time.here : (Zone -> msg) -> Cmd msg
Commands (Cmd) are "requests" to (e.g):
- HTTP
- Time
- Randomness
Commands (Cmd) are "requests" to (e.g):
- HTTP
- Time
- Randomness
- JS
port copyToClipboard : String -> Cmd msg
update msg model =
case msg of
CopyButtonClicked ->
( model
, copyToClipboard "Lorem Ipsum"
)
const app = Elm.Main.init({
node: document.getElementById('root'),
});
app.ports.copyToClipboard.subscribe(
content =>
navigator.clipboard.writeText(content)
);
index.js
Cmd
Elm Ports
Cmd
Sub
Elm Ports
It's just message exchange!
Microphone Volume
Continuously send volume
Microphone Volume
Ports?
Continuously send volume
Microphone Volume
Ports?
Please, start sending volume
Please, stop sending volume
Custom Element!
Custom Element!
function createMicListener(
callback: (volume: number) => void
): { disconnect: () => void }
{ /*...*/}
index.js
Custom Element!
function createMicListener(
callback: (volume: number) => void
): { disconnect: () => void }{ /*...*/}
customElements.define("audio-meter",
class extends HTMLElement {
connectedCallback() {
this.micListener = createMicListener((volume)=>{
this.dispatchEvent(new CustomEvent(
"volume",{"detail": volume}
));
});
}
disconnectedCallback() {
this.micListener.disconnect();
}
}
);
index.js
Custom Element!
import Json.Decode as D
view model =
if model.isRecording then
div []
[ node "audio-meter"
[ on "volume"
(D.field "detail" (D.map GotVolume float))
]
, text ("Volume:"++ Str.fromFloat model.volume)
]
[]
else
text "Not listening"
update msg model =
case msg of
GotVolume volume ->
{ model | volume = volume }
Custom Element!
Custom Elements for:
- Tiny widgets to get some atomic data (like microphone volume)
- Audio/Video player
- Integrate components from other framework (e.g. Google Maps)
What's a pure functional programming language?
Side effects are totally "controlled"
IO
List.head []
Binding to C (it is even "easy")
Elm Architecture
Ports for FFI
What's a pure functional programming language?
Side effects are totally "controlled"
IO
Elm Architecture
Ports for FFI
Custom Elements
-- run arbitrary code with HTML type
List.head []
Binding to C (it is even "easy")
JSON to Elm
Decode them all!
{
"name": "Super Camp",
"duration": 7.0,
"kids": ["Sébastien", "Thomas"]
}
JSON
import Json.Decode as D
type alias Camp =
{ name : String
, kids : List String
, duration : Float
}
campDecoder : D.Decoder Camp
campDecoder =
D.map3 Camp
(D.field "name" D.string)
(D.field "kids" (D.list D.string))
(D.field "duration" D.float)
import Simple.JSON as JSON
type Camp =
{ name : String
, kids : List String
, duration : Float
}
main = do
case JSON.readJSON theJSON of
Right (camp :: Camp) -> do
-- Do stuff with camp!
Left e -> do
-- Handle failre
{
"name": "Super Camp",
"duration": 7.0,
"kids": ["Sébastien", "Thomas"]
}
JSON
{
"name": "Super Camp",
"duration": 7.0,
"children": ["Sébastien", "Thomas"]
}
JSON
Multiple options:
- Change \(\texttt{kids}\) to \(\texttt{children}\) in the entire code base
- Write a new \(\texttt{CampJson}\) type and a \(\texttt{CampJson} \to \texttt{Camp}\)
- Write a custom decoder
import Json.Decode as D
type alias Camp =
{ name : String
, kids : List String
, duration : Float
}
campDecoder : D.Decoder Camp
campDecoder =
D.map3 Camp
(D.field "name" D.string)
(D.field "children" (D.list D.string))
(D.field "duration" D.float)
import Json.Decode as D
type alias Camp =
{ name : String
, kids : List String
, duration : Float
}
campDecoder : D.Decoder Camp
campDecoder =
D.map3 Camp
(D.field "name" D.string)
(D.field "children" (D.list D.string))
(D.field "duration" D.float)
Decoders define Explicit Boundaries with the Outside
{
"name": "Super Camp",
"duration": 7.0,
"children": ["Sébastien", "Thomas"]
}
JSON
{ "metadata": {
"name": "Super Camp",
"place": "Paris"
},
"duration": 7.0,
"children": [
{ "name": "Sébastien", "age": 42},
{ "name": "Thomas", "age": 42}
]
}
JSON
import Json.Decode as D
type alias Camp =
{ name : String
, kids : List String
, duration : Float
}
campDecoder : D.Decoder Camp
campDecoder =
D.map3 Camp
(D.at [ "metadata", "name" ] D.string)
(D.field "children" (D.list
(D.field "name" D.string)
))
(D.field "duration" D.float)
succeed : a -> Decoder a
field : String -> Decoder a -> Decoder a
string : Decoder String
int : Decoder Int
list : Decoder a -> Decoder (List a)
map2 :
(a -> b -> c)
-> Decoder a
-> Decoder b
-> Decoder c
succeed : a -> Decoder a
field : String -> Decoder a -> Decoder a
string : Decoder String
int : Decoder Int
list : Decoder a -> Decoder (List a)
map2 :
(a -> b -> c)
-> Decoder a
-> Decoder b
-> Decoder c
COMBINATORS
import Json.Decode as D
type alias Camp =
{ name : String
, kids : List String
, duration : Float
}
campDecoder : D.Decoder Camp
campDecoder =
D.map3 Camp
(D.at [ "metadata", "name" ] D.string)
(D.field "children" (D.list
(D.field "name" D.string)
))
(D.field "duration" D.float)
import Json.Decode as D
import Duration exposing (Duration)
type alias Camp =
{ name : String
, kids : List String
, duration : Duration
}
campDecoder : D.Decoder Camp
campDecoder =
D.map3 Camp
(D.at [ "metadata", "name" ] D.string)
(D.field "children" (D.list
(D.field "name" D.string)
))
(D.field "duration"
(D.map Duration.weeks D.float))
import Json.Decode as D
import Duration exposing (Duration)
type alias Camp =
{ name : String
, kids : List String
, duration : Duration
}
campDecoder : D.Decoder Camp
campDecoder =
D.map3 Camp
(D.at [ "metadata", "name" ] D.string)
(D.field "children" (D.list
(D.field "name" D.string)
))
(D.field "duration"
(D.map Duration.weeks D.float))
weeks : Float -> Duration
import Json.Decode as D
import Duration exposing (Duration)
type alias Camp =
{ name : String
, kids : List String
, duration : Duration
}
campDecoder : D.Decoder Camp
campDecoder =
D.map3 Camp
(D.at [ "metadata", "name" ] D.string)
(D.field "children" (D.list
(D.field "name" D.string)
))
(D.field "duration"
(D.map Duration.weeks D.float))
weeks : Float -> Duration
👆 Click me 👆
Json 2 Elm
{
"name": "Super Camp",
"duration": 7.0,
"children": ["Sébastien", "Thomas"]
}
GraphQL
query {
human(id: "1001") {
name
homePlanet
}
}
query : SelectionSet (Maybe Human) RootQuery
query =
Query.human { id = Id "1001" } humanSelection
type alias HumanData =
{ name : String, homePlanet : Maybe String }
humanSelection : SelectionSet Human StarWars.Object.Human
humanSelection =
SelectionSet.map2 HumanData
Human.name
Human.homePlanet
Entirely type safe requests
Type classes
Type classes
JSON decoding example:
- automatically "derive" the decoders!
Yeah!
Type classes
JSON decoding example:
- automatically "derive" the decoders!
- manually write the instance
How to find the implementation?
Type classes
JSON decoding example:
- automatically "derive" the decoders!
- manually write the instance
-
buttonDecoder : ButtonType -> Decoder Button
Type classes
JSON decoding example:
- automatically "derive" the decoders!
- manually write the instance
-
buttonDecoder : ButtonType -> Decoder Button
Rigidity
Type classes
Rigidity
Compilation time
Yeah!
How to find the implementation?
Type classes
Yeah!
How to find the implementation?
Code reuse
Abstraction
Rigidity
Compilation time
Type classes
Rigidity
Compilation time
Yeah!
How to find the implementation?
Code reuse
Abstraction
Cognitive & Skills overloading
Type classes
No Type Classes in Elm
(or in a really limited form)
Type classes
No Type Classes in Elm
(or in a really limited form)
We have newcomers go through the official Elm guide, we sit down with them to answer questions and do concrete work, and after a couple of weeks, we don’t get requests for help anymore.
https://www.humio.com/whats-new/blog/why-we-chose-elm-for-humio-s-web-ui
elm
make
download deps, compile, build really quickly
Type classes
No Type Classes in Elm
(or in a really limited form)
Related:
Internationalization
module Page.Team exposing
(Model, Locale, view, ...)
type alias Locale =
{ team : String }
view : Locale -> Model -> Html Msg
view locale model =
Html.text locale.team
module Lang.Fr exposing (locale)
import Global
import Page.Team
import Page.Book
locale : Global.Local
locale =
{ login = "Connexion"
, logout = "Déconnexion"
, teamPage = teamPage
, bookPage = bookPage
}
teamPage : Page.Team.Locale
teamPage =
{ team = "Équipe!" }
bookPage : Page.Book.Locale
bookPage =
{ book = "Livre!" }
module Lang.Fr exposing (locale)
import Global
import Page.Team
import Page.Book
locale : Global.Local
locale =
{ login = "Connexion"
, logout = "Déconnexion"
, teamPage = teamPage
, bookPage = bookPage
}
teamPage : Page.Team.Locale
teamPage =
{ team = "Équipe!" }
bookPage : Page.Book.Locale
bookPage =
{ book = "Livre!" }
Elm-review
Elm-review
Let users write rules PER PROJECT
E.g. forbid the use of \(\texttt{Html.button}\) and force the use of \(\texttt{Theme.button}\)
Elm-review
Some "global" rules
- Detect and automatically remove unused code (with \(\texttt{--fix}\) )
https://jfmengels.net/unused-patch-1-1-5/
- Detect situation where TCO doesn't apply
https://jfmengels.net/tail-call-optimization/
type SomeType
= Used
| Unused -- line can be removed
defaultValue = Used
toString : SomeType -> String
toString value =
case value of
Used -> "used"
Unused -> "unused" -- line can be removed
Elm-review
Some "global" rules
type Effect
= RequestUsersList
| Toast String
perform : Effect -> Model -> (Model, Cmd Msg)
update msg model =
let
(newModel, eff) =
case newModel.page of
Page1 -> myUpdate1 msg model
Page2 -> myUpdate2 msg model
in
perform eff newModel
myUpdate1 : Msg -> Model -> (Model, Effect)
myUpdate2 : Msg -> Model -> (Model, Effect)
type alias Layout msg =
{ mainPanel : Html msg, sidebar : Sidebar }
myView1 : Model -> Layout msg
myView2 : Model -> Layout msg
render : Layout msg -> Html msg
view model =
render <|
case model.page of
Page1 -> myView1 model
Page2 -> myView2 model
Elm architecture is a set of low level primitives
Releases
Releases
Releases
1.5 year
1.5 year
Elm, a functional language?
Sébastien Besnier
@_sebbes_
Purescript bundle size:
- https://discourse.purescript.org/t/what-is-the-state-of-the-art-minification-compression/1369/3 (may 2020)
- https://www.reddit.com/r/purescript/comments/ltm38p/how_do_you_deal_with_the_giant_bundle_sizes_from/ (march 2021)
Purescript: Hallogen: elm-similar api
Elm, a functional language?
By sebbes
Elm, a functional language?
- 1,086