Skip to content

Instantly share code, notes, and snippets.

@JSuder-xx
Last active March 31, 2022 20:49
Show Gist options
  • Save JSuder-xx/e854a85fa5acd0815966f2281f459506 to your computer and use it in GitHub Desktop.
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.
-- 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