elm / core

Elm's core libraries
http://package.elm-lang.org/packages/elm/core/latest
BSD 3-Clause "New" or "Revised" License
2.8k stars 359 forks source link

Dict.update is confusing #1095

Open gampleman opened 3 years ago

gampleman commented 3 years ago

Recently there was a discussion on Slack, where it turned out that even well seasoned Elm developers where confused about the semantics of Dict.update. Specifically, it is unclear what is supposed to happen when the provided function returns Nothing (i.e. should "nothing" happen - the value remains the same or does it cause the value to be removed?)

A secondary issue is that the code needed for one of the most common use cases - aggregation - is quite unwieldy:

-- for example to count by key is:
Dict.update
    key
    (Maybe.withDefault 0 >> (+) 1 >> Just)
    dict

I can see 2 possible solutions here:

  1. Improve the docs with a) specifying what happens when b) adding some practical examples to illustrate this.
  2. Change the API for a more Elixir like update : comparable -> v -> (v -> v) -> Dict comparable v -> Dict comparable v. This is less general, but arguably addresses one of the most common usecases much more explicitly:
Dict.update key 1 ((+) 1) dict

For situations where delete semantics are useful, it is not too hard to write explicit remove logic and it is probably easier to parse anyway:


toggle : comparable -> v -> Dict comparable v ->  Dict comparable v
toggle key val =
    Dict.update key (Maybe.andThen (always Nothing) >> Maybe.withDefault (Just val))

-- vs: --

toggle key val dict =
     case Dict.get key dict of
          Just v ->
                Dict.remove key dict
          Nothing ->
                Dict.insert key val
github-actions[bot] commented 3 years ago

Thanks for reporting this! To set expectations:

Finally, please be patient with the core team. They are trying their best with limited resources.

cmditch commented 3 years ago

I find the current Dict.update behavior very handy, and the signature was fairly self-explanatory for me. Though, I could definitely understand how the signature might be confusing and am all in favor of updating the docs.

update : id -> (Maybe a -> Maybe a) -> Dict id a -> Dict id a When I first read this signature it struck me as saying: "This item might be in the dictionary. If it's not you can add an item or leave it as nothing. If it is in there, you can look at the item and decide whether you want to update it or remove it altogether. `

dullbananas commented 3 years ago

Change the API for a more Elixir like update : comparable -> v -> (v -> v) -> Dict comparable v -> Dict comparable v. This is less general, but arguably addresses one of the most common usecases much more explicitly:

If this is done, there should also be an updateWithDefault variant