both or only one of them
The basic types of this package look like this:
type AndOr first second
= Both ( first, second )
| Only (Or first second)
type Or first second
= First first
| Second secondBoth types can be quite useful! ...But first
AndOr and Or are prone to misuse, just like Maybe, tuple, Bool etc.
When there is a representation of your data that's more descriptive, use that instead!
Example: Modeling the result of an operation that can fail, succeed or succeed with warnings
type alias Fallible a =
AndOr a ErrorThis is problematic from multiple standpoints:
- Why should error and warning types be required to be the same?
- Why not have a unified case for success:
type alias Fallible a = Or { result : a, warnings : List RecoverableError } UnrecoverableError
- Who tells you that "first" means "success" and "second" means "error"? (or the other way round?)
- Why not use descriptive names:
type Fallible a = Success { result : a, errors : List RecoverableError } | UnrecoverableError UnrecoverableError
AndOr and Or are useful for generic operations where no descriptive names exist –
similar to how tuples are used for partition results because there is no information
on what each side means.
- generic diffs
- see
KeysSet.fold2Fromwhich is similar toDict.mergebut more comprehensible and easier to work with thanks toAndOr
- see
- map2 where overflow elements aren't disregarded:
List.AndOr.map2 : (AndOr a b -> c) -> ( List a, List b ) -> List c List.AndOr.map2 combine lists = case lists of ( firsts, [] ) -> firsts |> List.map (\first -> Only (Or.First first) |> combine) ( [], seconds ) -> seconds |> List.map (\first -> Only (Or.Second first) |> combine) ( firstHead :: firstTail, secondHead :: secondTail ) -> (AndOr.Both firstHead secondHead |> combine) :: List.AndOr.map2 combine firstTail secondTail
- type-safe partitioning
List.Or.partition : (a -> Or first second) -> (List a -> ( List first, List second )) List.Or.partition chooseSide list = case list of [] -> [] head :: tail -> let consHead : ( List first, List second ) -> ( List first, List second ) consHead = case head |> chooseSide of First first -> And.firstMap ((::) first) Second second -> And.secondMap ((::) second) in tail |> List.Or.partition chooseSide |> consHead
prior art – AndOr
All of them don't separate out the "only one of both" case.
prior art – Or
kingwither/elmeither- (too few) simple helpers
toastal/either- a lot of helpers, though with weird haskell-ish names like
voidLeftfor setting,singletonforRightetc.
- a lot of helpers, though with weird haskell-ish names like
- haskell's
Data.Either
All of them use Left and Right as variant names whereas Or's First and Second are consistent with elm's tuple part names.
The API is pretty slim, only covering the needs I could think of. If you have an idea for a useful helper → contribute