1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
|
module Main exposing (..)
import Css exposing (hex, px)
import Html.Styled exposing (Html, button, text)
import Html.Styled.Attributes exposing (class)
import Html.Styled.Events exposing (onClick)
import Slides exposing (..)
import Slides.Styles
main =
Slides.app
{ slidesDefaultOptions
| title = "Web development with Elm"
, style =
Slides.Styles.elmMinimalist
(hex "#fefefe")
(hex "#eee")
(px 16)
(hex "#333")
}
[ md
"""
# Web development with Elm
## Impressions from a Typescript developer
_Jan Tuomi 2021_
"""
, md
"""
## Agenda
1. Presentation about the cool parts of the Elm language & ecosystem (30min)
2. Demos & interactive learning (30min)
3. Learning Elm in an online IDE w/ 🍺 (60min)
"""
, md
"""

* Designed for web UI development
* Pure functional (no side-effects, yes managed effects)
* Compiles to Javascript
* Enforces a specific app architecture
* Ships with useful tools (debugger, compiler, package manager...)
* Aims to be simple to learn and delightful to use
"""
, md
"""
## A snippet of Elm
```elm
module Main exposing (..)
import Css exposing (hex, px)
import Slides exposing (..)
import Slides.Styles
main =
Slides.app
{ slidesDefaultOptions
| style =
Slides.Styles.elmMinimalist
(hex "#fefefe")
(hex "#ccc")
(px 16)
(hex "#333")
}
[ md
\"\"\"
# Web development with Elm
## Impressions from a Typescript developer
_Jan Tuomi 2021_
\"\"\"
]
```
"""
, md
"""
## Elm architecture

"""
, md
"""
## Similarities to Typescript & React + Redux
* Immutable functional style in modern TS (filter, map, reduce)
* Declarative & reactive, VDOM based
* Elm architecture ≈ Redux state architecture
* Similar typing experience:
* TS intersection types ≈ Elm records
* TS union types ≈ Elm custom types
"""
, md
"""
### Immutable functional style in modern TS (filter, map, reduce)
TS:
```typescript
const sumOfNonNegativeCubes: Number = [1, -2, 3]
.map(num => Math.pow(num, 3))
.filter(num => num >= 0)
.reduce((sum, val) => sum + val, 0)
// Result: sumOfNonNegativeCubes == 28
```
Elm:
```elm
sumOfNonNegativeCubes : Int
sumOfNonNegativeCubes =
[ 1, -2, 3 ]
|> List.map (\\num -> num ^ 3)
|> List.filter (\\num -> num >= 0)
|> List.foldl (\\sum val -> sum + val) 0
-- Result: sumOfNonNegativeCubes == 28
```
"""
, md
"""
## Declarative & reactive, VDOM based
TS:
```typescript
const Button = ({ onClick, className }: Props) =>
<button onClick={onClick} className={className}>Click me</button>;
```
Elm:
```elm
viewButton : Msg -> String -> Html Msg
viewButton handleClick className =
button [ onClick handleClick, class className ] [ text "Click me" ]
```
"""
, md
"""
## Elm architecture ≈ Redux state architecture
Redux + Redux Saga (left) vs Elm (right):

Some differences:
* Elm runtime performs effects, while in TS any user code can have side effects
* All state in Elm must be managed this way, TS can have any state anywhere
"""
, md
"""
## Similar typing experience: TS intersection types ≈ Elm records
TS:
```typescript
interface Store {
user: {
id: string;
};
data: Thing[];
}
```
Elm:
```elm
type alias Store =
{ user :
{ id : String
}
, data : List Thing
}
}
```
"""
, md
"""
## TS union types ≈ Elm custom types
TS:
```typescript
type T = A | B | C | null;
```
Elm:
```elm
type Enum = A | B | C
type T = Maybe Enum
```
"""
, md
"""
## Cool things that I like about Elm
"""
, md
"""
## Cool thing #1: _A really smart type system_
Elm compiler can infer types very well (Hindley-Milner type system, ML languages)
Example: an implementation for `List.flatten`:
```elm
flatten listOfLists = List.foldl List.append [] listOfLists
-- or alternatively, with partial application
flatten = List.foldl List.append []
```
Elm automatically infers the function type:
```elm
flatten : List (List a) -> List a
```
"""
, md
"""
## Cool thing #2: _No null, undefined or exceptions_
* Possibly missing data is represented by monads like `Maybe` or `Result` and handled with pattern matching, pipelining
* Remember kids: monads are just monoids in the category of endofunctors
* "No exceptions" simplifies reasoning about code
* Functions only use parameters as input and return values as output -> _pure functions_
* Failures are represented as data, just like successes
```elm
processData: Maybe Data -> String
processData maybeData = maybeData
|> Maybe.andThen transformData
|> Maybe.andThen prettifyData
|> \\md -> case md of
Just data -> Data.toString data
Nothing -> "No data"
```
"""
, md
"""
## Cool thing #3: _Match expressions_
```elm
viewItemList : RemoteData (List Item) -> Html Msg
viewItemList itemsRemoteData =
case itemsRemoteData of
NotRequested ->
button [ onClick GetItems ] [ text "Click to load items" ]
Loading ->
div [] [ text "Loading items..." ]
Received (Err err) ->
div [ class "error" ] [ "Failed to load items. Error: " ++ errToString err ]
Received (Ok items) ->
div [ class "itemList" ] List.map viewItem items
```
* The match expression can go arbitrarily deep
* Type system ensures that all possible cases are always considered
"""
, md
"""
## Cool thing #4: _Fast development loop_
* Compilation times are short
* HMR works well
* Compile errors are helpful: fix errors -> fix software
* Good VS Code integration
"""
, md
"""
## Cool thing #5: _Time traveling debugger_
**[Demo](assets/Counter_debug_demo.html)**
"""
, md
"""
## Cool thing #6: _Javascript interoperability_
* If something is hard to do in Elm, you can do it outside of Elm, e.g.
* components like date pickers (web components are supported directly)
* specific 3rd party JS libraries (authentication)
* LocalStorage or other browser storage
* styling, especially with preprocessors
* Safety: Javascript interop is typed, arbitrary calls not supported in either direction
"""
, md
"""
# Next up...
1. Project demo: a barebones social media app called "Somebook"
* [book.jan.systems](https://book.jan.systems)
2. Kahoot quiz about this presentation
3. Hack: Elm in an online IDE
"""
, md
"""
## Elm exercises in ellie-app.com
Two exercises that you can do on your own computer:
* [List rendering](https://ellie-app.com/fN5YPKXNsq3a1) (easier)
* [Data transform and pipelining](https://ellie-app.com/fN6PQmwvypYa1) (harder)
Grab a 🍺. We'll go through example solutions at the end of the time slot (15min).
"""
]
flatten : List (List a) -> List a
flatten =
List.foldl List.append []
nonNegativeCubes =
[ 1, -2, 3 ]
|> List.map (\num -> num ^ 3)
|> List.filter (\num -> num >= 0)
|> List.foldl (\sum val -> sum + val) 0
viewButton : Msg -> String -> Html Msg
viewButton handleClick className =
button [ onClick handleClick, class className ] [ text "Click me" ]
-- viewItemList : RemoteData Item -> Html Msg
-- viewItemList itemsRemoteData =
-- case itemsRemoteData of
-- NotRequested ->
-- button [ onClick GetItems ] [ text "Click to load items" ]
-- Loading ->
-- div [] [ text "Loading items..." ]
-- Received (Err err) ->
-- div [ class "error" ] [ "Failed to load items. Error:" ++ errToString err ]
-- Received (Ok items) ->
-- div [ class "itemList" ] List.map viewItem items
-- processData: Maybe Data -> String
-- processData maybeData = maybeData
-- |> Maybe.andThen transformData
-- |> Maybe.andThen prettifyData
-- |> \md -> case md of
-- Just data -> Data.toString data
-- Nothing -> "No data"
|