Howto filter on Sub

778 views
Skip to first unread message

Tobias Burger

unread,
May 11, 2016, 10:56:53 AM5/11/16
to Elm Discuss
In elm 0.16 I could filter out specific Signals by using Signal.filter.
This was handy when dealing with all Kind of events, especially with Keyboard or Mouse events.
In elm 0.17 there is no Sub.filter and I wonder how to discard events I don't want to handle.

The only solution I've found so far is using a NoOp tag.

type Msg
  = Key String
  | NoOp


update msg model =
  case msg of
    Key keycode ->
      handleCode keycode model
      
    NoOp ->
      model


Keyboard.presses
  (\keycode ->
    if keysOfInterest keycode then
      Key keycode
      
    else
      NoOp
  )
But this is quite cumbersome and the update method gets unnecessarily called.

It would be nice if there exists a filter function with the signature:
Sub.filter : (a -> Bool) -> Sub a -> Sub a

So the code could be much easier:
Keyboard.presses
  |> Sub.filter keysOfInterest
  |> Sub.map Key



Maxime Dantec

unread,
May 11, 2016, 12:05:26 PM5/11/16
to Elm Discuss
You can map, then do an if inside:

filter : (-> Bool) -> Sub a -> Sub a
filter fn s =
  if fn s then s else Sub.none

Noah Hall

unread,
May 11, 2016, 12:10:30 PM5/11/16
to elm-d...@googlegroups.com
Check out the final example under filter here ->
http://noredink.github.io/posts/signalsmigration.html#filter

It explains why `subscriptions` is a function that looks like `model
-> Sub msg`, so that you can filter things.
> --
> You received this message because you are subscribed to the Google Groups
> "Elm Discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to elm-discuss...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Janis Voigtländer

unread,
May 11, 2016, 12:11:18 PM5/11/16
to elm-d...@googlegroups.com
Have you tried this? Type-checked it?

Janis Voigtländer

unread,
May 11, 2016, 12:15:08 PM5/11/16
to elm-d...@googlegroups.com

How does this address the use case from the original message in this thread? I don’t think it does. The aim here is not to turn on/off whether there is a subscription, but instead to have a subscrption that lets through only some events from Keyboard.presses.

Noah Hall

unread,
May 11, 2016, 12:33:07 PM5/11/16
to elm-d...@googlegroups.com
> How does this address the use case from the original message in this thread? I don’t think it does. The aim here is not to turn on/off whether there is a subscription, but instead to have a subscrption that lets through only some events from Keyboard.presses.

The point is that NoOp is the way to do right right now. Look at the
other examples for that use case. If you want to disable/enable a
subscription then you need to handle with your subscriptions function.
There is no concept of "filtering" a subscription in this way.

Janis Voigtländer

unread,
May 11, 2016, 12:36:54 PM5/11/16
to elm-d...@googlegroups.com

The point is that NoOp is the way to do right right now. Look at the other examples for that use case.

That other example is equivalent to what the OP gives as example code in the first message here, right? The code he tries to ask for an alternative about.

If you want to disable/enable a subscription then you need to handle with your subscriptions function.

Yes, that’s clear.

There is no concept of “filtering” a subscription in this way.

That there isn’t is also clear. The question of this thread is, maybe there should be, to not always force NoOp messages and the extra rounds of the update loop they force?

Tobias Burger

unread,
May 11, 2016, 12:39:54 PM5/11/16
to Elm Discuss
The signature is actually
filter : (Sub a -> Bool) -> Sub a -> Sub a
so sadly this is not very useful...

Noah Hall

unread,
May 11, 2016, 12:41:31 PM5/11/16
to elm-d...@googlegroups.com
I think the answer is: wait and see. Try it out for a bit. These seems
a bit too much like living in the old world. Subscriptions are not
signals.

On Wed, May 11, 2016 at 6:36 PM, Janis Voigtländer

Tobias Burger

unread,
May 11, 2016, 12:45:01 PM5/11/16
to Elm Discuss
Am I missing something in the example of howto migrate Signal to Sub?
The 0.16 example uses filter to filter only even seconds.
The 0.17 example isn't reflecting the functionality.

Noah Hall

unread,
May 11, 2016, 12:59:04 PM5/11/16
to elm-d...@googlegroups.com
No, that's the point. Filter things in your update function because they still get processed. If you want to ignore things, then the two ways you can do it is either to stop listening to the subscription, which has the benefit of no longer being listened to at all, or to filter it as you would any other value, not just ones from subscriptions.

Tobias Burger

unread,
May 11, 2016, 1:26:39 PM5/11/16
to Elm Discuss
Okay, maybe I need some time to get used to it. For now it looks quite imperative to me.
Are there any practical reasons of not bringing monadic properties to Sub?
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.

Peter Damoc

unread,
May 11, 2016, 1:44:18 PM5/11/16
to Elm Discuss
Hi Tobias, 

Can you please share a little more details as to what exactly do you see as imperative and why? 

My mental model of Elm is quite declarative and I'm interested in a different perspective in order to better understand. 
There is NO FATE, we are the creators.
blog: http://damoc.ro/

Janis Voigtländer

unread,
May 11, 2016, 2:02:53 PM5/11/16
to elm-d...@googlegroups.com

Just for the record: Adding a Sub.filter function would not make Sub “monadic” (in the “Haskell Monad“ sense).

Tobias Burger

unread,
May 11, 2016, 2:34:06 PM5/11/16
to Elm Discuss
But bind/flatMap and return would make it monadic... And with that in place it would be possible to implement filter, ... ;)

Is this new aproach in the same way composable/reusable as the old one?
Like I've said, maybe I need some time to get used to.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Janis Voigtländer

unread,
May 12, 2016, 8:47:15 AM5/12/16
to elm-d...@googlegroups.com

I’m not Tobias, but I do have an inkling of what makes a solution like the 0.17 version of the first example at http://noredink.github.io/posts/signalsmigration.html#filter feel “imperative style”.

For me, it’s the necessity of having a NoOp message. That’s a general phenomenon: Whenever modelling something in TEA requires a NoOp thing, I feel like the API is not quite right yet.

Why? When I teach students, one thing that comes up reliably in discussions of differences between declarative and imperative languages is the issue of whether the language’s if-then construct always needs an else. Since declarative languages are typically expression/value based, the answer for them is typically “Yes”. Since imperative languages are typically statement/instruction based, the answer for them is typically “No”. That’s why in Haskell and Elm and other functional languages an if-then-else without the else-part makes no sense, whereas in C etc. there are lone then-branches or empty statements.

So when I see that one has to add an “empty statement” kind of thing (a NoOp constructor) to some program in Elm to work around the non-availability of some piece of API (here Sub.filter, but also in other discussions, e.g., about Effects.silentTask), I always have the feeling that something’s wrong. Maybe that’s weird, but that’s how it is for me.

Peter Damoc

unread,
May 12, 2016, 9:25:50 AM5/12/16
to Elm Discuss
Thank you Janis! 

It is an interesting thing to think about.

Since you linked that example, I'm curious, if the filter would have used a Maybe would have still been perceived as imperative? 

for example:

type Msg
    = SetCurrentTime (Maybe Time)

setCurrentTime : Sub Msg
setCurrentTime =
    Time.every Time.second
        |> (\time ->
            if time % 2 == 0 then
                SetCurrentTime (Just time)
            else
                SetCurrentTime Nothing
            ) 

Somehow, I don't view the NoOp pattern from the link as being imperative. If anything, it makes things more explicit. 
If a msg that needs to be filtered out comes:
- in the Sub.filter case it would mean that the state of the app would not change but this would be an implicit behavior 
- in the current case, the no change is an explicit behavior. 



Noah Hall

unread,
May 12, 2016, 9:36:15 AM5/12/16
to elm-d...@googlegroups.com
I guess there is a flaw in the first solution in that it _suggests_
you should keep your filtering logic at the subscription itself! Now,
I believe the idea is "subscribe to things. If you want to ignore
things, cancel the subscription (don't return it in the subscriptions
function). Everything else should go into the update function"

That means that if you want to filter even numbers, you want to do it
in your update function. Eg:

currentTime = Time.every Time.second SetCurrentTime

type Msg = SetCurrentTime Time

update : Msg -> Model -> Model
update = ...
SetCurrentTime time =
if time % 2 == 0 then model
else { model | time = time }


Does that make sense?

On Thu, May 12, 2016 at 2:46 PM, Janis Voigtländer

Janis Voigtländer

unread,
May 12, 2016, 9:55:54 AM5/12/16
to elm-d...@googlegroups.com

I don’t know whether SetCurrentTime Nothing feels “as imperative” as NoOp, but it does still feel like some modelling decision taken away from me. Maybe I don’t want to have a concept of SetCurrentTime Nothing, under whatever name.

Previously the type used in that example would have been

type Msg = SetCurrentTime Time

Because there is no possibility to filter events, one is forced to change the type to

type Msg = SetCurrentTime (Maybe Time)

or

type Msg = SetCurrentTime Time | NoOp

In both cases, intent is taken away from the modeller.

You suggest it’s good because it makes it more explicit that there will be no change in some circumstances. I don’t really buy that, at least not as universal truth. Consider this case: Currently, the keyboard package contains a Sub for presses. Assume it would also contain a Sub for arrows (just firing for arrow key presses). Would it be bad that a program that only wants to react to arrow keys uses arrows instead of presses? Would it always be better, because more explicit, that the program takes in presses and then explicitly has to say in its update function “oh I’m not interested in non-arrow keys, so while I do have to take them, I’m not gonna do anything for them”? It could be considered more declarative to be able to say up front that one is only interested in arrow keys for this program, so wants to subscribe only to arrows, not to presses.

Tobias Burger

unread,
May 12, 2016, 9:58:06 AM5/12/16
to Elm Discuss
Janis, you couldn't explain my feelings better! ;) That's exactly how I feel about it.
Telling someone to do nothing, doesn't feel right.

SetCurrentTime (Maybe Time) was also on my radar, but then it feels strange to tell the update action to set the time and then there isn't any time.

Handling the logic directly in the update function seems the best solution for now.
But then again, there is this "god" update function which transforms and filters and reshapes everything...
There is this gut feeling again... ;)

Janis Voigtländer

unread,
May 12, 2016, 9:59:15 AM5/12/16
to elm-d...@googlegroups.com

Hi Noah, I see this message only now, but I guess the answer to Peter I have been writing and sent a minute ago conveys that I would not always prefer it that way. (Sometimes yes, I would want to model this decision in the update function, but not always.)

Janis Voigtländer

unread,
May 12, 2016, 10:05:17 AM5/12/16
to elm-d...@googlegroups.com

Also, ever since the Effects.silentTask discussion, a comment by Richard has been coming to my mind when encountering NoOp. It was based on looking through the NoRedInk code base, and yes, it is about a different reason for having NoOp, but still, it does to me signify something of NoOp being a possible antipattern in itself. Being able to rid an application’s Msg type of NoOp means to gain a static guarantee. For example, one is freed of making sure “by hand” that one actually does treat NoOp by not changing the model at all. If NoOp is creeping back in, I would be worried.

Janis Voigtländer

unread,
May 12, 2016, 10:54:43 AM5/12/16
to elm-d...@googlegroups.com

Sorry, I didn’t really address the suggestion that the solution given for the first example at http://noredink.github.io/posts/signalsmigration.html#filter isn’t the one it should be anyway. NoOp-aversion was still on my mind. :-)

So, the suggestion is that that solution should be different, moving the if-then-else into the update-function (and not having NoOp on a mapped Sub). A potential problem I see with this is when there is more than one update-function. Several components in a TEA setting, somehow combined. Couldn’t the new suggestion mean that one has to duplicate the if-then-else logic in several update-functions?

I imagine a situation where one wants to handle only arrow keys in several sub-components of an overall program, and where one has the following three potential choices:

  1. Turn the presses Sub into an arrows Sub by discarding unwanted keys (at one place), subscribe to arrows.

  2. Turn unwanted keys into NoOps by mapping over the presses Sub, subscribe to the mapped Sub, and extend several update-functions each by a case reacting to NoOp by not changing the model.

  3. Directly subscribe to the presses Sub, put essentially identical if-then-else logic into several update-functions.

In the absence of a Sub.filter functionality, the 1. choice is not available. But I am concerned that not always at least one of 2. and 3. is a better choice than 1.

Steve Schafer

unread,
May 12, 2016, 10:57:02 AM5/12/16
to Elm Discuss
The problem is that update is not an ordinary function, as it has side effects (rendering the view). If the view is complex, those side effects can be computationally expensive, so you still need some way to minimize redundant invocations of update. And it seems like the only practical way to do that would be to somehow prevent no-op events from ever reaching update. An alternative would be some sort of "escape clause" for update to be able to say, "I didn't change the model at all, so don't update the view."

Janis Voigtländer

unread,
May 12, 2016, 11:00:54 AM5/12/16
to elm-d...@googlegroups.com

http://package.elm-lang.org/packages/elm-lang/html/1.0.0/Html-Lazy may be at least a partial solution to this specific concern.

Peter Damoc

unread,
May 12, 2016, 11:03:38 AM5/12/16
to Elm Discuss
On Thu, May 12, 2016 at 5:54 PM, Janis Voigtländer <janis.voi...@gmail.com> wrote:

So, the suggestion is that that solution should be different, moving the if-then-else into the update-function (and not having NoOp on a mapped Sub). A potential problem I see with this is when there is more than one update-function. Several components in a TEA setting, somehow combined. Couldn’t the new suggestion mean that one has to duplicate the if-then-else logic in several update-functions?

Not in my understanding. 
In a TEA setting, the top most component could do all the processing needed and pass to the children only the relevant information. 
This top level component could also use something like Lazy in the view to optimize for "fake" updates. 

Basically, all the logic from around the subscriptions would end up in the too most component. 




Janis Voigtländer

unread,
May 12, 2016, 11:21:09 AM5/12/16
to elm-d...@googlegroups.com

It’s not clear to me that it will always be possible/desirable to manage things that way. But I also have no counter proof, not having built big TEA stuff myself. In a technical sense you are right, one could even artificially put a new component at the very top of the hierarchy whose only job it is to do the event filtering, only passing the arrow key events down through what would otherwise have been the top-most component. But will this work in practice? When there are potentially several subscriptions that need to be managed? When we do not only have to think abotu whether a given program can be written, but also whether a program can be maintained over time, whether it is easily refactorable, etc.?

Moreover, (how) does this actually work in the presence of encapsulation? Say we have components A, B, C. A has B and C as children, but should be decoupled from their implementation. In particular, A should not know anything about the message types of B and C, it should only pass messages through to them. B and C both want to subscribe to arrow keys. If there were a arrow Sub, B and C would both subscribe to it, and A would simply Sub.batch the subscriptions of B and C, not look inside them. Everything nicely decoupled. Now, since there is no arrow Sub, you propose that A takes responsibility for turning events from the presses Sub into arrow events of the kind B and C like. But for this to be possible, A has to give up its ignorance of what B and C are and want. Seems to destroy some aspect of TEA.


--

Noah Hall

unread,
May 12, 2016, 11:21:46 AM5/12/16
to elm-d...@googlegroups.com
> The problem is that update is not an ordinary function, as it has side effects (rendering the view). If the view is complex, those side effects can be computationally expensive, so you still need some way to minimize redundant invocations of update. And it seems like the only practical way to do that would be to somehow prevent no-op events from ever reaching update. An alternative would be some sort of "escape clause" for update to be able to say, "I didn't change the model at all, so don't update the view."

If you care a lot about this performance, then you should be using
either Html.Lazy or elm-lang/lazy. By default the renderer is already
very fast and very clever - this is how virtual-doms work, by
generating diffs and checking if they actually need to re-render or
not. If you want to not trigger things, then you need to use the model
to stop listening to that subscription.

Evan James

unread,
May 12, 2016, 1:09:05 PM5/12/16
to Elm Discuss
Others have discussed the philosophical reasons you might or might not be OK with having a NoOp in your code, but I think I can address something else:

There probably should not be a general Sub.filter because subscriptions have setup and teardown costs that are hidden from the application code.  In order to do the minimum work necessary, the effect manager must know about the subscription in domain-specific terms.  If you instead apply filtering at the general Sub level, it's sometimes only possible to apply the filter by doing all of the work to generate a message and then throwing the message away.

For a concrete example, take Websockets:  Imagine you're dealing with a socket and apply a filter so your app only receives messages that start with "a".  But the socket you're listening to only sends messages that start with "b".  There was no point in opening the socket, but the effect manager ends up keeping it alive anyway.  There's no way to know what the socket will send except to listen and find out, so you always incur 100% of the cost of the socket no matter how many application cycles you "save" by filtering it strictly.

That's a contrived example, but because Websockets can't shut down a connection based on the function passed to Sub.filter, these two subscription declarations that appear to be functionally equivalent actually aren't:

subscriptions : Model -> Sub Msg
subscriptions model
=
 
if model.chatClosed then
 
Sub.none
 
else
 
Websocket.listen "ws://echo.websocket.org" NewMessage

subscriptions
: Model -> Sub Msg
subscriptions model
=
 
Sub.filter (\_ -> ~model.chatClosed) (Websocket.listen "ws://echo.websocket.org" NewMessage)



The result of Sub.filter for some effect managers would be that the effect manager doesn't really know which subscriptions need to be set up or when it's safe to tear them down.  That undermines the concept of subscriptions, which is that you can declare the subscriptions you need, not care about the details, and it will never come back to bite you.  With Sub.filter, you can write an application that appears to "almost never" use subscriptions, but actually has a number of permanent resource drains behind the scenes.

I believe other Signal-style compositions are impossible or inadvisable for the same reason, with Websockets usually being the hard case.

Thanks,
Evan James

Janis Voigtländer

unread,
May 12, 2016, 2:22:32 PM5/12/16
to elm-d...@googlegroups.com

Couldn’t an analogous argument be made to show that List.filter should not exist either? Because of the danger that programmers badly write List.length (List.filter (\_ -> flag) expensiveGenerator) sometimes instead of if not flag then 0 else List.length expensiveGenerator?

Your argument relies on the danger that programmers may consider the two definitions of subscriptions you give to be interchangeable. They aren’t, and we would have to make sure that programmers know that they aren’t. Noah has repeatedly in this thread pointed out the importance of actually turning subscriptions off whenever it is known from the current model alone that no events at all are needed from them. That applies independently of whether Sub.filter is available or not. (Also in the 2. or 3. case from my earlier message a programmer could go wrong and have a subscription running even though it’s knowable from the current model that all the subscription’s events will turn into NoOp or will be ignored in the update function due to some model-but-not-event-dependent condition.)

Luckily, there is a simple rule to tell programmers: “Never use Sub.filter with a predicate, produced from the current model, that does not actually use its event(ual) argument.”

(In your example, the Sub.filter (\_ -> would have loudly screamed: DON’T.)


Evan Czaplicki

unread,
May 12, 2016, 3:02:27 PM5/12/16
to elm-d...@googlegroups.com
Folks, I don't just add things based on guts. I want to see how it plays out in practice. Obviously this can be added.

Use the stuff. We'll end up with SSCCE's of things. We can evaluate the tradeoffs.

Cristian Garcia

unread,
May 12, 2016, 3:42:59 PM5/12/16
to Elm Discuss
Elm 0.17 philosophically made the change from "FRP" -were we handled streams of events- to an "Abstract Machine" -where we give commands-. In this context I see two possibilities to achieved the desired goal

1. Just wait until Sub.filter and maybe Cmd.filter are implemented. 0.17 is incomplete in many ways, feedback from the community will drive the implementation of what is missing.

2. Propose a middle-ware system for this new abstract machine. I find this even more interesting and powerful because middle-ware not only lets you filter but also post-process. Here is the type signature I imagine for a middle-ware:

middleware :: (Msg -> Html) -> Html -> Model -> Msg -> Html 

For example, a filter for the clock example could be something like this

onlyEvenTicks nextMiddleware currentHml currentModel msg = 
  case msg of 
    Tick time -> 
      if isEven time then 
        nextMiddleware msg
      else
                   currentHml
     
    _ ->
       nextMiddleware msg


The interesting thing about middle-ware is that you can not only filter messages, you could also do post-processing (maybe create a CSS post-processor to inline style), authentication (maybe go to the logging view if e.g model.userLoggedin is False), you could even implement a whole routing system using as a middle-ware. With more complicated type for the middle ware signature you could make it such the the current program definition (init, update, view, subscriptions) is just the defacto last middleware. Maybe all we need is a way to create custom Programs to solve this and many other problems.

Mark Hamburg

unread,
May 12, 2016, 5:36:25 PM5/12/16
to elm-d...@googlegroups.com
Though that then speaks to being aggressive about not generating new data when doing nested updates. Consider, for example, the case where it isn't a NoOp message at the top level but one that is getting forwarded down to a piece of the model. The update function for that piece of the model will respond to the NoOp message by returning the existing model, but now the level up that was forwarding that message down needs to notice that the result of the update was no change and not make any change itself. For records, this could just be part of the implementation logic by optimizing assignments that make no change (on a "pointer-comparison" basis) but that won't help with values that have been wrapped inside type tags since we won't have the original available for comparison.

Mark

Mark Hamburg

unread,
May 12, 2016, 5:41:27 PM5/12/16
to elm-d...@googlegroups.com
My message should not be taken as an argument for or against Sub.filter but rather for thinking about how to best handle NoOps.

Mark

Evan James

unread,
May 13, 2016, 12:22:25 AM5/13/16
to Elm Discuss
I would be happier with that wait-and-see sentiment if it had been phrased as "We don't just add things based on guts" and "Obviously, this can be added if somebody writes a PR and a good proposal."

Until then - "What's the smart way to handle subscriptions?" is a question we have to answer in order to write large-scale code that use subscriptions, and trading pathological examples of the terrible things a programmer could do in a particular architecture is normal when spitballing ways to use a new API.  I know Janis means well, in other words, and I don't think it's a wasted discussion; it will inform the work I do.

Thanks,
Evan James

Steve Schafer

unread,
May 13, 2016, 10:19:45 AM5/13/16
to Elm Discuss
I am using Lazy, and in my specific 0.16 application (I haven't had a chance to update it to 0.17 yet), I haven't found the rendering optimizations to be all that smart. My view is a handful of Bootstrap accordion-style sections, with the inactive ones empty and the active one displaying a fairly large HTML table, typically twenty rows by twenty columns (think spreadsheet). A no-op update with zero changes to the model incurs about 200 ms of JavaScript processing time, essentially all of it in the virtual DOM diffing and rendering process, according to Chrome. There's no way that I can afford redundant updates that cost that much.

You keep saying that the model needs to stop listening to subscriptions if it doesn't want to handle them, but there's no practical way to make that work for some of the scenarios that have been mentioned here. For example, say that your app wants to handle onKeyDown for just the up- and down-arrow keys, and ignore all other keys. How does one make that work, short of dropping out to JavaScript to create a custom event stream?

Janis Voigtländer

unread,
May 13, 2016, 11:16:56 AM5/13/16
to elm-d...@googlegroups.com

Was your 0.16 code using Signal.forwardTo inside the view function? If so, there’s a good chance that 0.17 will please you with much better Html.lazy behavior.

Steve Schafer

unread,
May 17, 2016, 9:43:04 AM5/17/16
to Elm Discuss
No, I have a couple of ports that handle things like monitoring text selection in input fields, but that's about it. I think what's happening is that the differ is getting confused because of my use of a zipper data structure; when I traverse down the structure and then back up, it should be able to tell that the structure is unchanged, but it seems not to be able to do that. I need to investigate further to determine exactly what's going on.

Janis Voigtländer

unread,
May 17, 2016, 10:59:01 AM5/17/16
to elm-d...@googlegroups.com

You are aware that the functions from Html.Lazy use reference equality? So two models are only considered equal if they are the same JS object (at the the same pointer address). The zipper data structures I know don’t give that degree of equality after moving down and up again.

Steve Schafer

unread,
May 17, 2016, 11:56:47 AM5/17/16
to Elm Discuss
Yes, I'm aware of that. The model should be equal, but there may be something buried deep down that's causing a problem. I think it may have to do with something that effectively looks like this:

y = { x | a = x.a }

While Elm thinks that x == y, JavaScript does not. I've tried to expunge all of these, but there may be something lurking somewhere. And Elm doesn't make it easy to figure out where such things may exist.

Janis Voigtländer

unread,
May 17, 2016, 12:43:36 PM5/17/16
to elm-d...@googlegroups.com
If you are even thinking about "deep down", you are not considering reference equality. Because reference equality, which lazy uses, simply compares that the two objects/records are at the same memory address. A question of "deep down" cannot arise. 

Mark Hamburg

unread,
May 17, 2016, 1:55:20 PM5/17/16
to Elm Discuss
Should reference equality be a thing in a pure functional language? At least beyond a point where certain optimizations may or may not apply? So, for example, Html.Lazy uses reference equality as a fast way to short-circuit re-running a view function (and comparing the resulting tree) and that is a big win but from the standpoint of computed values, should you be able to care? Coming from more imperative languages, I would have tended to say "yes, reference equality should be a thing because as long as the language is strict we can be clear about where values are constructed". But then I think about the number of potential optimizations that forecloses. For example, an inline function, even if it closes over no values, is a value construction but they are so common that we would really like to be able to lift them out and avoid doing the construction repeatedly. So, then we're back to something like "No, Elm does not recognize any form of reference/construction equality ... but some low-level libraries may use it to provide optimizations". And in that case, it might be nice if the record update code recognized when it didn't need to construct a new record because a corollary to not supporting reference equality as a formal notion is that constructions may not always construct new values.

Mark

Steve Schafer

unread,
May 18, 2016, 10:30:58 AM5/18/16
to Elm Discuss
By "deep down," I mean that I'm looking to see if there's anywhere that the model is being passed down to some deeper level where that could be bypassed, thus avoiding reconstruction of the model and thereby preserving reference equality.
Reply all
Reply to author
Forward
0 new messages