One more thing for HTML - batches of attributes

683 views
Skip to first unread message

Evan Czaplicki

unread,
Aug 20, 2016, 4:27:58 PM8/20/16
to elm-dev
I mentioned a couple changes to HTML in this thread, but there was a borderline one that I had suggested to Richard without a clear resolution. Since then, he thinks it'd be pretty helpful. Curious what everyone else thinks!

The idea is to let you "collapse" attributes:

Html.Attributes.batch : List (Attribute msg) -> Attribute msg

What do folks think of having something like this?


Performance

One benefit of this is that append of attributes can now always be O(1). For example, say you have 40 attributes and want to add another to the end. In the existing API, you gotta say:

  fortyAttrs ++ [ another ]

Which needs 40 allocations. With a batch function, you can say:

  [ batch fortyAttrs, another ]

Which needs far fewer allocations. So that means we go from O(n) to O(1) for messing with this stuff.

For the implementation, it's quite easy to handle this, so in the end, it should just mean that scenario has way fewer allocations.


Out of Scope

An obvious thing to wonder is, if we can do this for attributes, can we do this for nodes too? In terms of implementation, this will not be too hard except if it is used at the root. That case would be quite tricky. Point is, I don't want to discuss this one right now. It seems like a nice idea, but I'll need time to let the idea percolate so I know if/how it can fit in with all the 0.18 stuff. So let's not discuss this in this thread.

Nick H

unread,
Aug 20, 2016, 4:40:45 PM8/20/16
to elm...@googlegroups.com
Maybe this is a stupid question, but does the order of attributes matter? In other words, could you avoid the performance problem by doing

another :: fortyAttrs

or would this risk changing the behavior of your program?

--
You received this message because you are subscribed to the Google Groups "elm-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-dev+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elm-dev/CAF7GuPHzEVBbZs_TGD9nVH0mM4GNvK7RBD3Pjaqw%2BOv7ZAh8ug%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Evan Czaplicki

unread,
Aug 20, 2016, 4:45:03 PM8/20/16
to elm...@googlegroups.com
Nick, yes, order matters. For example, if you say [ width, width, width ] we have to pick one. Or with multiple classes, CSS actually cares about the order for the same reason.

Also, if attributes have "map" and "batch", I think that's a good argument for them both to be in Html.Attributes so it's more obviously parallel with Sub and Cmd.

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


--
Sent from Gmail Mobile

Nick H

unread,
Aug 20, 2016, 4:48:33 PM8/20/16
to elm...@googlegroups.com
OK, that makes sense. And I guess using (::) wouldn't help in the case of fortyAttrs ++ fortyMoreAttrs anyway.

Evan Czaplicki

unread,
Aug 20, 2016, 10:40:02 PM8/20/16
to elm-dev
Yeah, that example would have been better :)

I guess I'm partly wondering if having this would lead people to write attributes less all-in-one-place and if that'd end up being bad. Like, maybe it's actually a benefit that this is harder to do because HTML and CSS are relatively frail to composition?

Josh Adams

unread,
Aug 20, 2016, 11:42:59 PM8/20/16
to elm-dev
I guess I'm partly wondering if having this would lead people to write attributes less all-in-one-place and if that'd end up being bad. Like, maybe it's actually a benefit that this is harder to do because HTML and CSS are relatively frail to composition?

I recoiled from this proposal for this reason.  When I was new to the language I would have wanted it badly.  Now I would really prefer it not be in there.  But when you pointed out the allocations and the potential performance impact, it seemed like maybe it was an acceptable tradeoff.  However, I'm doubtful that anyone has measured this as being the cause of any actual performance concerns in a real world application, and it seems like it makes it easier to build bad code out of laziness.

So I was wishy washy but I end up -1 on this. 

Richard Feldman

unread,
Aug 21, 2016, 1:08:04 AM8/21/16
to elm-dev
I specifically want this for accessibility helpers.

For example, suppose we have a class constant like this:

Theme.basicButton = "nri-button"

Here are two ways I could use this this:

button [ class Theme.basicButton ] [ text "I'm a button!" ]
a [ class Theme.basicButtonhref "/foo" ] [ text "I'm styled to look like a button, but I'm a link!" ]

If I have Html.Attributes.batch, I can incorporate accessibility attributes into this:

Theme.basicButton = batch [ class "button", role "button" ]

Here's how calling this would look:

button [ Theme.basicButton ] [ text "I'm a button!" ]
a [ Theme.basicButton, href "/foo" ] [ text "I'm styled to look like a button, but I'm a link!" ]

Now I've gotten the accessibility role attribute for free, all across the application!

Accessibility suffers from the reality that if it's more work, people most often don't bother. Sure, it's possible to achieve the same result by being disciplined about adding role="button" to all links that are being presented as buttons, but in practice, people often forget, don't know, or don't bother.

Granted, you could also make Theme.basicButton be a List.Attribute and force people to write button (Theme.basicButton ++ [ onClick whatever ]) but now the version that includes the accessibility attributes is less nice to use than the version that skips it, so this design creates an explicit incentive not to bother with accessibility.

Being able to write helpers where accessibility attributes "come along for free" makes it low-hanging fruit and thus more likely to benefit actual users in practice.

I think that's a worthwhile design goal. :)

Marcos Arias

unread,
Aug 21, 2016, 3:24:45 AM8/21/16
to elm-dev
Even though I find myself using long lists of attributes (using Svg mostly), I don't think it's a particular painful point in my Elm workflow, so I have mixed thoughts about this

On the one hand, following the idea of making Attribute more parallel with Cmd and Sub,  you could argue that if you want something like batch, shouldn't you also want something like none?, which will allow you to make things like

clickable =
    Maybe.withDefault (Attribute.none) (Maybe.map (onClick << SomeMsgThatNeedsAnUrl) model.url)

button [ clickable ] [ text "Click me if you can" ]

And, on the other hand, I think the Theme.button can be done without batch and still be nice and reusable. For instance:

prepend : appendable -> (appendable -> b) -> appendable -> b
prepend start func rest =
    func (start ++ rest)

basicButton : (List (Html.Attribute msg) -> List (Html msg) -> Html msg) -> List (Html.Attribute msg) -> List (Html msg) -> Html msg
basicButton =
    prepend [ class "button", role "button" ]

basicButton button [] [ text "I'm a button!" ]
basicButton a [ href "/foo" ] [ text "Link disguised as button" ]

Richard Feldman

unread,
Aug 21, 2016, 3:43:00 AM8/21/16
to elm-dev

And, on the other hand, I think the Theme.button can be done without batch and still be nice

basicButton : (List (Html.Attribute msg) -> List (Html msg) -> Html msg) -> List (Html.Attribute msg) -> List (Html msg) -> Html msg

Emphasis mine.

I don't think I am going to get nice error messages if I ever have a type mismatch with this function. :)

Marcos Arias

unread,
Aug 21, 2016, 4:41:03 AM8/21/16
to elm-dev
True, nice was a bit of an overstatement :)

I hope it still serves as a valid point though.

Nick H

unread,
Aug 22, 2016, 2:01:31 PM8/22/16
to elm...@googlegroups.com
I guess I'm partly wondering if having this would lead people to write attributes less all-in-one-place and if that'd end up being bad.

I don't think this style is all that difficult currently. If you want to, you can have lists of attributes all over the place and bring them together using List.concat. Although Html.batch would make this more performant, it seems equal to List.concat in terms of usability. So I am skeptical that it would encourage the sort of behavior you're describing.

OvermindDL1

unread,
Aug 22, 2016, 2:09:14 PM8/22/16
to elm-dev
As Marcos states, an Attribute.none would be exceedingly useful.  In elm-mdl I use Options.nop (their version of Attribute.none) excessively as I turn attributes on and off without needing to do repeated and costly (both performance but also to me in readability) list extensions.

Doing this:
```elm
button
  [ someAttributes
  , moreAttributes
  , if model.something then anotherAttribute else Attribute.none
  , if model.somethingElse then yetAnotherAttribute else Attribute.none
  ]
```
Is much better than this:
```elm
button
  [ someAttributes
  , moreAttributes
  ]
  ++ (if model.something then [anotherAttribute] else [])
  ++ (if model.somethingElse then [yetAnotherAttribute] else [])
```

Combined with Attribute.batch would be highly useful considering elm-mdl's use of adding extensive attributes to elements.

As well as another vote for Html.batch as well as I do similar things with elements themselves too.  I would also like it for the top level views as well since elm apps are always wrapped up inside a parent element regardless, but understand if it would not be.

Richard Feldman

unread,
Aug 22, 2016, 2:38:01 PM8/22/16
to elm-dev

If you have Attributes.batch [] then presumably a separate Attributes.none would just be a convenience. :)


--
You received this message because you are subscribed to the Google Groups "elm-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-dev+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elm-dev/b4fb6494-ae4c-4c65-ba34-81558fece799%40googlegroups.com.

OvermindDL1

unread,
Aug 22, 2016, 2:49:17 PM8/22/16
to elm-dev
On Monday, August 22, 2016 at 12:38:01 PM UTC-6, Richard Feldman wrote:

If you have Attributes.batch [] then presumably a separate Attributes.none would just be a convenience. :)


 Entirely yes.  ^.^

```elm
none = batch []
```

But it is very documentative.  :-)

Wouter In t Velt

unread,
Aug 26, 2016, 9:39:12 AM8/26/16
to elm-dev
Attributes.none would definitely be a convenience. A pattern (or trick) I usually apply for Attributes (and in other Lists too) is a List of Maybes:

button

 
List.filterMap identity
   
[ Just someAttributes
   
, Just moreAttributes    
   
, if model.something then (Just anotherAttribute) else Nothing
   
, if model.somethingElse then (Just yetAnotherAttribute) else Nothing
   
]


Are there drawbacks to this approach (performance or memory-wise) that I am not aware of?
Which would be solved with an Attributes.none?

Nick H

unread,
Aug 26, 2016, 11:40:46 AM8/26/16
to elm...@googlegroups.com
List.filterMap is O(n). Under the hood, filterMap is doing a foldr. It creates a second list and then checks every item in the first list to see if it should copy it over.

With Attributes.none, you wouldn't have to do any processing after creating your list. The process becomes O(1), just like the example in OP.

--
You received this message because you are subscribed to the Google Groups "elm-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-dev+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elm-dev/60d7cac0-4459-4933-8a80-60ea56fedc31%40googlegroups.com.

Evan Czaplicki

unread,
Aug 26, 2016, 11:47:22 AM8/26/16
to elm...@googlegroups.com

Nick H

unread,
Aug 26, 2016, 11:52:21 AM8/26/16
to elm...@googlegroups.com
Can you put Html nodes in an attribute list?

Wouter In t Velt

unread,
Aug 26, 2016, 1:51:48 PM8/26/16
to elm-dev
I see, thanks!

suttleco...@gmail.com

unread,
Aug 30, 2016, 10:19:07 AM8/30/16
to elm-dev
I think the :: syntax might be confusing as well, given the emerging ES function bind syntax: http://babeljs.io/docs/plugins/transform-function-bind/

Matthew Griffith

unread,
Jan 17, 2017, 8:57:04 AM1/17/17
to elm-dev

I realize this is an old thread, but I just wanted to mention that elm-style-animation would benefit from adding an Attributes.batch.  The Animation.render function has to return a list of attributes in order to property render svg properties.

Currently I have something like this, which obviously could be reduced.

div
        (Animation.render model.style
            ++ [ onClick FadeInFadeOut
               , style
                    [ ( "position", "relative" )
                    , ( "margin", "100px auto" )
                    , ( "padding", "25px" )
                    , ( "width", "200px" )
                    , ( "height", "200px" )
                    , ( "background-color", "#268bd2" )
                    , ( "color", "white" )
                    ]
               ]
        )
        [ text "Click to Animate!" ]


Secondly, I have another library that I'm experimenting with that deals with stylesheets.  I'm specifically playing with the idea of allowing elm-style-animation type animations to be embedded in the stylesheet.  This runs into the same situation as above, but also requires the library to both attach a class to a node and sometimes to render some inline styling if an animation is running on that class.  

Anyway, just wanted to put forth some more data points on the subject!
Reply all
Reply to author
Forward
0 new messages