Reputation: 1732
I'm rather new to Elm, and I'm deeply attracted by the way Elm dealing with GUI. But after some deep thought, I find it's hard to efficiently update just one element of a list or finger tree (Just like finger tree in Haskell, if it already exists in Elm's library) which is under a Signal and the size of it also varies against time.
Specifically, to express a dynamic finger tree, we have to write
Signal [ {-the finger tree's element type-} ]
But if we want to update just one element of the finger tree efficiently, we have to write
Signal [ Signal {-the core data type-} ]
But in Elm the Signal is not a Monad, then how to flatten the two layer Signals into one layer?
Comment 1: I don't know in detail how Elm behaves in this situation. Reprocessing the whole finger tree is just my guess.
Comment 2: For example, suppose we have a signal value, marked as s
, of type Signal (fingerTree Int)
, and the following function, marked as f
, whose input is s
is, for example, lift (fmap (+1))
whose type is Signal (fingerTree Int) -> Signal (fingerTree Int)
. And if s
has just one element changed, function f
has to re-do the (+1) operation for every element of s
. Obviously, it's a waste of time and I'm not sure if Elm is intelligent enough to detect the immutability.
Upvotes: 3
Views: 233
Reputation: 32455
TL;DR: Implement your logic/data processing as pure functions and lift them to transform signals.
The trick is to write a function processList : [elementType] -> [elementType]
that provides the logic you want (check if the third element is a wibbler and change it to a wobbler or whatever you wanted to do), and then use the lift function which has type
lift : (a -> b) -> Signal a -> Signal b
like lift processList mySignalThatProducesLists
to edit the data produced by mySignalThatProducesLists
using processList
.
The main idea here is that you encode the logic and data processing as pure functions and then use them as Signal transformers using lift
. It's like Elm automatically reapplies the function every time the data in the source signal is updated.
If you're used to programming in Haskell, you can think of Signal a
as a newtype wrapper around Time -> a
for some opaque Time type, and lift
as fmap
. You don't need to have a monad to edit data.
There are also lift2
andlift3
functions for lifting functions with more than one argument, so again to use a Haskell analogy, you essentially have the capabilities of an Applicative functor there.
The reason they don't have a Monad is that that imposes implementation details that reduce efficiency. It may be that there's some equivalent of ArrowApply in there that I haven't spotted (that would give you an expressibility eqiuivelent of Monad), but I'm not sure there is.
Upvotes: 5