I’ve recently added undo functionality to an Elm app I’ve been writing. The most straightforward way to code it seemed to me to be the following:
type alias Model = { data: Data
, undoStates: List Data
}
type Msg = ...
maybeChangeData: Msg -> Data -> Data
-- Return either a modified Data value, or the unchanged original
update : Msg -> Model -> Model
update msg model =
let
newData = maybeChangeData msg model.data
newUndoStates = if equalByReference model.data newData
then model.undoStates
else model.data :: model.undoStates -- Push the old data onto undoStates
in
{ data : newData
, undoStates : newUndoStates
}
The actual undo (not pictured in the above sketch) would just pop a Data
value off of the undoStates
list and put it in the data
field of the model.
This implementation is in fact something like an undo “middleware,” in the tradition of the clojurescript library re-frame, which might be a desirable thing for Elm to be able to have. (see https://github.com/Day8/re-frame/wiki/Using-Handler-Middleware; I’m passingly familiar with re-frame which is probably one reason why it occurred to me to write undo in this way).
The only problem is that (as far as I can tell) equalByReference
doesn’t exist in Elm. The language’s ==
operator uses equality by value instead. For my purposes, I was able to implement undo in a different way. But I thought I would ask the question of whether a reference equality primitive would be a useful addition to the language.
I brainstormed other use cases for such a primitive besides generic undo middleware. I came up with a couple:
- Reference equality is used by the virtual DOM library to implement the
lazy
family of functions (see https://github.com/elm-lang/virtual-dom/blob/2.0.4/src/Native/VirtualDom.js#L579). So, it would be needed to implementlazy
for a VDOM-like library in pure Elm (in the spirit of https://discourse.elm-lang.org/t/blog-implementing-vdom-in-elm/810). - It seems like it might be important for implementing functional data structures in pure Elm (though I don’t know enough about this to be sure one way or the other).
Has anyone else come across the need for a reference equality primitive in their coding? Are there any other situations where it would be useful? Alternatively, are there reasons why it would not be desirable to expose such a primitive?