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 1.0.6
Committed At 2018-12-05 00:11:48 UTC

Modules

    README

    elm-reference

    Build Status

    An immutable approach to mutable references.

    elm-reference-small

    Any PRs are welcome, even for documentation fixes. (The main author of this library is not an English native.)

    example

    Top of Contents

    What problem can Reference resolve?

    Many programs need to render lists of things. (e.g. TODOs, registered users, lists of posts.) Reference is here to help solve that problem.

    Here's a simple application that increments numbers in a list.

    init : ( Model, Cmd Msg )
    init =
        ( { nums = [ 1, 2, 3, 4, 5, 6 ]
          }
        , Cmd.none
        )
    
    
    
    -- MODEL
    
    
    type alias Model =
        { nums : List Int
        }
    
    
    
    -- UPDATE
    
    
    type Msg
        = ClickNumber Int
    
    
    update : Msg -> Model -> ( Model, Cmd Msg )
    update msg model =
        case msg of
            ClickNumber idx ->
                ( { model
                    | nums =
                        List.Extra.updateAt idx ((+) 1) model.nums
                  }
                , Cmd.none
                )
    
    
    
    -- VIEW
    
    
    view : Model -> Html Msg
    view model =
        div [] <| List.indexedMap renderRow model.nums
    
    
    renderRow : Int -> Int -> Html Msg
    renderRow idx n =
        div
            [ Events.onClick (ClickNumber idx)
            ]
            [ text <| toString n
            ]
    

    This code uses a technique common in the Elm architecture. However, it isn't as straightforward as it could be. How could we solve this problem more intuitively?

    Mutable references can help!

    Some mutable programing languages use references. Here's an example in JS:

    > arr = [ {val: 1}, {val: 2}, {val: 3} ]
    > x = arr[1]
    > x.val = 3
    > arr
    [ { val: 1 }, { val: 3 }, { val: 3 } ]
    

    If Elm could solve this problem in a similar way, a Msg type could be defined without an index like this:

    type Msg
        = ClickNumber SomeSortOfReference
    

    This is the motivation of the Reference library.

    Concept of a Reference

    A Reference internally tracks two values: this and root.

    • this is the currently focused value (x = arr[1] in the previous JS example)
    • root is the root value (arr in the previous JS example)

    The core data type of Reference is Reference this root where this is the type of an individual value and root is the container that the current value is stored inside of. For example, when referencing a List Int the signature would be Reference Int (List Int).

    Create a Reference by providing a this value and a function which specifies how root depends on the this value.

    fromRecord : { this : a, rootWith : a -> root } -> Reference a root
    

    To pick out the this value and the root value from a Reference, use these simple functions:

    this : Reference this root -> this
    root : Reference this root -> root
    

    Putting it all together:

    ref : Reference Int (List Int)
    ref = fromRecord
        { this = 3
        , rootWith = \x -> [1,2] ++ x :: [4,5]
        }
    
    this ref
    --> 3
    
    root ref
    --> [ 1, 2, 3, 4, 5 ]
    

    Here's where Reference really starts to shine. We'll modify the ref value we declared in the last example by using the modify function.

    modify : (a -> a) -> Reference a root -> Reference a root
    

    As you can see in this example, modify updates both the this and root values.

    ref2 : Reference Int (List Int)
    ref2 = modify (\n -> n + 1) ref
    
    this ref
    --> 3
    this ref2
    --> 4
    
    root ref
    --> [ 1, 2, 3, 4, 5 ]
    root ref2
    --> [ 1, 2, 4, 4, 5 ]
    

    Example code using Reference

    Remember the first application we looked at earlier? Here's the same application using Reference instead of indexes.

    type Msg
        = ClickNumber (Reference Int (List Int))
    
    
    update : Msg -> Model -> ( Model, Cmd Msg )
    update msg model =
        case msg of
            ClickNumber ref ->
                ( { model
                    | nums =
                        Reference.root <| Reference.modify ((+) 1) ref
                  }
                , Cmd.none
                )
    
    
    view : Model -> Html Msg
    view model =
        div [] <| Reference.List.unwrap renderRow <| Reference.top model.nums
    
    
    renderRow : Reference Int (List Int) -> Html Msg
    renderRow ref =
        div
            [ Events.onClick (ClickNumber ref)
            ]
            [ text <| toString <| Reference.this ref
            ]
    

    More examples

    Although working with just a List Int shows improvement, Reference can be even more powerful with more complex structures like Trees.

    type alias Model =
        { tree : List Node
        }
    
    
    type Node
        = Node Int (List Node)
    

    If you'd like to see what using Reference with this structure looks like, take a look in the example directory.

    Related works

    Lens

    Monocle-Lens is similar in concept to Reference. However, it's not quite the same. I developed this library for three reasons:

    First, Reference is at a slightly higher level of abstraction than Lens. If you used Lens to do what Reference does, you could write the type signature like this:

    type alias Reference this root = ( this, Lens this root )
    

    Since we want to update a specific value, we need to indicate what that value is. Reference makes this structure easy to work with. You could do it with Lens, but you'd write code that Reference already contains.

    Second, as an extension of the first reason, the Elm community recommends targeting concrete use cases. This is a concrete use case, so it should be published as an independent library.

    Third, the Reference.List.unwrap function is very powerful, but its implementation is not very easy. It might even be worth publishing elm-reference just to provide Reference.List.unwrap.

    Zipper

    There is another similar approach called Zippers.

    Here are a few implementations for Trees:

    • zwilias/elm-rosetree/Tree-Zipper simple and easy to use
    • turboMaCk/lazy-tree-with-zipper - [Experimental] lazy but very fast

    Reference and Zipper correspond pretty well:

    • this is equivalent to a label
    • root is equivalent to the zipped tree
    • Reference is equivalent to the zipper itself

    There are two main differences:

    First, Zippers (at least in Elm right now) are typically focused on viewing specific elements of a collection, while Reference is more focused on updating specific elements of a collection. Additionally, Reference is specifically designed for updating values using the Elm Architecture, while Zippers are generic structures designed for functional languages in general.

    Second, Zippers are targeted to specific collection types. There are List Zippers, and Binary Tree Zippers and Rose Tree Zippers, and probably more. Reference gives up some of the more convenient methods of those specific implementations (since it knows nothing about its collection), but gains the ability to work with very unusual and uncommon structures in exchange. Like this one: type BiTree = Node (List BiTree) (List BiTree), or the UpDown structure in this example.