lookups with arbitrary keys
KeySet for one key, log n runtimeKeysSet for multiple keys, n runtimeKeySetHolds no functions.
Still, each Sorting
required to access/operate is enforced to be the same
(by attaching an opaque tag).
See β example
β Solves problems listed in prior art alongside other goodies
import Emptiable exposing (Emptiable)
import Stack
import KeySet exposing (KeySet)
import User exposing (User(..))
users : Emptiable (KeySet User User.ByEmailHostFirst) neverEmpty_
users =
KeySet.fromStack User.byEmailHostFirst
(Stack.topBelow
(User { name = "Fred", email = [email protected].. })
[ User { name = "Ann", email = [email protected].. }
, User { name = "Annother", email = [email protected].. }
, User { name = "Bright", email = [email protected].. }
]
)
users |> KeySet.size
--β 3
users |> KeySet.element User.byEmailHostFirst [email protected]..
--β Emptiable.filled { name = "Ann", email = [email protected].. }
users |> KeySet.end Down -- minimum
--β { name = "Ann", email = [email protected].. } no Maybe
module User exposing (User(..), ByEmailHostFirst, byEmailHostFirst)
import KeySet
import Email
type User
= User
{ email : Email
, name : String
}
type ByEmailHostFirst
-- ! no exposing (..) β only constructable in this module
= ByEmailHostFirst
emailHostFirst : KeySet.Sorting User Email ByEmailHostFirst
emailHostFirst =
KeySet.sortingKey (\(User user) -> user.email)
{ tag = ByEmailHostFirst
, order =
Order.onTieNext
[ Order.by Email.host
(String.Order.greaterEarlier (Char.Order.alphabetically Order.tie))
, Order.by Email.label
(String.Order.greaterEarlier (Char.Order.alphabetically Order.tie))
]
}No typeclasses :)
Feel free to adapt this structure how you like it best,
for example separating Sortings from data to each their own module Data.By
Ordering key = key -> key -> Ordercomparablekey -> Stringlinear-direction Orderelement -> key function as part of a given SortingKeySet.end Up|Down, foldOnto, fold don't need Maybeallowable-stateemptiness-typedlinear-directionDict comparableKey valueelm/core DictDict wrapper for each custom type key.
Often more a hindrance than helpfulcomparable or k -> k -> Order)union, intersection, ...)key -> key -> OrderOrder APIcomparable keys. Therefore simpler while not relying on magickey -> comparablekey -> StringtoString implementation not returning a unique String for all keystoString needs to do heavy work like sortingedkelly303/elm-any-type-collections with a toComparable functioninsert from the wrong API "instance" with a different function is still possible but unlikely to happen in practicekey -> Maybe value instead of a data structure>= n runtime== among other things will failclear to only remove all elements but keep the functionn runtimeKeySet butcomparableKeysSetlook up elements by their unique aspects
For a KeysSet with some elements
{ flag = "π¦πΊ", code = "AU", name = "Australia" }
{ flag = "π¦πΆ", code = "AQ", name = "Antarctica" }
{ flag = "π±π§", code = "LB", name = "Lebanon" }you can specify aspects that will be unique across all elements
KeysSet.promising
[ unique .flag, unique .code ]With a key and an aspect to check for matches, you can find the matching element:
|> KeysSet.element ( .flag, "π¦πΆ" )
--β Just { flag = "π¦πΆ", code = "AQ", name = "Antarctica" }
|> KeysSet.element ( .code, "LB" )
--β Just { flag = "π±π§", code = "LB", name = "Lebanon" }Β
operators =
KeysSet.promising
[ unique .symbol, unique .name ]
|> KeysSet.insertList
[ { symbol = ">", name = "gt", kind = Binary }
, { symbol = "<", name = "lt", kind = Binary }
, { symbol = "==", name = "eq", kind = Binary }
, { symbol = "-", name = "negate", kind = Unary }
]
infixOperators =
operators
|> KeysSet.mapTry
(\operator ->
case operator.kind of
Binary ->
{ symbol = operator.symbol, kind = operator.kind }
|> Just
Unary ->
Nothing
)
[ unique .symbol, unique .name ]
nameOfOperatorSymbol operatorSymbol =
operators
|> KeysSet.element ( .symbol, operatorSymbol )-- https://dark.elm.dmy.fr/packages/lue-bird/elm-no-record-type-alias-constructor-function/latest/
import RecordWithoutConstructorFunction exposing (RecordWithoutConstructorFunction)
import KeysSet exposing (KeysSet, unique)
type alias Account =
RecordWithoutConstructorFunction
{ username : String
, email : String
, settings : Settings
}
type alias State =
RecordWithoutConstructorFunction
{ accounts : KeysSet Account
, currentUserName : String
}
initialModel =
{ accounts =
KeysSet.promising
[ unique .username, unique .email ]
}
reactTo event =
case event of
AccountSwitched username ->
\state -> { state | currentUserName = username }
SettingsChanged updateSettings ->
\state ->
{ state
| accounts =
state.accounts
|> KeysSet.elementAlter
( .username, state.currentUserName )
updateSettings
}
Registered username email ->
\state ->
if
state.accounts
|> KeysSet.any (\user -> user.username == username)
then
-- username taken already
else if
state.accounts
|> KeysSet.any (\user -> user.email == email)
then
-- email taken already
else
{ state
| accounts =
state.accounts
|> KeysSet.insert
{ username = username
, email = email
, settings = defaultSettings
}
}Β
answers =
KeysSet.promising [ unique .youSay ]
|> KeysSet.insertList
[ { youSay = "Hi"
, answer = "Hi there!"
}
, { youSay = "Bye"
, answer = "Ok, have a nice day and spread some love."
}
, { youSay = "How are you"
, answer = "I don't have feelings :("
}
, { youSay = "Are you a robot"
, answer = "I think the most human answer is 'Haha... yes'"
}
]We will only ever lookup answers to what youSay
β use a Dict where it is more appropriate: Dicts are for one-way access
translationsEnDe =
KeysSet.promising []
|> KeysSet.insertList
[ { english = "elm", german = "Ulme" }
, { english = "git", german = "Schwachkopf" }
, { german = "RΓΌste", english = "elm" }
]A KeysSet is only effective when there is only one matching key.
Please take a look at elm-bidict instead!
partners =
KeysSet.promising
[ unique .partner, unique .partnerOfPartner ]
|> KeysSet.insertList
[ { partner = "Ann", partnerOfPartner = "Alan" }
, { partner = "Alex", partnerOfPartner = "Alastair" }
, { partner = "Alan", partnerOfPartner = "Ann" }
-- wait, this is no duplicate and is inserted
]A KeysSet ony makes sense when the keys describe something different
Order, so that practically no manual opaque rules are neededInt.Order.increasing
--: Ordering Int Int.Order.Increasing
Order.by Record.Typed.name Int.Order.increasing
|> Order.onTie
(Order.by Record.Typed.status
(String.Order.greaterEarlier
(Char.Order.alphabetically Case.lowerUpper)
)
)
--: Ordering
--: User
--: (Order.OnTieNext
--: (Order.By Record.Typed.Name Int.Order.Increasing)
--: (Order.By Record.Typed.Status
--: (String.Order.GreaterEarlier
--: (Char.Order.Alphabetically Case.LowerUpper)
--: )
--: )
--: )with per project onemodule Record.Typed exposing (Name, name, Status, status)
import Typed exposing (Typed, Internal, Public, tag, isChecked)
type Name -- no (..)
= Name
type Status -- no (..)
= Status
name : Typed Internal Name Public ({ record | name : name } -> name)
name =
.name |> tag Name |> isChecked Name
status : Typed Internal Status Public ({ record | status : status } -> status)
status =
.status |> tag Status |> isChecked StatusI think that would come out neat!elm-review tool to auto-generate Record.TypedonTie is slightly more verbose than onTieNext [ ... ]onTie is more obvious and easier to read than onTieNext [ ... ]KeySet functionality include in KeysSet?KeysSet functionality while still providing log n for the most prominent keyKeySet for each?