This package allows you to use any custom types, records and other non-comparable types as keys for a Dict or members for a Set.
type Id =
Id Int
dict =
Any.Dict.makeInterface
{ fromComparable = Id
, toComparable = \(Id int) -> int
}
myFirstDict =
dict.fromList
[ (Id 1, "foo")
, (Id 2, "bar")
]
anotherDict =
dict.insert (Id 3) "baz" myFirstDictSee "How to use this package" for a more detailed walkthrough.
elm/core's Dict and SetThe Dict and Set types in this package each have an additional type parameter, which represents the comparable type used to store the key/member under the hood.
* Instead of Dict comparable value, we have Dict key value comparable
* Instead of Set comparable, we have Set member comparable.
Interface typesWe do not use top-level functions like Dict.get or Set.insert to interact with the Dict and Set types from this package.
Instead, we create Interfaces for each type. An Interface is simply a record containing functions that mirror the API of elm/core's Dict and Set implementations.
Its Dict and Set data structures do not contain any functions, which means you can use them in Model and Msg types without any caveats. This is a different approach from several other packages, including:
* turboMaCk/any-dict
* jjant/elm-dict
* owanturist/elm-avl-dict
* timo-weike/generic-collections in its AutoDict and AutoSet modules
It offers similar performance characteristics to the elm/core Dict and Set implementations. Each function has the same Big-O complexity as its elm-core equivalent. By contrast:
* pzp1997/assoc-list has similar performance characteristics to a List, which is no problem for small dictionaries but may get slower with larger ones.
* jjant/elm-dict takes an interesting approach, which may make it faster in some circumstances and slower in others - check its README for more details.
* See the "Performance" section for benchmarks.
Depending on your preferences, it may feel more ergonomic than packages whose API requires conversion functions to be passed in as an argument each time you call a Dict/Set function, such as:
* truqu/elm-dictset
* timo-weike/generic-collections in its ManualDict and ManualSet modules
It requires the user to define an Interface type for each type of Dict or Set, as well as functions to convert the key/member type to and from a comparable type - so there is a certain amount of boilerplate involved.
It may just feel too weird to call dict.get instead of Dict.get. This type of API, based on a record-of-functions, is not common in Elm packages, and may make the code harder to understand for people who are not familiar with it.
It may increase asset size, since the final bundle of compiled code will include all the functions from elm-core Dict and/or Set, even if your code doesn't use them all. See the "Asset size" section for more details.
A common use case is when you have a wrapper type, such as an Id:
-- module Id exposing (Id, fromInt)
type Id
= Id Int
toInt : Id -> Int
toInt (Id int) =
int
fromInt : Int -> Id
fromInt int =
Id intYou cannot use an Id as a key for an elm-core Dict, because Id is a custom type, and custom types are not comparable.
However, with the Dict implementation from this package, you can get around the problem. You just have to create an Interface by supplying two functions:
Id type into a comparablecomparable type back into an IdLet's define the Interface as a top-level value inside our Id module. We'll call it dict, and expose it from the module:
-- module Id exposing (Id, dict, fromInt)
import Any.Dict
dict =
Any.Dict.makeInterface
{ fromComparable = fromInt
, toComparable = toInt
}Now, we can use this Interface to create and work with Id-keyed Dicts anywhere in our code:
-- module SomeOtherModule exposing (..)
import Any.Dict exposing (Dict)
import Id exposing (Id)
myFirstDict : Dict Id String Int
myFirstDict =
Id.dict.fromList
[ (Id.fromInt 1, "foo")
, (Id.fromInt 2, "bar")
]
anotherDict : Dict Id String Int
anotherDict =
Id.dict.insert (Id.fromInt 3) "baz" myFirstDictThe benchmarks folder contains some basic benchmarks comparing some of the commonly used Dict functions in this package to their equivalents in other similar Elm packages.
Without going into too much detail, this package offers broadly similar performance characteristics to elm-core Dict. It is slightly slower in all cases due to the need to convert between the user's key type and the comparable type used to represent it internally.
Side note: jjant/elm-dict seems crazy fast for a lot of these operations! So if you don't need to worry about the fact that its data structure contains functions, it might be worth checking out.
See the README in the benchmarks folder for instructions on how to run the benchmarks.
The asset-size folder contains a script that allows you to compare the size of the JavaScript output from this package against the output of an equivalent elm-core program.
On my machine, the results are as follows:
>> Compiled size
Any.js: 121261 bytes
Core.js: 91544 bytes
>> Minified size
Any.min.js: 15144 bytes
Core.min.js: 7057 bytes
>> Gzipped size
Any.min.js.gz: 5241 bytes
Core.min.js.gz: 2927 bytesSee the README in the asset-size folder for instructions on how to run the script.