This is an alternative site for discovering Elm packages. You may be looking for the official Elm package site instead.
We were not able to find the expected elm-package.json file at this tag.
Tag 2.0.0
Committed At 2019-09-03 17:05:56 UTC

Modules

    README

    Elm-IO

    Documentation

    Some [rules] can be bent. Others can be broken. Understand? -- Morpheus

    This projects provides pure Elm tools whose aim is to make programming in Elm more composable even if it means bending a bit the rules of The Elm Architecture.

    A classic Elm application that follows The Elm Architecture is structured like this:

    • a model data type representing the model at any given time.
    • a msg data type representing events either fired by the view or the runtime (mouse click, input change, http response, ...) called messages.
    • the update: msg -> model -> (model, Cmd msg) function is called on every message received to compute the new value of the model. It can output commands which are tasks handled by the runtime. On completion, commands return a message which trigger once again the update function.
    • the view: model -> Html msg renders the current value of the model into HTML content. This HTML can define messages to send on events.

    This approach has some limitations:

    • The callback passed to onInput : (String -> msg) -> Attribute msg and many other event handlers can decide which message to send based on the input string, but is forced to send one.
    • if you want to execute a command in response to an event happening in the view, the view has to trigger a message that will be interpreted by the update function which will output the command ...
    • the command type (Cmd) is not a monad. It means commands do not compose! For example chaining commands has to be handled in the update function or by using another type such as tasks.

    All of this makes perfect sense from an architectural point of view. The Elm Architecture has many benefits like isolation of rendering, state and effects. This project is for those ready to trade these benefits for more flexibility and conciseness. If your update function is littered with command scheduling code or/and your message type looks more like boilerplate than business, then this package is made for you!

    You have two options:

    • the CmdM approach lets you program the way you used to but lets you trigger commands in the view and chain commands as you like. The model is still updated in the update function, not in the view!
    • the IO approach, in addition of the CmdM's benefits, lets you read and write the state directly in commands. You can then alter the state directly from the view.

    The CmdM monad

    The CmdM monad is the command type (Cmd) turned into a monad. It enables to chain effects easily using classic monadic operations without having to encode complex scheduling in the update function.

    A program using CmdM is generally built arround CmdM.program, CmdM.vDomProgram, CmdM.programWithFlags or CmdM.vDomProgramWithFlags depending on if this is a headless program or if flags are required. For more specific needs, you can use CmdM.transform and CmdM.transformWithFlags.

    CmdM is used very much like Cmd. The main difference is the view outputs Html (CmdM Msg) instead of Html Msg. You're not forced to refactor your view to use CmdM:

    classicTeaView: Model -> Html Msg
    
    cmdmView: Model -> Html (CmdM Msg)
    cmdmView model = classicTeaView |> Html.map CmdM.pure
    

    The general way of using CmdM is lifting a Cmd a value into a CmdM a one by CmdM.lift and chain them by CmdM.andThen or CmdM.ap. The module CmdM.Infix provides infix notation for these operators.

    The IO monad

    The IO monad is like the CmdM monad enriched with state altering effect. Thus command effects and model modifications can be mixed easily. Furthermore the view and subscriptions can not only emit messages but also IOs.

    Example

    Here is a complete example of a simple page showing a counter

    module Hello exposing (..)
    
    import Html exposing (..)
    import Html.Events exposing(..)
    import IO exposing (..)
    
    type alias Model  = Int 
    type alias Msg = ()
    
    increment : IO Model Msg
    increment = IO.modify ((+) 1)
    
    reset : IO Model Msg
    reset = IO.set 0
    
    view : Model -> Html (IO Model Msg) 
    view m = 
      div [] [
        h1 [] [text "Example of an IO program"],
        p [] [text ("Counter = " ++ (String.fromInt m))],
        button [onClick increment] [text "increment"],
        button [onClick reset] [text "reset"]
      ]
    
    main : IO.Program () Model Msg 
    main =
      IO.sandbox {
        init = \_ -> (0, IO.none),
        view = view ,
        subscriptions = IO.dummySub
      }
    

    Like CmdM, a program using IO is generally built arround one of the many IO.*Program* functions. These function cover web and headless programs, run with or without flags. In addition the functions named beginner* offer a simple and conside way to run most IO programs. For more specific needs, you can use IO.transform and IO.transformWithFlags.

    With IO, reading and writing the model is done with IO.get, IO.set and IO.modify. It means this kind of code becomes possible:

    action : IO Model Msg
    action =
      IO.get |> IO.andThen (\model -> -- First we read the model
        let
          -- The classic Http command
          httpCommand : Cmd (Result Error Model)
          httpCommand = Http.get { url = "https://example.com/my/api/action"
                                 , expect = Http.expectJson identity decoder
                                 }
        in
          -- First we lift the Cmd command into IO  
          -- then compose it by andThen with a function to deal with the response
          IO.lift httpCommand |> IO.andThen (\response ->
            case response of
              Ok newModel -> IO.set newModel -- and set the new model on success
              Err _       -> IO.none         -- or do nothing on failure
      ))
    

    Requiring all IO actions to work on the whole model would break composability, which would be petty bad obviously. Fortunately IO play well with optics:

    import Monocle.Lens exposing (..)
    
    -- An IO action whose model is an integer
    actionOnInt : IO Int ()
    actionOnInt = IO.modify (\x -> x + 1)
    
    type alias Model = { number : Int, name : String }
    
    lensFromIntToModel : Lens Model Int
    lensFromIntToModel =
      { get = \model -> model.number,
        set = \i model -> { model | number = i }
      }
    
    -- an IO action whose model is a Model
    actionOnModel : IO Model ()
    actionOnModel = IO.lens lensFromIntToModel actionOnInt
    

    To avoid having to use optics when not needed, it is advised to use CmdM for model agnostic actions and lift CmdM to IO at the last moment by IO.liftM.

    Examples from http://elm-lang.org/examples translated into CmdM and IO

    The examples folder contains examples from http://elm-lang.org/examples converted into CmdM and IO ways. Please read the README.md file in this folder for more details on examples.

    Need help?

    If you have questions and/or remarks, contact me on twitter at @chrilves