Elm, 4 mois et 10k lignes
Loïc Knuchel
Il était une fois...
Azimutt
🔎 recherche globale
🗺️ affichage du nécessaire
🔗 navigation par relations
📍 sauvegarde de l’affichage
✨ et bien plus...
Contexte
Mes expériences:
Les contraintes:
Mes envies:
Choix de la stack
Candidats:
<= Connu et trop éloigné pour espérer attirer des contributeurs
<= Similaire à Scala
<= Pas assez strict
<= Trop orienté Haskell
<= Très intéressant
<= Intriguant
Elm
Elm ?
Un langage purement fonctionnel construit pour les interfaces web.
Aucune erreur d’exécution ???
🤯
Sérieux ?
La philosophie initiale guide tout
La philosophie de Elm
Construire un écosystème robuste, cohérent et facile à utiliser
pour les années à venir
👌
Intéressant ?
Toujours motivé ?
Système de build
Gestion de packages
Isolation avec JavaScript
Typage fort exhaustif
Immutabilité
Pas de null
Pas de système d’erreur
Peu de fonctionnalités core
Ecosystème à construire
Forte courbe d’apprentissage
Boilerplate
Fonctions pures
Elm
#Robuste #Cohérent #Accessible
Voyons ça de plus près
Elm 😍: la communauté
Elm 🤩: tout sur mesure!
elm-test 🧪
🎬️ The Design Evolution of elm-css and elm-test (à 31’)
elm-test 🧪
test "basic" (\_ -> "id INT" |> Parser.run columnParser |> Expect.equal (Ok { column | name = "id", kind = "INT" }))
testParseTable : String -> SqlStatement -> ParsedTable -> Test
testParseTable name sql result =
test name (\_ -> sql |> Parser.run createTableParser |> Expect.equal (Ok result))
testParseTable "basic"
"""CREATE TABLE users (
id INT
);"""
{ schema = Nothing
, table = "users"
, columns = [ { column | name = "id", kind = "INT" } ]
}
elm-format 🕶️
elm-review 🕯️
elm-book 📖
Elm 🤗: langage simple et minimaliste
Elm 😍: fonctionnel, pur
Elm 😉: syntaxe ML
Elm 😥: syntaxe ML
number =
42
number : Int
add : Int -> Int -> Int
add a b =
a + b
paramètres
retour
map : (a -> b) -> List a -> List b
map f xs = ...
List.map String.fromInt [ 1, 2, 3 ]
[ 1, 2, 3 ] |> List.map String.fromInt
👌
HTML template ?
Elm 🤔: HTML en Elm
view : Html msg
view =
div [ class "some-class" ] [ text "Hello Elm" ]
viewHeader : Html msg
viewHeader =
div [ class "header" ]
[ ul []
[ li [] [ a [ href "#" ] [ text "link 1" ] ]
]
]
div : List (Attribute msg) -> List (Html msg) -> Html msg
class : String -> Attribute msg
text : String -> Html msg
The Elm Architecture
Elm runtime
view: Model -> Html Msg
init: Model
Html Msg
update: Msg -> Model -> Model
Msg
Model
Elm runtime
update: Msg -> Model -> Model
Msg
Model
Html Msg
init: Model
view: Model -> Html Msg
Elm runtime
Model
init: Model
view: Model -> Html Msg
update: Msg -> Model -> Model
Msg + Model
Html Msg
DOM
Effets
(js, http, time, random...)
Et côté code ?
type alias Model = Int
type Msg = Increment | Decrement
view : Model -> Html Msg
update : Msg -> Model -> Model
main = Browser.sandbox { init = 0, view = view, update = update }
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (String.fromInt model) ]
, button [ onClick Increment ] [ text "+" ]
]
update msg model =
case msg of
Increment -> model + 1
Decrement -> model - 1
Elm 😅: langage simple et minimaliste
withDefault : a -> Maybe a -> a
map : (a -> b) -> Maybe a -> Maybe b
map2 : (a -> b -> value) -> Maybe a -> Maybe b -> Maybe value
map3 : (a -> b -> c -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe value
map4 : (a -> b -> c -> d -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe d -> Maybe value
map5 : (a -> b -> c -> d -> e -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe d -> Maybe e -> Maybe value
andThen : (a -> Maybe b) -> Maybe a -> Maybe b
contains : a -> Maybe a -> Bool
exist : (a -> Bool) -> Maybe a -> Bool
filter : (a -> Bool) -> Maybe a -> Maybe a
orElse : Maybe a -> Maybe a -> Maybe a
zip : Maybe a -> Maybe b -> Maybe ( a, b )
zip3 : Maybe a -> Maybe b -> Maybe c -> Maybe ( a, b, c )
andThenZip : (a -> Maybe b) -> Maybe a -> Maybe ( a, b )
toList : Maybe a -> List a
sequenceR : Maybe (Result x a) -> Result x (Maybe a)
fold : b -> (a -> b) -> Maybe a -> b
Elm 😕: un peu trop minimaliste?
Elm 😍: aucun implicite
Le code Elm est écrit uniquement avec des fonctions qui composent.
Aucun mécanisme spécial implicite.
type alias Relation =
{ name : RelationName, src : ColumnRef, ref : ColumnRef }
decodeRelation : Decode.Decoder Relation
decodeRelation =
Decode.map3 Relation
(Decode.field "name" decodeRelationName)
(Decode.field "src" decodeColumnRef)
(Decode.field "ref" decodeColumnRef)
encodeRelation : Relation -> Value
encodeRelation value =
E.object
[ ( "name", value.name |> encodeRelationName )
, ( "src", value.src |> encodeColumnRef )
, ( "ref", value.ref |> encodeColumnRef )
]
Elm 😢: je galère toujours...
Grosses surprises
😍
Elm 😍: tient ses promesses
Conclusion
Dois-je me mettre à Elm?
Oui si:
Non si:
A quoi m’attendre aux débuts avec Elm?
Comment démarrer avec Elm?
The end
J+15
J+37
J+76