Last active
March 31, 2022 20:49
-
-
Save JSuder-xx/e854a85fa5acd0815966f2281f459506 to your computer and use it in GitHub Desktop.
Unidirectional data flow with Module Oriented programming by using a function a -> a rather than a sum type command.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Demonstrates | |
-- * Dispatching a function Model -> Model rather than a command/message and subsequently pattern matching in the reducer. | |
-- * Benefits include | |
-- * Module Oriented Programming. Grouping related operations with the type. Improved transparency. Better organization. | |
-- * Good ergonomics for composing larger states from smaller througn the use of Lenses. | |
-- * Slightly more succinct. | |
-- * Drawbacks | |
-- * Replay is lost because messages cannot be serialized. | |
-- * Logic cannot run in a web worker because functions will not serialize across boundary. | |
-- | |
-- NOTE: The logic for the application starts on line 54. Everything before that point consists of | |
-- definitions that would be found in a library but which are implemented here for a self-contained example. | |
-- | |
-- Paste this code into: http://elm-lang.org/try | |
module Main exposing (..) | |
import Browser | |
import Html exposing (Html, button, div, text, h3) | |
import Html.Events exposing (onClick) | |
import Html.Attributes exposing (style) | |
-- Updater (would belong to a library) | |
type alias Updater a = a -> a | |
update : Updater a -> a -> a | |
update fn a = fn a | |
-- Simple Lens (would belong to a library) | |
type alias Lens a b = | |
{ get: a -> b | |
, set: a -> b -> a | |
} | |
lens : (a -> b) -> ((a, b) -> a) -> Lens a b | |
lens get tupleSet = | |
{ get = get | |
, set = \a -> \b -> tupleSet (a, b) | |
} | |
over : Lens a b -> Updater b -> Updater a | |
over abL bUpdater a = | |
abL.set a (bUpdater <| abL.get a) | |
-- Html Utility (would belong to a library) | |
viewThroughLens: (b -> Html (Updater b)) -> a -> Lens a b -> Html (Updater a) | |
viewThroughLens bView a abLens = | |
bView (abLens.get a) |> Html.map (over abLens) | |
-- Main | |
main = | |
Browser.sandbox { init = init, update = update, view = view } | |
-- Adder Module | |
type Adder = Adder Int | |
inc : Adder -> Adder | |
inc (Adder n) = Adder (n + 1) | |
show : Adder -> String | |
show (Adder n) = String.fromInt n | |
reset : Adder -> Adder | |
reset _ = Adder 0 | |
viewAdder : Adder -> Html (Updater Adder) | |
viewAdder model = | |
div [] | |
[ div [ style "display" "inline-block", style "width" "40px"] [ text (show model) ] | |
, button [ onClick inc ] [ text "+" ] | |
, button [ onClick reset ] [ text "Reset" ] | |
] | |
-- App | |
type alias App = | |
{ left: Adder | |
, right: Adder | |
} | |
leftL : Lens App Adder | |
leftL = lens .left (\(a, b) -> {a | left = b}) | |
rightL : Lens App Adder | |
rightL = lens .right (\(a, b) -> {a | right = b}) | |
incBoth : App -> App | |
incBoth {left, right} = | |
{ left = inc left | |
, right = inc right | |
} | |
init : App | |
init = {left = Adder 0, right = Adder 10} | |
view : App -> Html (Updater App) | |
view app = | |
div [] | |
[ div [] <| List.map (viewThroughLens viewAdder app) [leftL, rightL] | |
, button [ onClick incBoth ] [ text "+ Both" ] | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment