diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Counter.elm | 47 | ||||
| -rw-r--r-- | src/Main.elm | 381 | ||||
| -rw-r--r-- | src/index.html | 20 | ||||
| -rw-r--r-- | src/index.js | 3 |
4 files changed, 451 insertions, 0 deletions
diff --git a/src/Counter.elm b/src/Counter.elm new file mode 100644 index 0000000..83bb04b --- /dev/null +++ b/src/Counter.elm @@ -0,0 +1,47 @@ +module Counter exposing (..) + +import Browser +import Html exposing (Html, button, div, text) +import Html.Events exposing (onClick) + + +type alias Model = + { count : Int } + + +initialModel : Model +initialModel = + { count = 0 } + + +type Msg + = Increment + | Decrement + + +update : Msg -> Model -> Model +update msg model = + case msg of + Increment -> + { model | count = model.count + 1 } + + Decrement -> + { model | count = model.count - 1 } + + +view : Model -> Html Msg +view model = + div [] + [ button [ onClick Increment ] [ text "+1" ] + , div [] [ text <| String.fromInt model.count ] + , button [ onClick Decrement ] [ text "-1" ] + ] + + +main : Program () Model Msg +main = + Browser.sandbox + { init = initialModel + , view = view + , update = update + } diff --git a/src/Main.elm b/src/Main.elm new file mode 100644 index 0000000..b48f442 --- /dev/null +++ b/src/Main.elm @@ -0,0 +1,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 interfaces ~= Elm record type aliases + + * TS union & intersection types ~= Elm "custom types" (algebraic 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 interfaces ~= Elm record type aliases + + TS: + + ```typescript + interface Store { + user: { + id: string; + }; + data: Thing[]; + } + ``` + + Elm: + + ```elm + type alias Store = + { user : + { id : String + } + , data : List Thing + } + } + ``` + """ + , md + """ + ## TS union & intersection types ~= Elm "custom types" (algebraic 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" + diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..270dabd --- /dev/null +++ b/src/index.html @@ -0,0 +1,20 @@ +<html lang="en"> + +<head> + <title>Web development with Elm</title> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/default.min.css"> + <link rel="icon" href="../assets/elm-icon.png"> + <script src="../highlight.min.js"></script> + <script type="module" src="./index.js"></script> + <style> + pre { + margin: 0 -2%; + } + </style> +</head> + +<body> + <div id="root"></div> +</body> + +</html>
\ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..65faf6c --- /dev/null +++ b/src/index.js @@ -0,0 +1,3 @@ +import { Elm } from "./Main.elm"; + +Elm.Main.init({ node: document.getElementById("root") });
\ No newline at end of file |
