Elm all the things!
Mateusz Pokora
https://github.com/elm-community/elm-webpack-starter/blob/master/src/static/img/elm.jpg
Czym jest Elm?
- Język programowania kompilowany do JS
- W pełni funkcyjny i statycznie typowany
- Tworzony od 2012 (pierwszy stable release 2016)
- Przeznaczony do tworzenia GUI
Elm - podstawy
Funkcje
- Explicit return - Ostatnia wartość w funkcji jest zwracana
- Pure functions - Funkcje zawsze zwracają to samo przy takich samych argumentach. Funkcje nie mają side effectów
Funkcje
// Deklaracja funkcji
sayHello : String -> String
sayHello name =
"Hello " ++ name ++ "!"
add : Int -> Int -> Int
add a b =
a + b
add5 : Int -> Int
add5 a =
add 5 a
Listy
names = [ "Alice", "Bob", "Chuck" ]
- Zbiór wartości o tym samym typie
- Wartości mogą się powtarzać
Record
point2D =
{ x = 0
, y = 0
}
point3D =
{ x = 3
, y = 4
, z = 12
}
Tuple
// name, points, isAdmin
("Mateusz", 20, True)
// host, port
("192.168.0.1", 3000)
- Zbiór kilku wartości
- Przydatny przy zwracaniu kilku wartości z funkcji
Immutability
var user = {
name: "Mateusz",
age: 21
};
user.age = 100;
-----------------------------
user : User
user = {
name = "Mateusz"
, age = 21
}
updatedUser = { user | age = 100 }
Nie modyfikujemy istniejących struktur. Zamiast tego tworzymy nowe aktualizując pola.
Immutability
var user = {
name: "Mateusz",
age: 21
};
user.age = 100;
-----------------------------
user : User
user = {
name = "Mateusz"
, age = 21
}
updatedUser = { user | age = 100 }
Nie modyfikujemy istniejących struktur. Zamiast tego tworzymy nowe aktualizując pola.
Pattern matching
skipHead : List String -> List String
skipHead items =
case items of
[] ->
items
head :: tail ->
tail
const skipHead = (items) => {
if (items.length === 0) {
return items;
} else {
return items.slice(0, 1);
}
}
pipe, compose
- Ułatwia łączenie wywołań wielu funkcji
- Przekazujemy wynik funkcji X do funkcji Y
// Javascript
displayName(
String.reverse(
String.capitalize("mateusz")
)
)
-------------------------------
var capitalName = String.capitalize("mateusz")
var reversedCapitalName = String.reverse(capitalName)
displayName(reversedCapitalName)
pipe, compose
// Right pipe
"mateusz" |> String.capitalize |> String.reverse |> displayName // "zsuetaM"
// Right compose
reverseCapitalize = String.capitalize >> String.reverse >> displayName
reverseCapitalize "mateusz" // "zsuetaM"
Maybe
description : Maybe String
description =
Just "I'm description"
description =
Nothing
case description of
Just body ->
String.reverse body
Nothing ->
""
Moduł
module UserForm exposing (Model, title, formNumber)
type alias Model = {
email : String
, name : String
, age : String
}
title : String
title =
"UserForm"
formNumber : Int
formNumber =
15
Co daje nam elm?
- Pełne środowisko do tworzenia aplikacji webowej
- Bezpieczeństwo w postaci silnego systemu typów i pomocnego kompilatora
- Bardzo dobry developer experience :D
Środowisko
- React
- Redux
- Immutable.js
- Flow/Typescript
- Webpack
- Babel
vs.
- Elm
- Html.program
System typów
- NoRedInk - 20 000 linii kodu i 0 runtime exception
- Bezpieczny refactor
- Mniej unit testów
- "Jak się skompiluje to działa"
System typów
System typów
printName : String -> String
printName name =
"My name is :" ++ name
printName 1
///////
Function `printName` is expecting the argument to be:
String
But it is:
number
type alias - własne typy
type alias FormState = {
nameInput : String
, emailInput : String
, ageInput : Int
, birthDate : Date
}
Union Types
type Visibility = All | Active | Completed
type Form =
NotTouched
| Editing
| Submitting
| SubmitSuccess
| SubmitError
Union Types
tableView : Visibility -> Html Msg
tableView visibility =
div [] [
case visibility of
All ->
text "Wszystkie"
Active ->
text "Aktywne"
Completed ->
text "Zakończone"
]
tableView All
const tableView = (visibility) => {
switch(visibility) {
case "All":
return "Wszystkie";
case "Active":
return "Aktywne";
case "Completed":
return "Zakończone";
}
}
tableView("All")
Obsługa wszystkich możliwości
type Visibility =
All
| Active
| Completed
tableView : Visibility -> Html Msg
tableView visibility =
div [] [
case visibility of
All ->
text "Wszystkie"
Active ->
text "Aktywne"
]
This `case` does not have branches for all possibilities.
90|> case visibility of
91|> All ->
92|> text "Wszystkie"
93|>
94|> Active ->
95|> text "Aktywne"
You need to account for the following values:
Main.Completed
Add a branch to cover this pattern!
type RemoteData e a
= NotAsked
| Loading
| Failure e
| Success a
Obsługa wszystkich możliwości
Elm architecture
Text
https://elmbridge.github.io/curriculum/images/elm-architecture-1.jpeg
Html.program
Html.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
Funkcja opisująca jak wygląda nasza aplikacja
Model
type alias Model = {
nameInput : String
, emailInput : String
, termsAndConditionsInput : Bool
, formValid : Bool
, modalOpen : Bool
}
- Struktura danych i aktualny stan aplikacji
- Aplikacja posiada jeden obiekt do reprezentacji stanu
View
view : Model -> Html Msg
view model =
div [] [
input [
value model.emailInput
, onClick UpdateEmail
]
[]
]
- Reprezentacja wizualna naszego modelu
- Produkuje Msg
Update
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
UpdateEmail email ->
({ model | email = email}, Cmd.none )
SubmitForm ->
// komunikacja z serwerem
- Aktualizuje model na podstawie aktualnego stanu i Msg
- Wywołuje side effecty (komunikacja z API)
Time travel debugging
- Podgląd stanu aplikacji w dowolnym momencie jej działania
- Cofnięcie aplikacji do stanu z przeszłości
Eliminacja niemożliwego stanu
Utworzenie struktury modelu w taki sposób aby wykluczyć stany które wzajemnie się wykluczają
Eliminacja niemożliwego stanu
const emailField = {
value: "",
errorMessage: "",
valid: true,
touched: false,
}
Eliminacja niemożliwego stanu
const emailField = {
value: "email@gmail.com",
errorMessage: "email is invalid",
valid: false,
touched: false,
}
Eliminacja niemożliwego stanu
type EmailField =
NotTouched
| Valid String
| Invalid String String
Eliminacja niemożliwego stanu
type EmailField =
NotTouched
| Valid String
| Invalid String String
Jak zacząć?
// 1. Instalacja globalna przy pomocy npm
npm install -g elm
// 2. Stworzenie początkowego projektu
// A. create-elm-app
npm install create-elm-app -g
create-elm-app my-elm-project
cd my-app/
elm-app start
// B. git clone
git clone https://github.com/moarwick/elm-webpack-starter my-elm-project
cd my-elm-project
npm install
npm start
Elm all the things
By vrael560
Elm all the things
- 486