diff --git a/unison-src/transcripts/pretty-print-libraries.md b/unison-src/transcripts/pretty-print-libraries.md
new file mode 100644
index 0000000000..639f13a9fd
--- /dev/null
+++ b/unison-src/transcripts/pretty-print-libraries.md
@@ -0,0 +1,13 @@
+This transcript is to detect changes in the pretty-printer for a few major public libraries.
+
+We clone releases and not dev branches to avoid external changes, and also to reduce the time needed to clone the libraries.
+
+```ucm
+scratch/main> clone @unison/base/releases/3.19.0
+@unison/base/releases/3.19.0> edit.namespace
+```
+
+```ucm
+scratch/main> clone @unison/http/releases/3.3.2
+@unison/http/releases/3.3.2> edit.namespace
+```
diff --git a/unison-src/transcripts/pretty-print-libraries.output.md b/unison-src/transcripts/pretty-print-libraries.output.md
new file mode 100644
index 0000000000..f78faa9916
--- /dev/null
+++ b/unison-src/transcripts/pretty-print-libraries.output.md
@@ -0,0 +1,87504 @@
+This transcript is to detect changes in the pretty-printer for a few major public libraries.
+
+We clone releases and not dev branches to avoid external changes, and also to reduce the time needed to clone the libraries.
+
+``` ucm
+scratch/main> clone @unison/base/releases/3.19.0
+
+ Downloaded 13100 entities.
+
+ Cloned @unison/base/releases/3.19.0.
+
+@unison/base/releases/3.19.0> edit.namespace
+
+ ☝️
+
+ I added 6902 definitions to the top of scratch.u
+
+ You can edit them there, then run `update` to replace the
+ definitions currently in this namespace.
+
+```
+```` unison:added-by-ucm scratch.u
+structural ability abilities.Abort where abort : {abilities.Abort} a
+
+structural ability abilities.Ask a where ask : {abilities.Ask a} a
+
+ability abilities.Clock where
+ elapsed : {abilities.Clock} Duration
+ now : {abilities.Clock} Instant
+
+structural ability abilities.Each where
+ lazily : '{Stream a} () ->{abilities.Each} a
+
+structural ability abilities.Exception where
+ raise : Failure ->{abilities.Exception} x
+
+type abilities.Exception.Generic
+ =
+
+ability abilities.Label where
+ label : Text -> a ->{abilities.Label} ()
+ popScope : {abilities.Label} ()
+ pushScope : Text ->{abilities.Label} ()
+
+ability abilities.Random where
+ split! : {abilities.Random} (∀ g a. '{g, abilities.Random} a ->{g} a)
+ nat! : {abilities.Random} Nat
+ bytes : Nat ->{abilities.Random} Bytes
+
+structural type abilities.Random.RNG
+ = RNG (∀ g a. '{Random, g} a ->{g} a)
+
+-- abilities.Request is built-in.
+
+structural ability abilities.Store a where
+ put : a ->{abilities.Store a} ()
+ get : {abilities.Store a} a
+
+structural ability abilities.Throw e where throw : e ->{abilities.Throw e} a
+
+ability abilities.Wait where wait : Duration ->{abilities.Wait} ()
+
+-- Any is built-in.
+
+-- Boolean is built-in.
+
+-- Bytes is built-in.
+
+structural type Bytes.base32Hex.Hex32Piece
+ = Single Nat Nat Bytes
+ | Double Nat Nat Nat Bytes
+
+-- Char is built-in.
+
+-- Char.Class is built-in.
+
+type crypto.CryptoFailure
+ =
+
+type crypto.Ed25519.PrivateKey
+ = PrivateKey Bytes
+
+type crypto.Ed25519.PublicKey
+ = PublicKey Bytes
+
+type crypto.Ed25519.Signature
+ = Signature Bytes
+
+-- crypto.HashAlgorithm is built-in.
+
+type crypto.Rsa.PrivateKey
+ = PrivateKey Bytes
+
+type crypto.Rsa.PublicKey
+ = PublicKey Bytes
+
+type crypto.Rsa.Signature
+ = Signature Bytes
+
+structural type data.Array a
+ = Arr Nat Nat (data.Array.Raw a)
+
+type data.Array.ArrayFailure
+ =
+
+-- data.Array.Raw is built-in.
+
+structural type data.Bag a
+ = internal.MkBag (Map a Nat)
+
+structural type data.ByteArray
+ = BArr Nat Nat data.ByteArray.Raw
+
+-- data.ByteArray.Raw is built-in.
+
+structural type data.deprecated.Heap k v
+ = Heap Nat k v [data.deprecated.Heap k v]
+
+structural type data.deprecated.Weighted a
+ = Weight Nat ('data.deprecated.Weighted a)
+ | Fail
+ | Yield a (data.deprecated.Weighted a)
+
+type data.Graph v
+ = AdjLists (data.Array.Raw [Nat]) (data.Array.Raw v)
+
+type data.Graph.SCC v
+ = Acyclic v
+ | Cyclic [v]
+
+structural type data.Id a
+ = Id a
+
+-- data.List is built-in.
+
+structural type data.List.Nonempty a
+ = Nonempty a [a]
+
+type data.Map k v
+ = internal.Bin Nat k v (data.Map k v) (data.Map k v)
+ | internal.Tip
+
+type data.Map.internal.MaxView k v
+ = MaxView k v (Map k v)
+
+type data.Map.internal.MinView k v
+ = MinView k v (Map k v)
+
+type data.Map.Nonempty k v
+ = Bin Nat k v (Map k v) (Map k v)
+
+type data.NatBag
+ = NatBag (NatMap Nat)
+
+type data.NatBag.Nonempty
+ = NatBag.Nonempty (NatMap.Nonempty Nat)
+
+type data.NatMap a
+ = NatMap (Optional (NatMap.Nonempty a))
+
+type data.NatMap.Nonempty a
+ = NatMap.Nonempty.Tip Nat a
+ | NatMap.Nonempty.Bin
+ Nat Nat Nat (data.NatMap.Nonempty a) (data.NatMap.Nonempty a)
+
+type data.NatSet
+ = NatSet (Optional NatSet.Nonempty)
+
+type data.NatSet.Nonempty
+ = Tip Nat Nat
+ | Bin Nat Nat Nat data.NatSet.Nonempty data.NatSet.Nonempty
+
+structural type data.OneOrBoth a b
+ = Both a b
+ | That b
+ | This a
+
+structural type data.SeqView a b
+ = VElem a b
+ | VEmpty
+
+structural type data.Set a
+ = internal.Set (Map a ())
+
+structural type data.Set.Nonempty a
+ = Set (Map.Nonempty a ())
+
+structural ability data.Stream e where emit : e ->{data.Stream e} ()
+
+ability data.Stream.collate.test.Counter where
+ comma! : {data.Stream.collate.test.Counter} ()
+ nat! : {data.Stream.collate.test.Counter} ()
+
+structural type data.Trie k v
+ = Trie (Optional v) (Map k (data.Trie k v))
+
+structural type data.Tuple a b
+ = Cons a b
+
+type Doc
+ = Folded Boolean Doc Doc
+ | Tooltip Doc Doc
+ | NamedLink Doc Doc
+ | Callout (Optional Doc) Doc
+ | NumberedList Nat [Doc]
+ | Special SpecialForm
+ | Code Doc
+ | Bold Doc
+ | Italic Doc
+ | Strikethrough Doc
+ | Blockquote Doc
+ | Aside Doc
+ | Group Doc
+ | Section Doc [Doc]
+ | Image Doc Doc (Optional Doc)
+ | Blankline
+ | Linebreak
+ | SectionBreak
+ | Table [[Doc]]
+ | Word Text
+ | Paragraph [Doc]
+ | BulletedList [Doc]
+ | Join [Doc]
+ | UntitledSection [Doc]
+ | Column [Doc]
+ | CodeBlock Text Doc
+ | Style Text Doc
+ | Anchor Text Doc
+
+type Doc.Deprecated
+ = Blob Text
+ | Link Link
+ | Source Link
+ | Signature Link.Term
+ | Evaluate Link.Term
+ | Join [Doc.Deprecated]
+
+type Doc.EmbedSvg
+ = EmbedSvg Text
+
+type Doc.FrontMatter
+ = FrontMatter [(Text, Text)]
+
+type Doc.LaTeXInline
+ = LaTeXInline Text
+
+type Doc.MediaSource
+ = { sourceUrl : Text, mimeType : Optional Text }
+
+type Doc.SpecialForm
+ = Example Nat Doc.Term
+ | ExampleBlock Nat Doc.Term
+ | Signature [Doc.Term]
+ | Link (Either Type Doc.Term)
+ | Embed Any
+ | EmbedInline Any
+ | Source [(Either Type Doc.Term, [Doc.Term])]
+ | FoldedSource [(Either Type Doc.Term, [Doc.Term])]
+ | SignatureInline Doc.Term
+ | Eval Doc.Term
+ | EvalInline Doc.Term
+
+type Doc.Term
+ = Term Any
+
+type Doc.Video
+ = { sources : [MediaSource], config : [(Text, Text)] }
+
+structural type Either a b
+ = Right b
+ | Left a
+
+-- Float is built-in.
+
+type GUID
+ = GUID Bytes
+
+-- Int is built-in.
+
+-- IO is built-in.
+
+-- IO.concurrent.MVar is built-in.
+
+-- IO.concurrent.Promise is built-in.
+
+-- IO.concurrent.STM is built-in.
+
+type IO.concurrent.STM.STMFailure
+ =
+
+type IO.concurrent.STM.TMap a
+ = TMap (TVar (Optional a)) [TVar (IO.concurrent.STM.TMap.impl.F a)]
+
+type IO.concurrent.STM.TMap.impl.F a
+ = One Bytes a
+ | Many (IO.concurrent.STM.TMap a)
+ | Empty
+
+type IO.concurrent.STM.TQueue a
+ = TQueue (TVar [a]) (TVar Nat)
+
+-- IO.concurrent.ThreadId is built-in.
+
+type IO.concurrent.ThreadKilledFailure
+ =
+
+structural type IO.concurrent.TMVar a
+ = TMVar (TVar (Optional a))
+
+-- IO.concurrent.TVar is built-in.
+
+type IO.deprecated.EpochTime
+ = EpochTime Nat
+
+type IO.Failure
+ = Failure Type Text Any
+
+type IO.Failure.ArithmeticFailure
+ =
+
+type IO.Failure.MiscFailure
+ =
+
+type IO.Failure.RuntimeFailure
+ =
+
+type IO.FilePath
+ = FilePath Text
+
+type IO.FilePath.FileMode
+ = Read
+ | Write
+ | Append
+ | ReadWrite
+
+-- IO.Handle is built-in.
+
+type IO.Handle.BufferMode
+ = NoBuffering
+ | LineBuffering
+ | BlockBuffering
+ | SizedBlockBuffering Nat
+
+type IO.Handle.SeekMode
+ = AbsoluteSeek
+ | RelativeSeek
+ | SeekFromEnd
+
+type IO.Handle.Std
+ = StdIn
+ | StdOut
+ | StdErr
+
+type IO.IOError
+ = AlreadyExists
+ | NoSuchThing
+ | ResourceBusy
+ | ResourceExhausted
+ | EOF
+ | IllegalOperation
+ | PermissionDenied
+ | UserError
+
+type IO.IOFailure
+ =
+
+type IO.net.Connection
+ = Connection
+ (Bytes ->{IO, Exception} ())
+ ('{IO, Exception} Bytes)
+ ('{IO, Exception} ())
+
+type IO.net.HostName
+ = HostName Text
+
+type IO.net.Port
+ = Port Text
+
+-- IO.net.Socket is built-in.
+
+type IO.net.Socket.BoundServerSocket
+ = BoundServerSocket Socket
+
+type IO.net.Socket.ListeningServerSocket
+ = ListeningServerSocket Socket
+
+type IO.net.Socket.UnboundServerSocket
+ = UnboundServerSocket Socket
+
+-- IO.net.Tls is built-in.
+
+-- IO.net.Tls.Cipher is built-in.
+
+-- IO.net.Tls.ClientConfig is built-in.
+
+-- IO.net.Tls.PrivateKey is built-in.
+
+-- IO.net.Tls.ServerConfig is built-in.
+
+-- IO.net.Tls.SignedCert is built-in.
+
+type IO.net.Tls.TlsFailure
+ =
+
+type IO.net.Tls.TlsSocket
+ = TlsSocket Tls
+
+-- IO.net.Tls.Version is built-in.
+
+type IO.net.URI
+ = URI Scheme (Optional Authority) Path RawQuery Fragment
+
+type IO.net.URI.Authority
+ = { userInfo : Optional UserInfo, host : HostName, port : Optional Port }
+
+type IO.net.URI.Fragment
+ = Fragment Text
+
+type IO.net.URI.Method
+ = GET
+ | HEAD
+ | POST
+ | PUT
+ | DELETE
+ | CONNECT
+ | TRACE
+ | PATCH
+ | OPTIONS
+
+type IO.net.URI.ParseError
+ =
+
+type IO.net.URI.Path
+ = { segments : [Text] }
+
+type IO.net.URI.Query
+ = Query (Map Text [Text])
+
+type IO.net.URI.RawQuery
+ = RawQuery Text
+
+type IO.net.URI.Scheme
+ = Scheme Text
+
+type IO.net.URI.UserInfo
+ = UserInfo Text
+
+-- IO.Process is built-in.
+
+type IPattern n a
+ = IPattern (Pattern a)
+
+type IPattern.And l r
+ =
+
+type IPattern.Capture
+ =
+
+type math.ArithmeticException
+ = DividedByZero
+ | Overflow
+ | Underflow
+ | NotANumber
+ | NegativeInfinityNotAllowed
+ | PositiveInfinityNotAllowed
+
+type math.Natural
+ = internal.Natural (List.Nonempty Nat)
+
+type metadata.Author
+ = Author GUID Text
+
+type metadata.CopyrightHolder
+ = CopyrightHolder GUID Text
+
+type metadata.IsPropagated
+ = IsPropagated
+
+type metadata.IsTest
+ = IsTest
+
+type metadata.License
+ = License [CopyrightHolder] [Year] LicenseType
+
+type metadata.LicenseType
+ = LicenseType Doc
+
+type metadata.Year
+ = Year Nat
+
+structural type mutable.Array g a
+ = MArr Nat Nat (mutable.Array.Raw g a)
+
+-- mutable.Array.Raw is built-in.
+
+structural type mutable.ByteArray g
+ = MBArr Nat Nat (mutable.ByteArray.Raw g)
+
+-- mutable.ByteArray.Raw is built-in.
+
+-- mutable.Ref is built-in.
+
+-- mutable.Ref.Ticket is built-in.
+
+-- mutable.Scope is built-in.
+
+-- Nat is built-in.
+
+structural type Optional a
+ = Some a
+ | None
+
+type Ordering
+ = Less
+ | Equal
+ | Greater
+
+-- Pattern is built-in.
+
+structural type Pretty txt
+ = Pretty (Annotated () txt)
+
+type Pretty.Annotated w txt
+ = Table w [[Pretty.Annotated w txt]]
+ | Append w [Pretty.Annotated w txt]
+ | OrElse w (Pretty.Annotated w txt) (Pretty.Annotated w txt)
+ | Indent
+ w
+ (Pretty.Annotated w txt)
+ (Pretty.Annotated w txt)
+ (Pretty.Annotated w txt)
+ | Group w (Pretty.Annotated w txt)
+ | Wrap w (Pretty.Annotated w txt)
+ | Empty
+ | Lit w txt
+
+-- reflection.Code is built-in.
+
+type reflection.Link
+ = Term Link.Term
+ | Type Type
+
+-- reflection.Link.Term is built-in.
+
+-- reflection.Link.Type is built-in.
+
+type reflection.RewriteCase a b
+ = RewriteCase a b
+
+type reflection.Rewrites a
+ = Rewrites a
+
+type reflection.RewriteSignature a b
+ = RewriteSignature (a -> b -> ())
+
+type reflection.RewriteTerm a b
+ = RewriteTerm a b
+
+-- reflection.Value is built-in.
+
+type system.ANSI.Color
+ = Black
+ | Red
+ | Green
+ | Yellow
+ | Blue
+ | Magenta
+ | Cyan
+ | White
+ | BrightBlack
+ | BrightRed
+ | BrightGreen
+ | BrightYellow
+ | BrightBlue
+ | BrightMagenta
+ | BrightCyan
+ | BrightWhite
+
+type system.ConsoleText
+ = Bold system.ConsoleText
+ | Underline system.ConsoleText
+ | Invert system.ConsoleText
+ | Plain Text
+ | Foreground Color system.ConsoleText
+ | Background Color system.ConsoleText
+
+structural type test.deprecated.Domain a
+ = Large (Weighted a)
+ | Small [a]
+
+structural ability test.deprecated.Gen where
+ sample : Weighted a ->{test.deprecated.Gen} a
+
+type test.deprecated.internals.v1.Test.Labels
+ = Labels [Text]
+
+structural type test.deprecated.internals.v1.Test.Report
+ = Report (Trie Text Status)
+
+structural type test.deprecated.internals.v1.Test.Status
+ = Expected Success
+ | Unexpected Success
+ | Failed
+ | Pending
+
+structural type test.deprecated.internals.v1.Test.Success
+ = Passed Nat
+ | Proved
+
+structural type test.deprecated.Test
+ = Test (Labels -> Report)
+
+type test.Result
+ = Fail Text
+ | Ok Text
+
+type test.TestFailure
+ =
+
+-- Text is built-in.
+
+-- time.Clock.internals.TimeSpec is built-in.
+
+type time.DayOfWeek
+ = Sat
+ | Sun
+ | Mon
+ | Tue
+ | Wed
+ | Thu
+ | Fri
+
+type time.Duration
+ = internal.Duration Int Nat
+
+type time.Instant
+ = internal.Instant Int Nat
+
+type time.LocalDate
+ = { year : Int, month : Nat, day : Nat }
+
+type time.LocalDateTime
+ = { date : LocalDate, time : LocalTime }
+
+type time.LocalTime
+ = { hour : Nat, minute : Nat, second : Nat, nanosecond : Nat }
+
+structural type time.OffsetDateTime
+ = OffsetDateTime UTCOffset LocalDateTime
+
+type time.OffsetTime
+ = { offset : UTCOffset, time : LocalTime }
+
+type time.TimeZone
+ = { offset : UTCOffset, summerOnly : Boolean, name : Text }
+
+type time.UTCOffset
+ = UTCOffset Int
+
+structural type Unit
+ = Unit
+
+structural type Void
+ =
+
+abilities.Abort.abortWhen : Boolean ->{Abort} ()
+abilities.Abort.abortWhen b = if b then abort else ()
+
+abilities.Abort.abortWhen.doc : Doc
+abilities.Abort.abortWhen.doc =
+ {{ Abort if the given {type Boolean} is ``true``, otherwise do nothing. }}
+
+abilities.Abort.doc : Doc
+abilities.Abort.doc =
+ use Nat /
+ {{
+ The {type Abort} ability is used to terminate a computation without returning
+ a value or an error. This is useful for partial functions, where the function
+ is not defined for the given argument and either the caller doesn't need to
+ know why or it's obvious why the function is not defined.
+
+ 📚 Guide:
+ [Handling errors with abilities](https://www.unison-lang.org/learn/fundamentals/abilities/error-handling/)
+
+ # Aborting a computation
+
+ {type Abort}'s only request constructor is {abort}. Here's an example of a
+ partial function that uses it:
+
+ @typecheck ```
+ divBy : Nat -> Nat ->{Abort} Nat
+ divBy a b = match b with
+ 0 -> abort
+ n -> a / b
+ ```
+
+ Abort if a condition is ``true``:
+
+ @signature{abortWhen}
+
+ # Relationship to {type Optional}
+
+ The {type Abort} ability is similar to {type Optional}, in that both allow
+ a computation to fail without returning a value. There is a tradeoff
+ between the two approaches:
+
+ * {type Abort} is an ability, while {type Optional} is a data type. This
+ means {type Optional} values can be passed around, used in data
+ structures, and as the return type of the constructors of abilities,
+ while {type Abort} can only be used in computations and functions.
+ * {type Abort} is more efficient, because it doesn't require allocating a
+ an {type Optional} value.
+ * {type Abort} readily composes with other abilities, while {type Optional}
+ does not. For example, you can use both {type Abort} and {type Random} to
+ combine partiality with randomness, but doing so with {type Optional} can
+ be awkward.
+ * Partial functions that use {type Abort} compose with other functions
+ normally, while partial functions that use {type Optional} must be
+ composed using {Optional.map}, {Optional.flatMap}, or explicit pattern
+ matching.
+
+ However, {type Optional} and {type Abort} can be readily converted between
+ each other, so you can use whichever is more convenient for a given
+ situation.
+
+ # Handling {type Abort}
+
+ Throw a runtime error on {abort}:
+
+ @signature{Abort.toBug}
+
+ Fall back to the first computation if the second computation aborts:
+
+ @signatures{toDefault, toDefault!}
+
+ Fall back to the given value if the computation aborts:
+
+ @signatures{toDefaultValue, toDefaultValue!}
+
+ Raise a {type Failure} of a particular type if the computation aborts:
+
+ @signature{Abort.toException}
+
+ Raise a {type Generic} {type Failure} if the computation aborts:
+
+ @signature{Abort.toGenericException}
+
+ Return {None} if the computation aborts:
+
+ @signatures{Abort.toOptional, toOptional!}
+ }}
+
+test> abilities.Abort.tests.ex1 = check ((toOptional! do abort) === None)
+
+test> abilities.Abort.tests.ex2 =
+ use Nat +
+ check ((toOptional! do 1 + abort) === None)
+
+test> abilities.Abort.tests.ex3 =
+ use Nat +
+ check ((toOptional! do Optional.toAbort (Some 1) + 1) === Some 2)
+
+abilities.Abort.toBug : '{g, Abort} a ->{g} a
+abilities.Abort.toBug x =
+ handle x()
+ with cases
+ { abort -> _ } -> bug "Aborted unexpectedly."
+ { a } -> a
+
+abilities.Abort.toBug.doc : Doc
+abilities.Abort.toBug.doc =
+ use Abort toBug
+ {{
+ Takes a computation that may use the {type Abort} ability, and turns it into
+ a computation that crashes whenever the original would have called {abort}.
+
+ # Examples
+
+ ```
+ toBug do if true then 42 else abort
+ ```
+
+ ```
+ toBug do abort
+ ```
+ }}
+
+abilities.Abort.toDefault : '{g} a -> '{g, Abort} a -> '{g} a
+abilities.Abort.toDefault default thunk = do toDefault! default thunk
+
+abilities.Abort.toDefault.doc : Doc
+abilities.Abort.toDefault.doc =
+ {{
+ `` toDefault default thunk `` returns a delayed computation of
+ ``toDefault! default thunk``.
+ }}
+
+abilities.Abort.toDefault! : '{g} a -> '{g, Abort} a ->{g} a
+abilities.Abort.toDefault! default thunk =
+ handle thunk() with toDefault!.handler default
+
+abilities.Abort.toDefault!.doc : Doc
+abilities.Abort.toDefault!.doc =
+ {{
+ `` toDefault! default thunk `` handles a {type Abort} computation `thunk` by
+ returning the result of the computation or, if the computation calls
+ ``abort``, returning the result of a delayed `default` computation.
+ }}
+
+abilities.Abort.toDefault!.handler : '{g} a -> Request {Abort} a ->{g} a
+abilities.Abort.toDefault!.handler default = cases
+ { a } -> a
+ { abort -> _ } -> default()
+
+abilities.Abort.toDefaultValue : a -> '{g, Abort} a -> '{g} a
+abilities.Abort.toDefaultValue default thunk = do toDefaultValue! default thunk
+
+abilities.Abort.toDefaultValue.doc : Doc
+abilities.Abort.toDefaultValue.doc =
+ {{
+ `` toDefaultValue default thunk `` returns a delayed computation of
+ ``toDefaultValue! default thunk``.
+ }}
+
+abilities.Abort.toDefaultValue! : a -> '{g, Abort} a ->{g} a
+abilities.Abort.toDefaultValue! default thunk =
+ handle thunk() with toDefaultValue!.handler default
+
+abilities.Abort.toDefaultValue!.doc : Doc
+abilities.Abort.toDefaultValue!.doc =
+ {{
+ `` toDefaultValue! default thunk `` handles a {type Abort} computation
+ `thunk` by returning the result of the computation or, if the computation
+ calls ``abort``, returning the default value `default`.
+ }}
+
+abilities.Abort.toDefaultValue!.handler : a -> Request {Abort} a -> a
+abilities.Abort.toDefaultValue!.handler default = cases
+ { a } -> a
+ { abort -> _ } -> default
+
+abilities.Abort.toException : '{g, Abort} a -> Type -> Text ->{g, Exception} a
+abilities.Abort.toException a t msg =
+ toDefault! (do Exception.raise (Failure t msg (Any ()))) a
+
+abilities.Abort.toException.doc : Doc
+abilities.Abort.toException.doc =
+ {{
+ Takes a computation that may {abort}, and turns it into a computation that
+ will raise an {type Exception} with the given {type Failure} type and message
+ instead of aborting.
+
+ # Example
+
+ ```
+ catch do
+ Abort.toException (do abort) (typeLink Generic) "Something went wrong"
+ ```
+ }}
+
+test> abilities.Abort.toException.doesn'tThrowOnNoAbort =
+ check
+ (isRight (catch do Abort.toException (do ()) (typeLink Generic) "oops"))
+
+test> abilities.Abort.toException.throwsOnAbort =
+ check
+ (isLeft (catch do Abort.toException (do abort) (typeLink Generic) "oops"))
+
+abilities.Abort.toGenericException : Text -> e -> '{Abort} a ->{Exception} a
+abilities.Abort.toGenericException msg e =
+ toDefault! do Exception.raise (failure msg e)
+
+abilities.Abort.toGenericException.doc : Doc
+abilities.Abort.toGenericException.doc =
+ use Abort toGenericException
+ {{
+ Converts a computation that may call {abort} to one that throws a
+ {type Generic} {type Exception} instead.
+
+ # Example
+
+ ```
+ catch do
+ toGenericException "oops" "payload" do
+ abort
+ "result"
+ ```
+
+ ```
+ catch do toGenericException "oops" "payload" do "result"
+ ```
+ }}
+
+test> abilities.Abort.toGenericException.doesn'tThrowOnNoAbort =
+ check (isRight (catch do Abort.toGenericException "oops" () do ()))
+
+test> abilities.Abort.toGenericException.throwsOnAbort =
+ check (isLeft (catch do Abort.toGenericException "oops" () do abort))
+
+abilities.Abort.toOptional : '{g, Abort} a -> '{g} Optional a
+abilities.Abort.toOptional thunk = do toOptional! thunk
+
+abilities.Abort.toOptional.doc : Doc
+abilities.Abort.toOptional.doc =
+ {{
+ `` Abort.toOptional a `` returns a delayed computation of `` toOptional! a ``
+ }}
+
+abilities.Abort.toOptional! : '{g, Abort} a ->{g} Optional a
+abilities.Abort.toOptional! thunk = toDefaultValue! None do Some thunk()
+
+abilities.Abort.toOptional!.doc : Doc
+abilities.Abort.toOptional!.doc =
+ {{
+ `` toOptional! `` runs a {type Abort} computation, returning `` Some `` of
+ the result if the computation succeeds, and `` None `` if it aborts.
+ }}
+
+abilities.Abort.toThrow : e -> '{g, Abort} a ->{g, Throw e} a
+abilities.Abort.toThrow e c =
+ handle c()
+ with cases
+ { a } -> a
+ { abort -> _ } -> throw e
+
+abilities.Abort.toThrow.doc : Doc
+abilities.Abort.toThrow.doc =
+ {{
+ Takes a computation that may {type Abort} and executes it, throwing the given
+ error in {type Throw} if it calls {abort}.
+
+ # Example
+
+ ```
+ toEither do Abort.toThrow "aborted" do abort
+ ```
+ }}
+
+abilities.Ask.ask.doc : Doc
+abilities.Ask.ask.doc =
+ use Nat +
+ {{
+ The sole request constructor of the {type Ask} ability. It asks for a value
+ of a particular type, to be provided by the nearest enclosing handler.
+
+ # Examples
+
+ ```
+ provide 2 do ask + ask
+ ```
+
+ ```
+ Stream.toList (pipe (Stream.range 0 10) do forever do emit (ask + ask))
+ ```
+ }}
+
+abilities.Ask.doc : Doc
+abilities.Ask.doc =
+ use Nat +
+ {{
+ {type Ask} is the ability to read a value of type `a` provided by the handler
+ by calling {ask}.
+
+ # Handlers
+
+ ## Providing the same value each time
+
+ The function {provide} is a handler for {type Ask} that provides the
+ given value wherever {ask} is called in the body of its second argument.
+
+ @signature{provide}
+
+ ### Example
+
+ ```
+ provide 42 do ask + ask
+ ```
+
+ ## Varying the value provided
+
+ The function {provide'} is a handler like {provide}, but instead of
+ always providing the same value, can use an ability to provide a
+ different value each time.
+
+ @signature{provide'}
+
+ ### Example
+
+ ```
+ splitmix 1 do provide' (do Random.natIn 1 7) do fill' 8 do ask
+ ```
+
+ ## Using with other abilities
+
+ {toStore} allows you to use an {type Ask} computation in the context of
+ a stateful {type Store} computation. Each call to {ask} will be treated
+ as a call to {Store.get}.
+
+ @signature{toStore}
+
+ # Applications
+
+ {type Ask} is used by the {type Stream} ability's {pipe} function to model
+ transducers between streams:
+
+ @signature{pipe!}
+ }}
+
+abilities.Ask.map : (a ->{g1} b) -> '{g2, Ask b} r -> '{g1, g2, Ask a} r
+abilities.Ask.map f = delay (Ask.map! f)
+
+abilities.Ask.map.doc : Doc
+abilities.Ask.map.doc =
+ {{
+ `` Ask.map transformation op `` returns a delayed computation of {Ask.map!}
+ `transformation op`.
+ }}
+
+abilities.Ask.map! : (a ->{g1} b) -> '{g2, Ask b} r ->{g1, g2, Ask a} r
+abilities.Ask.map! f =
+ go = cases
+ { r } -> r
+ { ask -> k } ->
+ a = ask
+ handle k (f a) with go
+ thunk -> (handle thunk() with go)
+
+abilities.Ask.map!.doc : Doc
+abilities.Ask.map!.doc =
+ use map! example
+ {{
+ `` Ask.map! transformation op `` runs `op` with a modified {type Ask}
+ environment. Each time that `op` calls {ask}, `transformation` will be
+ applied to the value before it is provided to `op`.
+
+ # Examples
+
+ @source{example}
+
+ ```
+ example
+ ```
+ }}
+
+abilities.Ask.map!.example : Text
+abilities.Ask.map!.example =
+ use Text ++
+ indentation : '{Ask Nat} Text
+ indentation = do
+ indentationLevel = ask
+ Text.repeat indentationLevel " "
+ indent : '{g, Ask Nat} r ->{g, Ask Nat} r
+ indent = Ask.map! (indentationLevel -> Nat.increment indentationLevel)
+ line : Text ->{Ask Nat, Stream Text} ()
+ line t = emit (indentation() ++ t)
+ lines : '{Ask Nat, Stream Text} ()
+ lines = do
+ line "no indentation"
+ line "still no indentation"
+ indent do
+ line "one level of indentation"
+ line "still one level of indentation"
+ indent do
+ line "two levels of indentation"
+ line "still two levels of indentation"
+ line "back to one level of indentation"
+ line "back to no indentation"
+ provide 0 do Stream.toList lines |> Text.join "\n"
+
+test> abilities.Ask.map!.test = test.verify do
+ expected =
+ """
+ no indentation
+ still no indentation
+ one level of indentation
+ still one level of indentation
+ two levels of indentation
+ still two levels of indentation
+ back to one level of indentation
+ back to no indentation
+ """
+ actual = map!.example
+ ensureEqual expected actual
+
+abilities.Ask.provide : a -> '{g, Ask a} r ->{g} r
+abilities.Ask.provide a asker = handle asker() with provide.handler a
+
+abilities.Ask.provide.doc : Doc
+abilities.Ask.provide.doc =
+ use Nat +
+ {{
+ A handler for the {type Ask} ability that provides the given value wherever
+ {ask} is called in the given computation.
+
+ # Example
+
+ ```
+ provide 42 do ask + ask
+ ```
+ }}
+
+abilities.Ask.provide.handler : a -> Request {Ask a} r -> r
+abilities.Ask.provide.handler a =
+ h = cases
+ { r } -> r
+ { ask -> resume } -> handle resume a with h
+ h
+
+abilities.Ask.provide' : '{g} a -> '{g, Ask a} r ->{g} r
+abilities.Ask.provide' currentValue thunk =
+ handle thunk() with provide'.handler currentValue
+
+abilities.Ask.provide'.doc : Doc
+abilities.Ask.provide'.doc =
+ {{
+ `` provide' currentValue f `` runs `f`, evaluating `currentValue` __each__
+ time that {ask} is invoked. This differs from {provide}, which provides the
+ __same__ value each time that {ask} is called.
+
+ # Examples
+
+ If `currentValue` doesn't perform any effects, then the {ask} result will
+ be the same each time.
+
+ ```
+ provide' (do "hi") do fill' 3 do ask
+ ```
+
+ If `currentValue` performs an effect, then the {ask} result can vary.
+
+ ```
+ results : '{Ask Int} [Int]
+ results = do fill' 3 do ask
+ (do provide' Random.int results) |> lcg 42
+ ```
+ }}
+
+abilities.Ask.provide'.handler : '{g} a -> Request {Ask a} r ->{g} r
+abilities.Ask.provide'.handler currentValue =
+ h = cases
+ { r } -> r
+ { ask -> resume } -> handle resume currentValue() with h
+ h
+
+test> abilities.Ask.provide'.tests = test.verify do
+ ensureEqual (provide' (do "hi") do (ask, ask, ask)) ("hi", "hi", "hi")
+ Scope.run do
+ n = Scope.ref 0
+ incrementAndGet = do
+ Ref.modify n Nat.increment
+ Ref.read n
+ ensureEqual (provide' incrementAndGet do (ask, ask, ask)) (1, 2, 3)
+
+test> abilities.Ask.tests.ex1 =
+ use Nat +
+ check ((provide 10 do 1 + ask + ask) === 21)
+
+abilities.Ask.toStore : '{g, Ask a} r ->{g, Store a} r
+abilities.Ask.toStore f = handle f() with toStore.handler
+
+abilities.Ask.toStore.doc : Doc
+abilities.Ask.toStore.doc =
+ {{ Convert each {ask} in the provided computation into {Store.get}. }}
+
+abilities.Ask.toStore.handler : Request {Ask a} r ->{Store a} r
+abilities.Ask.toStore.handler =
+ h = cases
+ { r } -> r
+ { ask -> resume } -> handle resume Store.get with h
+ h
+
+test> abilities.Ask.toStore.tests = test.verify do
+ inc = do Store.modify Nat.increment
+ program = do
+ x = ask
+ inc()
+ y = ask
+ inc()
+ z = ask
+ (x, y, z)
+ ensureEqual (withInitialValue 0 do toStore program) (0, 1, 2)
+
+abilities.Clock.doc : Doc
+abilities.Clock.doc =
+ use Clock now
+ use Duration second
+ use Instant +
+ {{
+ The {type Clock} ability is an abstract interface for querying the current
+ time and the time elapsed since a fixed point in the past. The {now}
+ operation returns the current time as an {type Instant}. The {elapsed}
+ operation returns the time elapsed on the system clock since a fixed point in
+ the past as a {type Duration}.
+
+ The {type Clock} ability is useful for writing code that needs to be aware of
+ the current time or the time elapsed since a fixed point in the past, without
+ having to depend on the system clock directly.
+
+ The Base library provides an implementation of the {type Clock} ability that
+ uses the system clock: {runClock}. You can use this implementation to run
+ computations that use the {type Clock} ability, in the {type IO} ability.
+
+ # Example
+
+ @typecheck ```
+ runClock do now + elapsed
+ ```
+
+ # Example handler implementation
+
+ The following is an example handler implementation for the {type Clock}
+ ability that uses an abstrack clock. Whenever the {now} or {elapsed}
+ operations are called, the handler advances the clock by one second.
+
+ ```
+ testClock startTime p =
+ handle p()
+ with cases
+ { now -> k } -> testClock (startTime + second) do k startTime
+ { elapsed -> k } ->
+ testClock (startTime + second) do
+ k (Instant.timeSinceEpoch startTime)
+ { a } -> a
+ testClock epoch do Instant.toRFC2822 (now + elapsed)
+ ```
+ }}
+
+abilities.Clock.runClock : (a ->{g, Clock} b) -> a ->{g, IO, Exception} b
+abilities.Clock.runClock f a =
+ h : '{g, IO, Exception, Clock} b ->{g, IO, Exception} b
+ h x =
+ handle x()
+ with cases
+ { Clock.now -> k } -> h do k realtime()
+ { elapsed -> k } -> h do k Clock.monotonic()
+ { a } -> a
+ h do f a
+
+abilities.Clock.runClock.doc : Doc
+abilities.Clock.runClock.doc =
+ use Clock monotonic now
+ {{
+ `` runClock f `` runs the computation `f` that uses the {type Clock} ability.
+ The {type Clock} ability provides two operations: {now} and {elapsed}. In
+ this implementation, the {now} operation returns the current civic time as an
+ {type Instant}, by calling {realtime}. The {elapsed} operation returns the
+ time elapsed on the system clock since a fixed point in the past, by calling
+ {monotonic}.
+
+ # See also
+
+ * {type Clock} - The ability type.
+ * {realtime} - Returns the current civic time in the {type IO} ability.
+ * {monotonic} - Returns the time elapsed on the system clock since a fixed
+ point in the past in the {type IO} ability.
+ }}
+
+abilities.Each.allowThrow : '{Throw e, Stream a} () ->{Each, Throw e} a
+abilities.Each.allowThrow s =
+ x = lazily do catchWith (emit << Left) (Stream.map Right s)
+ match x with
+ Left e -> throw e
+ Right a -> a
+
+abilities.Each.allowThrow.doc : Doc
+abilities.Each.allowThrow.doc =
+ {{
+ Converts a computation that may {type Throw} in a {type Stream}, to a
+ computation that may {type Throw} in {type Each}.
+
+ # Example
+
+ ```
+ Each.toList do
+ catchWith (do 0) do
+ allowThrow do
+ emit 1
+ throw "oops"
+ emit 2
+ ```
+ }}
+
+abilities.Each.append : '{g, Each} a -> '{g, Each} a ->{g, Each} a
+abilities.Each.append a b =
+ x = each [a, b]
+ x()
+
+abilities.Each.append.doc : Doc
+abilities.Each.append.doc =
+ use Each append
+ {{
+ Concatenates two {type Each} computations. The computation `` append x y ``
+ first produces all the results of `x` and then all the results of `y`.
+
+ # Example
+
+ ```
+ Each.toList do append (do each [1, 2, 3]) do each [4, 5, 6]
+ ```
+
+ # See also
+
+ * {Each.interleave} - interleaves two computations in a fair manner.
+ }}
+
+abilities.Each.count : '{g, Each} a ->{g} Nat
+abilities.Each.count a =
+ use Nat +
+ step n _ = n + 1
+ Each.toStream a |> Stream.fold step 0
+
+abilities.Each.count.doc : Doc
+abilities.Each.count.doc =
+ use Each count
+ {{
+ Counts the number of elements produced by the given {type Each} computation,
+ without producing an intermediate {type List}.
+
+ Examples:
+
+ ```
+ count do each [1, 2, 3]
+ ```
+
+ ```
+ count do each []
+ ```
+
+ A computation that doesn't use nondeterminism at all has a count of `1`:
+
+ ```
+ count do 99
+ ```
+ }}
+
+test> abilities.Each.count.tests = test.verify do
+ N = Each.range 0 25
+ ns = Nat.range 0 N
+ c1 = Each.count do each ns
+ c2 = (Each.toList do each ns) |> List.size
+ ensureEqual c1 c2
+
+abilities.Each.doc : Doc
+abilities.Each.doc =
+ use Each range toList
+ use Nat * <
+ {{
+ A nondeterminism ability that produces output lazily. It can be used for
+ nested loops, mimicking
+ [list comprehensions](https://en.wikipedia.org/wiki/List_comprehension) in
+ languages like Haskell or Python, and can also be used for logic programming
+ in the style of Prolog.
+
+ # Basic usage
+
+ ```
+ toList do
+ a = range 0 5
+ b = each [1, 2, 3]
+ guard (a < b)
+ (a, b)
+ ```
+
+ In the above example, `a` gets each value in the range `0` to `5` (not
+ including `5`), and `b` gets each value from the list ``[1, 2, 3]``. The
+ {guard} removes all pairs of `a` and `b` that don't satisfy the condition.
+
+ Note that we don't need to declare a new binding for every use of
+ nondeterminism. For instance:
+
+ ```
+ toList do each [1, 2, 3, 4] * 100
+ ```
+
+ This example multiplies each of the values in the list by 100.
+
+ # Iteration
+
+ The {type Each} ability can be used to iterate over data structures, such
+ as lists, streams, etc.
+
+ Iterate over a {type List}:
+
+ @signature{each}
+
+ Iterate over a {type Stream}:
+
+ @signature{lazily}
+
+ Iterate over a {type Stream} that may throw exceptions:
+
+ @signature{allowThrow}
+
+ Iterate over a {type Text}:
+
+ @signature{eachChar}
+
+ Iterate over a range of numbers:
+
+ @signature{range}
+
+ Repeat the rest of the computation a number of times:
+
+ @signature{Each.repeat}
+
+ Iterate over the captures of a {type Pattern}:
+
+ @signature{eachCapture}
+
+ Get the value from an {type Optional}, or fail if it's {None}:
+
+ @signature{optionally}
+
+ Get the {Left} value from an {type Either}, or fail if it's {Right}:
+
+ @signature{Each.left}
+
+ Get the {Right} value from an {type Either}, or fail if it's {Left}:
+
+ @signature{Each.right}
+
+ Get the value from a computation that uses {type Abort}, or fail if it
+ aborts:
+
+ @signature{fromAbort}
+
+ Get the value from a computation that uses {type Exception}, or fail if it
+ raises an exception:
+
+ @signature{fromException}
+
+ Get the value from a computation that uses {type Throw}, or fail if it
+ throws:
+
+ @signature{fromThrow}
+
+ # Filtering and failure
+
+ Fail the current branch unconditionally (i.e. produce no values):
+
+ @signature{Each.fail}
+
+ Fail if a {type Boolean} condition is not satisfied:
+
+ @signature{guard}
+
+ Fail if a predicate returns ``false``:
+
+ @signature{Each.filter}
+
+ Succeed if a given computation fails, and fail if it succeeds:
+
+ @signature{Each.negate}
+
+ # Extracting values
+
+ Get a {type List} of all the results:
+
+ @signature{toList}
+
+ Get a {type Stream} of all the results:
+
+ @signature{Each.toStream}
+
+ Get the first result, if any:
+
+ @signature{observe}
+
+ Split a computation into its first result, if any, and the rest of the
+ computation:
+
+ @signature{Each.split}
+
+ Run a computation just for its effects, ignoring all results:
+
+ @signature{Each.run}
+
+ Count the number of elements emitted:
+
+ @signature{Each.count}
+
+ # Combining computations
+
+ Append the results of one computation to another:
+
+ @signature{Each.append}
+
+ Interleave the results of one computation with another:
+
+ @signature{Each.interleave}
+
+ Interleave a computation with another that may depend on the outputs of the
+ first computation:
+
+ @signature{interleaveMap}
+
+ Proceed differently depending on whether one computation succeeds or fails:
+
+ @signature{ifThenElse}
+
+ # Pruning
+
+ Stop a computation after it produces one value:
+
+ @signature{once}
+
+ Stop a computation after it produces a specific number of values:
+
+ @signature{limit}
+
+ # Logic programming
+
+ The {type Each} ability can be used for logic programming in the style of
+ Prolog.
+
+ Here is a worked example of a logic program that finds all people in a list
+ who are not ancestors of a given person:
+
+ @source{notJimsAncestors'}
+ }}
+
+abilities.Each.each : [a] ->{Each} a
+abilities.Each.each as = lazily (Stream.fromList as)
+
+abilities.Each.each.doc : Doc
+abilities.Each.each.doc =
+ {{
+ `` each xs `` produces each of the elements of `xs`.
+
+ ```
+ Each.toList do each [1, 2, 3, 4]
+ ```
+ }}
+
+abilities.Each.eachCapture : Pattern t -> t ->{Each} t
+abilities.Each.eachCapture p t =
+ (ts, _) = optionally (Pattern.run p t)
+ each ts
+
+abilities.Each.eachCapture.doc : Doc
+abilities.Each.eachCapture.doc =
+ use Pattern capture
+ {{
+ `` eachCapture p t `` produces each capture made by running the pattern `p`
+ on the input `t`.
+
+ # Example
+
+ ```
+ Each.toList do
+ eachCapture
+ (sepMany
+ (some (patterns.char (Class.not Class.letter)))
+ (capture (some patterns.letter)))
+ "Hello123, this-is*an_example#of some_text!"
+ ```
+
+ # See also
+
+ * {Pattern.run}
+ * {capture}
+ }}
+
+abilities.Each.eachChar : Text ->{Each} Char
+abilities.Each.eachChar t = lazily do Text.toStream t
+
+abilities.Each.eachChar.doc : Doc
+abilities.Each.eachChar.doc =
+ {{
+ Produces each character of the given {type Text}.
+
+ # Example
+
+ ```
+ Each.toList do
+ c = eachChar "abcde"
+ (c, Char.toNat c)
+ ```
+
+ # See also
+
+ * {type Each}
+ * {each}
+ * {Each.fail}
+ }}
+
+abilities.Each.fail : '{Each} a
+abilities.Each.fail _ = lazily do ()
+
+abilities.Each.fail.doc : Doc
+abilities.Each.fail.doc =
+ use Each fail
+ {{
+ `` fail `` aborts the current branch of this computation.
+
+ For example, in:
+
+ ```
+ Each.toList do
+ fail()
+ each (Nat.range 0 100000)
+ ```
+
+ the `` each (List.range 0 100000) `` will never actually be computed, because
+ the current branch is aborted by the call to ``fail()``.
+
+ Also see {guard} if you want to conditionally abort the current branch.
+ }}
+
+abilities.Each.filter : (a ->{e} Boolean) -> a ->{e, Each} a
+abilities.Each.filter p a = if p a then a else Each.fail()
+
+abilities.Each.filter.doc : Doc
+abilities.Each.filter.doc =
+ {{
+ Aborts the current branch of the computation if the given predicate returns
+ `` false `` for the given value.
+
+ # Example
+
+ ```
+ Each.toList do Each.range 0 10 |> Each.filter Nat.isEven
+ ```
+ }}
+
+abilities.Each.fromAbort : '{g, Abort} a ->{g, Each} a
+abilities.Each.fromAbort c =
+ h = cases
+ { abort -> _ } -> lazily do ()
+ { a } -> lazily do emit a
+ handle c() with h
+
+abilities.Each.fromAbort.doc : Doc
+abilities.Each.fromAbort.doc =
+ use Bag counts
+ use Each toList
+ use Map getOrAbort
+ use Nat +
+ use Text toBag
+ {{
+ Given a computation `x` that may use the {type Abort} ability, `` fromAbort x
+ `` returns the value of `x`, or fails if the computation `x` calls {abort}.
+
+ # Examples
+
+ ```
+ m = toBag "abracadabra" |> counts
+ toList do
+ a = fromAbort do getOrAbort ?a m
+ b = fromAbort do getOrAbort ?b m
+ a + b
+ ```
+
+ ```
+ m = toBag "abracadabra" |> counts
+ toList do
+ z = fromAbort do getOrAbort ?z m
+ c = fromAbort do getOrAbort ?c m
+ c + z
+ ```
+
+ # See also
+
+ * {each}
+ * {Each.fail}
+ }}
+
+abilities.Each.fromException : '{g, Exception} a ->{g, Each} a
+abilities.Each.fromException c =
+ h = cases
+ { Exception.raise _ -> _ } -> lazily do ()
+ { a } -> lazily do emit a
+ handle c() with h
+
+abilities.Each.fromException.doc : Doc
+abilities.Each.fromException.doc =
+ use Nat +
+ use data.Array read
+ {{
+ Given a computation `x` that may throw an {type Exception}, ``
+ fromException x `` returns the value of `x`, or fails if the computation `x`
+ calls {Exception.raise}.
+
+ # Example
+
+ In this example, {fromException} suppresses exceptions from {read} when the
+ index is out of bounds:
+
+ ```
+ Scope.run do
+ Each.toList do
+ x = Array.fromList [1, 2, 3]
+ i = Each.range 0 10
+ fromException do read x i + 1
+ ```
+
+ # See also
+
+ * {each}
+ * {Each.fail}
+ }}
+
+abilities.Each.fromThrow : '{g, Throw e} a ->{g, Each} a
+abilities.Each.fromThrow c =
+ h = cases
+ { throw _ -> _ } -> lazily do ()
+ { a } -> lazily do emit a
+ handle c() with h
+
+abilities.Each.fromThrow.doc : Doc
+abilities.Each.fromThrow.doc =
+ use Bag counts
+ use Each toList
+ use Map getOrThrow
+ use Nat +
+ use Text toBag
+ {{
+ Given a computation `x` that may {type Throw}, `` fromThrow x `` returns the
+ value of `x`, or fails if the computation `x` calls {throw}.
+
+ # Examples
+
+ ```
+ m = toBag "abracadabra" |> counts
+ toList do
+ a = fromThrow do getOrThrow "no a" ?a m
+ b = fromThrow do getOrThrow "no b" ?b m
+ a + b
+ ```
+
+ ```
+ m = toBag "abracadabra" |> counts
+ toList do
+ z = fromThrow do getOrThrow "no z" ?z m
+ c = fromThrow do getOrThrow "no c" ?c m
+ c + z
+ ```
+
+ # See also
+
+ * {each}
+ * {Each.fail}
+ }}
+
+abilities.Each.guard : Boolean ->{Each} ()
+abilities.Each.guard = cases
+ true -> lazily do emit()
+ false -> lazily do ()
+
+abilities.Each.guard.doc : Doc
+abilities.Each.guard.doc =
+ use Each range
+ use Nat + ==
+ {{
+ `` guard b `` aborts the current branch of the computation if `b` is
+ ``false``.
+
+ This is useful for adding filters to a list comprehension:
+
+ ```
+ Each.toList do
+ x = range 0 10
+ y = range 0 10
+ guard (x + x == y)
+ (x, y)
+ ```
+ }}
+
+abilities.Each.ifThenElse :
+ '{g, Each} a -> (a ->{g, Each} b) -> '{g, Each} b ->{g, Each} b
+abilities.Each.ifThenElse cond onValue onFail = match Each.split cond with
+ None -> onFail()
+ Some (a, rest) -> Each.append (do onValue a) do onValue rest()
+
+abilities.Each.ifThenElse.doc : Doc
+abilities.Each.ifThenElse.doc =
+ use Nat range
+ {{
+ `` ifThenElse c th el `` evaluates the computation `c`, and on each success
+ proceeds with `th`. If `c` fails, then it proceeds with `el`.
+
+ # Example
+
+ This example finds even integers in the first argument and multiplies them
+ by 100. If it finds none, it returns the odd integers in the second
+ argument.
+
+ ```
+ numbers xs ys =
+ use Nat *
+ first = do
+ x = each xs
+ guard (Nat.isEven x)
+ x
+ second = do
+ y = each ys
+ guard (Nat.isOdd y)
+ y
+ ifThenElse first ((*) 100) second
+ Each.toList do numbers (range 1 10) (range 21 30)
+ ```
+
+ # Motivation
+
+ The normal `if/then/else` conditional, and the {guard} and {when}
+ functions, can be used within an {type Each} computation to restrict it to
+ only succeed if some condition is met.
+
+ But we sometimes want to proceed one way on success and another way on
+ failure, as in the above example.
+
+ The normal conditional is not adequate for this, as it doesn't allow us to
+ detect the case when a computation fails.
+
+ In the example below, we want to find everyone who is __not__ a particular
+ person's ancestor. This is difficult to express with {guard}, {when}, or
+ the `if/then/else` control construct, but it's straightforward with the
+ {ifThenElse} function:
+
+ @source{notJimsAncestors}
+
+ Here {ifThenElse} checks if the `ancestorOf` computation succeeds for the
+ current person. If it does, then we fail the computation, which means that
+ the current person is not included in the results. If `ancestorOf` doesn't
+ succeed, then we return a success, which means the current person is
+ included in the results.
+
+ # See also
+
+ * {guard} for a simpler way to restrict a computation to only succeed when
+ some condition is met.
+
+ * {Each.negate} uses {ifThenElse} internally to invert the success/failure
+ of a computation.
+ * {when} performs an action when a condition is `` true `` or succeeds
+ without performing that action when it's ``false``.
+ }}
+
+abilities.Each.ifThenElse.examples.jimsAncestors : [Text]
+abilities.Each.ifThenElse.examples.jimsAncestors =
+ use Text ==
+ relations =
+ [ ("John", "Jim")
+ , ("John", "Anna")
+ , ("Jim", "Bob")
+ , ("Jim", "Alice")
+ , ("Anna", "Eve")
+ , ("Anna", "Fred")
+ ]
+ people = ["John", "Anna", "Bob", "Alice", "Eve", "Fred"]
+ parents : Text ->{Each} Text
+ parents person =
+ (child, parent) = each relations
+ guard (child == person)
+ parent
+ ancestorOf : Text -> Text ->{Each} ()
+ ancestorOf descendant ancestor =
+ parent = parents descendant
+ if parent == ancestor then () else ancestorOf parent ancestor
+ ancestors : Text ->{Each} Text
+ ancestors person =
+ p = each people
+ ancestorOf person p
+ p
+ Each.toList do ancestors "Jim"
+
+abilities.Each.ifThenElse.examples.notJimsAncestors : [Text]
+abilities.Each.ifThenElse.examples.notJimsAncestors =
+ use Text ==
+ relations =
+ [ ("John", "Jim")
+ , ("John", "Anna")
+ , ("Jim", "Bob")
+ , ("Jim", "Alice")
+ , ("Anna", "Eve")
+ , ("Anna", "Fred")
+ ]
+ people = ["John", "Anna", "Bob", "Alice", "Eve", "Fred"]
+ parents : Text ->{Each} Text
+ parents person =
+ (child, parent) = each relations
+ guard (child == person)
+ parent
+ ancestorOf : Text -> Text ->{Each} ()
+ ancestorOf descendant ancestor =
+ parent = parents descendant
+ if parent == ancestor then () else ancestorOf parent ancestor
+ ancestors : Text ->{Each} Text
+ ancestors person =
+ p = each people
+ ancestorOf person p
+ p
+ notAncestorOf : Text -> Text ->{Each} ()
+ notAncestorOf descendant ancestor =
+ ifThenElse (do ancestorOf descendant ancestor) Each.fail do ()
+ notAncestors : Text ->{Each} Text
+ notAncestors person =
+ p = each people
+ notAncestorOf person p
+ p
+ Each.toList do notAncestors "Jim"
+
+abilities.Each.ifThenElse.examples.notJimsAncestors' : [Text]
+abilities.Each.ifThenElse.examples.notJimsAncestors' =
+ use Text ==
+ relations =
+ [ ("John", "Jim")
+ , ("John", "Anna")
+ , ("Jim", "Bob")
+ , ("Jim", "Alice")
+ , ("Anna", "Eve")
+ , ("Anna", "Fred")
+ ]
+ people = ["John", "Anna", "Bob", "Alice", "Eve", "Fred"]
+ parents : Text ->{Each} Text
+ parents person =
+ (child, parent) = each relations
+ guard (child == person)
+ parent
+ ancestorOf : Text -> Text ->{Each} ()
+ ancestorOf descendant ancestor =
+ use Text !=
+ parent = parents descendant
+ when (parent != ancestor) do ancestorOf parent ancestor
+ notAncestors : Text ->{Each} Text
+ notAncestors person =
+ p = each people
+ Each.negate do ancestorOf person p
+ p
+ Each.toList do notAncestors "Jim"
+
+abilities.Each.interleave : '{g, Each} a -> '{g, Each} a ->{g, Each} a
+abilities.Each.interleave a b = match Each.split a with
+ None -> b()
+ Some (a, rest) -> Each.append (do a) do abilities.Each.interleave b rest
+
+abilities.Each.interleave.doc : Doc
+abilities.Each.interleave.doc =
+ use Each interleave
+ {{
+ Fair interleaving disjunction of two {type Each} computations. The
+ computation `` interleave x y `` produces the first result of `x`, then the
+ first result of `y`, then the second result of `x`, then the second result of
+ `y`, and so on.
+
+ If either side fails, the computation proceeds with the other side.
+
+ # Example
+
+ ```
+ Each.toList do interleave (do each [1, 2, 3]) do each [4, 5, 6, 7, 8]
+ ```
+
+ # See also
+
+ * {Each.append} - concatenates two computations.
+ * {interleaveMap} - interleaves the results of applying a nondeterministic
+ function to all results of a computation.
+ }}
+
+abilities.Each.interleaveMap : (a ->{g, Each} b) -> '{g, Each} a ->{g, Each} b
+abilities.Each.interleaveMap f m =
+ match Each.split m with
+ None -> Each.fail()
+ Some (a, rest) ->
+ Each.interleave (do f a) do abilities.Each.interleaveMap f rest
+
+abilities.Each.interleaveMap.doc : Doc
+abilities.Each.interleaveMap.doc =
+ use Each append fail toList
+ use Nat + isEven
+ {{
+ Fair interleaving conjunction of {type Each} computations. The computation ``
+ interleaveMap f xs `` produces the first result of `f x` for each `x` result
+ of `xs`, then the second result of `f x` for each result of `xs`, and so on.
+
+ If `xs` fails, the computation fails. If `f x` fails for any result `x` from
+ `xs`, the computation proceeds with the other results.
+
+ Thus, this works like a fair equivalent of {List.flatMap}.
+
+ # Examples
+
+ In this example, the inner calls to {interleaveMap} are interleaved by the
+ outer call.
+
+ ```
+ toList do
+ (do each [1, 2, 3])
+ |> interleaveMap
+ (x -> (do each [?a, ?b]) |> interleaveMap (y -> (x, y)))
+ ```
+
+ Compare with the following normal conjunction, where each result of `y` is
+ considered before backtracking to the next result of `x`:
+
+ ```
+ toList do
+ x = each [1, 2, 3]
+ y = each [?a, ?b]
+ (x, y)
+ ```
+
+ The unfairness of such conjunction can lead to a computation diverging, as
+ in this example:
+
+ @typecheck ```
+ odds : '{Each} Nat
+ odds = do
+ append (do 1) do
+ a = odds()
+ a + 2
+ observe do
+ n = each [0, 1]
+ x = odds() + n
+ if isEven x then x else fail()
+ ```
+
+ This diverges, even though there's an infinite number of answers that
+ satisfy the condition. The problem is that `!odds + 1` never gets a chance
+ to produce any results since `!odds + 0` produces infinite answers that
+ fail the condition.
+
+ We can fix this with fair conjunction via {interleaveMap}:
+
+ ```
+ odds : '{Each} Nat
+ odds = do
+ append (do 1) do
+ a = odds()
+ a + 2
+ observe do
+ ns = do each [0, 1]
+ x = interleaveMap (n -> odds() + n) ns
+ if isEven x then x else fail()
+ ```
+
+ # See also
+
+ * {Each.interleave} - fair disjunction of two computations
+ * {append} - unfair disjunction
+ }}
+
+abilities.Each.lazily.doc : Doc
+abilities.Each.lazily.doc =
+ {{
+ `` lazily s `` generates the values from the {type Stream} `s`.
+
+ ```
+ Each.toList do lazily (Stream.fromList ["a", "b", "c"])
+ ```
+ }}
+
+abilities.Each.left : Either a b ->{Each} a
+abilities.Each.left = cases
+ Left a -> lazily do emit a
+ Right _ -> Each.fail()
+
+abilities.Each.left.doc : Doc
+abilities.Each.left.doc =
+ use Each left toList
+ use Nat +
+ {{
+ Given an {type Either} value `x`, `` left x `` returns the value from `x` if
+ it is {Left}, or fails if the value is {Right}.
+
+ # Examples
+
+ ```
+ toList do
+ x = left (Left 1)
+ y = left (Right 2)
+ x + y
+ ```
+
+ ```
+ toList do
+ x = left (Left 1)
+ y = left (Left 2)
+ x + y
+ ```
+
+ # See also
+
+ * {Each.right}
+ * {each}
+ * {Each.fail}
+ }}
+
+abilities.Each.limit : Nat -> '{g, Each} a ->{g, Each} a
+abilities.Each.limit n a =
+ use Each fail
+ use Nat - >
+ go n a = match Each.split a with
+ None -> fail()
+ Some (a, rest) | n > 0 -> Each.append (do a) do go (n - 1) rest
+ Some (a, _) -> fail()
+ go n a
+
+abilities.Each.limit.doc : Doc
+abilities.Each.limit.doc =
+ {{
+ `` limit n c `` selects `n` results from the computation `c`, pruning all
+ other branches.
+
+ # Example
+
+ ```
+ Each.toList do limit 3 do each [1, 2, 3, 4, 5]
+ ```
+
+ # See also
+
+ * {once} - to get just one result.
+ }}
+
+abilities.Each.negate : '{g, Each} a ->{g, Each} ()
+abilities.Each.negate c = ifThenElse (do once c) (do Each.fail()) do ()
+
+abilities.Each.negate.doc : Doc
+abilities.Each.negate.doc =
+ use Each negate
+ use Nat + == >
+ {{
+ Generalized negation for the {type Each} ability, implementing "negation as
+ failure". The computation `` negate c `` succeeds by producing `` () `` when
+ `c` fails (i.e. produces no results) and fails if `c` produces any results.
+
+ # Example
+
+ This example finds odd prime numbers, by eliminating numbers that are
+ divisible by any other number:
+
+ ```
+ odds : '{Each} Nat
+ odds = do
+ Each.append (do 1) do
+ a = odds()
+ a + 2
+ Each.toList do
+ limit 10 do
+ n = odds()
+ guard (n > 1)
+ negate do
+ d = Each.range 2 n
+ guard (Nat.mod n d == 0)
+ n
+ ```
+
+ # See also
+
+ * {ifThenElse} - a more general function for handling success and failure
+ of a computation.
+ }}
+
+abilities.Each.observe : '{g, Each} a ->{g} Optional a
+abilities.Each.observe c =
+ h = cases
+ { emit a -> _ } -> Some a
+ { _ } -> None
+ handle Each.toStream c () with h
+
+abilities.Each.observe.doc : Doc
+abilities.Each.observe.doc =
+ {{
+ Observes the first result of a {type Each} computation, returning {None} if
+ the computation fails.
+
+ # Examples
+
+ ```
+ observe do each [1, 2, 3]
+ ```
+
+ ```
+ observe do each []
+ ```
+
+ # See also
+
+ * {once} - Limits an {type Each} computation to its first result.
+ }}
+
+abilities.Each.once : '{g, Each} a ->{g, Each} a
+abilities.Each.once a = match Each.split a with
+ None -> Each.fail()
+ Some (a, rest) -> each [a]
+
+abilities.Each.once.doc : Doc
+abilities.Each.once.doc =
+ use Each toList
+ use List +:
+ use Nat <=
+ {{
+ Selects a single result from the given computation, pruning all other
+ branches.
+
+ # Examples
+
+ {once} can be used to get the first result of a computation:
+
+ ```
+ toList do once do each [1, 2, 3]
+ ```
+
+ A more involved example is the following, which sorts a list by generating
+ all permutations and testing them:
+
+ ```
+ insert : a -> [a] ->{Each} [a]
+ insert e = cases
+ [] -> [e]
+ l@(h +: t) ->
+ Each.append (do e +: l) do
+ t' = insert e t
+ h +: t'
+ permutations : [a] ->{Each} [a]
+ permutations = cases
+ [] -> []
+ h +: t ->
+ tt = permutations t
+ insert h tt
+ sorted = cases
+ e1 +: (e2 +: r) -> e1 <= e2 && sorted (e2 +: r)
+ _ -> true
+ bogosort xs = once do
+ p = permutations xs
+ if sorted p then p else Each.fail()
+ toList do bogosort [5, 0, 3, 4, 0, 1]
+ ```
+
+ Since the list to be sorted may have duplicates, there may be several
+ permutations of the list that are all sorted. But since the difference
+ between them is not observable, we use {once} to stop as soon as we see one
+ sorted permutation.
+
+ # See also
+
+ * {limit} - to get the first `n` results instead of just the first one.
+ * {observe} - to run a computation until it produces one result.
+ }}
+
+abilities.Each.optionally : Optional a ->{Each} a
+abilities.Each.optionally = cases
+ None -> Each.fail()
+ Some a -> lazily do emit a
+
+abilities.Each.optionally.doc : Doc
+abilities.Each.optionally.doc =
+ use Nat +
+ {{
+ Given an {type Optional} value `x`, `` optionally x `` returns the value from
+ `x` if it is {Some}, or fails if the value is {None}.
+
+ `` optionally x `` is equivalent to ``each (Optional.toList x)``.
+
+ # Examples
+
+ ```
+ Each.toList do
+ x = optionally (Some 1)
+ y = optionally (Some 2)
+ x + y
+ ```
+
+ ```
+ Each.toList do
+ x = optionally (Some 1)
+ y = optionally None
+ x + y
+ ```
+
+ # See also
+
+ * {each}
+ * {Each.fail}
+ }}
+
+abilities.Each.range : Nat -> Nat ->{Each} Nat
+abilities.Each.range start stopExclusive =
+ lazily (Stream.range start stopExclusive)
+
+abilities.Each.range.doc : Doc
+abilities.Each.range.doc =
+ use Each range toList
+ {{
+ `` range i j `` generates the values from `i` up to but not including `j`.
+
+ ```
+ toList do range 2 8
+ ```
+
+ ```
+ toList do range 200 0
+ ```
+ }}
+
+abilities.Each.rangeClosed : Nat -> Nat ->{Each} Nat
+abilities.Each.rangeClosed start stopInclusive =
+ lazily (Stream.rangeClosed start stopInclusive)
+
+abilities.Each.rangeClosed.doc : Doc
+abilities.Each.rangeClosed.doc =
+ use Each rangeClosed
+ {{
+ `` rangeClosed start end `` generates the values from `start` to `end`
+ inclusive, using the {type Each} ability.
+
+ # Examples
+
+ ```
+ Each.toList do rangeClosed 1 5
+ ```
+ }}
+
+abilities.Each.repeat : Nat ->{Each} ()
+abilities.Each.repeat n =
+ lazily (Stream.range 0 n)
+ ()
+
+abilities.Each.repeat.doc : Doc
+abilities.Each.repeat.doc =
+ use Each repeat
+ {{
+ `` repeat n `` repeats `` () `` `n` times. It can be used to
+
+ ```
+ Each.toList do
+ repeat 4
+ "hi"
+ ```
+ }}
+
+abilities.Each.repeatForever : '{Each} ()
+abilities.Each.repeatForever = do lazily do forever do emit()
+
+abilities.Each.repeatForever.doc : Doc
+abilities.Each.repeatForever.doc =
+ {{
+ Repeats the continuation of the program forever. This is useful for
+ generating infinite sequences of values with {type Each}. Use with {limit} to
+ generate a finite number of values from an infinite sequence.
+
+ # Example
+
+ We can generate an infinite sequence of random numbers and then take the
+ first 10 values:
+
+ ```
+ splitmix 1 do
+ Each.toList do
+ limit 10 do
+ repeatForever()
+ Random.nat!
+ ```
+ }}
+
+abilities.Each.right : Either a b ->{Each} b
+abilities.Each.right = cases
+ Left _ -> Each.fail()
+ Right b -> lazily do emit b
+
+abilities.Each.right.doc : Doc
+abilities.Each.right.doc =
+ use Each right toList
+ use Nat +
+ {{
+ Given an {type Either} value `x`, `` right x `` returns the value from `x` if
+ it is {Right}, or fails if the value is {Left}.
+
+ # Examples
+
+ ```
+ toList do
+ x = right (Right 1)
+ y = right (Left "oops")
+ x + y
+ ```
+
+ ```
+ toList do
+ x = right (Right 1)
+ y = right (Right 2)
+ x + y
+ ```
+
+ # See also
+
+ * {Each.left}
+ * {each}
+ * {Each.fail}
+ }}
+
+abilities.Each.run : '{g, Each} a ->{g} ()
+abilities.Each.run g =
+ handle g()
+ with cases
+ { a } -> ()
+ { lazily s -> k } ->
+ Stream.foreach ignore (Stream.flatMap (a -> toStream! do k a) s)
+
+abilities.Each.run.doc : Doc
+abilities.Each.run.doc =
+ use Each run
+ use Nat +
+ use Store get
+ {{
+ `` run e `` ignores the elements of the {type Each} `e` and executes it just
+ for its effects.
+
+ # Example
+
+ ```
+ withInitialValue 0 do
+ run do
+ x = each [1, 2, 3]
+ Store.put (get + x)
+ get
+ ```
+ }}
+
+abilities.Each.split : '{g, Each} a ->{g} Optional (a, '{g, Each} a)
+abilities.Each.split c =
+ use Stream uncons
+ h :
+ '{Stream ('{Each, g} a)} ()
+ -> Request {Each} a
+ ->{g} Optional (a, '{g, Each} a)
+ h jq = cases
+ { a } ->
+ match uncons jq with
+ Left _ -> Some (a, Each.fail)
+ _ ->
+ Some
+ (a, do
+ x = lazily jq
+ x())
+ { lazily s -> k } ->
+ match (uncons s, uncons jq) with
+ (Left _, Left _) -> None
+ (Left _, Right (j, jq)) -> handle j() with h jq
+ (Right (v, vs), _) ->
+ conts = Stream.map (x -> do k x) vs
+ handle k v
+ with
+ h do
+ conts()
+ jq()
+ handle c() with h do ()
+
+abilities.Each.split.doc : Doc
+abilities.Each.split.doc =
+ use Each split
+ use Int >
+ {{
+ Probes a non-deterministic computation that uses the {type Each} ability,
+ returning the first result and a computation that generates the rest of the
+ results, or {None} if there are no results.
+
+ # Examples
+
+ In this code, `numbers` is a non-deterministic computation that generates
+ some integers. We use the {split} function to find the first positive
+ number, or return `` +0 `` if there isn't one:
+
+ ```
+ numbers : '{Each} Int
+ numbers = do each [-1, +0, +1, +2, +3, -2]
+ firstPositive : '{Each} Int -> Int
+ firstPositive numbers = match split numbers with
+ None -> +0
+ Some (x, moreNumbers) -> if x > +0 then x else firstPositive moreNumbers
+ firstPositive numbers
+ ```
+
+ In this example, we use {split} to interleave the results of one
+ computation with another:
+
+ @source{Each.interleave}
+ }}
+
+abilities.Each.toList : '{g, Each} a ->{g} [a]
+abilities.Each.toList a = Stream.toList do toStream! a
+
+abilities.Each.toList.doc : Doc
+abilities.Each.toList.doc =
+ {{
+ `` Each.toList e `` converts `e` back to a {type List}.
+
+ Also see {Each.toStream} and {toStream!}.
+ }}
+
+abilities.Each.toOptional : '{g, Each} a ->{g} Optional a
+abilities.Each.toOptional e =
+ Optional.map at1 (Either.toOptional (Stream.uncons (Each.toStream e)))
+
+abilities.Each.toOptional.doc : Doc
+abilities.Each.toOptional.doc =
+ use Each toOptional
+ {{
+ Return the first value yielded by an {type Each} computation, if any, or
+ {None} if the computation yields no values.
+
+ # Examples
+
+ ```
+ toOptional do each [1, 2, 3]
+ ```
+
+ ```
+ toOptional do each []
+ ```
+ }}
+
+abilities.Each.toStream : '{g, Each} a -> '{g, Stream a} ()
+abilities.Each.toStream e _ = toStream! e
+
+abilities.Each.toStream.doc : Doc
+abilities.Each.toStream.doc =
+ use Each toStream
+ {{
+ `` toStream e `` converts `e` to an unevaluated {type Stream}.
+
+ ```
+ (toStream do each [1, 2, 3]) |> Stream.toList
+ ```
+ }}
+
+abilities.Each.toStream! : '{g, Each} a ->{g, Stream a} ()
+abilities.Each.toStream! a =
+ handle a()
+ with cases
+ { a } -> emit a
+ { lazily s -> k } -> flatMap! (a -> abilities.Each.toStream! do k a) s
+
+abilities.Each.toStream!.doc : Doc
+abilities.Each.toStream!.doc =
+ {{
+ `` toStream! e `` converts `e` to a forced {type Stream}.
+
+ ```
+ Stream.toList do toStream! do each [1, 2, 3]
+ ```
+ }}
+
+abilities.Exception.abortOnException : '{g, Exception} a ->{g, Abort} a
+abilities.Exception.abortOnException action = match catch action with
+ Left e -> abort
+ Right a -> a
+
+abilities.Exception.abortOnException.doc : Doc
+abilities.Exception.abortOnException.doc =
+ {{
+ Runs an action, and if it throws an exception, calls {abort}.
+
+ # Examples
+
+ ```
+ toOptional! do abortOnException do Exception.raise (failure "oops" ())
+ ```
+
+ ```
+ toOptional! do abortOnException do 1
+ ```
+
+ # See also
+
+ * {catch} for a version of this that returns the {type Failure} instead of
+ aborting.
+ }}
+
+abilities.Exception.bracket :
+ '{g, Exception} r
+ -> (r ->{g, Exception} ())
+ -> (r ->{g, Exception} a)
+ ->{g, Exception} a
+abilities.Exception.bracket provision final run =
+ r = provision()
+ finally (do final r) do run r
+
+abilities.Exception.bracket.doc : Doc
+abilities.Exception.bracket.doc =
+ use Exception raise
+ use Nat +
+ {{
+ {bracket} is used to provision a resource, perform some action on it, and
+ release it, even if the action throws an {type Exception}.
+
+ `` bracket make onComplete op ``
+
+ The first argument, `make` is used for provisioning the resource. If `make`
+ is successful, the argument `op` is executed. Finally, `onComplete`, is
+ executed.
+
+ `onComplete` will be executed even if an exception is raised when evaluating
+ `op`.
+
+ {{
+ docCallout
+ (Some {{ 🚨 }})
+ {{
+ It is possible that `onComplete` never runs, if `op` or `make` use an
+ ability that can terminate the computation, such as {type Abort} or
+ {type Throw}. Using {bracket} with such computations may lead to resource
+ leaks.
+
+ It's best to convert those abilities to {type Exception} before calling
+ {bracket}, for example with {Abort.toException} and {Throw.toException}.
+ }} }}
+
+ # Examples
+
+ Some use cases for {bracket} include opening and closing a {type Socket}
+ for receiving data, or opening and closing a file to read from it without
+ leaving file handles open.
+
+ In the example below `onComplete` will be run, even after the function
+ using the resource raises an exception. The result of {bracket} is the
+ raised exception.
+
+ ```
+ catch do
+ mockRead : Text ->{Exception} Text
+ mockRead n =
+ if n === "BadInput" then raise (failure "Oh No" ()) else "Success"
+ mockCloseFile : Text ->{Exception} ()
+ mockCloseFile n = ()
+ mockOpenFile : '{Exception} Text
+ mockOpenFile = do "BadInput"
+ bracket mockOpenFile mockCloseFile mockRead
+ ```
+
+ If making the resource fails, the error will be immediately returned
+
+ ```
+ catch do
+ op : '{Exception} Nat
+ op = do raise (failure "Exception in body" ())
+ onComplete : '{Exception} ()
+ onComplete = do ()
+ make : '{Exception} ()
+ make = do raise (failure "Trouble making the resource" ())
+ bracket make onComplete op
+ ```
+
+ If the desired operation to run succeeds, the result of it will be returned
+ and the `onComplete` block will be executed.
+
+ ```
+ catch do
+ op : Nat ->{Exception} Nat
+ op n = if n === 0 then raise (failure "Oh No" ()) else n + 1
+ onComplete : Nat ->{Exception} ()
+ onComplete n = ()
+ make : '{Exception} Nat
+ make = do 5
+ bracket make onComplete op
+ ```
+ }}
+
+abilities.Exception.catch : '{g, Exception} a ->{g} Either Failure a
+abilities.Exception.catch ex =
+ handle ex()
+ with cases
+ { a } -> Right a
+ { Exception.raise f -> _ } -> Left f
+
+abilities.Exception.catch.doc : Doc
+abilities.Exception.catch.doc =
+ {{
+ {catch} tries to run an operation which potentially raises an exception. It
+ catches all exceptions, returning an {type Either} representing the failure
+ or success of executing the {type Exception} ability.
+
+ # Examples
+
+ ```
+ catch do Exception.raise (failure "number too big!" 42)
+ ```
+
+ ```
+ catch do "No exception raised"
+ ```
+ }}
+
+abilities.Exception.catchMany :
+ [Type] -> '{g, Exception} a ->{g, Exception} Either Failure a
+abilities.Exception.catchMany ls ex =
+ match catch ex with
+ Left f@(Failure t _ _)| Boolean.not (List.contains t ls) ->
+ Exception.raise f
+ r -> r
+
+abilities.Exception.catchMany.doc : Doc
+abilities.Exception.catchMany.doc =
+ use Exception raise
+ {{
+ Tries to run code which potentially raises an {type Exception}, transforming
+ it to an {type Either). If an exception is raised and contained in the target
+ list of types, {catchMany} returns a {Left} otherwise it reraises the error.
+
+ # Examples
+
+ {catchMany} wraps an exception found in the target list of exceptions in a
+ {Left}.
+
+ ```
+ unsafeRun! do
+ catchMany [typeLink TlsFailure] do
+ raise (Failure (typeLink TlsFailure) "Oh no" (Any ()))
+ ```
+
+ {catchMany} should reraise the exception when it is not contained in the
+ supplied list. We re-catch the raised exception using {catch} for
+ rendering.
+
+ ```
+ catch do
+ opFailure : '{Exception} a
+ opFailure = do raise (Failure (typeLink Generic) "Oh no" (Any ()))
+ catchMany [typeLink TlsFailure, typeLink IOFailure] opFailure
+ ```
+ }}
+
+abilities.Exception.catchOnly :
+ Type -> '{g, Exception} a ->{g, Exception} Either Failure a
+abilities.Exception.catchOnly tpe ex = match catch ex with
+ Left f@(Failure t _ _) | Boolean.not (t === tpe) -> Exception.raise f
+ r -> r
+
+abilities.Exception.catchOnly.doc : Doc
+abilities.Exception.catchOnly.doc =
+ use Exception raise
+ {{
+ Tries to run code which potentially raises an {type Exception}, transforming
+ it to an {type Either}. If an exception is raised, {catchOnly} catches only
+ the given failure type {Type} as a {Left}, otherwise it re-raises the
+ exception.
+
+ # Examples
+
+ {catchOnly} should wrap the type {type TlsFailure} in a {Left} when an
+ {type Exception} of that type is raised.
+
+ ```
+ unsafeRun! do
+ catchOnly (typeLink TlsFailure) do
+ raise (Failure (typeLink TlsFailure) "Oh no" (Any ()))
+ ```
+
+ Here's an example with no type match below. We're re-catching the raised
+ exception using {catch} for rendering.
+
+ ```
+ catch do
+ catchOnly (typeLink Generic) do
+ raise
+ (Failure
+ (typeLink TlsFailure) "Exception type did not match" (Any ()))
+ ```
+ }}
+
+abilities.Exception.doc : Doc
+abilities.Exception.doc =
+ use doc div
+ {{
+ {type Exception} is an ability used to represent when an exceptional state
+ has occurred. Its sole request constructor, {Exception.raise}, expects a
+ value of type {type Failure} to capture information about the error.
+
+ {catch} is a handler which translates the {type Exception} into a value of
+ type {type Either}.
+
+ @source{div}
+
+ ```
+ catch do div 1 0
+ ```
+
+ # Common functions
+
+ @signatures{reraise, bracket, finally, catchOnly, unsafeRun!}
+
+ # See also
+
+ * [Failure]({type Failure})
+ * [Generic.failure]({failure})
+ * [Either.toException]({reraise})
+ }}
+
+abilities.Exception.doc.div : Nat -> Nat ->{Exception} Nat
+abilities.Exception.doc.div a b =
+ use Nat / ==
+ if b == 0 then Exception.raise (failure "cannot divide by zero" b) else a / b
+
+abilities.Exception.failOnError : Text -> Either e a ->{Exception} a
+abilities.Exception.failOnError msg = cases
+ Left e -> Exception.raise (failure msg e)
+ Right a -> a
+
+abilities.Exception.failOnError.doc : Doc
+abilities.Exception.failOnError.doc =
+ {{
+ Fails with a {type Generic} {type Exception} containing the given {type Text}
+ message if the given {type Either} is a {Left} value.
+
+ # Example
+
+ ```
+ catch do failOnError "oops" (Left "something went wrong")
+ ```
+
+ ```
+ catch do failOnError "oops" (Right 1)
+ ```
+ }}
+
+abilities.Exception.finally :
+ '{g, Exception} () -> '{g, Exception} a ->{g, Exception} a
+abilities.Exception.finally end ex =
+ r = catch ex
+ end()
+ reraise r
+
+abilities.Exception.finally.doc : Doc
+abilities.Exception.finally.doc =
+ use Nat +
+ {{
+ `` finally onComplete op `` calls a computation, `op`, which may raise an
+ {type Exception}. After `op` is executed, `onComplete` is run, even if an
+ {type Exception} is raised when evaluating `op`.
+
+ {{
+ docCallout
+ (Some {{ 🚨 }})
+ {{
+ It is possible that `onComplete` never runs, if `op` makes use of an
+ ability that can terminate the computation, such as {type Abort} or
+ {type Throw}. Using {finally} with such computations may lead to resource
+ leaks.
+
+ It's best to convert those abilities to {type Exception} before calling
+ {finally}, for example with {Abort.toException} and {Throw.toException}.
+ }} }}
+
+ # Examples
+
+ An exeption is generated during the call to `op`
+
+ ```
+ catch do
+ op : Nat -> '{Exception} Nat
+ op n =
+ if n === 0 then
+ do Exception.raise (failure "Exception during operation" ())
+ else do n + 1
+ onComplete = do ()
+ finally onComplete (op 0)
+ ```
+
+ When no exception is raised:
+
+ ```
+ onComplete = do ()
+ unsafeRun! do finally onComplete do "NoExceptionRaised"
+ ```
+ }}
+
+abilities.Exception.Generic.doc : Doc
+abilities.Exception.Generic.doc =
+ use Exception raise
+ {{
+ A generic {type Failure} type that can be used to raise an {type Exception}
+ with a custom message.
+
+ # Example
+
+ ```
+ catch do
+ raise
+ (Failure
+ (typeLink Generic)
+ "Something went wrong"
+ (Any "This is what went wrong"))
+ ```
+
+ ```
+ catch do raise (failure "Something went wrong" ())
+ ```
+ }}
+
+abilities.Exception.Generic.failure : Text -> a -> Failure
+abilities.Exception.Generic.failure msg a =
+ Failure (typeLink Generic) msg (Any a)
+
+abilities.Exception.Generic.failure.doc : Doc
+abilities.Exception.Generic.failure.doc =
+ {{
+ Creates a {type Generic} {type Failure} with the given {type Text} message
+ and payload.
+
+ # Example
+
+ ```
+ catch do Exception.raise (failure "oops" "payload")
+ ```
+ }}
+
+abilities.Exception.hush : '{g, Exception} t ->{g} Optional t
+abilities.Exception.hush e = match catch e with
+ Left _ -> None
+ Right a -> Some a
+
+abilities.Exception.hush.doc : Doc
+abilities.Exception.hush.doc =
+ {{
+ Returns {None} if the given {type Exception} computation raises a { type
+ Failure } and {Some} of the result otherwise.
+
+ # Examples
+
+ ```
+ hush do ArrayFailure.raise "oops" ()
+ ```
+
+ ```
+ hush do "hello"
+ ```
+ }}
+
+abilities.Exception.onException :
+ (Failure ->{e} ()) -> '{g, Exception} a ->{e, g, Exception} a
+abilities.Exception.onException f g = match catch g with
+ Left e ->
+ f e
+ Exception.raise e
+ Right x -> x
+
+abilities.Exception.onException.doc : Doc
+abilities.Exception.onException.doc =
+ use Store put
+ use Text ++
+ {{
+ `` onException handler p `` runs `p`, and calls `handler` if an exception is
+ raised. The {type Failure} is passed to `handler`, and the exception is
+ reraised afterwards.
+
+ # Example
+
+ This example uses the {type Store} ability. On exception, it appends a
+ value to the {type Store} and reraises the exception:
+
+ ```
+ withInitialValue "" do
+ e = catch do
+ onException (_ -> Store.modify (x -> x ++ "💔")) do
+ put "💚"
+ Exception.raise (failure "❗️" "🪦")
+ put "💛"
+ (e, Store.get)
+ ```
+ }}
+
+abilities.Exception.orElse : '{f} a -> '{g, Exception} a ->{f, g} a
+abilities.Exception.orElse f g = match catch g with
+ Left _ -> f()
+ Right r -> r
+
+abilities.Exception.orElse.doc : Doc
+abilities.Exception.orElse.doc =
+ {{
+ If the second argument raises an exception when forced, this forces the first
+ argument and returns its result. Otherwise, returns the result of the second
+ argument.
+
+ # Example
+
+ ```
+ Exception.orElse (do 42) do raiseGeneric "Not found" ()
+ ```
+
+ # See also
+
+ * {catch} - to catch an exception.
+ }}
+
+abilities.Exception.raise.doc : Doc
+abilities.Exception.raise.doc =
+ {{
+ Raises an {type Exception} with the given {type Failure}.
+
+ # Example
+
+ ```
+ catch do Exception.raise (failure "oops" ())
+ ```
+ }}
+
+abilities.Exception.raiseFailure : Type -> Text -> a ->{Exception} x
+abilities.Exception.raiseFailure typ msg payload =
+ Exception.raise (Failure typ msg (Any payload))
+
+abilities.Exception.raiseFailure.doc : Doc
+abilities.Exception.raiseFailure.doc =
+ use Nat / ==
+ {{
+ Raises a {type Failure} with the given type, message, and payload, in the
+ {type Exception} ability.
+
+ # Example
+
+ ```
+ safeDiv x y =
+ if y == 0 then
+ Exception.raiseFailure
+ (typeLink ArithmeticException) "Divide by zero" (x, y)
+ else x / y
+ catch do safeDiv 1 0
+ ```
+ }}
+
+abilities.Exception.raiseGeneric : Text -> a ->{Exception} b
+abilities.Exception.raiseGeneric msg a =
+ Exception.raiseFailure (typeLink Generic) msg a
+
+abilities.Exception.raiseGeneric.doc : Doc
+abilities.Exception.raiseGeneric.doc =
+ {{
+ Raises a {type Generic} exception with the given message and value.
+
+ # Example
+
+ ```
+ catch do raiseGeneric "Not found" ()
+ ```
+ }}
+
+abilities.Exception.reraise.doc : Doc
+abilities.Exception.reraise.doc =
+ {{
+ Converts `` Left e `` to ``Exception.raise e``.
+
+ # Examples
+
+ ```
+ e = Left (failure "msg" 0)
+ (catch do reraise e) === e
+ ```
+
+ ```
+ r = Right "ok"
+ (catch do reraise r) === r
+ ```
+ }}
+
+abilities.Exception.unsafeRun! : '{g, Exception} a ->{g} a
+abilities.Exception.unsafeRun! e =
+ h : Request {Exception} a -> a
+ h = cases
+ { Exception.raise fail -> _ } -> bug fail
+ { a } -> a
+ handle e() with h
+
+abilities.Exception.unsafeRun!.doc : Doc
+abilities.Exception.unsafeRun!.doc =
+ {{
+ {unsafeRun!} is a handler which, if given an argument which raises an
+ exception, will stop the program with a call to the {bug} builtin.
+
+ It is an unsafe computation, meaning it will stop the runtime of your program
+ if an exception is raised, but it can be useful in circumstances where
+ referential transparency is not material.
+
+ # Examples
+
+ The following example will call the {bug} builtin:
+
+ ```
+ unsafeRun! do Exception.raise (failure "A failure occurred" ())
+ ```
+
+ If no exception is raised, the result of evaluating the expression is
+ returned.
+
+ ```
+ maybeRaiseException : '{Exception} Text
+ maybeRaiseException _ = "No Exception raised in the body of this function"
+ unsafeRun! maybeRaiseException
+ ```
+
+ # See also
+
+ {catch} is another {type Exception} ability handler which transforms the
+ raised exception into an {type Either}.
+ }}
+
+abilities.force : '{e} a ->{e} a
+abilities.force op = op()
+
+abilities.force.doc : Doc
+abilities.force.doc =
+ {{
+ `` force op `` forces a delayed computation `op``; it is equivalent to `!op`,
+ but {force} is a first-class value (unlike `!`).
+ }}
+
+abilities.forever : '{e} a ->{e} b
+abilities.forever op =
+ ignore op()
+ abilities.forever op
+
+abilities.forever.doc : Doc
+abilities.forever.doc =
+ {{
+ `` forever op `` performs an effectful computation `op` repeatedly forever,
+ never returning.
+ }}
+
+abilities.forever' : '{g} a -> '{g} b
+abilities.forever' a = do forever a
+
+abilities.forever'.doc : Doc
+abilities.forever'.doc =
+ {{
+ `` forever' op `` returns a delayed computation that when forced will run
+ `!op` repeatedly, forever.
+ }}
+
+abilities.Label.doc : Doc
+abilities.Label.doc =
+ use Nat +
+ use Random natIn
+ {{
+ Provides the ability to add labels to a test to help identify the values or
+ conditions that caused the test to fail.
+
+ # Label scopes
+
+ Labels can be nested within scopes. The scope of a label is the innermost
+ scope that contains it. When a label is added, it is added to the current
+ scope.
+
+ To create a new scope, use {labeled} with a {type Text} argument.
+
+ @signature{labeled}
+
+ The label is added to the current scope, and any labels added within the
+ scope are added to the same scope. When the scope ends, the labels are
+ removed. For example:
+
+ @typecheck ```
+ test.verify do
+ labeled "This will randomly fail" do
+ x = natIn 0 10
+ y = natIn 0 100
+ label "x" x
+ label "y" y
+ ensureEqual (x + y) 100
+ ```
+
+ In this example, the labels "x" and "y" are added to the scope "This will
+ randomly fail". If the test fails, the error message will include the
+ labels and their values, and the error message that caused the test to
+ fail, along with the scope in which the test failed:
+
+ ``` raw
+ 🚫 FAILED
+ This will randomly fail:
+ x: 3
+ y: 67
+ elements not equal
+ (70, 100)
+ ```
+
+ # Labeled values
+
+ The {label} function takes a {type Text} and a value of any type. The
+ {type Text} is the label, and the value is the information to be associated
+ with the label.
+
+ @signature{label}
+
+ The test failure report will only ever show the last value associated with
+ a label in a given scope (including the top-level, unnamed scope). This
+ avoids cluttering the report with information irrelevant to the failure.
+ }}
+
+abilities.Label.formatLabels : [(Text, Map Text Text)] -> Text
+abilities.Label.formatLabels labels =
+ use Text ++ ==
+ go ind acc = cases
+ rest :+ (label, m) ->
+ go
+ (ind ++ " ")
+ (Map.foldLeftWithKey
+ (a k v -> a ++ ind ++ " " ++ k ++ ": " ++ v ++ "\n")
+ (acc ++ ind ++ label ++ (if label == "" then "" else ":\n"))
+ m)
+ rest
+ [] -> acc
+ go "" "" labels
+
+abilities.Label.formatLabels.doc : Doc
+abilities.Label.formatLabels.doc =
+ {{
+ Formats a list of labels and their values as {type Text}. The labels are
+ indented according to their scope.
+
+ # Example
+
+ ```
+ formatLabels [("test", Map.fromList [("x", "10"), ("y", "20")])]
+ ```
+ }}
+
+abilities.Label.getLabels : '{g, Label} a ->{g} [(Text, Map Text Text)]
+abilities.Label.getLabels a = at1 (Label.run a)
+
+abilities.Label.getLabels.doc : Doc
+abilities.Label.getLabels.doc =
+ {{
+ Gets the labels from a {type Label} computation. The labels are returned as a
+ list of pairs of the scope names and the labels in each scope.
+ }}
+
+abilities.Label.label.doc : Doc
+abilities.Label.label.doc =
+ use Nat +
+ use Random natIn
+ {{
+ Provides the ability to add labels to a test to help identify the values or
+ conditions that caused the test to fail.
+
+ # Label scopes
+
+ Labels can be nested within scopes. The scope of a label is the innermost
+ scope that contains it. When a label is added, it is added to the current
+ scope.
+
+ To create a new scope, use {labeled} with a {type Text} argument.
+
+ @signature{labeled}
+
+ The label is added to the current scope, and any labels added within the
+ scope are added to the same scope. When the scope ends, the labels are
+ removed. For example:
+
+ @typecheck ```
+ verifyAndIgnore do
+ labeled "This will randomly fail" do
+ x = natIn 0 10
+ y = natIn 0 100
+ label "x" x
+ label "y" y
+ ensureEqual (x + y) 100
+ ```
+
+ In this example, the labels "x" and "y" are added to the scope "This will
+ randomly fail". If the test fails, the error message will include the
+ labels and their values, and the error message that caused the test to
+ fail, along with the scope in which the test failed:
+
+ ``` raw
+ 🚫 FAILED
+ This will randomly fail:
+ x: 3
+ y: 67
+ elements not equal
+ (70, 100)
+ ```
+
+ # Labeled values
+
+ The {label} function takes a {type Text} and a value of any type. The
+ {type Text} is the label, and the value is the information to be associated
+ with the label.
+
+ @signature{label}
+
+ The test failure report will only ever show the last value associated with
+ a label in a given scope (including the top-level, unnamed scope). This
+ avoids cluttering the report with information irrelevant to the failure.
+ }}
+
+abilities.Label.labeled : Text -> '{g} a ->{g, Label} a
+abilities.Label.labeled l a =
+ pushScope l
+ x = a()
+ popScope
+ x
+
+abilities.Label.labeled.doc : Doc
+abilities.Label.labeled.doc =
+ use Nat +
+ use Random natIn
+ {{
+ Wraps a computation in a new {type Label} scope with the given name. The
+ labels added within the scope are added to the same scope. When the scope
+ ends, the labels are removed.
+
+ @typecheck ```
+ test.verify do
+ labeled "This will randomly fail" do
+ x = natIn 0 10
+ y = natIn 0 100
+ label "x" x
+ label "y" y
+ ensureEqual (x + y) 100
+ ```
+
+ In this example, the labels "x" and "y" are added to the scope "This will
+ randomly fail". If the test fails, the error message will include the labels
+ and their values, and the error message that caused the test to fail, along
+ with the scope in which the test failed:
+
+ ``` raw
+ 🚫 FAILED
+ This will randomly fail:
+ x: 3
+ y: 67
+ elements not equal
+ (70, 100)
+ ```
+ }}
+
+abilities.Label.run : '{g, Label} r ->{g} ([(Text, Map Text Text)], r)
+abilities.Label.run x =
+ use List +:
+ use Map empty
+ h = cases
+ (scope, labels) +: scopes ->
+ cases
+ { label l v -> k } ->
+ f : Request Label r -> ([(Text, Map Text Text)], r)
+ f = h ((scope, Map.insert l (toDebugText v) labels) +: scopes)
+ handle k() with f
+ { pushScope l -> k } ->
+ handle k() with h ((l, empty) +: ((scope, labels) +: scopes))
+ { popScope -> k } ->
+ match scopes with
+ [] -> handle k() with h [("", empty)]
+ s +: ss -> handle k() with h scopes
+ { x } -> ((scope, labels) +: scopes, x)
+ _ -> bug "Label.run: no scopes"
+ handle x() with h [("", empty)]
+
+abilities.Label.run.doc : Doc
+abilities.Label.run.doc =
+ {{
+ Runs a computation with labels. The labels are collected as the computation
+ runs, and the result is a pair of the collected labels and the result of the
+ computation.
+ }}
+
+abilities.Label.runToText : '{g, Label} a ->{g} (Text, a)
+abilities.Label.runToText a = first formatLabels (Label.run a)
+
+abilities.Label.runToText.doc : Doc
+abilities.Label.runToText.doc =
+ {{
+ Runs a {type Label} computation and returns the labels as a formatted
+ {type Text} string along with the result of the computation.
+ }}
+
+abilities.Label.toText : '{g, Label} a ->{g} Text
+abilities.Label.toText a = at1 (runToText a)
+
+abilities.Label.toText.doc : Doc
+abilities.Label.toText.doc =
+ {{
+ Runs a {type Label} computation and returns the labels as a formatted
+ {type Text} string, dropping the result of the computation.
+ }}
+
+abilities.Random.boolean : '{Random} Boolean
+abilities.Random.boolean _ = Universal.gteq (Nat.popCount Random.nat()) 32
+
+abilities.Random.boolean.doc : Doc
+abilities.Random.boolean.doc =
+ use Random boolean
+ {{
+ Generates a random {type Boolean}.
+
+ # Examples
+
+ ```
+ lcg 3 boolean
+ ```
+
+ ```
+ lcg 93 do boolean() || boolean()
+ ```
+
+ ```
+ lcg 30 do List.replicate 7 boolean
+ ```
+ }}
+
+abilities.Random.bytes.base32Hex : '{Random} Bytes
+abilities.Random.bytes.base32Hex _ = Text.toUtf8 text.base32Hex()
+
+abilities.Random.bytes.base32Hex.doc : Doc
+abilities.Random.bytes.base32Hex.doc =
+ {{
+ Produces a byte string with the ASCII/UTF-8 bytes of a base 32 hex encoding
+ of another bytes value.
+ }}
+
+abilities.Random.bytes.doc : Doc
+abilities.Random.bytes.doc =
+ use Random bytes
+ {{
+ `` bytes n `` produces random {type Bytes} of length `n`.
+
+ # Examples
+
+ ```
+ lcg 3290 do bytes 24
+ ```
+
+ ```
+ lcg 19 do bytes 0
+ ```
+ }}
+
+abilities.Random.char.ascii.control : '{Random} Char
+abilities.Random.char.ascii.control = do interval ?\0 ?
+
+abilities.Random.char.ascii.control.doc : Doc
+abilities.Random.char.ascii.control.doc =
+ {{
+ A random character in the ASCII control character range.
+
+ # Example
+
+ ```
+ splitmix 42 do Text.toUtf8 (ofChars ascii.control 4)
+ ```
+ }}
+
+test> abilities.Random.char.ascii.control.tests = test.verify do
+ Each.run do
+ _ = Each.repeat 100
+ isControl ascii.control()
+
+abilities.Random.char.ascii.lower : '{Random} Char
+abilities.Random.char.ascii.lower = do interval ?a ?z
+
+abilities.Random.char.ascii.lower.doc : Doc
+abilities.Random.char.ascii.lower.doc =
+ {{
+ A random character in the ASCII lowercase character range, `a` to `z`.
+
+ # Example
+
+ ```
+ splitmix 42 do ofChars ascii.lower 4
+ ```
+ }}
+
+test> abilities.Random.char.ascii.lower.tests = test.verify do
+ Each.run do
+ _ = Each.repeat 100
+ isLower ascii.lower()
+
+abilities.Random.char.ascii.printable : '{Random} Char
+abilities.Random.char.ascii.printable = do interval ?\s ?
+
+abilities.Random.char.ascii.printable.doc : Doc
+abilities.Random.char.ascii.printable.doc =
+ use ascii printable
+ {{
+ A random character in the ASCII printable character range.
+
+ # Examples
+
+ ```
+ printable |> splitmix 42
+ ```
+
+ ```
+ (do ofChars printable 4) |> splitmix 42
+ ```
+ }}
+
+test> abilities.Random.char.ascii.printable.tests = test.verify do
+ Each.run do
+ _ = Each.repeat 100
+ isPrint ascii.printable()
+
+abilities.Random.char.ascii.upper : '{Random} Char
+abilities.Random.char.ascii.upper = do interval ?A ?Z
+
+abilities.Random.char.ascii.upper.doc : Doc
+abilities.Random.char.ascii.upper.doc =
+ {{
+ A random character in the ASCII uppercase character range, `A` to `Z`.
+
+ # Example
+
+ ```
+ splitmix 42 do ofChars ascii.upper 4
+ ```
+ }}
+
+test> abilities.Random.char.ascii.upper.tests = test.verify do
+ Each.run do
+ _ = Each.repeat 100
+ isUpper ascii.upper()
+
+abilities.Random.char.base32Hex : '{Random} Char
+abilities.Random.char.base32Hex _ =
+ Random.either (do interval ?0 ?9) do interval ?a ?v
+
+abilities.Random.char.base32Hex.doc : Doc
+abilities.Random.char.base32Hex.doc =
+ use char base32Hex
+ {{
+ Produces a valid base32 hex character.
+
+ # Examples
+
+ ```
+ lcg 4096 base32Hex
+ ```
+
+ ```
+ lcg 2517 base32Hex
+ ```
+ }}
+
+abilities.Random.char.digit : '{Random} Char
+abilities.Random.char.digit _ = interval ?0 ?9
+
+abilities.Random.char.digit.doc : Doc
+abilities.Random.char.digit.doc =
+ {{ Produces a random decimal digit character }}
+
+abilities.Random.char.hex : '{Random} Char
+abilities.Random.char.hex _ =
+ Random.either (do interval ?0 ?9) do interval ?a ?f
+
+abilities.Random.char.hex.doc : Doc
+abilities.Random.char.hex.doc =
+ {{ Produces a random hexadecimal digit character }}
+
+abilities.Random.char.interval : Char -> Char ->{Random} Char
+abilities.Random.char.interval lo hi =
+ use Char toNat
+ use Nat +
+ fromNat.impl (Random.natIn (toNat lo) (toNat hi + 1))
+
+abilities.Random.char.interval.doc : Doc
+abilities.Random.char.interval.doc =
+ {{
+ Produces a random character in the closed interval between two given
+ characters.
+
+ # Examples
+
+ ```
+ lcg 4096 do interval ?a ?z
+ ```
+
+ ```
+ lcg 2517 do interval ?a ?z
+ ```
+ }}
+
+abilities.Random.char.unicode : '{Random} Char
+abilities.Random.char.unicode =
+ range min maxInclusive =
+ use Nat -
+ maxExclusive = Nat.increment maxInclusive
+ (maxExclusive - min, do fromNat.impl (Random.natIn min maxExclusive))
+ Random.weighted [range 0 55295, range 57344 65533, range 65536 1114111]
+
+abilities.Random.char.unicode.doc : Doc
+abilities.Random.char.unicode.doc =
+ {{
+ A random valid Unicode character.
+
+ # Example
+
+ ```
+ splitmix 42 do ofChars unicode 8
+ ```
+ }}
+
+abilities.Random.doc : Doc
+abilities.Random.doc =
+ use List replicate
+ use Nat * range
+ use Random boolean natIn run
+ {{
+ An ability for generating pseudorandom values.
+
+ # Sample usage
+
+ ```
+ splitmix 1 do replicate 5 boolean
+ ```
+
+ This example uses the {splitmix} algorithm, initialized from an arbitrarily
+ chosen seed value, ``1``. This provides random values to the {boolean}
+ generator, and {replicate} uses that generator 5 times to produce a list of
+ 5 random booleans.
+
+ If you'd rather not select a seed and you're in a context where you're able
+ to use {type IO}, you can use {run} instead of {splitmix}. This will use
+ the system's secure random number generator to seed the {splitmix}
+ algorithm, and then use that to generate values:
+
+ @typecheck ```
+ run do replicate 5 boolean
+ ```
+
+ # Generating random values
+
+ Generate random {type Bytes} of a given length:
+
+ @signature{Random.bytes}
+
+ Generate a random {type Boolean}:
+
+ @signature{boolean}
+
+ ## Random numbers
+
+ Generate a random {type Nat} value in the range 0 to {maxNat}:
+
+ @signature{Random.nat}
+
+ Generate a random {type Int} value in the range {minInt} to {maxInt}:
+
+ @signature{Random.int}
+
+ Generate a random {type Nat} in a given range:
+
+ @signature{natIn}
+
+ Generate a random {type Int} in a given range:
+
+ @signature{intIn}
+
+ Generate a uniformly distributed {type Float} from `` 0.0 `` up to but
+ not including `` 1.0 `` :
+
+ @signature{Random.float}
+
+ ## Text and characters
+
+ Generate a random [Base32Hex](https://en.wikipedia.org/wiki/Base32)
+ string:
+
+ @signature{text.base32Hex}
+
+ Generate a random [Base32Hex](https://en.wikipedia.org/wiki/Base32)
+ character:
+
+ @signature{char.base32Hex}
+
+ Generate a random decimal digit:
+
+ @signature{char.digit}
+
+ Generate a random hexadecimal digit:
+
+ @signature{hex}
+
+ Generate a random {type Char} in a given range:
+
+ @signature{interval}
+
+ # Combinators on generators
+
+ Generate a random value from a list of possible values:
+
+ @signature{Random.oneOf}
+
+ Generate a {type List} of values of a given length from a generator:
+
+ @signature{Random.listOf}
+
+ Generate a value from one of two generators with equal probability:
+
+ @signature{Random.either}
+
+ Shuffle a {type List} of values:
+
+ @signature{shuffle}
+
+ # Splitting the generator
+
+ Create a new supply of {type Random} values from an existing one:
+
+ @signature{Random.split}
+
+ This is useful for concurrent use: each thread can be given its own supply
+ of random numbers.
+
+ # Handlers
+
+ {splitmix} is a prototypical handler of this ability that uses the
+ [SplitMix](https://gee.cs.oswego.edu/dl/papers/oopsla14.pdf) algorithm to
+ generate pseudorandom values. It is seeded with a value provided by the
+ user. The generator is not cryptographically secure, but it's fast and has
+ good statistical properties.
+
+ ## Examples
+
+ {{ docEval (_ -> splitmix 853322303 do replicate 10 do natIn 0 100) }}
+
+ {{ docEval (_ -> splitmix 1 do shuffle (range 1 11)) }}
+
+ {run} is a handler that uses {type IO} to generate the initial seed
+ value, and then uses {splitmix} to generate values. This is useful when
+ you have access to the {type IO} ability and don't want to choose a seed
+ value yourself.
+
+ {lcg} is a simpler handler of this ability that uses a linear
+ congruential generator to generate pseudorandom values. It is seeded
+ with a value provided by the user. The generator is not
+ cryptographically secure and prone to predictability in the output, but
+ it's very simple.
+
+ ## Examples
+
+ {{ docEval (_ -> lcg 853322303 do replicate 10 boolean) }}
+
+ {{ docEval (_ -> lcg 1 do List.map (i -> natIn 0 i * 100) (range 1 11))
+ }}
+ }}
+
+abilities.Random.either : '{Random} r -> '{Random} r ->{Random} r
+abilities.Random.either l r = if Random.boolean() then l() else r()
+
+abilities.Random.either.doc : Doc
+abilities.Random.either.doc =
+ use Random either
+ {{
+ Randomly chooses between one of two other random generators.
+
+ # Examples
+
+ ```
+ lcg 2048 do either (do 0) do 1
+ ```
+
+ ```
+ lcg 2517 do either (do 0) do 1
+ ```
+ }}
+
+abilities.Random.exponential : Float ->{Random} Float
+abilities.Random.exponential lambda =
+ use Float - /
+ Float.negate (log (1.0 - Random.float())) / lambda
+
+abilities.Random.exponential.doc : Doc
+abilities.Random.exponential.doc =
+ use Float ==
+ use List replicate
+ {{
+ `` exponential lambda `` generates an
+ [exponentially distributed](https://en.wikipedia.org/wiki/Exponential_distribution)
+ random {type Float} using `lambda` as the rate parameter.
+
+ Most values drawn from the exponential distribution will be close to 0,
+ dropping off exponentially with distance from 0. The higher the "rate"
+ parameter, `lambda`, the quicker this dropoff.
+
+ For instance, in ``exponential 0.5``, the mean value will be
+ ``1.0 / 0.5 == 2.0``:
+
+ ```
+ splitmix 1 do replicate 10 do exponential 0.5
+ ```
+
+ In ``exponential 2.0``, the mean value will be ``1.0 Float./ 2.0``:
+
+ ```
+ splitmix 1 do replicate 10 do exponential 2.0
+ ```
+ }}
+
+abilities.Random.float : '{Random} Float
+abilities.Random.float _ =
+ use Float *
+ Float.fromNat (Nat.shiftRight Random.nat! 11) * ulp 0.5
+
+abilities.Random.float.doc : Doc
+abilities.Random.float.doc =
+ {{
+ Generate a uniformly distributed random {type Float} from 0 up to but not
+ including 1.
+
+ ```
+ splitmix 1 do List.replicate 10 Random.float
+ ```
+ }}
+
+abilities.Random.functionOf : '{g, Random} b ->{Random} (∀ a. a ->{g} b)
+abilities.Random.functionOf valueGen =
+ rngSeed = Random.nat!
+ input ->
+ let
+ inputSeed =
+ hash Blake2b_256 input |> decodeNat64be
+ |> getOrBug "Blake2b_256 returned fewer than 64 bits"
+ |> at1
+ seed = Nat.xor rngSeed inputSeed
+ splitmix seed valueGen
+
+abilities.Random.functionOf.doc : Doc
+abilities.Random.functionOf.doc =
+ use GUID new toBase16
+ use Text ++
+ {{
+ `` functionOf valueGen `` generates a function that returns random but
+ deterministic values. The pool of possible return values is determined by
+ `valueGen`.
+
+ The {type Random} effect occurs when the __function__ is generated as opposed
+ to when an input is applied to the function. Therefore the output of the
+ function is deterministic with respect a given input argument (see examples
+ below).
+
+ # Examples
+
+ ```
+ (userGUID1, userGUID2) = splitmix 42 do (functionOf new, functionOf new)
+ [ "Grace GUID 1: " ++ toBase16 (userGUID1 "Grace")
+ , "Grace GUID 2: " ++ toBase16 (userGUID2 "Grace")
+ , "Grace GUID 1 (again): " ++ toBase16 (userGUID1 "Grace")
+ , "Ada GUID 1: " ++ toBase16 (userGUID1 "Ada")
+ , "Ada GUID 2: " ++ toBase16 (userGUID2 "Ada")
+ , "Alan GUID 1: " ++ toBase16 (userGUID1 "Alan")
+ , "Alan GUID 2: " ++ toBase16 (userGUID2 "Alan")
+ ]
+ ```
+ }}
+
+abilities.Random.impl.bytesFromNats :
+ '{g, Random} Nat -> Nat ->{g, Random} Bytes
+abilities.Random.impl.bytesFromNats nat n =
+ use Bytes ++
+ go acc =
+ if Universal.gteq (Bytes.size acc) n then Bytes.take n acc
+ else go (acc ++ toBytesLittleEndian nat())
+ go Bytes.empty
+
+abilities.Random.impl.bytesFromNats.doc : Doc
+abilities.Random.impl.bytesFromNats.doc =
+ {{
+ A default implementation of {Random.bytes} in terms of {Random.nat}.
+
+ This is useful for generators that don't have a more direct way of producing
+ random bytes. See usage in {lcg.handler}.
+ }}
+
+abilities.Random.int : '{Random} Int
+abilities.Random.int = do Nat.toInt Random.nat!
+
+abilities.Random.int.doc : Doc
+abilities.Random.int.doc =
+ {{
+ Generates a random {type Int}.
+
+ # Example
+
+ ```
+ splitmix 0 Random.int
+ ```
+ }}
+
+abilities.Random.intIn : Int -> Int ->{Random} Int
+abilities.Random.intIn start stopExclusive =
+ use Int >= abs toRepresentation
+ use Nat mod
+ if Universal.lt start stopExclusive then
+ startPositive = start >= +0
+ stopPositive = stopExclusive >= +0
+ range =
+ if startPositive then toRepresentation (stopExclusive Int.- start)
+ else
+ if stopPositive then toRepresentation stopExclusive Nat.+ abs start
+ else abs start Nat.- abs stopExclusive
+ maxAcceptableValue = maxNat Nat.- mod maxNat range
+ loop =
+ do
+ use Int +
+ use Nat - < > toInt
+ rngOutput = Random.nat()
+ if rngOutput > maxAcceptableValue then loop()
+ else
+ n = mod rngOutput range
+ if n < 4294967296 then start + toInt n
+ else start + +4294967296 + toInt (n - 4294967296)
+ loop()
+ else bug ("Random.intIn start must be < stop", start, stopExclusive)
+
+abilities.Random.intIn.doc : Doc
+abilities.Random.intIn.doc =
+ {{
+ Generates a random {type Int} in the given range, exclusive of the upper
+ bound. The upper bound must be greater than the lower bound.
+
+ Every value in the range has an equal chance of being generated.
+
+ # Example
+
+ ```
+ splitmix 1 do intIn +0 +10
+ ```
+ }}
+
+test> abilities.Random.intIn.tests.ex1 : [Result]
+abilities.Random.intIn.tests.ex1 =
+ use List all replicate
+ check
+ (lcg 99 do
+ ok start stop n = Universal.gteq n start && Universal.lt n stop
+ all (ok -100 +100) (replicate 100 do intIn -100 +100)
+ && all (ok -10 +10) (replicate 100 do intIn -10 +10)
+ && all (ok -1000 +1000) (replicate 100 do intIn -1000 +1000)
+ && all (ok -9999 +9999) (replicate 100 do intIn -9999 +9999))
+
+test> abilities.Random.intIn.tests.fairness =
+ use Float <= fromNat
+ use Nat toInt
+ check
+ (splitmix 99 do
+ bins = 10
+ samples = 100000
+ ignore "21.666 is the critical value for Chi-Squared at 1% significance"
+ critical = 21.666
+ bag =
+ Bag.fromList
+ (List.replicate samples do
+ intIn (Int.negate (toInt (bins Nat./ 2))) (toInt (bins Nat./ 2)))
+ expected = fromNat samples Float./ fromNat bins
+ chiSquared =
+ Float.sum
+ (List.map
+ (cases
+ (gen, observed) ->
+ Float.pow (fromNat observed Float.- expected) 2.0
+ Float./ expected)
+ (Bag.occurrenceList bag))
+ freedom = bins Nat.- 1
+ chiSquared <= critical)
+
+abilities.Random.lcg : Nat -> '{g, Random} t ->{g} t
+abilities.Random.lcg seed r = handle r() with lcg.handler seed
+
+abilities.Random.lcg.doc : Doc
+abilities.Random.lcg.doc =
+ use List replicate
+ use Random natIn
+ {{
+ `` lcg seed r `` provides random numbers to `r` using a
+ [linear congruential random number generator](https://en.wikipedia.org/wiki/Linear_congruential_generator).
+
+ The `seed` parameter can be any {type Nat}. Using different seeds will
+ produce different streams of random numbers.
+
+ # Examples
+
+ ```
+ lcg 0 do replicate 10 do natIn 1 100
+ ```
+
+ ```
+ lcg 939394837 do replicate 10 do natIn 1 100
+ ```
+
+ # Implementation notes
+
+ This implementation uses the MMIX choice of parameters for `modulus`, `a`,
+ and `c`
+ [discussed here](https://en.wikipedia.org/wiki/Linear_congruential_generator).
+
+ Other generators can be patterned after {lcg.handler}.
+ }}
+
+abilities.Random.lcg.handler : Nat -> Request {Random} a -> a
+abilities.Random.lcg.handler =
+ use Nat * + shiftRight
+ c = 11
+ a = 25214903917
+ mask48 = 281474976710655
+ update seed = Nat.and (a * seed + c) mask48
+ go : Nat -> Request {Random} x -> x
+ go seed = cases
+ { a } -> a
+ { Random.nat! -> resume } ->
+ seed1 = update seed
+ part1 = shiftRight seed1 16
+ seed2 = update seed1
+ part2 = shiftRight seed2 16
+ full64 = Nat.or (Nat.shiftLeft part1 32) part2
+ handle resume full64 with go seed2
+ { Random.bytes n -> resume } ->
+ handle resume (bytesFromNats Random.nat n) with go seed
+ { split! -> resume } ->
+ seed' = finish (Murmur.add seed initialSeed)
+ seed'' = update seed'
+ handle resume (r -> (handle r() with go seed')) with go seed''
+ go
+
+abilities.Random.lcg.handler.doc : Doc
+abilities.Random.lcg.handler.doc =
+ {{
+ Implementation detail of {lcg}.
+
+ Other {type Random} handlers can be patterned after this. You can use
+ different static parameters (for `a` and/or `c`), state (expressed as handler
+ parameter(s) to the `go` function), and/or a different update rule.
+ }}
+
+abilities.Random.listOf : '{g, Random} a -> '{g, Random} Nat ->{g, Random} [a]
+abilities.Random.listOf gen n = Each.toList do
+ Each.repeat n()
+ gen()
+
+abilities.Random.listOf.doc : Doc
+abilities.Random.listOf.doc =
+ use Random listOf natIn
+ use fromNat impl
+ {{
+ `` listOf gen n `` returns a list of random values generated by the generator
+ `gen`, where the length of the list is generated by the generator `n`.
+
+ # Examples
+
+ ```
+ lcg 1 do listOf (do natIn 1 10) do 10
+ ```
+
+ ```
+ lcg 1 do
+ listOf
+ (do
+ fromCharList
+ (listOf (do interval (impl 127813) (impl 127832)) do natIn 0 7))
+ do 5
+ ```
+ }}
+
+abilities.Random.mapOf :
+ '{g1, Random} k
+ -> '{g2, Random} v
+ -> '{g1, g2, Random} Nat
+ ->{g1, g2, Random} Map k v
+abilities.Random.mapOf genKey genValue =
+ (Random.listOf do (genKey(), genValue())) >> Map.fromList
+
+abilities.Random.mapOf.doc : Doc
+abilities.Random.mapOf.doc =
+ use Map toList
+ use Random mapOf
+ {{
+ `` mapOf keyGen valueGen n `` returns a {type Map} with entries generated by
+ the provided key and value generators. The number of entries in the map is
+ generated by the generator `n`.
+
+ # Examples
+
+ ```
+ splitmix 42 do
+ (mapOf (do intIn -9 +9) Random.float do Random.natIn 0 10) |> toList
+ ```
+
+ ```
+ splitmix 42 do (mapOf text.base32Hex Random.boolean do 4) |> toList
+ ```
+ }}
+
+abilities.Random.nat : '{Random} Nat
+abilities.Random.nat = do Random.nat!
+
+abilities.Random.nat.doc : Doc
+abilities.Random.nat.doc =
+ {{ Generate a random {type Nat} between `` 0 `` and {maxNat} inclusive. }}
+
+abilities.Random.nat.natsWithSum : Nat -> Nat ->{Random} [Nat]
+abilities.Random.nat.natsWithSum sum count =
+ use List :+
+ use Nat - ==
+ if count == 0 then
+ if sum == 0 then []
+ else bug "count must be greater than 0 if sum is greater than 0"
+ else
+ upperExclusive = if sum === maxNat then maxNat else Nat.increment sum
+ intervals = cases
+ (acc, prev), current ->
+ interval = current - prev
+ (acc :+ interval, current)
+ (fill' (Nat.decrement count) do Random.natIn 0 upperExclusive) |> List.sort
+ |> List.foldLeft intervals ([], 0)
+ |> (cases (acc, last) -> acc :+ (sum - last))
+
+abilities.Random.nat.natsWithSum.doc : Doc
+abilities.Random.nat.natsWithSum.doc =
+ {{
+ `` natsWithSum sum count `` returns a list of `count` uniformly-distributed
+ natural numbers whose sum is `sum`.
+
+ If `count` is 0, `sum` must be 0 or else this will result in a runtime {bug}.
+
+ # Examples
+
+ ```
+ splitmix 13 do natsWithSum 20 4
+ ```
+
+ ```
+ splitmix 42 do natsWithSum 0 4
+ ```
+
+ ```
+ splitmix 42 do natsWithSum maxNat 2
+ ```
+
+ # Implementation details
+
+ Below is a description of how {natsWithSum} will generate 4 numbers that
+ sum to `20` in the example of ``natsWithSum 20 4``:
+
+ * Generate `count - 1` (3) uniformly-distributed natural numbers between 0
+ and `sum` (20). In this example we generate ``[8, 10, 16]``.
+ * Sort the numbers. They happened to be sorted in this example!
+ * Calculate the difference between each number and the previous number.
+ This interval size becomes the number in the output. Use 0 as the
+ starting point and `sum` (20) as the ending point. ``[8, 2, 6, 4]``.
+ * Done! `` [8, 2, 6, 4] `` are 4 numbers that sum to `20`.
+
+ ``` raw
+ |--------|--|------|----|
+ generated numbers: 0 8 10 16 20
+ \______/ \/ \____/ \__/
+ intervals: 8 2 6 4
+ ```
+ }}
+
+test> abilities.Random.nat.natsWithSum.tests = test.verify do
+ use Each repeat run
+ use Random natIn
+ check sum count =
+ use Nat ==
+ nats = natsWithSum sum count
+ ensuring do List.size nats == count
+ ensuring do Nat.sum nats == sum
+ run do
+ repeat 10
+ sum = 0
+ count = natIn 0 101
+ check sum count
+ run do
+ repeat 100
+ sum = Random.nat!
+ count = natIn 1 101
+ check sum count
+
+abilities.Random.natIn : Nat -> Nat ->{Random} Nat
+abilities.Random.natIn start stopExclusive =
+ use Nat - mod
+ if Universal.lt start stopExclusive then
+ range = stopExclusive - start
+ maxAcceptableValue = maxNat - mod maxNat range
+ loop =
+ do
+ use Nat + >
+ rngOutput = Random.nat()
+ if rngOutput > maxAcceptableValue then loop()
+ else start + mod rngOutput range
+ loop()
+ else bug ("Random.natIn start must be < stop", start, stopExclusive)
+
+abilities.Random.natIn.doc : Doc
+abilities.Random.natIn.doc =
+ use Random natIn
+ {{
+ `` natIn i j `` generates a {type Nat} between `i` and `j`, not including
+ `j`.
+
+ If `j` is less than or equal to `i`, throws an error.
+
+ # Examples
+
+ ```
+ lcg 384 do List.replicate 5 do natIn 5 15
+ ```
+
+ Here, the upper bound is less than the lower bound, resulting in an error:
+
+ ```
+ lcg 37 do natIn 8 3
+ ```
+ }}
+
+test> abilities.Random.natIn.tests.ex1 : [Result]
+abilities.Random.natIn.tests.ex1 =
+ use List all replicate
+ use Random natIn
+ check
+ (lcg 99 do
+ ok start stop n = Universal.gteq n start && Universal.lt n stop
+ all (ok 0 100) (replicate 100 do natIn 0 100)
+ && all (ok 1 10) (replicate 100 do natIn 1 10)
+ && all (ok 100 1000) (replicate 100 do natIn 100 1000)
+ && all (ok 1000 9999) (replicate 100 do natIn 1000 9999))
+
+test> abilities.Random.natIn.tests.fairness =
+ use Float / <= fromNat
+ check
+ (splitmix 99 do
+ bins = 10
+ samples = 100000
+ ignore "21.666 is the critical value for Chi-Squared at 1% significance"
+ critical = 21.666
+ bag = Bag.fromList (List.replicate samples do Random.natIn 0 bins)
+ expected = fromNat samples / fromNat bins
+ chiSquared =
+ Float.sum
+ (List.map
+ (cases
+ (gen, observed) ->
+ Float.pow (fromNat observed Float.- expected) 2.0 / expected)
+ (Bag.occurrenceList bag))
+ freedom = bins Nat.- 1
+ chiSquared <= critical)
+
+abilities.Random.oneOf : [a] ->{Random} a
+abilities.Random.oneOf l =
+ i = Random.natIn 0 (List.size l)
+ match List.at i l with
+ None -> bug "Random.oneOf: impossible"
+ Some x -> x
+
+abilities.Random.oneOf.doc : Doc
+abilities.Random.oneOf.doc =
+ use Random oneOf
+ {{
+ Picks a random value from a list. Assumes that the list is not empty, so an
+ empty list will cause a runtime exception.
+
+ # Examples
+
+ ```
+ lcg 4096 do oneOf [0, 3, 5, 7]
+ ```
+
+ ```
+ lcg 2517 do oneOf [?x, ?y, ?z]
+ ```
+
+ ```
+ lcg 128 do oneOf [char.digit, hex] ()
+ ```
+
+ # See also
+
+ {oneOfNonempty} ensures the list is not empty using the type system.
+ }}
+
+abilities.Random.oneOfNonempty : List.Nonempty a ->{Random} a
+abilities.Random.oneOfNonempty as =
+ n = Random.natIn 0 (List.Nonempty.size as)
+ Abort.toBug do List.at! n (List.Nonempty.toList as)
+
+abilities.Random.oneOfNonempty.doc : Doc
+abilities.Random.oneOfNonempty.doc =
+ {{
+ Picks a random value from a {type List.Nonempty}.
+
+ # Examples
+
+ ```
+ lcg 4096 do oneOfNonempty (0 +| [3, 5, 7])
+ ```
+
+ ```
+ lcg 2517 do oneOfNonempty (?x +| [?y, ?z])
+ ```
+
+ ```
+ lcg 128 do oneOfNonempty (char.digit +| [hex]) ()
+ ```
+
+ # See also
+
+ {Random.oneOf} takes a {type List} and assumes it is not empty.
+ }}
+
+abilities.Random.optional : '{g, Random} a ->{g} '{g, Random} Optional a
+abilities.Random.optional gen =
+ Random.weighted [(9, gen >> Some), (1, do None)]
+
+abilities.Random.optional.doc : Doc
+abilities.Random.optional.doc =
+ use Random optional
+ {{
+ `` optional gen `` returns a generator that will sometimes product {None} and
+ sometimes produce {Some} values wrapping a value generated by the provided
+ `gen`.
+
+ # Example
+
+ ```
+ optionGen = optional do Random.natIn 0 100
+ splitmix 17 do Random.listOf optionGen do 20
+ ```
+
+ # Related
+
+ See {optional!} for a variant whose result is not delayed.
+ }}
+
+abilities.Random.optional! : '{g, Random} a ->{g, Random} Optional a
+abilities.Random.optional! = Random.optional >> force
+
+abilities.Random.optional!.doc : Doc
+abilities.Random.optional!.doc =
+ {{
+ A variant of {Random.optional} that eagerly returns a result.
+
+ # Example
+
+ ```
+ splitmix 17 do Random.listOf (do optional! do Random.natIn 0 100) do 20
+ ```
+ }}
+
+abilities.Random.RNG.doc : Doc
+abilities.Random.RNG.doc =
+ {{
+ A random number generator as a value, rather than as a handler, useful for
+ passing random number generators to functions or stashing them in data
+ structures.
+
+ @signatures{fromSplitmix, fromLcg, RNG.split, RNG.run}
+ }}
+
+abilities.Random.RNG.fromLcg : Nat -> RNG
+abilities.Random.RNG.fromLcg seed = RNG (lcg seed)
+
+abilities.Random.RNG.fromLcg.doc : Doc
+abilities.Random.RNG.fromLcg.doc =
+ {{ Creates an {type RNG} from a call to {lcg}. }}
+
+abilities.Random.RNG.fromSplitmix : Nat -> RNG
+abilities.Random.RNG.fromSplitmix seed = RNG (splitmix seed)
+
+abilities.Random.RNG.fromSplitmix.doc : Doc
+abilities.Random.RNG.fromSplitmix.doc =
+ {{ Creates an {type RNG} from a call to {splitmix}. }}
+
+abilities.Random.RNG.run : RNG -> '{g, Random} a ->{g} a
+abilities.Random.RNG.run = cases RNG h -> h
+
+abilities.Random.RNG.run.doc : Doc
+abilities.Random.RNG.run.doc =
+ use RNG run
+ {{
+ `` run r c `` uses `r` to handle requests for randomness in `c`.
+
+ ```
+ run (fromSplitmix 0) do List.replicate 5 do Random.natIn 0 10
+ ```
+ }}
+
+abilities.Random.RNG.split : RNG -> (RNG, RNG)
+abilities.Random.RNG.split = cases RNG h -> h do (RNG split!, RNG split!)
+
+abilities.Random.RNG.split.doc : Doc
+abilities.Random.RNG.split.doc =
+ use RNG run split
+ {{
+ `` split r `` returns two independent generators by splitting `r`.
+
+ ```
+ p = split (fromSplitmix 0)
+ c = do List.replicate 5 do Random.natIn 0 10
+ (run (at1 p) c, run (at2 p) c)
+ ```
+ }}
+
+abilities.Random.run : '{g, Random} a ->{g, IO} a
+abilities.Random.run r = splitmix randomNat() r
+
+abilities.Random.run.doc : Doc
+abilities.Random.run.doc =
+ {{
+ Runs a computation that uses {type Random}, generating a random seed using
+ the system's secure random number generator.
+
+ Requires the {type IO} ability, so it can only be used where I/O is allowed.
+
+ This generator is not cryptographically secure, as it uses the
+ [SplitMix](https://gee.cs.oswego.edu/dl/papers/oopsla14.pdf) algorithm to
+ generate pseudorandom values.
+
+ # Example
+
+ This example generates 10 random numbers between 0 and 100 (not including
+ 100):
+
+ @typecheck ```
+ Random.run do List.replicate 10 do Random.natIn 0 100
+ ```
+ }}
+
+abilities.Random.shuffle : [a] ->{Random} [a]
+abilities.Random.shuffle as = Scope.run do
+ use List at map
+ use Nat + -
+ use Ref read write
+ refs = map Scope.ref as
+ swap i j = match (at i refs, at j refs) with
+ (Some r1, Some r2) ->
+ tmp = read r1
+ write r1 (read r2)
+ write r2 tmp
+ _ -> bug ("out of bounds", i, j)
+ go = cases
+ 0 -> ()
+ i ->
+ j = Random.natIn 0 (i + 1)
+ swap i j
+ go (i - 1)
+ go (List.size refs - 1)
+ map read refs
+
+abilities.Random.shuffle.doc : Doc
+abilities.Random.shuffle.doc =
+ use Nat range
+ {{
+ `` shuffle xs `` randomly shuffles the list `xs`. All permutations of the
+ list are equally likely.
+
+ ```
+ splitmix 0 do shuffle (range 0 10)
+ ```
+
+ ```
+ splitmix 1 do shuffle (range 0 10)
+ ```
+
+ ```
+ splitmix 2 do shuffle []
+ ```
+
+ ```
+ splitmix 3 do shuffle [1]
+ ```
+
+ # Implementation notes
+
+ Uses a
+ [Fischer-Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle),
+ using local mutable state to do the swapping of elements.
+ }}
+
+test> abilities.Random.shuffle.tests = test.verify do
+ use Set fromList toList
+ Each.repeat 25
+ as = Nat.range 0 (Random.natIn 0 20)
+ as2 = shuffle as
+ ensureEqual (toList (fromList as)) (toList (fromList as2))
+
+abilities.Random.split : '{Random} ('{g, Random} t ->{g} t)
+abilities.Random.split = do split!
+
+abilities.Random.split.doc : Doc
+abilities.Random.split.doc =
+ use List replicate
+ use Random natIn split
+ {{
+ `` split() `` creates a new supply of random numbers uncorrelated from the
+ current supply.
+
+ This is useful for concurrent use: a forked thread can be given its own
+ supply of random numbers.
+
+ # Examples
+
+ ```
+ example = do
+ rng = split()
+ t1 = rng do replicate 5 do natIn 5 15
+ (natIn 5 15, t1)
+ lcg 94859830948 example
+ ```
+
+ The result of {split} also acts as a checkpoint and can be used to produce
+ the same random numbers multiple times.
+
+ ```
+ example = do
+ rng = split()
+ run1 = rng do replicate 4 do natIn 1 100
+ run2 = rng do replicate 4 do natIn 1 100
+ (run1, run2)
+ lcg 74 example
+ ```
+ }}
+
+abilities.Random.splitmix : Nat -> '{g, Random} t ->{g} t
+abilities.Random.splitmix seed r =
+ use Nat +
+ seed0 = mix64 seed
+ gamma0 = mixGamma (goldenGamma + seed)
+ handle r() with splitmix.handler seed0 gamma0
+
+abilities.Random.splitmix.doc : Doc
+abilities.Random.splitmix.doc =
+ use Random natIn
+ {{
+ `` splitmix seed r `` provides random numbers to `r`.
+
+ The `seed` parameter can be any {type Nat}. Using different seeds will
+ produce different streams of random numbers.
+
+ # Examples
+
+ ```
+ splitmix 0 do List.replicate 10 do natIn 1 100
+ ```
+
+ ```
+ example = do
+ x = natIn 1 100
+ y = (natIn 1 100, natIn 0 10)
+ (x, y)
+ splitmix 3849 example
+ ```
+
+ # References
+
+ Based on
+ [Fast Splittable Pseudorandom Number Generators](http://gee.cs.oswego.edu/dl/papers/oopsla14.pdf).
+
+ # Credits
+
+ This implementation borrows heavily from the
+ [Haskell splitmix package](https://hackage.haskell.org/package/splitmix),
+ which is {bsd3}-licensed.
+ }}
+
+abilities.Random.splitmix.handler : Nat -> Nat -> Request {Random} a2 -> a2
+abilities.Random.splitmix.handler seed gamma =
+ use Nat +
+ use Random nat!
+ go : ∀ a2. Nat -> Nat -> Request {Random} a2 -> a2
+ go seed gamma = cases
+ { a } -> a
+ { split! -> k } ->
+ seed' = seed + gamma
+ seed'' = seed' + gamma
+ handle k (c -> (handle c() with go seed'' gamma))
+ with go (mix64 seed') (mixGamma seed'')
+ { nat! -> k } ->
+ seed' = seed + gamma
+ handle k (mix64 seed') with go seed' gamma
+ { Random.bytes n -> k } ->
+ handle k (bytesFromNats (do nat!) n) with go seed gamma
+ go seed gamma
+
+abilities.Random.splitmix.impl.goldenGamma : Nat
+abilities.Random.splitmix.impl.goldenGamma = 11400714819323198485
+
+abilities.Random.splitmix.impl.mix64 : Nat -> Nat
+abilities.Random.splitmix.impl.mix64 z0 =
+ z1 = shiftXorMultiply 33 18397679294719823053 z0
+ z2 = shiftXorMultiply 33 14181476777654086739 z1
+ z3 = shiftXor 33 z2
+ z3
+
+abilities.Random.splitmix.impl.mix64variant13 : Nat -> Nat
+abilities.Random.splitmix.impl.mix64variant13 z0 =
+ z1 = shiftXorMultiply 30 13787848793156543929 z0
+ z2 = shiftXorMultiply 27 10723151780598845931 z1
+ z3 = shiftXor 31 z2
+ z3
+
+abilities.Random.splitmix.impl.mixGamma : Nat -> Nat
+abilities.Random.splitmix.impl.mixGamma z0 =
+ use Nat >= xor
+ z1 = Nat.or (mix64variant13 z0) 1
+ n = Nat.popCount (xor z1 (Nat.shiftRight z1 1))
+ if n >= 24 then z1 else xor z1 12297829382473034410
+
+abilities.Random.splitmix.impl.shiftXor : Nat -> Nat -> Nat
+abilities.Random.splitmix.impl.shiftXor n w = Nat.xor w (Nat.shiftRight w n)
+
+abilities.Random.splitmix.impl.shiftXorMultiply : Nat -> Nat -> Nat -> Nat
+abilities.Random.splitmix.impl.shiftXorMultiply n k w =
+ use Nat *
+ shiftXor n w * k
+
+abilities.Random.splits :
+ (a ->{g1} Nat)
+ -> (Nat -> a ->{g2} (a, a))
+ -> Nat
+ -> a
+ -> '{g1, g2, Random, Stream a} ()
+abilities.Random.splits size splitAt =
+ go remaining count = match splitAt count remaining with
+ (chunk, remaining) ->
+ emit chunk
+ remaining
+ chunkCount original ->
+ do
+ natsWithSum (size original) chunkCount |> List.foldLeft go original
+ |> ignore
+
+abilities.Random.splits.bytes : Nat -> Bytes -> '{Random, Stream Bytes} ()
+abilities.Random.splits.bytes = Random.splits Bytes.size Bytes.splitAt
+
+abilities.Random.splits.bytes.doc : Doc
+abilities.Random.splits.bytes.doc =
+ use fromList impl
+ {{
+ `` splits.bytes chunkCount bytes `` splits `bytes` into `chunkCount` chunks
+ of uniformly-distributed size.
+
+ # Examples
+
+ ```
+ splits.bytes 3 0xsc0decafe |> toDelayedList |> splitmix 41
+ ```
+
+ ```
+ splits.bytes 3 0xs |> toDelayedList |> splitmix 42
+ ```
+ }}
+
+test> abilities.Random.splits.bytes.tests = test.verify do
+ use Bytes empty
+ use Each repeat run
+ use Random natIn
+ check bytes chunkCount =
+ use Bytes ++
+ use Nat ==
+ chunks = splits.bytes chunkCount bytes |> Stream.toList
+ ensuring do List.size chunks == chunkCount
+ ensuring do List.foldLeft (++) empty chunks === bytes
+ run do
+ repeat 10
+ bytes = empty
+ chunkCount = natIn 0 101
+ check bytes chunkCount
+ run do
+ repeat 100
+ bytes = Random.bytes (natIn 0 100)
+ chunkCount = natIn 1 101
+ check bytes chunkCount
+
+abilities.Random.splits.doc : Doc
+abilities.Random.splits.doc =
+ {{
+ `` Random.splits size splitAt chunkCount original `` splits `original` into
+ `chunkCount` chunks of uniformly-distributed size.
+
+ `size` takes the input and returns its total size. For splitting {type Bytes}
+ this would be {Bytes.size}.
+
+ `splitAt index value` splits `value` into two parts at `index`. For splitting
+ {type Bytes} this would be {Bytes.splitAt}.
+
+ `chunkCount` is the number of chunks to split `original` into.
+
+ `original` is the value to split.
+
+ # Examples
+
+ @source{splits.bytes}
+
+ @source{splits.text}
+
+ @source{list}
+ }}
+
+abilities.Random.splits.list : Nat -> [a] -> '{Random, Stream [a]} ()
+abilities.Random.splits.list = Random.splits List.size List.splitAt
+
+abilities.Random.splits.list.doc : Doc
+abilities.Random.splits.list.doc =
+ {{
+ `` splits.list chunkCount list `` splits `list` into `chunkCount` chunks of
+ uniformly-distributed size.
+
+ # Examples
+
+ ```
+ splits.list 3 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |> toDelayedList
+ |> splitmix 42
+ ```
+
+ ```
+ splits.list 3 [] |> toDelayedList |> splitmix 42
+ ```
+ }}
+
+test> abilities.Random.splits.list.tests = test.verify do
+ use Each repeat run
+ use List empty
+ use Random natIn
+ check list chunkCount =
+ use List ++
+ use Nat ==
+ chunks = splits.list chunkCount list |> Stream.toList
+ ensuring do List.size chunks == chunkCount
+ ensuring do List.foldLeft (++) empty chunks === list
+ run do
+ repeat 10
+ list = empty
+ chunkCount = natIn 0 101
+ check list chunkCount
+ run do
+ repeat 100
+ list = fill' (natIn 0 101) Random.nat
+ chunkCount = natIn 1 101
+ check list chunkCount
+
+abilities.Random.splits.text : Nat -> Text -> '{Random, Stream Text} ()
+abilities.Random.splits.text = Random.splits Text.size Text.splitAt
+
+abilities.Random.splits.text.doc : Doc
+abilities.Random.splits.text.doc =
+ {{
+ `` splits.text chunkCount text `` splits `text` into `chunkCount` chunks of
+ uniformly-distributed size.
+
+ # Examples
+
+ ```
+ splits.text 3 "Hello, world!" |> toDelayedList |> splitmix 42
+ ```
+
+ ```
+ splits.text 3 "" |> toDelayedList |> splitmix 42
+ ```
+ }}
+
+abilities.Random.text.base32Hex : '{Random} Text
+abilities.Random.text.base32Hex _ =
+ use List :+ replicate
+ use Nat *
+ use Random natIn oneOf
+ use Text ++
+ use char base32Hex
+ b8 : '{Random} [Char]
+ b8 _ = [base32Hex(), b32h (4 * natIn 0 8)]
+ b16 : '{Random} [Char]
+ b16 _ = replicate 3 base32Hex :+ oneOf [?0, ?g]
+ b24 : '{Random} [Char]
+ b24 _ = replicate 4 base32Hex :+ b32h (2 * natIn 0 16)
+ b32 : '{Random} [Char]
+ b32 _ = replicate 6 base32Hex :+ oneOf [?0, ?8, ?g, ?o]
+ b40 : '{Random} [Char]
+ b40 _ = replicate 8 base32Hex
+ ck : '{Random} Text
+ ck _ = fromCharList b40()
+ finish : '{Random} Text
+ finish _ =
+ use Nat -
+ t = fromCharList (oneOf [b8, b16, b24, b32, b40] ())
+ k = 8 - Text.size t
+ t ++ Text.repeat k "="
+ n = natIn 0 8
+ Text.join "" (replicate n ck) ++ finish()
+
+abilities.Random.text.base32Hex.doc : Doc
+abilities.Random.text.base32Hex.doc =
+ use text base32Hex
+ {{
+ Produces a valid base 32 hex string that encodes a byte aligned {type Bytes}
+ value.
+
+ # Examples
+
+ ```
+ lcg 4096 base32Hex
+ ```
+
+ ```
+ lcg 2517 base32Hex
+ ```
+ }}
+
+abilities.Random.text.ofChars : '{Random} Char -> Nat ->{Random} Text
+abilities.Random.text.ofChars chars length =
+ fromCharList
+ (Each.toList do
+ _ = Each.range 0 length
+ chars())
+
+abilities.Random.text.ofChars.doc : Doc
+abilities.Random.text.ofChars.doc =
+ {{
+ Generates random {type Text} of the given length, containing characters
+ generated by the given {type Char} generator.
+
+ # Example
+
+ ```
+ splitmix 2 do ofChars (do interval ?a ?z) 20
+ ```
+ }}
+
+abilities.Random.weighted : [(Nat, '{g, Random} a)] ->{g} '{g, Random} a
+abilities.Random.weighted weightedGen =
+ fromStream (Stream.fromList weightedGen)
+
+abilities.Random.weighted.doc : Doc
+abilities.Random.weighted.doc =
+ use Random weighted
+ {{
+ Creates a random generator that is a weighted sampling of the provided
+ generators. The weights are provided as a list of pairs, where the first
+ element of each pair is the weight and the second element is the generator.
+
+ # Examples
+
+ In the following example, the generator will produce a random character
+ that is either `a` or `b`. The probability of `a` is 1/4 and the
+ probability of `b` is 3/4.
+
+ ```
+ splitmix 42 do ofChars (weighted [(1, do ?a), (3, do ?b)]) 12
+ ```
+
+ In the following example, the generator will produce a random character
+ that has a 2/5 chance of being lowercase, a 2/5 chance of being uppercase,
+ and a 1/5 chance of being a digit.
+
+ ```
+ splitmix 42 do
+ ofChars
+ (weighted [(2, ascii.lower), (2, ascii.upper), (1, char.digit)]) 20
+ ```
+
+ # See also
+
+ * {fromStream} for a variant of this that takes a {type Stream} of
+ generators instead of a {type List}.
+ }}
+
+abilities.Random.weighted.fromStream :
+ '{g, Stream (Nat, '{g, Random} a)} r ->{g} '{g, Random} a
+abilities.Random.weighted.fromStream weights =
+ use Nat +
+ go :
+ (Nat, NatMap ('{g, Random} a))
+ -> (Nat, '{g, Random} a)
+ -> (Nat, NatMap ('{g, Random} a))
+ go = cases
+ (prevTotal, m), (weight, gen) ->
+ total = prevTotal + weight
+ (total, NatMap.insert total gen m)
+ let
+ (total, m) = Stream.fold go (0, NatMap.empty) weights
+ endExclusive = Nat.increment total
+ do
+ n = Random.natIn 1 endExclusive
+ NatMap.getAtLeast n m
+ |> getOrBug
+ "a weighted random sampling requires at least one non-zero weight"
+ |> at2
+ |> force
+
+abilities.Random.weighted.fromStream.doc : Doc
+abilities.Random.weighted.fromStream.doc =
+ {{
+ A variant of {Random.weighted} that takes a {type Stream} of generators
+ instead of a {type List}.
+ }}
+
+abilities.repeat : Nat -> '{e} a ->{e} ()
+abilities.repeat n op =
+ use Nat -
+ if n === 0 then ()
+ else
+ ignore op()
+ abilities.repeat (n - 1) op
+
+abilities.repeat.doc : Doc
+abilities.repeat.doc =
+ use abilities repeat
+ {{
+ `` repeat n op `` repeats an effectful computation `op`, `n` times.
+
+ # Examples
+
+ ```
+ withInitialValue 0 do
+ repeat 5 do Store.modify Nat.increment
+ Store.get
+ ```
+
+ ```
+ Each.toList do
+ repeat 2 do Each.range 0 3
+ "x"
+ ```
+ }}
+
+abilities.Request.doc : Doc
+abilities.Request.doc =
+ {{
+ A {type Request} is the type of object that is passed to a handler when an
+ ability's constructor is called. It contains the constructor, the arguments
+ to the constructor, and the continuation to be called when the request is
+ handled.
+
+ {type Request} is parameterized on the ability type and the return type of
+ the continuation. For a {type Request} of type `Request e r` the ability type
+ is `e`, and if the constructor requested has type `x ->{e} y`, then the
+ continuation has type `y ->{e} r`.
+
+ 📚 Guide:
+ [Writing ability handlers](https://www.unison-lang.org/learn/fundamentals/abilities/writing-abilities/)
+
+ # Example
+
+ ```
+ h : Request {Ask Text} Nat -> Nat
+ h = cases
+ { ask -> k } -> handle k "foo" with h
+ { a } -> a
+ handle Text.size ask with h
+ ```
+ }}
+
+abilities.Store.accumulateLeft : (a ->{g, Store s} b) -> s -> [a] ->{g} [b]
+abilities.Store.accumulateLeft f s xs = withInitialValue s do List.map f xs
+
+abilities.Store.accumulateLeft.doc : Doc
+abilities.Store.accumulateLeft.doc =
+ use Nat +
+ {{
+ `` accumulateLeft f s xs `` applies `f` to every element of `xs`,
+ accumulating effects in left-to-right order. The value `s` is the initial
+ state of the {type Store}. The final state of the {type Store} is discarded.
+
+ # Example
+
+ ```
+ accumulateLeft
+ (x -> let
+ n = Store.get
+ Store.put (n + 1)
+ (n, x)) 0 ["a", "b", "c"]
+ ```
+ }}
+
+abilities.Store.accumulateRight : (a ->{g, Store s} b) -> s -> [a] ->{g} [b]
+abilities.Store.accumulateRight f s xs =
+ withInitialValue s do List.mapRight f xs
+
+abilities.Store.accumulateRight.doc : Doc
+abilities.Store.accumulateRight.doc =
+ use Nat +
+ {{
+ `` accumulateRight f s xs `` applies `f` to every element of `xs`,
+ accumulating effects in right-to-left order. The value `s` is the initial
+ state of the {type Store}. The final state of the {type Store} is discarded.
+
+ # Example
+
+ ```
+ accumulateRight
+ (x -> let
+ n = Store.get
+ Store.put (n + 1)
+ (n, x)) 0 ["a", "b", "c"]
+ ```
+ }}
+
+abilities.Store.doc : Doc
+abilities.Store.doc =
+ use stack add pop push
+ {{
+ {type Store} represents the ability to store a value with {Store.put} and to
+ retrieve the stored value with {Store.get}.
+
+ See {withInitialValue} for a polymorphic handler that stores a value in
+ memory.
+
+ {local!} allows you to focus on some attribute of the stored value using a
+ getter and setter for that attribute.
+
+ {Store.modify} applies a function to the stored value.
+
+ {unfold!} generates a list of values by iterating a state machine implemented
+ as a {type Store} computation.
+
+ # Example : a simple stack machine
+
+ If we can {type Store} a stack, we can push onto the stack:
+
+ @source{push}
+
+ We can pop an element off the stack:
+
+ @source{pop}
+
+ We can add the top two elements from the stack and push the result:
+
+ @source{binop}
+
+ @source{add}
+
+ We can run the machine starting with an empty stack:
+
+ @source{runStack}
+
+ And now we can use our stack machine to add some numbers:
+
+ ```
+ runStack do
+ push +10
+ push +20
+ add()
+ push +100
+ add()
+ pop()
+ ```
+ }}
+
+abilities.Store.examples.stack.add : '{Abort, Store [Int]} ()
+abilities.Store.examples.stack.add = do binop (Int.+)
+
+abilities.Store.examples.stack.binop :
+ (a ->{e} a ->{g} a) ->{e, g, Abort, Store [a]} ()
+abilities.Store.examples.stack.binop f =
+ use stack pop
+ x = pop()
+ y = pop()
+ stack.push (f x y)
+
+abilities.Store.examples.stack.pop : '{Abort, Store [a]} a
+abilities.Store.examples.stack.pop = do
+ use Optional toAbort
+ stack = Store.get
+ Store.put (toAbort (List.tail stack))
+ toAbort (List.head stack)
+
+abilities.Store.examples.stack.push : a ->{Store [a]} ()
+abilities.Store.examples.stack.push n =
+ use List +:
+ Store.put (n +: Store.get)
+
+abilities.Store.examples.stack.runStack : '{Abort, Store [Int]} a -> Optional a
+abilities.Store.examples.stack.runStack p =
+ Abort.toOptional (do withInitialValue [] p) ()
+
+abilities.Store.get.doc : Doc
+abilities.Store.get.doc =
+ use Store get
+ {{
+ The `` get `` operation for the {type Store} ability retrieves the stored
+ state.
+
+ # Example
+
+ ```
+ withInitialValue 1 do get
+ ```
+ }}
+
+abilities.Store.get.examples.ex1 : Nat
+abilities.Store.get.examples.ex1 = withInitialValue 1 do Store.get
+
+abilities.Store.getOrUpdate : k -> '{Store (Map k v)} v ->{Store (Map k v)} v
+abilities.Store.getOrUpdate k ifEmpty = match Map.get k Store.get with
+ Some v -> v
+ None ->
+ v = ifEmpty()
+ Store.modify (Map.insert k v)
+ v
+
+abilities.Store.getOrUpdate.doc : Doc
+abilities.Store.getOrUpdate.doc =
+ {{
+ `` getOrUpdate k ifEmpty `` returns the value under the key 'k' in the stored
+ {type Map} if it exists, otherwise it returns the result of the computation
+ 'ifEmpty' and updates the {type Map} so that it contains that result under
+ the key 'k'.
+
+ # Example
+
+ We can use {getOrUpdate} to implement a cache that memoizes the result of a
+ function. Here the {fibonacci} function re-uses previous results to avoid
+ unnecessary recursion, by storing the results in a {type Map}.
+
+ @source{fibonacci}
+
+ ```
+ toDecimalText (withInitialValue Map.empty do fibonacci 200)
+ ```
+ }}
+
+abilities.Store.getOrUpdate.examples.fibonacci :
+ Nat ->{Store (Map Nat Natural)} Natural
+abilities.Store.getOrUpdate.examples.fibonacci = cases
+ n
+ | n Nat.< 2 -> Natural.fromNat n
+ | otherwise ->
+ use Nat -
+ use Natural +
+ use abilities.Store.getOrUpdate.examples fibonacci
+ n2 = getOrUpdate (n - 2) do fibonacci (n - 2)
+ n1 = getOrUpdate (n - 1) do fibonacci (n - 1)
+ n2 + n1
+
+abilities.Store.local :
+ (a ->{g} b) -> (a ->{g} b ->{g} a) -> '{g, Store b} v -> '{g, Store a} v
+abilities.Store.local getter setter thunk _ = local! getter setter thunk
+
+abilities.Store.local.deprecated : a -> '{g, Store a} v ->{g, Store a} v
+abilities.Store.local.deprecated a op =
+ use Store put
+ old = Store.get
+ put a
+ res = op()
+ put old
+ res
+
+abilities.Store.local.deprecated.examples.ex1 : (Nat, Nat, Nat)
+abilities.Store.local.deprecated.examples.ex1 = withInitialValue 1 do
+ use Store get
+ before = get
+ during = local.deprecated 2 do get
+ after = get
+ (before, during, after)
+
+test> abilities.Store.local.deprecated.test =
+ deprecated.run (expect ((1, 2, 1) === deprecated.examples.ex1))
+
+abilities.Store.local.doc : Doc
+abilities.Store.local.doc =
+ {{
+ `` local getter setter thunk `` returns a delayed computation of
+ ``local! getter setter thunk``.
+ }}
+
+abilities.Store.local! :
+ (a ->{g} b) -> (a ->{g} b ->{g} a) -> '{g, Store b} v ->{g, Store a} v
+abilities.Store.local! getter setter thunk =
+ use Store get
+ op : '{g, Store b} (v, b)
+ op _ = (thunk(), get)
+ let
+ (v, b) = withInitialValue (getter get) op
+ Store.modify (a -> setter a b)
+ v
+
+abilities.Store.local!.doc : Doc
+abilities.Store.local!.doc =
+ {{
+ `` local! getter setter thunk `` provides {type Store} access to __part__ of
+ a state, determined by `getter` and `setter` functions. Specifically, given
+ some state type `a`, a partial state type `b`, a `getter` function for
+ computing a `b` from an `a`, a `setter` function for updating an `a` with a
+ new `b` value, and a `{type Store} b` computation `thunk`, ``
+ local! getter setter thunk `` evaluates `thunk` in a context where the `{type
+ Store} a` ability is provided by translating operations on partial states of
+ type `b` into operations on the complete state of type `a`.
+ }}
+
+abilities.Store.local!.examples.ex1 : (Text, Nat)
+abilities.Store.local!.examples.ex1 = withInitialValue ("a", 1) do
+ use Nat +
+ use Store get
+ ignore {{ use {local!} to modify the second element of the state pair. }}
+ local! at2 (p v -> (at1 p, v)) do
+ n = get
+ Store.put (n + 1)
+ get
+
+test> abilities.Store.local!.tests.ex1 =
+ deprecated.run (expect (assertEquals local!.examples.ex1 ("a", 2)))
+
+abilities.Store.modify : (a ->{g} a) ->{g, Store a} ()
+abilities.Store.modify f = Store.put (f Store.get)
+
+abilities.Store.modify.doc : Doc
+abilities.Store.modify.doc =
+ use Store get modify
+ {{
+ {modify} applies a function to the stored value (returned by {get}) and
+ stores the result (using {Store.put}).
+
+ # Example
+
+ ```
+ withInitialValue 3 do
+ modify Nat.increment
+ get
+ ```
+ }}
+
+abilities.Store.modify.examples.increment : Nat
+abilities.Store.modify.examples.increment = withInitialValue 3 do
+ use Nat +
+ Store.modify (x -> x + 1)
+ Store.get
+
+test> abilities.Store.modify.test =
+ deprecated.run (expect (4 === examples.increment))
+
+abilities.Store.put.doc : Doc
+abilities.Store.put.doc =
+ use Store put
+ {{
+ The {put} operation for the {type Store} ability sets the stored state to the
+ given value.
+
+ # Example
+
+ ```
+ withInitialValue 10 do
+ put 15
+ Store.get
+ ```
+ }}
+
+abilities.Store.put.examples.ex1 : Nat
+abilities.Store.put.examples.ex1 = withInitialValue 10 do
+ Store.put 15
+ Store.get
+
+abilities.Store.unfold : s -> '{g, Abort, Store s} r -> '{g} [r]
+abilities.Store.unfold init thunk = do unfold! init thunk
+
+abilities.Store.unfold.doc : Doc
+abilities.Store.unfold.doc =
+ use Nat <
+ use Store get
+ {{
+ Starting with the given initial state, repeatedly runs the given {type Store}
+ action and returns a list of the results. The action is run until it calls
+ {abort}.
+
+ # Example
+
+ ```
+ Store.unfold
+ 0 (do
+ if get < 10 then Store.modify Nat.increment else abort
+ get) ()
+ ```
+
+ # See also
+
+ {unfold!} is a strict version of this function.
+ }}
+
+abilities.Store.unfold! : s -> '{g, Abort, Store s} r ->{g} [r]
+abilities.Store.unfold! initialState computeNext =
+ use List :+
+ loop lst = toDefaultValue! lst do loop (lst :+ computeNext())
+ withInitialValue initialState do loop []
+
+abilities.Store.unfold!.doc : Doc
+abilities.Store.unfold!.doc =
+ use Nat * >
+ {{
+ Generate a list of values using a {type Store s} computation. The first
+ argument is the initial state for the Store computation, the second is the
+ stateful computation used to generate the values for the list. At each step,
+ the second argument is called to generate a new value to be added to the
+ output list. The computation should call {abort} to stop generating values.
+
+ Examples:
+
+ ```
+ unfold! 1 do
+ n = Store.get
+ Store.put (n * 2)
+ if n > 1024 then abort else n
+ ```
+
+ Also see {List.iterate} for an example of a function defined using unfold!
+ }}
+
+test> abilities.Store.unfold!.tests.ex1 =
+ go : '{Abort, Store Nat} Nat
+ go = do
+ use Nat + <
+ n = Store.get
+ Store.put (n + 1)
+ if n < 10 then n else abort
+ ns : [Nat]
+ ns = unfold! 1 go
+ check (ns === [1, 2, 3, 4, 5, 6, 7, 8, 9])
+
+abilities.Store.withInitialValue : a -> '{g, Store a} v ->{g} v
+abilities.Store.withInitialValue init thunk =
+ handle thunk() with withInitialValue.handler init
+
+abilities.Store.withInitialValue.doc : Doc
+abilities.Store.withInitialValue.doc =
+ use Nat +
+ use Store get put
+ {{
+ `` withInitialValue init thunk `` provides the {type Store} ability to the
+ delayed computation `thunk`. Initially, {get} operations will return the
+ given initial value `init`. After a {put} operation, {get} will return the
+ value `v` stored by the most recent `` put v `` operation.
+
+ # Example
+
+ ```
+ withInitialValue 1 do
+ put (get + 2)
+ put (get + 3)
+ get
+ ```
+ }}
+
+abilities.Store.withInitialValue.handler : a -> Request (Store a) v -> v
+abilities.Store.withInitialValue.handler init = cases
+ { v } -> v
+ { Store.get -> k } ->
+ handle k init with abilities.Store.withInitialValue.handler init
+ { Store.put v -> k } ->
+ handle k() with abilities.Store.withInitialValue.handler v
+
+abilities.Store.withInitialValue.laws.law1 : v ->{Store v} Test
+abilities.Store.withInitialValue.laws.law1 v =
+ expect
+ (v === let
+ Store.put v
+ Store.get)
+
+test> abilities.Store.withInitialValue.tests.law1 =
+ runs 20 do
+ use List +:
+ vs = gen.listOf natInOrder ()
+ Test.tests
+ (withInitialValue 0 do expect (Store.get === 0) +: List.map laws.law1 vs)
+
+abilities.Throw.catchWith : (e ->{g1} a) -> '{g2, Throw e} a ->{g1, g2} a
+abilities.Throw.catchWith handleError thunk =
+ handle thunk()
+ with cases
+ { a } -> a
+ { throw e -> _ } -> handleError e
+
+abilities.Throw.catchWith.doc : Doc
+abilities.Throw.catchWith.doc =
+ use Float / ==
+ {{
+ Catch thrown values and handle them with the provided handler function.
+
+ Examples:
+
+ ```
+ divideOrThrow : Float -> Float ->{Throw Text} Float
+ divideOrThrow numerator denominator =
+ if denominator == 0.0 then throw "division by zero"
+ else numerator / denominator
+ catchWith Left do Right (divideOrThrow 3.0 0.0)
+ ```
+ }}
+
+abilities.Throw.doc : Doc
+abilities.Throw.doc =
+ use Nat /
+ {{
+ The {type Throw} ability will stop a computation and pass a value of a
+ **specific type** to the enclosing handler. It is parameterized on the type
+ of the value that is thrown. It's similar to {type Abort}, but can provide
+ some additional information about why the computation stopped. {type Throw}
+ is useful for partial functions, where the function is not defined for the
+ given argument and we want to provide a reason for the failure.
+
+ 📚 Guide:
+ [Handling errors with abilities](https://www.unison-lang.org/learn/fundamentals/abilities/error-handling/)
+
+ # Throwing an error
+
+ {type Throw}'s only request constructor is {throw}. Here's an example of a
+ partial function that uses it:
+
+ @typecheck ```
+ divBy : Nat -> Nat ->{Throw Text} Nat
+ divBy a b = match b with
+ 0 -> throw "divide by zero"
+ n -> a / b
+ ```
+
+ # Relationship to {type Either}
+
+ The {type Throw} ability is similar to {type Either}, in that both allow a
+ computation to fail with an error. There is a tradeoff between the two
+ approaches:
+
+ * {type Throw} is an ability, while {type Either} is a data type. This
+ means {type Either} values can be passed around, used in data structures,
+ and as the return type of the constructors of abilities, while
+ {type Throw} can only be used in computations and functions.
+ * {type Throw} is more efficient, because it doesn't require allocating a
+ an {type Either} value.
+ * {type Throw} readily composes with other abilities, while {type Either}
+ does not. For example, you can use both {type Throw} and {type Random} to
+ combine partiality with randomness, but doing so with {type Either} can
+ be awkward.
+ * Partial functions that use {type Throw} compose with other functions
+ normally, while partial functions that use {type Either} must be composed
+ using {Either.mapRight}, {Either.flatMapRight}, or explicit pattern
+ matching.
+
+ # Handling {type Throw}
+
+ Catch the error with a function that returns a value of the same type as
+ the computation:
+
+ @signature{catchWith}
+
+ Crash the program with a runtime error if the computation throws:
+
+ @signature{Throw.toBug}
+
+ Convert a {type Throw} to an {type Either}:
+
+ @signature{toEither}
+
+ Convert a {type Throw} to an {type Exception} by catching the error with a
+ function that returns a {type Failure}:
+
+ @signature{Throw.toException}
+
+ For a computation that throws the same type as it returns, return the
+ thrown value if the computation throws:
+
+ @signatures{Throw.unwrap, unwrap!}
+ }}
+
+test> abilities.Throw.tests.ex1 = check ((toEither do throw 42) === Left 42)
+
+test> abilities.Throw.tests.ex2 =
+ use Nat +
+ check ((toEither do 1 + throw 42) === Left 42)
+
+test> abilities.Throw.tests.ex3 =
+ use Nat +
+ check ((toEither do Either.toThrow (Right 1) + 42) === Right 43)
+
+abilities.Throw.toBug : '{g, Throw e} o ->{g} o
+abilities.Throw.toBug p =
+ handle p()
+ with cases
+ { throw e -> k } -> bug e
+ { a } -> a
+
+abilities.Throw.toBug.doc : Doc
+abilities.Throw.toBug.doc =
+ {{
+ A handler for the {type Throw} ability that crashes the program with a call
+ to {bug} whenever {throw} is called in the given computation.
+
+ # Example
+
+ ```
+ Throw.toBug do throw 42
+ ```
+ }}
+
+abilities.Throw.toEither : '{g, Throw e} a ->{g} Either e a
+abilities.Throw.toEither a = handle a() with toEither.handler
+
+abilities.Throw.toEither.doc : Doc
+abilities.Throw.toEither.doc =
+ {{
+ Converts a computation that may {type Throw}, to an {type Either} that
+ returns any {type Throw}n value in the {Left} case, and the result of the
+ computation in the {Right} case.
+
+ # Example
+
+ ```
+ toEither do throw "oops"
+ ```
+
+ ```
+ toEither do 1
+ ```
+ }}
+
+abilities.Throw.toEither.handler : Request {Throw e} a -> Either e a
+abilities.Throw.toEither.handler = cases
+ { a } -> Right a
+ { throw e -> _ } -> Left e
+
+abilities.Throw.toException :
+ (e ->{g1} Failure) -> '{g2, Throw e} a ->{g1, g2, Exception} a
+abilities.Throw.toException convert = catchWith (Exception.raise << convert)
+
+abilities.Throw.toException.doc : Doc
+abilities.Throw.toException.doc =
+ use Float / ==
+ {{
+ Convert a computation that can {type Throw} an error into one that can raise
+ an {type Exception}.
+
+ Example:
+
+ ```
+ divideOrThrow : Float -> Float ->{Throw Text} Float
+ divideOrThrow numerator denominator =
+ if denominator == 0.0 then throw "division by zero"
+ else numerator / denominator
+ catch do
+ Throw.toException (e -> failure e do 3.0 / 0.0) do divideOrThrow 3.0 0.0
+ ```
+ }}
+
+abilities.Throw.toOptional : '{g, Throw e} a ->{g} Optional a
+abilities.Throw.toOptional c =
+ h = cases
+ { a } -> Some a
+ { throw _ -> _ } -> None
+ handle c() with h
+
+abilities.Throw.toOptional.doc : Doc
+abilities.Throw.toOptional.doc =
+ use Throw toOptional
+ {{
+ Converts a {type Throw} effect to an {type Optional} value.
+
+ # Examples
+
+ ```
+ toOptional do throw "error"
+ ```
+
+ ```
+ toOptional do "hello"
+ ```
+ }}
+
+abilities.Throw.unwrap : '{g, Throw a} a -> '{g} a
+abilities.Throw.unwrap thunk = do unwrap! thunk
+
+abilities.Throw.unwrap.doc : Doc
+abilities.Throw.unwrap.doc =
+ {{ Similar to {unwrap!} but does not eagerly evaluate the result. }}
+
+abilities.Throw.unwrap! : '{g, Throw a} a ->{g} a
+abilities.Throw.unwrap! thunk =
+ handle thunk()
+ with cases
+ { a } -> a
+ { throw a -> _ } -> a
+
+abilities.Throw.unwrap!.doc : Doc
+abilities.Throw.unwrap!.doc =
+ {{
+ {unwrap!} takes a delayed computation that can either return __or__ throw a
+ value of type `a` and returns the `a` value that was either returned or
+ thrown.
+
+ This can be used to achieve the same behavior as an early `return` statement
+ in an imperative language, using {throw} instead of `return`. For example,
+ consider the following {sumUntil} function. It will sum all of the numbers in
+ a {type List}, but if the sum reaches the provided maximum value, it will
+ stop iterating through the list and immediately return the maximum value.
+
+ @source{sumUntil}
+
+ ```
+ sumUntil 10 (Nat.range 1 1000)
+ ```
+
+ ```
+ sumUntil 10 [1, 2]
+ ```
+ }}
+
+abilities.Throw.unwrap!.docs.sumUntil : Nat -> [Nat] -> Nat
+abilities.Throw.unwrap!.docs.sumUntil max values =
+ go : Nat -> Nat ->{Throw Nat} Nat
+ go accumulator currentValue =
+ use Nat + >=
+ sum = accumulator + currentValue
+ if sum >= max then throw max else sum
+ unwrap! do List.foldLeft go 0 values
+
+abilities.Wait.doc : Doc
+abilities.Wait.doc =
+ use Wait wait
+ {{
+ The {type Wait} ability is an abstract interface for waiting for a specified
+ {type Duration}. The {wait} operation should block the current thread for
+ that duration, though the exact behavior is left up to the implementation.
+
+ The Base library provides an implementation of the {type Wait} ability that
+ uses the {type IO} ability: {runWait}. You can use this implementation to run
+ computations that use the {type Wait} ability where the {type IO} ability is
+ available.
+
+ The {type Wait} ability is useful for writing code that needs to wait or
+ sleep for some duration without having to depend on a specific implementation
+ of thread blocking.
+
+ # Example
+
+ @typecheck ```
+ runWait do wait Duration.second
+ ```
+
+ # Example handler implementation
+
+ The following is an example handler implementation for the {type Wait}
+ ability that simply increments a total wait time whenever the {wait}
+ operation is called.
+
+ ```
+ testWait : '{Wait} a -> (a, Duration)
+ testWait p =
+ use Duration +
+ h totalWait p =
+ handle p()
+ with cases
+ { wait d -> k } -> h (totalWait + d) k
+ { a } -> (a, totalWait)
+ h Duration.zero p
+ Tuple.second
+ Duration.toText (testWait do
+ wait (minutes +4)
+ wait (seconds +3)
+ wait (minutes +2)
+ wait (hours +1)
+ "Done")
+ ```
+ }}
+
+abilities.Wait.runWait : (a ->{g, Wait} b) -> a ->{g, IO, Exception} b
+abilities.Wait.runWait f a =
+ h : '{g, IO, Exception, Wait} b ->{g, IO, Exception} b
+ h x =
+ handle x()
+ with cases
+ { Wait.wait d -> k } -> h do k (sleep d)
+ { a } -> a
+ h do f a
+
+abilities.Wait.runWait.doc : Doc
+abilities.Wait.runWait.doc =
+ use Wait wait
+ {{
+ `` runWait f `` runs the computation `f` that uses the {type Wait} ability.
+ The {type Wait} ability provides one operation: {wait}. In this
+ implementation, the {wait} operation blocks the current thread for the
+ specified {type Duration}, by calling {sleep}.
+
+ # See also
+
+ * {type Wait} - The ability type.
+ * {sleep} - Blocks the current thread for the specified duration in the
+ {type IO} ability.
+ }}
+
+-- builtin Any.Any : a -> Any
+
+Any.doc : Doc
+Any.doc =
+ {{
+ The {type Any} type is an
+ [existential type](http://en.wikipedia.org/wiki/Existential_type), which
+ means that values of type {type Any} could be any type. It is used to
+ represent values of unknown type or where the type is not important.
+
+ # Usage in the Base library
+
+ The {type Any} type is used internally:
+
+ * In the {type Doc} type. Documentation often needs to refer to Unison
+ code, where the type of the code is not important. For example,
+ {type Doc.Term} wraps a Unison expression of type {type Any}.
+ * In the {type Failure} type. The {type Failure} type is used with the
+ {type Exception} ability to allow the programmer to include arbitrary
+ data in a {type Failure}.
+
+ # Construction
+
+ The {type Any} type has one constructor. It can take a value of any type:
+
+ @signature{Any}
+
+ # Extraction
+
+ The value of type {type Any} can be extracted in an unsafe way:
+
+ @signature{unsafeExtract}
+
+ This operation is __unsafe__ because it can fail at runtime if the value is
+ not of the expected type. It is up to you to ensure that the value is of
+ the expected type.
+
+ # Example
+
+ ```
+ unsafeExtract (Any 42)
+ ```
+ }}
+
+-- builtin Any.unsafeExtract : Any -> a
+
+Any.unsafeExtract.doc : Doc
+Any.unsafeExtract.doc =
+ use Nat +
+ {{
+ Unsafely cast an {type Any} to any value. Use with care!
+
+ It's recommended that you provide a type annotation on the result of this
+ function, to make it clear what type you're casting to (and to produce a type
+ error if the surrounding context isn't expecting this type).
+
+ ```
+ (unsafeExtract (Any "yay!")) : Text
+ ```
+
+ This function won't give you a type error if you cast the value inside to the
+ wrong type, but you will get a runtime error if you try to use the value in a
+ way that doesn't match its type. For instance:
+
+ ```
+ unsafeExtract (Any "nay!") + 42
+ ```
+ }}
+
+(Boolean.!=) : Boolean -> Boolean -> Boolean
+x Boolean.!= y = (x || y) && Boolean.not (x && y)
+
+(Boolean.<) : Boolean -> Boolean -> Boolean
+a Boolean.< b = Boolean.and (Boolean.not a) b
+
+(Boolean.<=) : Boolean -> Boolean -> Boolean
+a Boolean.<= b = Boolean.not a || b
+
+(Boolean.==) : Boolean -> Boolean -> Boolean
+a Boolean.== b =
+ use Boolean !=
+ Boolean.not (a != b)
+
+(Boolean.>) : Boolean -> Boolean -> Boolean
+a Boolean.> b = Boolean.and a (Boolean.not b)
+
+(Boolean.>=) : Boolean -> Boolean -> Boolean
+a Boolean.>= b = implies b a
+
+Boolean.and : Boolean -> Boolean -> Boolean
+Boolean.and x y = x && y
+
+Boolean.and.doc : Doc
+Boolean.and.doc =
+ {{
+ {type Boolean} conjunction. Returns `` true `` only if both inputs are
+ ``true``, otherwise ``false``. Note that this function cannot short-circuit,
+ as it's strict in both arguments. For a short-circuiting version, use the
+ built-in syntax {{ docExample 2 do a b -> a && b }}.
+
+ # Truth table
+
+ {{ binaryTruthTable "and" Boolean.and }}
+ }}
+
+Boolean.doc : Doc
+Boolean.doc =
+ use Boolean != and not or
+ {{
+ The {type Boolean} type is built in to Unison. Expressions of type
+ {type Boolean} evaluate to either `` true `` or ``false``. This type can be
+ used to represent any choice between two possibilities (on/off, yes/no,
+ pass/fail).
+
+ For example, the result of a comparison is `` true `` if the comparison
+ succeeds, and `` false `` if it fails:
+
+ ```
+ 1 Nat.< 2
+ ```
+
+ ```
+ 1 Nat.> 2
+ ```
+
+ # Conditional statements
+
+ A conditional expression has the form
+ {{ docExample 3 do c t f -> (if c then t else f) }}, where `c` is a
+ __condition__, an expression of type {type Boolean}, and `t` and `f` are
+ expressions of any type, but `t` and `f` must have the same type. If `c`
+ evaluates to ``true``, then the result of the whole expression is whatever
+ `t` evaluates to. If `c` evaluates to ``false``, then the result of the
+ whole expression is whatever `f` evaluates to.
+
+ Evaluation of conditional expressions is __non-strict__. The evaluation
+ semantics of {{ docExample 3 do c t f -> (if c then t else f) }} are:
+
+ * The condition `c` is always evaluated.
+ * If `c` evaluates to ``true``, the expression `t` is evaluated and `f`
+ remains unevaluated. The whole expression reduces to the value of `t`.
+ * If `c` evaluates to ``false``, the expression `f` is evaluated and `t`
+ remains unevaluated. The whole expression reduces to the value of `f`
+
+ # Boolean conjunction and disjunction
+
+ A __Boolean conjunction expression__ is a {type Boolean} expression of the
+ form {{ docExample 2 do a b -> a && b }} where `a` and `b` are
+ {type Boolean} expressions. Note that {{ docExample 2 do a b -> a && b }}
+ is not a function call, but built-in syntax.
+
+ The evaluation semantics of {{ docExample 2 do a b -> a && b }} are
+ equivalent to {{ docExample 2 do a b -> (if a then b else false) }}.
+
+ A Boolean disjunction expression is a Boolean expression of the form {{
+ docExample 2 do a b -> a || b }} where `a` and `b` are Boolean expressions.
+ Note that {{ docExample 2 do a b -> a || b }} is not a function call, but
+ built-in syntax.
+
+ The evaluation semantics of {{ docExample 2 do a b -> a || b }} are
+ equivalent to {{ docExample 2 do a b -> (if a then true else b) }}.
+
+ # Boolean functions
+
+ The base libraries provide a number of operations on {type Boolean} values:
+
+ ## Negation (not)
+
+ `` not a `` returns the opposite of `a`. If `a` is ``true``, then ``
+ not a `` is ``false``, and vice versa.
+
+ ## Exclusive-or
+
+ `` a != b `` is `` true `` if either `a` or `b` are ``true``, but not
+ both. Put another way, it's `` true `` only if `a` and `b` are not the
+ same. This is the complement of ``iff``.
+
+ ## Not-and
+
+ The complement of ``and``. `` nand a b `` is `` false `` ony if both `a`
+ and `b` are ``true``.
+
+ ## Not-or
+
+ The complement of ``or``. `` nor a b `` is `` true `` if neither `a` nor
+ `b` are ``true``.
+
+ ## Equivalence
+
+ The complement of ``(!=)``. `` iff a b `` is `` true `` if `a` and `b`
+ are the same.
+
+ ## Implication
+
+ `` implies a b `` is `` true `` unless `a` is `` true `` and `b` is
+ ``false``. This is the same as logical implication.
+
+ `` given a b `` is `` true `` unless `a` is `` false `` and `b` is
+ ``true``. This is the converse of logical implication.
+
+ ## Inhibition (not-implies)
+
+ `` a Boolean.> b `` is `` true `` only if `a` is `` true `` but `b` is
+ ``false``. Equivalent to {{ docExample 2 do a b -> not (implies a b) }}.
+
+ `` a Boolean.< b `` is `` true `` only if `a` is `` false `` but `b` is
+ ``true``. Equivalent to {{ docExample 3 do a b -> not (implies b a) }}.
+
+ ## Conjunction (as a function)
+
+ `` and a b `` is `` true `` only if both `a` and `b` are ``true``. It's
+ equivalent to {{ docExample 2 do a b -> a && b }}, except `` and `` is
+ an ordinary function (rather than built-in syntax), and doesn't
+ short-circuit.
+
+ ## Disjunction (as a function)
+
+ `` or a b `` is `` true `` if either `a` or `b` are ``true``. It's
+ equivalent to {{ docExample 2 do a b -> a || b }}, except `` or `` is an
+ ordinary function (rather than built-in syntax), and doesn't
+ short-circuit.
+ }}
+
+Boolean.docs.binaryTruthTable :
+ Text -> (Boolean ->{g} Boolean ->{g1} Boolean) ->{g, g1} Doc
+Boolean.docs.binaryTruthTable name f =
+ use List ++ map
+ table =
+ List.flatMap (x -> map (y -> [x, y, f x y]) [false, true]) [false, true]
+ body = map (map (column -> {{ {{ docExample 0 (_ -> column) }} }})) table
+ {{
+ {{
+ docTable
+ ([[{{ `x` }}, {{ `y` }}, docCode {{ {{ (docWord name) }} x y }}]] ++ body)
+ }}
+ }}
+
+Boolean.docs.binaryTruthTable.doc : Doc
+Boolean.docs.binaryTruthTable.doc =
+ use Boolean !=
+ {{
+ Generates a {type Doc} that shows the truth table for the given binary
+ Boolean function. The {type Text} argument is a name to show for the
+ function.
+
+ # Examples
+
+ ```
+ binaryTruthTable "and" Boolean.and
+ ```
+
+ ```
+ binaryTruthTable "or" Boolean.or
+ ```
+
+ ```
+ binaryTruthTable "xor" (!=)
+ ```
+ }}
+
+Boolean.docs.unaryTruthTable : Text -> (Boolean -> Boolean) -> Doc
+Boolean.docs.unaryTruthTable name f =
+ {{
+ {{
+ docTable
+ [ [{{ `x` }}, docCode (docWord (name Text.++ " ") Doc.++ {{ x }})]
+ , [{{ `` false `` }}, {{ @eval{ f false } }}]
+ , [{{ `` true `` }}, {{ @eval{ f true } }}]
+ ] }}
+ }}
+
+Boolean.docs.unaryTruthTable.doc : Doc
+Boolean.docs.unaryTruthTable.doc =
+ {{
+ Generates a {type Doc} that shows the truth table for the given unary Boolean
+ function. The {type Text} argument is a name to show for the function.
+
+ # Example
+
+ ```
+ unaryTruthTable "not" Boolean.not
+ ```
+ }}
+
+Boolean.eq.doc : Doc
+Boolean.eq.doc =
+ use Boolean not
+ {{
+ {type Boolean} equivalence. Returns `` true `` only if both inputs are the
+ same, otherwise returns ``false``. Note that this function cannot
+ short-circuit, as it's strict in both arguments. For a short-circuiting
+ version, use the built-in syntax
+ {{ docExample 2 do a b -> a && b || not a && not b }}.
+
+ # Truth table
+
+ {{ binaryTruthTable "iff" Boolean.eq }}
+ }}
+
+Boolean.given.doc : Doc
+Boolean.given.doc =
+ {{
+ Inverse {type Boolean} implication. An expression `` given a b `` Returns ``
+ false `` only if `a` is `` false `` and `b` is ``true``, otherwise returns
+ ``true``. Note that this function cannot short-circuit, as it's strict in
+ both arguments. For a short-circuiting version, use the built-in syntax
+ {{ docExample 2 do a b -> a || Boolean.not b }}.
+
+ # Truth table
+
+ {{ binaryTruthTable "given" given }}
+ }}
+
+test> Boolean.given.test =
+ deprecated.run
+ (Test.tests
+ [ check' (given false false === true)
+ , check' (given false true === false)
+ , check' (given true false === true)
+ , check' (given true true === true)
+ ])
+
+Boolean.gt.doc : Doc
+Boolean.gt.doc =
+ use Boolean <
+ {{
+ {type Boolean} inhibition. An expression `` Boolean.gt a b `` Returns `` true
+ `` only if `a` is `` true `` and `b` is ``false``, otherwise returns
+ ``false``. Note that this function cannot short-circuit, as it's strict in
+ both arguments. For a short-circuiting version, use the built-in syntax
+ {{ docExample 2 do a b -> a && Boolean.not b }}.
+
+ # Truth table
+
+ {{ binaryTruthTable "gt" (<) }}
+ }}
+
+Boolean.implies.doc : Doc
+Boolean.implies.doc =
+ {{
+ {type Boolean} implication. An expression `` implies a b `` Returns `` false
+ `` only if `a` is `` true `` and `b` is ``false``, otherwise returns
+ ``true``. Note that this function cannot short-circuit, as it's strict in
+ both arguments. For a short-circuiting version, use the built-in syntax
+ {{ docExample 2 do a b -> Boolean.not a || b }}.
+
+ # Truth table
+
+ {{ binaryTruthTable "implies" implies }}
+ }}
+
+test> Boolean.implies.test =
+ deprecated.run
+ (Test.tests
+ [ check' (implies false false === true)
+ , check' (implies false true === true)
+ , check' (implies true false === false)
+ , check' (implies true true === true)
+ ])
+
+Boolean.lt.doc : Doc
+Boolean.lt.doc =
+ use Boolean lt
+ {{
+ {type Boolean} inhibition. An expression `` lt a b `` Returns `` true `` only
+ if `a` is `` false `` and `b` is ``true``, otherwise returns ``false``. Note
+ that this function cannot short-circuit, as it's strict in both arguments.
+ For a short-circuiting version, use the built-in syntax
+ {{ docExample 2 do a b -> Boolean.not a && b }}.
+
+ # Truth table
+
+ {{ binaryTruthTable "lt" lt }}
+ }}
+
+Boolean.nand : Boolean -> Boolean -> Boolean
+Boolean.nand x y = Boolean.not (Boolean.and x y)
+
+Boolean.nand.doc : Doc
+Boolean.nand.doc =
+ {{
+ Negated {type Boolean} conjunction. Returns `` false `` only if both inputs
+ are ``true``, otherwise returns ``true``. Note that this function cannot
+ short-circuit, as it's strict in both arguments. For a short-circuiting
+ version, use the built-in syntax
+ {{ docExample 2 do a b -> Boolean.not (a && b) }}.
+
+ # Truth table
+
+ {{ binaryTruthTable "nand" nand }}
+ }}
+
+Boolean.nor : Boolean -> Boolean -> Boolean
+Boolean.nor x y = Boolean.not (Boolean.or x y)
+
+Boolean.nor.doc : Doc
+Boolean.nor.doc =
+ {{
+ Negated {type Boolean} disjunction. Returns `` true `` only if both inputs
+ are ``false``, otherwise returns ``false``. Note that this function cannot
+ short-circuit, as it's strict in both arguments. For a short-circuiting
+ version, use the built-in syntax
+ {{ docExample 2 do a b -> Boolean.not (a || b) }}.
+
+ # Truth table
+
+ {{ binaryTruthTable "nor" nor }}
+ }}
+
+-- builtin Boolean.not : Boolean -> Boolean
+
+Boolean.not.doc : Doc
+Boolean.not.doc =
+ {{
+ {type Boolean} negation. Returns `` true `` if the input is ``false``, and
+ vice versa.
+ }}
+
+Boolean.or : Boolean -> Boolean -> Boolean
+Boolean.or x y = x || y
+
+Boolean.or.doc : Doc
+Boolean.or.doc =
+ {{
+ {type Boolean} disjunction. Returns `` false `` only if both inputs are
+ ``false``, otherwise ``true``. Note that this function cannot short-circuit,
+ as it's strict in both arguments. For a short-circuiting version, use the
+ built-in syntax {{ docExample 2 do a b -> a || b }}.
+
+ # Truth table
+
+ {{ binaryTruthTable "or" Boolean.or }}
+ }}
+
+Boolean.toNat : Boolean -> Nat
+Boolean.toNat b = if b then 1 else 0
+
+Boolean.toNat.doc : Doc
+Boolean.toNat.doc =
+ use Boolean toNat
+ {{
+ Converts a {type Boolean} into either `` 0 `` (if it's ``false``) or `` 1 ``
+ (if it's ``true``).
+
+ ```
+ toNat true
+ ```
+
+ ```
+ toNat false
+ ```
+ }}
+
+Boolean.toText : Boolean -> Text
+Boolean.toText = cases
+ true -> "true"
+ false -> "false"
+
+Boolean.toText.doc : Doc
+Boolean.toText.doc =
+ use Boolean toText
+ {{
+ Renders a {type Boolean} into {type Text}.
+
+ # Examples
+
+ ```
+ toText true
+ ```
+
+ ```
+ toText false
+ ```
+ }}
+
+Boolean.until : (a ->{e} Boolean) -> '{e} a ->{e} a
+Boolean.until pred op =
+ r = op()
+ if pred r then r else Boolean.until pred op
+
+Boolean.until.doc : Doc
+Boolean.until.doc =
+ {{
+ `` Boolean.until predicate op `` repeatedly runs `op`, returning the first
+ result for which the `predicate` is ``true``.
+
+ Does not terminate if the `predicate` never becomes ``true``.
+ }}
+
+test> Boolean.until.test =
+ use Boolean until
+ use Store get
+ use withInitialValue handler
+ go n _ =
+ use Nat +
+ new = get + 1
+ Store.put new
+ new === n
+ t3 = handle expect (until id (go 3) && get === 3) with handler 0
+ t1 = handle expect (until id (go 1) && get === 1) with handler 0
+ deprecated.run (Test.both t1 t3)
+
+Boolean.when : Boolean -> '{e} () ->{e} ()
+Boolean.when b x = if b then x() else ()
+
+Boolean.when.doc : Doc
+Boolean.when.doc =
+ {{
+ `` when b x `` forces `x` and performs its effects if the {type Boolean} `b`
+ is ``true``, and otherwise does nothing.
+
+ # Examples
+
+ ```
+ toOptional! do when true do abort
+ ```
+
+ ```
+ toOptional! do when false do abort
+ ```
+ }}
+
+Boolean.while : (a ->{e} Boolean) -> '{e} a ->{e} a
+Boolean.while pred op = Boolean.until (Boolean.not << pred) op
+
+Boolean.while.doc : Doc
+Boolean.while.doc =
+ {{
+ `` while predicate op `` repeatedly runs `op`, returning the first result for
+ which the `predicate` is ``false``.
+
+ Does not terminate if the `predicate` never becomes ``false``.
+ }}
+
+test> Boolean.while.test =
+ use Boolean not
+ use Store get
+ use withInitialValue handler
+ go n _ =
+ use Nat +
+ new = get + 1
+ Store.put new
+ Universal.lt new n
+ t3 = handle expect (not (while id (go 3)) && get === 3) with handler 0
+ t1 = handle expect (not (while id (go 1)) && get === 1) with handler 0
+ deprecated.run (Test.both t1 t3)
+
+Boolean.xor.doc : Doc
+Boolean.xor.doc =
+ {{
+ {type Boolean} exclusive-or. Returns `` false `` only if the inputs are the
+ same, otherwise ``true``.
+
+ # Truth table
+
+ {{ binaryTruthTable "xor" Boolean.xor }}
+ }}
+
+test> Boolean.xor.test =
+ use Boolean xor
+ deprecated.run
+ (Test.tests
+ [ check' (xor false false === false)
+ , check' (xor false true === true)
+ , check' (xor true false === true)
+ , check' (xor true true === false)
+ ])
+
+-- builtin bug : a -> b
+
+bug.doc : Doc
+bug.doc =
+ {{
+ The {bug} function forces a program to halt with a specific value. Often used
+ for runtime invariants.
+
+ This can be any value:
+
+ @typecheck ```
+ bug "halt the program"
+ ```
+
+ @typecheck ```
+ bug 42
+ ```
+
+ **Example:**
+
+ ``` ucm
+ >bug (Some ?👋)
+ ```
+
+ The value will be printed out as the program halts:
+
+ ``` ucm
+ 💔💥
+
+ I've encountered a call to builtin.bug with the following value:
+
+ Some ?👋
+
+ I'm sorry this message doesn't have more detail about the location of the failure. My makers
+ plan to fix this in a future release. 😢
+ ```
+
+ [Learn more about bug](https://www.unison-lang.org/learn/fundamentals/control-flow/exception-handling/bug/)
+ }}
+
+bug.impossible : Request Exception a -> a
+bug.impossible = cases
+ { x } -> x
+ { Exception.raise f -> _ } -> bug f
+
+bug.impossible.doc : Doc
+bug.impossible.doc = {{ Handler for exceptions that shouldn't happen. }}
+
+-- builtin Bytes.++ : Bytes -> Bytes -> Bytes
+
+Bytes.++.doc : Doc
+Bytes.++.doc =
+ use Bytes ++
+ use fromList impl
+ {{
+ Append two {type Bytes} values.
+
+ # Examples
+
+ ```
+ 0xs ++ 0xs
+ ```
+
+ ```
+ 0xs01 ++ 0xs02
+ ```
+
+ ```
+ 0xsfeed ++ 0xsface
+ ```
+ }}
+
+(Bytes.==) : Bytes -> Bytes -> Boolean
+(Bytes.==) = (===)
+
+-- builtin Bytes.at : Nat -> Bytes -> Optional Nat
+
+Bytes.at.doc : Doc
+Bytes.at.doc =
+ use Bytes at
+ use Optional flatten
+ use fromList impl
+ {{
+ Returns the byte at the given index in the {type Bytes} value, as a
+ {type Nat}.
+
+ # Examples
+
+ ```
+ flatten (hush do at 0 0xs08090a)
+ ```
+
+ ```
+ flatten (hush do at 1 0xs08090a)
+ ```
+
+ ```
+ flatten (hush do at 3 0xs08090a)
+ ```
+ }}
+
+Bytes.at! : Nat -> Bytes ->{Abort} Nat
+Bytes.at! n bs = Optional.toAbort (Bytes.at n bs)
+
+Bytes.at!.doc : Doc
+Bytes.at!.doc =
+ use Bytes at!
+ {{
+ `` at! i bs `` returns the `i`-th (0-based) byte in `bs` and calls {abort} if
+ the index is out of bounds.
+
+ # Examples
+
+ ```
+ toOptional! do at! 9 Bytes.empty
+ ```
+
+ ```
+ toOptional! do at! 1 0xs1122334455
+ ```
+ }}
+
+Bytes.base32Hex : Bytes -> Text
+Bytes.base32Hex =
+ use Text ++
+ go acc bs =
+ match grab bs with
+ None -> acc
+ Some (Double m n pad bs) ->
+ go (acc ++ encodeChunk m 0 ++ encodeChunk n pad) bs
+ Some (Single n pad bs) -> go (acc ++ encodeChunk n pad) bs
+ go ""
+
+Bytes.base32Hex.b32h : Nat -> Char
+Bytes.base32Hex.b32h n =
+ use Nat + <
+ use fromNat impl
+ if n < 10 then impl (n + 48) else impl (n + 87)
+
+Bytes.base32Hex.b32h.doc : Doc
+Bytes.base32Hex.b32h.doc =
+ {{ Translates 5 bits to is base 32 hex ASCII encoding. }}
+
+Bytes.base32Hex.doc : Doc
+Bytes.base32Hex.doc =
+ {{ Encodes a bytes value as the textual base 32 hex encoding. }}
+
+Bytes.base32Hex.encodeChunk : Nat -> Nat -> Text
+Bytes.base32Hex.encodeChunk n pad =
+ use List +:
+ use Nat -
+ use Text ++
+ go acc m = cases
+ 0 -> fromCharList acc
+ i ->
+ c = b32h (Nat.and m 31)
+ n = Nat.shiftRight m 5
+ go (c +: acc) n (i - 1)
+ go [] n (8 - pad) ++ Text.repeat pad "="
+
+Bytes.base32Hex.encodeChunk.doc : Doc
+Bytes.base32Hex.encodeChunk.doc =
+ {{
+ Encodes a single {type Nat} of up to 40 bits as base 32 hex. The second
+ argument specifies how much padding should be used instead of bits from the
+ first argument.
+ }}
+
+Bytes.base32Hex.grab : Bytes -> Optional Hex32Piece
+Bytes.base32Hex.grab bs = match decodeNat64be bs with
+ Some (n, bs) ->
+ use Nat or shiftLeft
+ m = Nat.shiftRight n 24
+ k = Nat.and n 16777215
+ match decodeNat16be bs with
+ Some (n, bs) -> Some <| Double m (or (shiftLeft k 16) n) 0 bs
+ None ->
+ match Bytes.at 0 bs with
+ Some n ->
+ r = shiftLeft (or n (shiftLeft k 8)) 3
+ Some <| Double m r 1 (Bytes.drop 1 bs)
+ None -> Some (Double m (shiftLeft k 1) 3 bs)
+ None -> grab1 bs
+
+Bytes.base32Hex.grab.doc : Doc
+Bytes.base32Hex.grab.doc =
+ {{ Extracts the next portion of a {type Bytes} for encoding. }}
+
+Bytes.base32Hex.grab1 : Bytes -> Optional Hex32Piece
+Bytes.base32Hex.grab1 bs =
+ match decodeNat32be bs with
+ Some (n, bs) ->
+ match Bytes.at 0 bs with
+ None -> Some <| Single (Nat.shiftLeft n 3) 1 bs
+ Some k ->
+ Some <| Single (Nat.or k (Nat.shiftLeft n 8)) 0 (Bytes.drop 1 bs)
+ None ->
+ match decodeNat16be bs with
+ Some (n, bs) ->
+ match Bytes.at 0 bs with
+ None -> Some <| Single (Nat.shiftLeft n 4) 4 bs
+ Some k ->
+ use Nat shiftLeft
+ m = shiftLeft (Nat.or k (shiftLeft n 8)) 1
+ Some <| Single m 3 (Bytes.drop 1 bs)
+ None ->
+ match Bytes.at 0 bs with
+ Some n -> Some <| Single (Nat.shiftLeft n 2) 6 (Bytes.drop 1 bs)
+ None -> None
+
+Bytes.base32Hex.grab1.doc : Doc
+Bytes.base32Hex.grab1.doc =
+ {{ Extracts up to 40 bits of a {type Bytes} for encoding. }}
+
+Bytes.constantTimeEqual : Bytes -> Bytes -> Boolean
+Bytes.constantTimeEqual b1 b2 =
+ use Bytes at! size
+ use Nat != == >=
+ use Optional toAbort
+ if size b1 != size b2 then false
+ else
+ go acc b1 b2 = match size b1 with
+ n
+ | n >= 8 ->
+ match decodeNat64be b1 |> toAbort with
+ (hd1, b1) ->
+ match decodeNat64be b2 |> toAbort with
+ (hd2, b2) ->
+ hdEq = hd1 == hd2
+ go (hdEq && acc) b1 b2
+ | n >= 4 ->
+ match decodeNat32be b1 |> toAbort with
+ (hd1, b1) ->
+ match decodeNat32be b2 |> toAbort with
+ (hd2, b2) ->
+ hdEq = hd1 == hd2
+ go (hdEq && acc) b1 b2
+ 0 -> acc
+ 1 ->
+ hdEq = at! 0 b1 == at! 0 b2
+ hdEq && acc
+ n ->
+ match decodeNat16be b1 |> toAbort with
+ (hd1, b1) ->
+ match decodeNat16be b2 |> toAbort with
+ (hd2, b2) ->
+ hdEq = hd1 == hd2
+ go (hdEq && acc) b1 b2
+ toDefault! (do false) do go true b1 b2
+
+Bytes.constantTimeEqual.doc : Doc
+Bytes.constantTimeEqual.doc =
+ use fromList impl
+ {{
+ Like {===} but examines every byte of both inputs even if an earlier byte
+ doesn't match. Used to avoid timing attacks when (say) verifying an {hmac}.
+
+ ```
+ constantTimeEqual 0xsaabbccdd 0xsaabbccdd
+ ```
+
+ Note: if the two inputs aren't of the same {Bytes.size}, returns `false`
+ immediately.
+ }}
+
+test> Bytes.constantTimeEqual.tests = test.verify do
+ use Bytes ++
+ Each.repeat 100
+ n = Random.natIn 10 50
+ b1 = Random.bytes n
+ ensureEqual b1 b1
+ ensure (constantTimeEqual b1 b1)
+ ensure (Boolean.not (constantTimeEqual b1 (Bytes.drop 1 b1 ++ 0xs00)))
+
+-- builtin Bytes.decodeNat16be : Bytes -> Optional (Nat, Bytes)
+
+Bytes.decodeNat16be.doc : Doc
+Bytes.decodeNat16be.doc =
+ use fromList impl
+ {{
+ Decodes a {type Nat} from the first two bytes of a {type Bytes} value in
+ big-endian order. Returns a pair containing the decoded {type Nat} and the
+ remaining {type Bytes} value, or {None} if the {type Bytes} value is shorter
+ than two bytes. The returned {type Nat} value is in the range `` 0 `` to
+ ``65535``.
+
+ # Examples
+
+ ```
+ decodeNat16be 0xs0102
+ ```
+
+ ```
+ decodeNat16be 0xs0102030405060708
+ ```
+
+ ```
+ decodeNat16be 0xs
+ ```
+
+ ```
+ decodeNat16be 0xs01
+ ```
+
+ # See also
+
+ * {decodeNat16le}
+ * {decodeNat32be}
+ * {decodeNat32le}
+ * {decodeNat64be}
+ * {decodeNat64le}
+ }}
+
+-- builtin Bytes.decodeNat16le : Bytes -> Optional (Nat, Bytes)
+
+Bytes.decodeNat16le.doc : Doc
+Bytes.decodeNat16le.doc =
+ use fromList impl
+ {{
+ Decodes a {type Nat} from the first two bytes of a {type Bytes} value in
+ little-endian order. Returns a pair containing the decoded {type Nat} and the
+ remaining {type Bytes} value, or {None} if the {type Bytes} value is shorter
+ than two bytes. The returned {type Nat} value is in the range `` 0 `` to
+ ``65535``.
+
+ # Examples
+
+ ```
+ decodeNat16le 0xs0102
+ ```
+
+ ```
+ decodeNat16le 0xs0102030405060708
+ ```
+
+ ```
+ decodeNat16le 0xs
+ ```
+
+ ```
+ decodeNat16le 0xs01
+ ```
+
+ # See also
+
+ * {decodeNat16be}
+ * {decodeNat32be}
+ * {decodeNat32le}
+ * {decodeNat64be}
+ * {decodeNat64le}
+ }}
+
+Bytes.decodeNat16sbe : Nat -> Bytes -> ([Nat], Bytes)
+Bytes.decodeNat16sbe n bs =
+ use List :+
+ use Nat - ==
+ go n bs acc =
+ if n == 0 then (acc, bs)
+ else
+ match decodeNat16be bs with
+ None -> (acc, bs)
+ Some (w, rest) -> go (n - 1) rest (acc :+ w)
+ go n bs []
+
+Bytes.decodeNat16sbe.doc : Doc
+Bytes.decodeNat16sbe.doc =
+ use fromList impl
+ {{
+ Decodes **at most** a given number of 16-bit unsigned integers in big-endian
+ order from the given {type Bytes} and returns them as a list of {type Nat}
+ values together with the rest of the {type Bytes}.
+
+ If the {type Bytes} value doesn't contain enough bytes to decode the given
+ number of integers, the returned list will contain fewer integers than
+ requested.
+
+ # Examples
+
+ ```
+ decodeNat16sbe 2 0xs01020304
+ ```
+
+ ```
+ decodeNat16sbe 2 0xs010203
+ ```
+ }}
+
+-- builtin Bytes.decodeNat32be : Bytes -> Optional (Nat, Bytes)
+
+Bytes.decodeNat32be.doc : Doc
+Bytes.decodeNat32be.doc =
+ use fromList impl
+ {{
+ Decodes a {type Nat} from the first four bytes of a {type Bytes} value in
+ big-endian order. Returns a pair containing the decoded {type Nat} and the
+ remaining {type Bytes} value, or {None} if the {type Bytes} value is shorter
+ than four bytes. The returned {type Nat} value is in the range `` 0 `` to
+ ``4294967295``.
+
+ # Examples
+
+ ```
+ decodeNat32be 0xs01020304
+ ```
+
+ ```
+ decodeNat32be 0xs0102030405060708
+ ```
+
+ ```
+ decodeNat32be 0xs
+ ```
+
+ ```
+ decodeNat32be 0xs01
+ ```
+
+ # See also
+
+ * {decodeNat16be}
+ * {decodeNat16le}
+ * {decodeNat32le}
+ * {decodeNat64be}
+ * {decodeNat64le}
+ }}
+
+-- builtin Bytes.decodeNat32le : Bytes -> Optional (Nat, Bytes)
+
+Bytes.decodeNat32le.doc : Doc
+Bytes.decodeNat32le.doc =
+ use fromList impl
+ {{
+ Decodes a {type Nat} from the first four bytes of a {type Bytes} value in
+ little-endian order. Returns a pair containing the decoded {type Nat} and the
+ remaining {type Bytes} value, or {None} if the {type Bytes} value is shorter
+ than four bytes. The returned {type Nat} value is in the range `` 0 `` to
+ ``4294967295``.
+
+ # Examples
+
+ ```
+ decodeNat32le 0xs01020304
+ ```
+
+ ```
+ decodeNat32le 0xs0102030405060708
+ ```
+
+ ```
+ decodeNat32le 0xs
+ ```
+
+ ```
+ decodeNat32le 0xs01
+ ```
+
+ # See also
+
+ * {decodeNat16be}
+ * {decodeNat16le}
+ * {decodeNat32be}
+ * {decodeNat64be}
+ * {decodeNat64le}
+ }}
+
+Bytes.decodeNat32sbe : Nat -> Bytes -> ([Nat], Bytes)
+Bytes.decodeNat32sbe n bs =
+ use List :+
+ use Nat - ==
+ go n bs acc =
+ if n == 0 then (acc, bs)
+ else
+ match decodeNat32be bs with
+ None -> (acc, bs)
+ Some (w, rest) -> go (n - 1) rest (acc :+ w)
+ go n bs []
+
+Bytes.decodeNat32sbe.doc : Doc
+Bytes.decodeNat32sbe.doc =
+ use fromList impl
+ {{
+ Decodes **at most** a given number of 32-bit unsigned integers in big-endian
+ order from the given {type Bytes} and returns them as a list of {type Nat}
+ values together with the rest of the {type Bytes}.
+
+ If the {type Bytes} value doesn't contain enough bytes to decode the given
+ number of integers, the returned list will contain fewer integers than
+ requested.
+
+ # Examples
+
+ ```
+ decodeNat32sbe 2 0xs0102030405060708
+ ```
+
+ ```
+ decodeNat32sbe 2 0xs010203040506
+ ```
+ }}
+
+-- builtin Bytes.decodeNat64be : Bytes -> Optional (Nat, Bytes)
+
+Bytes.decodeNat64be.doc : Doc
+Bytes.decodeNat64be.doc =
+ use fromList impl
+ {{
+ Decodes a {type Nat} from the first eight bytes of a {type Bytes} value in
+ big-endian order. Returns a pair containing the decoded {type Nat} and the
+ remaining {type Bytes} value, or {None} if the {type Bytes} value is shorter
+ than eight bytes. The returned {type Nat} value is in the range `` 0 `` to
+ ``18446744073709551615``.
+
+ # Examples
+
+ ```
+ decodeNat64be 0xs0102030405060708
+ ```
+
+ ```
+ decodeNat64be 0xs0102030405060708090a0b0c0d0e0f
+ ```
+
+ ```
+ decodeNat64be 0xs
+ ```
+
+ ```
+ decodeNat64be 0xs01
+ ```
+
+ # See also
+
+ * {decodeNat16be}
+ * {decodeNat16le}
+ * {decodeNat32be}
+ * {decodeNat32le}
+ * {decodeNat64le}
+ }}
+
+-- builtin Bytes.decodeNat64le : Bytes -> Optional (Nat, Bytes)
+
+Bytes.decodeNat64le.doc : Doc
+Bytes.decodeNat64le.doc =
+ use fromList impl
+ {{
+ Decodes a {type Nat} from the first eight bytes of a {type Bytes} value in
+ little-endian order. Returns a pair containing the decoded {type Nat} and the
+ remaining {type Bytes} value, or {None} if the {type Bytes} value is shorter
+ than eight bytes. The returned {type Nat} value is in the range `` 0 `` to
+ ``18446744073709551615``.
+
+ # Examples
+
+ ```
+ decodeNat64le 0xs0102030405060708
+ ```
+
+ ```
+ decodeNat64le 0xs0102030405060708090a0b0c0d0e0f
+ ```
+
+ ```
+ decodeNat64le 0xs
+ ```
+
+ ```
+ decodeNat64le 0xs01
+ ```
+
+ # See also
+
+ * {decodeNat16be}
+ * {decodeNat16le}
+ * {decodeNat32be}
+ * {decodeNat32le}
+ * {decodeNat64be}
+ }}
+
+Bytes.decodeNat64sbe : Nat -> Bytes -> ([Nat], Bytes)
+Bytes.decodeNat64sbe n bs =
+ use List :+
+ use Nat - ==
+ go n bs acc =
+ if n == 0 then (acc, bs)
+ else
+ match decodeNat64be bs with
+ None -> (acc, bs)
+ Some (w, rest) -> go (n - 1) rest (acc :+ w)
+ go n bs []
+
+Bytes.decodeNat64sbe.doc : Doc
+Bytes.decodeNat64sbe.doc =
+ use fromList impl
+ {{
+ Decodes **at most** a given number of 64-bit unsigned integers in big-endian
+ order from the given {type Bytes} and returns them as a list of {type Nat}
+ values together with the rest of the {type Bytes}.
+
+ If the {type Bytes} value doesn't contain enough bytes to decode the given
+ number of integers, the returned list will contain fewer integers than
+ requested.
+
+ # Examples
+
+ ```
+ decodeNat64sbe 2 0xs0102030405060708090a0b0c0d0e0f10
+ ```
+
+ ```
+ decodeNat64sbe 2 0xs0102030405060708090a0b0c0d0e
+ ```
+ }}
+
+Bytes.doc : Doc
+Bytes.doc =
+ use Bytes ++
+ use Text toUtf8
+ use fromList impl
+ {{
+ {type Bytes} is a type of in-memory data represented as strings of bytes.
+
+ # Constructing {type Bytes}
+
+ A **byte literal** is an even number of hexadecimal digits preceded by
+ `0xs`, such as ``0xsdeadbeef``.
+
+ The empty {type Bytes} is `` 0xs `` or {Bytes.empty}.
+
+ Convert a hexadecimal {type Text} value to {type Bytes}:
+
+ ```
+ catch do Bytes.fromHex "deadbeef"
+ ```
+
+ Convert a {type Text} value to {type Bytes} using UTF-8 encoding:
+
+ ```
+ toUtf8 "Hello, world!"
+ ```
+
+ Convert a {type List} of {type Nat} values to {type Bytes}. Raises an
+ {type Exception} if any of the {type Nat} values are greater than 255:
+
+ ```
+ catch do Bytes.fromList [222, 173, 190, 175]
+ ```
+
+ # Converting {type Bytes} to other types
+
+ Convert {type Bytes} to a {type Text} value using UTF-8 encoding:
+
+ ```
+ catch do fromUtf8 0xsf09f8c88e2ad90
+ ```
+
+ Convert {type Bytes} to a {type List} of {type Nat} values:
+
+ ```
+ Bytes.toList 0xsf09f8c88e2ad90
+ ```
+
+ Convert {type Bytes} to a {type Text} value using hexadecimal encoding:
+
+ ```
+ Bytes.toHex 0xsf09f8c88e2ad90
+ ```
+
+ # Manipulating {type Bytes}
+
+ Append two {type Bytes} values:
+
+ ```
+ 0xsf09f8c88 ++ 0xse2ad90
+ ```
+
+ Take a prefix of a {type Bytes} value:
+
+ ```
+ Bytes.take 3 0xsf09f8c88e2ad90
+ ```
+
+ Drop a prefix of a {type Bytes} value:
+
+ ```
+ Bytes.drop 3 0xsf09f8c88e2ad90
+ ```
+
+ # Bitwise operations
+
+ Truncate a {type Bytes} value to a given number of bits:
+
+ ```
+ truncateLeft 12 0xsf09f8c88e2ad90
+ ```
+
+ ```
+ truncateRight 12 0xsf09f8c88e2ad90
+ ```
+
+ Shift a {type Bytes} value to the **left** by a number of bits:
+
+ ```
+ Bytes.shiftLeft 4 0xsf09f8c88e2ad90
+ ```
+
+ Shift a {type Bytes} value to the **right** by a number of bits:
+
+ ```
+ Bytes.shiftRight 4 0xsf09f8c88e2ad90
+ ```
+
+ # Querying and indexing
+
+ Get the byte at a given index:
+
+ ```
+ Bytes.at 3 0xsf09f8c88e2ad90
+ ```
+
+ Get the length of a {type Bytes} value:
+
+ ```
+ Bytes.size 0xsf09f8c88e2ad90
+ ```
+
+ Check if a {type Bytes} value is empty:
+
+ ```
+ Bytes.isEmpty 0xsf09f8c88e2ad90
+ ```
+
+ Find a substring of {type Bytes} in a {type Bytes} value:
+
+ ```
+ Bytes.indexOf 0xsbeef 0xscafebeef
+ ```
+
+ # Basic binary encoding and decoding
+
+ Encode a {type Nat} value as {type Bytes} in big-endian order:
+
+ ```
+ encodeNat64be 2301017106344378350
+ ```
+
+ And in little-endian order:
+
+ ```
+ encodeNat64le 2301017106344378350
+ ```
+
+ Decode just the first 32 bits of a {type Bytes} value as a {type Nat} in
+ big-endian order:
+
+ ```
+ decodeNat32be 0xs1feedaddedc0ffee
+ ```
+
+ And in little-endian order:
+
+ ```
+ decodeNat32le 0xs1feedaddedc0ffee
+ ```
+
+ There are also {decodeNat16be} and {decodeNat16le} for the first 16 bits.
+
+ # Serializing and deserializing values
+
+ Any Unison value can be turned into {type Bytes} via the {type Value} type:
+
+ ```
+ Value.serialize (value ["a", "b", "c"])
+ ```
+
+ If you know the type of a previously serialized {type Value}, you can
+ deserialize it into a Unison value:
+
+ @typecheck ```
+ catch do
+ bytes = 0xs00000003030103030001610300016203000163
+ v = Value.deserialize bytes
+ load v
+ ```
+
+ # Encodings, hashing, and compression
+
+ Functions that encode data into various formats usually operate on
+ {type Bytes}.
+
+ Encode a {type Bytes} value as
+ [Base64](https://en.wikipedia.org/wiki/Base64):
+
+ ```
+ toBase64 0xsf09f8c88e2ad90
+ ```
+
+ Decode a [Base64](https://en.wikipedia.org/wiki/Base64)-encoded value to
+ {type Bytes}:
+
+ ```
+ catch do fromBase64 (toUtf8 "8J+YgQ==")
+ ```
+
+ Encode a {type Bytes} value as
+ [Base32Hex](https://tools.ietf.org/html/rfc4648#section-6):
+
+ ```
+ catch do fromUtf8 (toBase32Hex 0xsf09f8c88e2ad90)
+ ```
+
+ Decode a [Base32Hex](https://tools.ietf.org/html/rfc4648#section-6)-encoded
+ value to {type Bytes}:
+
+ ```
+ fromBase32Hex (toUtf8 "f09f8c88e2ad90")
+ ```
+
+ Compute the SHA2-256 hash of a {type Bytes} value:
+
+ ```
+ hash Sha2_256 0xsf09f8c88e2ad90
+ ```
+
+ Compress a {type Bytes} value using the
+ [gzip](https://www.ietf.org/rfc/rfc1952.txt) format:
+
+ ```
+ gzip.compress 0xsdeadbeef
+ ```
+
+ Decompress a {type Bytes} value compressed with
+ [gzip](https://www.ietf.org/rfc/rfc1952.txt):
+
+ ```
+ catch do
+ gzip.decompress 0xs1f8b0800000000000013bbb776df7b005aa39c7c04000000
+ ```
+ }}
+
+-- builtin Bytes.drop : Nat -> Bytes -> Bytes
+
+Bytes.drop.doc : Doc
+Bytes.drop.doc =
+ use Bytes drop
+ use fromList impl
+ {{
+ `` drop n b `` returns the {type Bytes} `b` with the first `n` bytes removed.
+
+ If `n` is greater than or equal to the length of `b`, the result is
+ {Bytes.empty}.
+
+ # Examples
+
+ ```
+ drop 0 0xsfeedface
+ ```
+
+ ```
+ drop 1 0xsfeedface
+ ```
+
+ ```
+ drop 10 0xsfeedface
+ ```
+ }}
+
+-- builtin Bytes.empty : Bytes
+
+Bytes.empty.doc : Doc
+Bytes.empty.doc = {{ The empty {type Bytes} value, ``0xs``. }}
+
+-- builtin Bytes.encodeNat16be : Nat -> Bytes
+
+Bytes.encodeNat16be.doc : Doc
+Bytes.encodeNat16be.doc =
+ {{
+ Encodes the lower-order 16 bits of a {type Nat} value to {type Bytes} in
+ big-endian order.
+
+ # Examples
+
+ ```
+ encodeNat16be 258
+ ```
+
+ ```
+ encodeNat16be 16
+ ```
+
+ ```
+ encodeNat16be 16777216
+ ```
+
+ # See also
+
+ * {encodeNat16le}
+ * {encodeNat32be}
+ * {encodeNat32le}
+ * {encodeNat64be}
+ * {encodeNat64le}
+ }}
+
+-- builtin Bytes.encodeNat16le : Nat -> Bytes
+
+Bytes.encodeNat16le.doc : Doc
+Bytes.encodeNat16le.doc =
+ {{
+ Encodes the lower-order 16 bits of a {type Nat} value to {type Bytes} in
+ little-endian order.
+
+ # Examples
+
+ ```
+ encodeNat16le 258
+ ```
+
+ ```
+ encodeNat16le 16
+ ```
+
+ ```
+ encodeNat16le 16777216
+ ```
+
+ # See also
+
+ * {encodeNat16be}
+ * {encodeNat32be}
+ * {encodeNat32le}
+ * {encodeNat64be}
+ * {encodeNat64le}
+ }}
+
+-- builtin Bytes.encodeNat32be : Nat -> Bytes
+
+Bytes.encodeNat32be.doc : Doc
+Bytes.encodeNat32be.doc =
+ {{
+ Encodes the lower-order 32 bits of a {type Nat} value to {type Bytes} in
+ big-endian order.
+
+ # Examples
+
+ ```
+ encodeNat32be 16909060
+ ```
+
+ ```
+ encodeNat32be 16
+ ```
+
+ ```
+ encodeNat32be 72623859790382856
+ ```
+
+ # See also
+
+ * {encodeNat16be}
+ * {encodeNat16le}
+ * {encodeNat32le}
+ * {encodeNat64be}
+ * {encodeNat64le}
+ }}
+
+-- builtin Bytes.encodeNat32le : Nat -> Bytes
+
+Bytes.encodeNat32le.doc : Doc
+Bytes.encodeNat32le.doc =
+ {{
+ Encodes the lower-order 32 bits of a {type Nat} value to {type Bytes} in
+ little-endian order.
+
+ # Examples
+
+ ```
+ encodeNat32le 16909060
+ ```
+
+ ```
+ encodeNat32le 16
+ ```
+
+ ```
+ encodeNat32le 72623859790382856
+ ```
+
+ # See also
+
+ * {encodeNat16be}
+ * {encodeNat16le}
+ * {encodeNat32be}
+ * {encodeNat64be}
+ * {encodeNat64le}
+ }}
+
+-- builtin Bytes.encodeNat64be : Nat -> Bytes
+
+Bytes.encodeNat64be.doc : Doc
+Bytes.encodeNat64be.doc =
+ {{
+ Encodes the 64 bits of a {type Nat} value to {type Bytes} in big-endian
+ order.
+
+ # Examples
+
+ ```
+ encodeNat64be 72623859790382856
+ ```
+
+ ```
+ encodeNat64be 16
+ ```
+
+ ```
+ encodeNat64be 579005069656919567
+ ```
+
+ # See also
+
+ * {encodeNat16be}
+ * {encodeNat16le}
+ * {encodeNat32be}
+ * {encodeNat32le}
+ * {encodeNat64le}
+ }}
+
+-- builtin Bytes.encodeNat64le : Nat -> Bytes
+
+Bytes.encodeNat64le.doc : Doc
+Bytes.encodeNat64le.doc =
+ {{
+ Encodes the 64 bits of a {type Nat} value to {type Bytes} in little-endian
+ order.
+
+ # Examples
+
+ ```
+ encodeNat64le 72623859790382856
+ ```
+
+ ```
+ encodeNat64le 16
+ ```
+
+ ```
+ encodeNat64le 579005069656919567
+ ```
+
+ # See also
+
+ * {encodeNat16be}
+ * {encodeNat16le}
+ * {encodeNat32be}
+ * {encodeNat32le}
+ * {encodeNat64be}
+ }}
+
+-- builtin Bytes.flatten : Bytes -> Bytes
+
+Bytes.flatten.doc : Doc
+Bytes.flatten.doc =
+ {{
+ Internally, {type Bytes} values are represented as a
+ [rope](https://en.wikipedia.org/wiki/Rope_%28data_structure%29) of byte
+ arrays. {Bytes.flatten} flattens a {type Bytes} value into a single
+ contiguous byte array by copying the bytes into a new array. This conserves
+ memory and improves performance for later operations on the {type Bytes}
+ value, at the cost of a potentially expensive copy operation.
+ }}
+
+Bytes.fromBase16 : Bytes ->{Exception} Bytes
+Bytes.fromBase16 v = raiseMessage v (fromBase16.impl v)
+
+Bytes.fromBase16.doc : Doc
+Bytes.fromBase16.doc =
+ {{
+ Decodes a [hexadecimal-encoded](https://en.wikipedia.org/wiki/Hexadecimal)
+ {type Bytes} value into the {type Bytes} it encodes.
+
+ # Example
+
+ ```
+ catch do fromBase16 (Text.toUtf8 "48656C6C6F20576F726C6421")
+ ```
+ }}
+
+-- builtin Bytes.fromBase16.impl : Bytes -> Either Text Bytes
+
+Bytes.fromBase16.impl.doc : Doc
+Bytes.fromBase16.impl.doc =
+ use Text toUtf8
+ use fromBase16 impl
+ {{
+ Transforms a {type Bytes} from a
+ [hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) string in the UTF-8
+ encoding to the {type Bytes} those characters represent. Returns {Left} with
+ an error message {type Text} if the input is not a valid hexadecimal string
+ or is not an even number of characters long.
+
+ # Example
+
+ ```
+ impl (toUtf8 "68656c6c6f20776f726c64")
+ ```
+
+ ```
+ impl (toUtf8 "Hello, World!")
+ ```
+
+ ```
+ impl (toUtf8 "Hello, World")
+ ```
+ }}
+
+Bytes.fromBase32 : Bytes ->{Exception} Bytes
+Bytes.fromBase32 v = raiseMessage v (fromBase32.impl v)
+
+Bytes.fromBase32.doc : Doc
+Bytes.fromBase32.doc =
+ {{
+ Decodes a [base32-encoded](https://en.wikipedia.org/wiki/Base32) {type Bytes}
+ value into the {type Bytes} it encodes.
+
+ # Example
+
+ ```
+ catch do fromBase32 (Text.toUtf8 "JBSWY3DPEBLW64TMMQQQ====")
+ ```
+ }}
+
+-- builtin Bytes.fromBase32.impl : Bytes -> Either Text Bytes
+
+Bytes.fromBase32.impl.doc : Doc
+Bytes.fromBase32.impl.doc =
+ use Text toUtf8
+ use fromBase32 impl
+ {{
+ Transforms {type Bytes} from their representation in the
+ [RFC 4648 base32 alphabet](https://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet)
+ to the {type Bytes} those characters represent. Returns {Left} with an error
+ message {type Text} if the input is not a valid base32 string or is not a
+ multiple of 8 characters long.
+
+ # Example
+
+ ```
+ catch do
+ fromUtf8 (raiseMessage () (impl (toUtf8 "JBSWY3DPEBLW64TMMQ======")))
+ ```
+
+ ```
+ impl (toUtf8 "Hello, World!")
+ ```
+
+ ```
+ impl (toUtf8 "Goodbye!")
+ ```
+ }}
+
+Bytes.fromBase32Hex : Bytes -> Either Text Bytes
+Bytes.fromBase32Hex bs =
+ use Bytes ++ flatten
+ use Nat + - ==
+ go bcc acc i j = match Bytes.at i bs with
+ None -> Right <| flatten (bcc ++ finalize acc j)
+ Some c
+ | c == 61 -> Right <| flatten (bcc ++ finalize acc j)
+ | otherwise ->
+ match h32b c with
+ Left err -> Left err
+ Right n ->
+ match fromBase32Hex.push acc n j with
+ Left acc -> go bcc acc (i + 1) (j + 5)
+ Right (bs, acc) -> go (bcc ++ bs) acc (i + 1) (j + 5 - 64)
+ go Bytes.empty 0 0 0
+
+Bytes.fromBase32Hex.alignment : Nat -> Nat
+Bytes.fromBase32Hex.alignment = clearBit 0 << clearBit 1 << clearBit 2
+
+Bytes.fromBase32Hex.alignment.doc : Doc
+Bytes.fromBase32Hex.alignment.doc =
+ {{ Rounds an encoded bits number down to an even number of bytes. }}
+
+Bytes.fromBase32Hex.doc : Doc
+Bytes.fromBase32Hex.doc =
+ {{
+ Decodes a base 32 hex encoded byte string given a valid ASCII byte string as
+ input.
+
+ Note: this assumes that the input represents a {type Bytes} value, where
+ there is an integral number of bytes. Base 32 hex strings represent multiples
+ of 5 bits, so for strings that do not represent a multiple of 40 bits, some
+ information will be discarded under the assumption that it is just padding.
+ }}
+
+Bytes.fromBase32Hex.finalize : Nat -> Nat -> Bytes
+Bytes.fromBase32Hex.finalize acc j =
+ use Nat -
+ acc' = Nat.shiftLeft acc (64 - j)
+ ali = Nat.shiftRight j 3
+ Bytes.take ali (encodeNat64be acc')
+
+Bytes.fromBase32Hex.finalize.doc : Doc
+Bytes.fromBase32Hex.finalize.doc =
+ {{
+ Gets a final byte string from a {type Nat} accumulator and a count of its
+ stored bits.
+ }}
+
+Bytes.fromBase32Hex.h32b : Nat -> Either Text Nat
+Bytes.fromBase32Hex.h32b = cases
+ n
+ | 48 Nat.<= n && n Nat.< 58 -> Right (n Nat.- 48)
+ | 65 Nat.<= n && n Nat.< 87 -> Right (n Nat.- 55)
+ | 97 Nat.<= n && n Nat.< 119 -> Right (n Nat.- 87)
+ | otherwise ->
+ Left ("h32b: unrecognized byte: " Text.++ Nat.toText n)
+
+Bytes.fromBase32Hex.h32b.doc : Doc
+Bytes.fromBase32Hex.h32b.doc =
+ {{ Translates a base 32 hex digit back to its 5-bit meaning. }}
+
+Bytes.fromBase32Hex.push : Nat -> Nat -> Nat -> Either Nat (Bytes, Nat)
+Bytes.fromBase32Hex.push acc new j =
+ use Nat - < or shiftLeft
+ if j < 60 then Left <| or (shiftLeft acc 5) new
+ else
+ k = 64 - j
+ k' = 5 - k
+ out = or (shiftLeft acc k) (Nat.shiftRight new k')
+ mask = setBit k' 0 - 1
+ Right (encodeNat64be out, Nat.and new mask)
+
+Bytes.fromBase32Hex.push.doc : Doc
+Bytes.fromBase32Hex.push.doc =
+ {{
+ Adds bits to a {type Nat} accumulator for decoding base 32 hex.
+
+ * The first argument is the accumulator.
+ * The second argument is the 5 new bytes.
+ * The third argument is the number of bits already in the accumulator.
+
+ The result is the new accumulator, with a byte string if the new bytes won't
+ fit in the natural.
+ }}
+
+test> Bytes.fromBase32Hex.tests.inverse = test.verify do
+ Each.repeat 100
+ n = Random.natIn 0 10
+ bs = Random.bytes n
+ err = test.raiseFailure "fromBase32Hex failed"
+ ensure <| Either.fold err ((===) bs) (fromBase32Hex (toBase32Hex bs))
+
+Bytes.fromBase64 : Bytes ->{Exception} Bytes
+Bytes.fromBase64 v = raiseMessage v (fromBase64.impl v)
+
+Bytes.fromBase64.doc : Doc
+Bytes.fromBase64.doc =
+ {{
+ Decodes a [base64-encoded](https://en.wikipedia.org/wiki/Base64) {type Bytes}
+ value into the {type Bytes} it encodes.
+
+ # Example
+
+ ```
+ catch do fromBase64 (Text.toUtf8 "SGVsbG8gV29ybGQh")
+ ```
+ }}
+
+-- builtin Bytes.fromBase64.impl : Bytes -> Either Text Bytes
+
+Bytes.fromBase64.impl.doc : Doc
+Bytes.fromBase64.impl.doc =
+ use Text toUtf8
+ use fromBase64 impl
+ {{
+ Transforms a {type Bytes} from a
+ [base64](https://en.wikipedia.org/wiki/Base64) string in the UTF-8 encoding
+ to the {type Bytes} those characters represent. Returns {Left} with an error
+ message {type Text} if the input is not a valid base64 string or is not a
+ multiple of 4 characters long.
+
+ # Example
+
+ ```
+ catch do fromUtf8 (raiseMessage () (impl (toUtf8 "aGVsbG8gd29ybGQ=")))
+ ```
+
+ ```
+ impl (toUtf8 "Hello, World!")
+ ```
+
+ ```
+ impl (toUtf8 "Goodbye!")
+ ```
+ }}
+
+-- builtin Bytes.fromBase64UrlUnpadded : Bytes -> Either Text Bytes
+
+Bytes.fromBase64UrlUnpadded.doc : Doc
+Bytes.fromBase64UrlUnpadded.doc =
+ use Text toUtf8
+ {{
+ Decodes [base64url](https://tools.ietf.org/html/rfc4648#section-5)-encoded
+ {type Text} into {type Bytes}. The input must be a multiple of 4 bytes long,
+ and must not contain any padding characters. Returns {Left} with an error
+ message if the input is invalid, or {Right} with the decoded bytes otherwise.
+
+ # Examples
+
+ ```
+ catch do
+ fromUtf8
+ (raiseMessage () (fromBase64UrlUnpadded (toUtf8 "aGVsbG8gd29ybGQ")))
+ ```
+
+ ```
+ fromBase64UrlUnpadded (toUtf8 "aGVsbG8gd29ybGQ=")
+ ```
+ }}
+
+Bytes.fromHex : Text ->{Exception} Bytes
+Bytes.fromHex = fromBase16 << Text.toUtf8
+
+Bytes.fromHex.doc : Doc
+Bytes.fromHex.doc =
+ {{
+ Decodes a hexadecimal string into a byte array.
+
+ # Example
+
+ ```
+ catch do Bytes.fromHex "deadbeef"
+ ```
+ }}
+
+Bytes.fromHex.impl : Text -> Either Text Bytes
+Bytes.fromHex.impl = fromBase16.impl << Text.toUtf8
+
+Bytes.fromList : [Nat] ->{Exception} Bytes
+Bytes.fromList nats =
+ if List.all (Nat.inRange 0 256) nats then fromList.impl nats
+ else Exception.raise (failure "Bytes.fromList: value out of range" nats)
+
+Bytes.fromList.doc : Doc
+Bytes.fromList.doc =
+ use Bytes fromList
+ {{
+ Creates a {type Bytes} from a {type List} of {type Nat} values in the range 0
+ to 255 (inclusive). Throws an error if the list contains any values outside
+ this range.
+
+ # Example
+
+ ```
+ catch do fromList [192, 168, 0, 1]
+ ```
+
+ ```
+ catch do fromList [256]
+ ```
+ }}
+
+-- builtin Bytes.fromList.impl : [Nat] -> Bytes
+
+-- builtin Bytes.gzip.compress : Bytes -> Bytes
+
+Bytes.gzip.compress.doc : Doc
+Bytes.gzip.compress.doc =
+ use fromList impl
+ use gzip compress
+ {{
+ Compresses a {type Bytes} value using the
+ [gzip](https://tools.ietf.org/html/rfc1952) format.
+
+ # Examples
+
+ ```
+ compress 0xs4b696c726f79207761732068657265
+ ```
+
+ ```
+ compress 0xs
+ ```
+
+ # See also
+
+ * {gzip.decompress}
+ }}
+
+Bytes.gzip.decompress : Bytes ->{Exception} Bytes
+Bytes.gzip.decompress bs = match gzip.decompress.impl bs with
+ Left e -> raiseGeneric e bs
+ Right bs' -> bs'
+
+Bytes.gzip.decompress.doc : Doc
+Bytes.gzip.decompress.doc =
+ {{
+ Decompresses the given [gzip](https://en.wikipedia.org/wiki/Gzip) compressed
+ {type Bytes}.
+
+ # Example
+
+ ```
+ catch do gzip.decompress (gzip.compress 0xsfeedfacecafebeef)
+ ```
+ }}
+
+-- builtin Bytes.gzip.decompress.impl : Bytes -> Either Text Bytes
+
+Bytes.gzip.decompress.impl.doc : Doc
+Bytes.gzip.decompress.impl.doc =
+ {{
+ Decompresses the given [gzip](https://en.wikipedia.org/wiki/Gzip) compressed
+ {type Bytes}.
+
+ # Example
+
+ ```
+ gzip.decompress.impl (gzip.compress 0xsfeedfacecafebeef)
+ ```
+ }}
+
+-- builtin Bytes.indexOf : Bytes -> Bytes -> Optional Nat
+
+Bytes.indexOf.doc : Doc
+Bytes.indexOf.doc =
+ {{
+ `` Bytes.indexOf needle haystack `` returns the index of the first occurrence
+ of 'needle' in 'haystack'.
+ }}
+
+test> Bytes.indexOf.tests.infix =
+ test.verify do
+ use Bytes ++ indexOf
+ use Nat ==
+ use Random bytes natIn
+ Each.repeat 100
+ szHaystackLeft = natIn 0 100
+ szNeedle = natIn 0 100
+ szHaystackRight = natIn 0 100
+ haystackLeft = bytes szHaystackLeft
+ needle = bytes szNeedle
+ haystackRight = bytes szHaystackRight
+ haystack = haystackLeft ++ needle ++ haystackRight
+ ix = indexOf needle haystack
+ if szNeedle == 0 then ensure (ix === Some 0)
+ else ensuring do indexOf needle haystack === Some szHaystackLeft
+
+Bytes.isEmpty : Bytes -> Boolean
+Bytes.isEmpty bs =
+ use Nat ==
+ Bytes.size bs == 0
+
+Bytes.isEmpty.doc : Doc
+Bytes.isEmpty.doc =
+ use Bytes isEmpty
+ use fromList impl
+ {{
+ Returns `` true `` if the {type Bytes} is empty.
+
+ # Examples
+
+ ```
+ isEmpty 0xs
+ ```
+
+ ```
+ isEmpty Bytes.empty
+ ```
+
+ ```
+ isEmpty 0xs2390
+ ```
+ }}
+
+Bytes.shiftLeft : Nat -> Bytes -> Bytes
+Bytes.shiftLeft n bs =
+ nats = Bytes.toList bs
+ f b =
+ use Nat -
+ carry = Store.get
+ Store.put (Nat.shiftRight b (8 - n))
+ dropBits 56 (Nat.or (Nat.shiftLeft b n) carry)
+ fromList.impl (accumulateRight f 0 nats)
+
+Bytes.shiftLeft.doc : Doc
+Bytes.shiftLeft.doc =
+ use Bytes shiftLeft
+ use fromList impl
+ {{
+ `` shiftLeft n bs `` shifts all the {type Bytes} `bs` left by `n` bits, where
+ `n` is at most ``8``. Passing `n` larger than `` 8 `` will have the same
+ effect as passing ``8``.
+
+ # Examples
+
+ ```
+ shiftLeft 2 0xs010203
+ ```
+
+ ```
+ shiftLeft 4 0xsdeadbeef
+ ```
+
+ ```
+ shiftLeft 100 0xsfeedface
+ ```
+ }}
+
+Bytes.shiftRight : Nat -> Bytes -> Bytes
+Bytes.shiftRight n bs =
+ nats = Bytes.toList bs
+ f b =
+ use Nat -
+ carry = Store.get
+ Store.put (Nat.and b (dropBits (64 - n) b))
+ Nat.or (Nat.shiftRight b n) (dropBits 56 (Nat.shiftLeft carry (8 - n)))
+ fromList.impl (accumulateLeft f 0 nats)
+
+Bytes.shiftRight.doc : Doc
+Bytes.shiftRight.doc =
+ use Bytes shiftRight
+ use fromList impl
+ {{
+ `` shiftRight n bs `` shifts all the {type Bytes} `bs` right by `n` bits,
+ where `n` is at most ``8``. Passing `n` larger than `` 8 `` will have the
+ same effect as passing ``8``.
+
+ # Examples
+
+ ```
+ shiftRight 2 0xs010203
+ ```
+
+ ```
+ shiftRight 4 0xsdeadbeef
+ ```
+
+ ```
+ shiftRight 100 0xsfeedface
+ ```
+ }}
+
+-- builtin Bytes.size : Bytes -> Nat
+
+Bytes.size.doc : Doc
+Bytes.size.doc =
+ use Bytes size
+ use fromList impl
+ {{
+ Returns the number of bytes in the given {type Bytes}.
+
+ # Examples
+
+ ```
+ size 0xsfeedface
+ ```
+
+ ```
+ size 0xs
+ ```
+ }}
+
+Bytes.splitAt : Nat -> Bytes -> (Bytes, Bytes)
+Bytes.splitAt index bytes = (Bytes.take index bytes, Bytes.drop index bytes)
+
+Bytes.splitAt.doc : Doc
+Bytes.splitAt.doc =
+ use Bytes splitAt
+ use fromList impl
+ {{
+ `` splitAt index bytes `` splits the provided bytes into two pieces at the
+ given index. The length of the first piece will be the given index, and the
+ length of the second piece will be the length of the original text minus the
+ given index.
+
+ If the index is out of bounds, the first piece will be the original bytes and
+ the second piece will be empty.
+
+ # Examples
+
+ {{
+ docCallout
+ (Some {{ ℹ️ }})
+ {{
+ {type Bytes} are printed in hexadecimal format, so the two characters `c`
+ and `0` combined in `` 0xsc0 `` represents a single byte.
+ }} }}
+
+ ```
+ splitAt 1 0xsc0decafe
+ ```
+
+ ```
+ splitAt 3 0xsc0decafe
+ ```
+
+ ```
+ splitAt 10 0xsc0decafe
+ ```
+
+ ```
+ splitAt 0 0xsc0decafe
+ ```
+ }}
+
+test> Bytes.splitAt.tests =
+ test.verify do
+ (index, expected) =
+ each
+ [ (1, (0xsc0, 0xsdecafe))
+ , (3, (0xsc0deca, 0xsfe))
+ , (10, (0xsc0decafe, 0xs))
+ , (0, (0xs, 0xsc0decafe))
+ ]
+ ensuring do Bytes.splitAt index 0xsc0decafe === expected
+
+-- builtin Bytes.take : Nat -> Bytes -> Bytes
+
+Bytes.take.doc : Doc
+Bytes.take.doc =
+ use Bytes take
+ use fromList impl
+ {{
+ `` take n b `` returns the first `n` bytes of `b`.
+
+ If `n` is greater than or equal to the length of `b`, the result is `b`.
+
+ # Examples
+
+ ```
+ take 0 0xsfeedface
+ ```
+
+ ```
+ take 2 0xsfeedface
+ ```
+
+ ```
+ take 10 0xsfeedface
+ ```
+ }}
+
+-- builtin Bytes.toBase16 : Bytes -> Bytes
+
+Bytes.toBase16.doc : Doc
+Bytes.toBase16.doc =
+ use Bytes toBase16
+ use fromList impl
+ {{
+ Transforms {type Bytes} to their hexadecimal representation in the ASCII
+ character set. See also {toBase16.text} if you'd like to convert directly to
+ {type Text}.
+
+ # Example
+
+ {{ docEval (_ -> toBase16 0xsfeedfacecafebeef) }}
+
+ {{ docEval (_ -> catch do fromUtf8 (toBase16 0xsfeedfacecafebeef)) }}
+ }}
+
+-- builtin Bytes.toBase32 : Bytes -> Bytes
+
+Bytes.toBase32.doc : Doc
+Bytes.toBase32.doc =
+ use fromList impl
+ {{
+ Transforms {type Bytes} to their representation in the
+ [RFC 4648 base32 alphabet](https://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet).
+
+ # Example
+
+ ```
+ toBase32 0xsfeedfacecafebeef
+ ```
+
+ ```
+ catch do fromUtf8 (toBase32 0xsfeedfacecafebeef)
+ ```
+ }}
+
+Bytes.toBase32Hex : Bytes -> Bytes
+Bytes.toBase32Hex = Text.toUtf8 << Bytes.base32Hex
+
+Bytes.toBase32Hex.doc : Doc
+Bytes.toBase32Hex.doc =
+ {{
+ Encodes {type Bytes} using the
+ [base32hex](https://tools.ietf.org/html/rfc4648#section-6) encoding.
+
+ The output is the {type Bytes} representing the ASCII characters of the
+ encoding which uses the characters `0-9` and `A-V`. Use {toBase32Hex.text} if
+ you'd like to instead produce {type Text}.
+
+ # Example
+
+ {{ docEval (_ -> toBase32Hex 0xs14fb9c03d97e) }}
+
+ # See also
+
+ * {fromBase32Hex}
+ * {toBase32}
+ * {fromBase32.impl}
+ * {toBase64}
+ * {fromBase64.impl}
+ * {Bytes.toBase16}
+ * {fromBase16.impl}
+ }}
+
+test> Bytes.toBase32Hex.tests.inverse = test.verify do
+ Each.repeat 100
+ bs = bytes.base32Hex()
+ err = test.raiseFailure "fromBase32Hex failed"
+ ensure (bs === Either.fold err toBase32Hex (fromBase32Hex bs))
+
+Bytes.toBase32Hex.text : Bytes -> Text
+Bytes.toBase32Hex.text bs = match catch do toBase32Hex bs |> fromUtf8 with
+ Left e -> bug "bug in Bytes.toBase32Hex, it somehow produced invalid utf8"
+ Right a -> a
+
+Bytes.toBase32Hex.text.doc : Doc
+Bytes.toBase32Hex.text.doc =
+ {{
+ Like {toBase32Hex}, but converts the result to {type Text}.
+
+ ```
+ toBase32Hex.text 0xs2a3b9082340202394802
+ ```
+ }}
+
+-- builtin Bytes.toBase64 : Bytes -> Bytes
+
+Bytes.toBase64.doc : Doc
+Bytes.toBase64.doc =
+ use fromList impl
+ {{
+ Transforms {type Bytes} to their representation in the
+ [RFC 4648 base64 alphabet](https://en.wikipedia.org/wiki/Base64).
+
+ # Example
+
+ ```
+ toBase64 0xsfeedfacecafebeef
+ ```
+
+ ```
+ catch do fromUtf8 (toBase64 0xsfeedfacecafebeef)
+ ```
+ }}
+
+-- builtin Bytes.toBase64UrlUnpadded : Bytes -> Bytes
+
+Bytes.toBase64UrlUnpadded.doc : Doc
+Bytes.toBase64UrlUnpadded.doc =
+ {{
+ Converts a {type Bytes} to a {type Text} containing the Base64 URL encoding
+ of the bytes, without any padding.
+
+ # Examples
+
+ ```
+ toBase64UrlUnpadded 0xs01020304
+ ```
+
+ ```
+ toBase64UrlUnpadded (Text.toUtf8 "Hello, World!")
+ ```
+ }}
+
+Bytes.toHex : Bytes -> Text
+Bytes.toHex bs = unsafeRun! do Bytes.toBase16 bs |> fromUtf8
+
+Bytes.toHex.deprecated : Bytes -> Text
+Bytes.toHex.deprecated bs =
+ Text.join "" (List.somes (List.map (Nat.toTextBase 16) (Bytes.toList bs)))
+
+Bytes.toHex.doc : Doc
+Bytes.toHex.doc =
+ {{
+ Converts {type Bytes} to its hexadecimal representation in {type Text}.
+
+ # Example
+
+ ```
+ Bytes.toHex (Text.toUtf8 "Hello")
+ ```
+ }}
+
+-- builtin Bytes.toList : Bytes -> [Nat]
+
+Bytes.toList.doc : Doc
+Bytes.toList.doc =
+ {{
+ Convert a {type Bytes} value to a {type List} of {type Nat} values in the
+ range 0 to 255.
+
+ # Example
+
+ ```
+ Bytes.toList 0xs4564646965206c69766573
+ ```
+ }}
+
+Bytes.toNat64sbe : Bytes -> ([Nat], Bytes)
+Bytes.toNat64sbe bs =
+ use List :+
+ go ns bs = match decodeNat64be bs with
+ None -> (ns, bs)
+ Some (n, bs) -> go (ns :+ n) bs
+ go [] bs
+
+Bytes.toNat64sbe.doc : Doc
+Bytes.toNat64sbe.doc =
+ use fromList impl
+ {{
+ Decodes a list of {type Nat}s in
+ [big-endian](https://en.wikipedia.org/wiki/Endianness) order from
+ {type Bytes}. If the {type Bytes} do not contain a complete {type Nat} at the
+ end, returns the {type Nat}s decoded so far and the remaining {type Bytes}.
+
+ # Examples
+
+ ```
+ toNat64sbe 0xs0000000000000001
+ ```
+
+ ```
+ toNat64sbe 0xs00000000000000010000000000000002
+ ```
+
+ ```
+ toNat64sbe 0xscafe
+ ```
+
+ ```
+ toNat64sbe 0xs
+ ```
+ }}
+
+Bytes.truncateLeft : Nat -> Bytes -> Bytes
+Bytes.truncateLeft n =
+ use Nat + /
+ f b =
+ use Nat - == >=
+ use Store put
+ bits = Store.get
+ if bits >= 8 then
+ put (bits - 8)
+ b
+ else
+ if bits == 0 then 0
+ else
+ put 0
+ Nat.and b (Nat.shiftLeft 255 (8 - bits))
+ Bytes.take ((n + 7) / 8) << fromList.impl << accumulateLeft f n
+ << Bytes.toList
+
+Bytes.truncateLeft.doc : Doc
+Bytes.truncateLeft.doc =
+ use fromList impl
+ {{
+ `` truncateLeft n bs `` truncates the {type Bytes} `bs` to `n` bits, counting
+ from the left.
+
+ # Examples
+
+ ```
+ truncateLeft 1 0xsdeadbeef
+ ```
+
+ ```
+ truncateLeft 4 0xsdeadbeef
+ ```
+
+ ```
+ truncateLeft 17 0xsdeadbeef
+ ```
+ }}
+
+Bytes.truncateRight : Nat -> Bytes -> Bytes
+Bytes.truncateRight n bs =
+ use Nat + - /
+ f w =
+ use Nat == >=
+ use Store put
+ bits = Store.get
+ if bits >= 8 then
+ put (bits - 8)
+ w
+ else
+ if bits == 0 then
+ put 0
+ 0
+ else
+ put 0
+ dropBits (64 - bits) w
+ Bytes.toList bs |> accumulateRight f n |> fromList.impl
+ |> Bytes.drop (Bytes.size bs - (n + 7) / 8)
+
+Bytes.truncateRight.doc : Doc
+Bytes.truncateRight.doc =
+ use fromList impl
+ {{
+ `` truncateRight n bs `` truncates the {type Bytes} `bs` to `n` bits,
+ counting from the right.
+
+ # Examples
+
+ ```
+ truncateRight 1 0xsdeadbeef
+ ```
+
+ ```
+ truncateRight 4 0xsdeadbeef
+ ```
+
+ ```
+ truncateRight 19 0xsdeadbeef
+ ```
+ }}
+
+-- builtin Bytes.zlib.compress : Bytes -> Bytes
+
+Bytes.zlib.compress.doc : Doc
+Bytes.zlib.compress.doc =
+ use zlib compress
+ {{
+ Compresses a {type Bytes} value using the
+ [zlib](https://tools.ietf.org/html/rfc1950) format.
+
+ # Examples
+
+ ```
+ compress 0xs4b696c726f79207761732068657265
+ ```
+
+ ```
+ compress 0xs
+ ```
+
+ # See also
+
+ * {zlib.decompress.impl}
+ }}
+
+Bytes.zlib.decompress : Bytes ->{Exception} Bytes
+Bytes.zlib.decompress bs = match zlib.decompress.impl bs with
+ Left e -> raiseGeneric e bs
+ Right bs' -> bs'
+
+Bytes.zlib.decompress.doc : Doc
+Bytes.zlib.decompress.doc =
+ {{
+ Decompresses the given [zlib](https://en.wikipedia.org/wiki/Zlib) compressed
+ {type Bytes}.
+
+ # Example
+
+ ```
+ catch do zlib.decompress (zlib.compress 0xsfeedfacecafebeef)
+ ```
+ }}
+
+-- builtin Bytes.zlib.decompress.impl : Bytes -> Either Text Bytes
+
+Bytes.zlib.decompress.impl.doc : Doc
+Bytes.zlib.decompress.impl.doc =
+ {{
+ Decompresses the given [zlib](https://en.wikipedia.org/wiki/Zlib) compressed
+ {type Bytes}.
+
+ # Example
+
+ ```
+ zlib.decompress.impl (zlib.compress 0xsfeedfacecafebeef)
+ ```
+ }}
+
+CHANGELOG : [(LocalDate, Doc)]
+CHANGELOG =
+ use Stream takeWhile
+ use authors stew
+ use patterns hexDigit
+ [ (LocalDate +2022 7 15, {{ M4 release }})
+ , (LocalDate +2022 7 19, {{ Added {Map.unions} }})
+ , (LocalDate +2022 7 20, {{ Added {onException} }})
+ , (LocalDate +2022 7 29, {{ Added {Nat.fromInt} }})
+ , (LocalDate +2022 9 13, {{ {runarorama} added {flipped.deprecated} }})
+ , ( LocalDate +2022 9 17
+ , {{
+ {runarorama} added {List.flatMap} and renamed old version to
+ {List.flatMapRight}
+ }}
+ )
+ , ( LocalDate +2022 9 20
+ , {{
+ {runarorama} added {Stream.repeat} and {Stream.unfold}
+ }}
+ )
+ , ( LocalDate +2022 9 21
+ , {{
+ {runarorama} renamed `List.foldb` to `List.foldMap`, and added
+ {MVar.modify}, {tryModify}, and {Random.listOf}.
+ }}
+ )
+ , (LocalDate +2022 9 21, {{ {stew} added {hexDigit} and {asciiLetter} }})
+ , ( LocalDate +2022 9 21
+ , {{
+ {runarorama} added {drain}, {takeWhile}, {takeWhile!}.
+ }}
+ )
+ , ( LocalDate +2022 9 22
+ , {{
+ {runarorama} fixed {Stream.take}, {takeWhile}, added tests
+ }}
+ )
+ , (LocalDate +2022 9 22, {{ {runarorama} added {ensuring} and docs }})
+ , ( LocalDate +2022 9 22
+ , {{
+ {runarorama} changed the return type of {ensureWith} to {type Unit}
+ }}
+ )
+ , (LocalDate +2022 9 22, {{ {stew} added {hexDigit} and {asciiLetter} }})
+ , ( LocalDate +2022 9 22
+ , {{
+ {runarorama} moved around a lot of names in the `io` namespace
+ }}
+ )
+ , ( LocalDate +2022 9 22
+ , {{
+ {runarorama} removed buffer size argument from {Socket.receive} and
+ renamed old version to {receiveAtMost}
+ }}
+ )
+ , ( LocalDate +2022 9 23
+ , {{
+ {runarorama} added {type BoundServerSocket} and friends. Cleaned up
+ {type Socket} and {type Tls} API.
+ }}
+ )
+ , ( LocalDate +2022 9 24
+ , {{
+ {dolio} added more
+ [array operations](https://github.com/unisonweb/base/issues/109)
+ }}
+ )
+ , ( LocalDate +2022 9 24
+ , {{
+ {runarorama} merged `io` into `IO` namespace. Deleted `io` namespace.
+ }}
+ )
+ , ( LocalDate +2022 9 28
+ , {{
+ {runarorama} moved `Test.Scope` to `Test.Labels`
+ }}
+ )
+ , ( LocalDate +2022 9 28
+ , {{
+ {runarorama} added {type Map.Nonempty} and friends
+ }}
+ )
+ , ( LocalDate +2022 10 24
+ , {{
+ {stew} added {isInfinity}, {isNegativeInfinity}, {isNaN}
+ }}
+ )
+ , ( LocalDate +2022 11 15
+ , {{
+ {runarorama} added {twosComplement}, {reverseBits}, and {Text.indexOf}
+ }}
+ )
+ , ( LocalDate +2022 11 18
+ , {{
+ {runarorama} added {Nat.isPrefixOf} and {Nat.isSuffixOf}
+ }}
+ )
+ , ( LocalDate +2022 11 18
+ , {{
+ {runarorama} added {type NatSet} and {type NatMap}
+ }}
+ )
+ , ( LocalDate +2022 11 23
+ , {{
+ {runarorama} added `addDuration` to {type OffsetDateTime},
+ {type LocalDateTime}, and friends. Fixed an off-by-one error in
+ {fromInstant}.
+ }}
+ )
+ , ( LocalDate +2022 12 5
+ , {{
+ {runarorama} added {break}, {words}, and {splitOnNewline}.
+ }}
+ )
+ , ( LocalDate +2022 12 6
+ , {{
+ {runarorama} moved `scanr` and `scanl` to `scanLeft` and `scanRight`
+ }}
+ )
+ , ( LocalDate +2023 1 30
+ , {{
+ {runarorama} moved `console.getLine` to `console.readLine`
+ }}
+ )
+ , ( LocalDate +2023 1 30
+ , {{
+ {runarorama} added {type Process} builtin and supporting functions.
+ }}
+ )
+ , ( LocalDate +2023 2 16
+ , {{
+ {runarorama} added {type Class} builtin and supporting functions.
+ }}
+ )
+ , ( LocalDate +2023 5 4
+ , {{
+ {runarorama} added {Optional.toException}, {Pattern.oneOf}, and ISO 8601
+ formatting/parsing of time/calendar types.
+ }}
+ )
+ , ( LocalDate +2023 5 15
+ , {{
+ {runarorama} added {Each.append}, {ifThenElse}, {Each.interleave},
+ {interleaveMap}, {limit}, {Each.negate}, {observe}, {once}, {Each.split}
+ }}
+ )
+ , ( LocalDate +2023 5 15
+ , {{
+ {runarorama} replaced {Bytes.indexOf} with builtin
+ }}
+ )
+ , ( LocalDate +2023 6 21
+ , {{
+ {runarorama} added {type TimeZone}, {getTimeZone}, {currentTimeZone}, and
+ friends.
+ }}
+ )
+ ]
+
+(Char.!=) : Char -> Char -> Boolean
+a Char.!= b =
+ use Char ==
+ Boolean.not (a == b)
+
+Char.!=.doc : Doc
+Char.!=.doc =
+ use Char !=
+ {{
+ Returns `` true `` if the two characters are not equal, and `` false ``
+ otherwise.
+
+ # Examples
+
+ ```
+ ?a != ?a
+ ```
+
+ ```
+ ?a != ?b
+ ```
+ }}
+
+(Char.<) : Char -> Char -> Boolean
+a Char.< b =
+ use Char toNat
+ use Nat <
+ toNat a < toNat b
+
+Char.<.doc : Doc
+Char.<.doc =
+ use Char <
+ {{
+ Returns `` true `` if the Unicode code point of the first character is less
+ than the Unicode code point of the second character, and `` false ``
+ otherwise.
+
+ # Examples
+
+ ```
+ ?a < ?b
+ ```
+
+ ```
+ ?b < ?a
+ ```
+
+ ```
+ ?a < ?a
+ ```
+ }}
+
+(Char.<=) : Char -> Char -> Boolean
+a Char.<= b =
+ use Char toNat
+ use Nat <=
+ toNat a <= toNat b
+
+Char.<=.doc : Doc
+Char.<=.doc =
+ use Char <=
+ {{
+ Returns `` true `` if the Unicode code point of the first character is less
+ than or equal to the Unicode code point of the second character, and `` false
+ `` otherwise.
+
+ # Examples
+
+ ```
+ ?a <= ?b
+ ```
+
+ ```
+ ?b <= ?a
+ ```
+
+ ```
+ ?a <= ?a
+ ```
+ }}
+
+(Char.==) : Char -> Char -> Boolean
+a Char.== b =
+ use Char toNat
+ use Nat ==
+ toNat a == toNat b
+
+Char.==.doc : Doc
+Char.==.doc =
+ use Char ==
+ {{
+ Returns `` true `` if the two characters are equal, and `` false ``
+ otherwise.
+
+ # Examples
+
+ ```
+ ?a == ?a
+ ```
+
+ ```
+ ?a == ?b
+ ```
+ }}
+
+(Char.>) : Char -> Char -> Boolean
+a Char.> b =
+ use Char toNat
+ use Nat >
+ toNat a > toNat b
+
+Char.>.doc : Doc
+Char.>.doc =
+ use Char >
+ {{
+ Returns `` true `` if the Unicode code point of the first character is
+ greater than the Unicode code point of the second character, and `` false ``
+ otherwise.
+
+ # Examples
+
+ ```
+ ?a > ?b
+ ```
+
+ ```
+ ?b > ?a
+ ```
+
+ ```
+ ?a > ?a
+ ```
+ }}
+
+(Char.>=) : Char -> Char -> Boolean
+a Char.>= b =
+ use Char toNat
+ use Nat >=
+ toNat a >= toNat b
+
+Char.>=.doc : Doc
+Char.>=.doc =
+ use Char >=
+ {{
+ Returns `` true `` if the Unicode code point of the first character is
+ greater than or equal to the Unicode code point of the second character, and
+ `` false `` otherwise.
+
+ # Examples
+
+ ```
+ ?a >= ?b
+ ```
+
+ ```
+ ?b >= ?a
+ ```
+
+ ```
+ ?a >= ?a
+ ```
+ }}
+
+Char.ascii.fromBase36Digit : Nat -> Optional Char
+Char.ascii.fromBase36Digit n =
+ use Char toNat
+ use Nat + - < >
+ use fromNat impl
+ if n > 36 then None
+ else
+ if n < 10 then Some (impl (toNat ?0 + n))
+ else Some (impl (toNat ?A + n - 10))
+
+Char.ascii.fromBase36Digit.doc : Doc
+Char.ascii.fromBase36Digit.doc =
+ {{
+ Turns a {type Nat} into a {type Char} representing it as a base-36 digit. If
+ the input is in the range 0-9, then this returns the respective numeric
+ character. If the input is in the range 10-35, then this returns a character
+ in the range A (for 10) through Z (for 35). If the input is higher than 35,
+ this returns ``None``.
+
+ # Examples
+
+ ```
+ fromBase36Digit 2
+ ```
+
+ ```
+ fromBase36Digit 10
+ ```
+
+ ```
+ fromBase36Digit 256
+ ```
+ }}
+
+Char.ascii.isAlphaNum : Char -> Boolean
+Char.ascii.isAlphaNum c = isLetter c || isDigit c
+
+Char.ascii.isAlphaNum.doc : Doc
+Char.ascii.isAlphaNum.doc =
+ {{
+ Returns whether its argument is an ascii alphanumeric character (letter or
+ digit).
+ }}
+
+Char.ascii.isAscii : Char -> Boolean
+Char.ascii.isAscii c = Universal.lt (Char.toNat c) 128
+
+Char.ascii.isAscii.doc : Doc
+Char.ascii.isAscii.doc =
+ {{ Returns whether its argument is an ascii character. }}
+
+Char.ascii.isBlank : Char -> Boolean
+Char.ascii.isBlank = cases
+ ?\s -> true
+ ?\t -> true
+ _ -> false
+
+Char.ascii.isBlank.doc : Doc
+Char.ascii.isBlank.doc = {{ Returns whether its argument is a space or tab. }}
+
+Char.ascii.isControl : Char -> Boolean
+Char.ascii.isControl c =
+ use Char toNat
+ use Nat <
+ toNat c < toNat ?\s
+
+Char.ascii.isControl.doc : Doc
+Char.ascii.isControl.doc =
+ {{ Returns whether its argument is an ascii control character. }}
+
+Char.ascii.isDigit : Char -> Boolean
+Char.ascii.isDigit = Char.inRange ?0 ?9
+
+Char.ascii.isDigit.doc : Doc
+Char.ascii.isDigit.doc = {{ Returns whether its argument is an ascii digit }}
+
+Char.ascii.isGraph : Char -> Boolean
+Char.ascii.isGraph c = if c === ?\s then false else isPrint c
+
+Char.ascii.isGraph.doc : Doc
+Char.ascii.isGraph.doc =
+ {{
+ Returns whether its argument is an ascii graphical character.
+
+ i.e. any printable character except space.
+ }}
+
+Char.ascii.isHexDigit : Char -> Boolean
+Char.ascii.isHexDigit c =
+ use Char inRange
+ isDigit c || inRange ?a ?f c || inRange ?A ?F c
+
+Char.ascii.isHexDigit.doc : Doc
+Char.ascii.isHexDigit.doc =
+ {{ Returns whether its argument is a hexidecimal digit. }}
+
+Char.ascii.isLetter : Char -> Boolean
+Char.ascii.isLetter c = isLower c || isUpper c
+
+Char.ascii.isLetter.doc : Doc
+Char.ascii.isLetter.doc = {{ Returns whether its argument is an ascii letter }}
+
+Char.ascii.isLower : Char -> Boolean
+Char.ascii.isLower = Char.inRange ?a ?z
+
+Char.ascii.isLower.doc : Doc
+Char.ascii.isLower.doc =
+ {{ Returns whether its argument is a lowercase ascii letter. }}
+
+Char.ascii.isPrint : Char -> Boolean
+Char.ascii.isPrint = Char.inRange ?\s ?~
+
+Char.ascii.isPrint.doc : Doc
+Char.ascii.isPrint.doc =
+ {{ Returns whether its argument is an ascii printable character }}
+
+Char.ascii.isPunct : Char -> Boolean
+Char.ascii.isPunct c = if c === ?\s || isAlphaNum c then false else isPrint c
+
+Char.ascii.isPunct.doc : Doc
+Char.ascii.isPunct.doc =
+ {{ Returns whether its argument is an ascii punctuation mark. }}
+
+Char.ascii.isSpace : Char -> Boolean
+Char.ascii.isSpace = cases
+ ?\s -> true
+ ?\f -> true
+ ?\n -> true
+ ?\r -> true
+ ?\t -> true
+ ?\v -> true
+ _ -> false
+
+Char.ascii.isSpace.doc : Doc
+Char.ascii.isSpace.doc =
+ {{ Returns whether its argument is an ascii whitespace character. }}
+
+Char.ascii.isUpper : Char -> Boolean
+Char.ascii.isUpper = Char.inRange ?A ?Z
+
+Char.ascii.isUpper.doc : Doc
+Char.ascii.isUpper.doc =
+ {{ Returns whether its argument is an uppercase ascii letter. }}
+
+Char.ascii.lowerUpperDiff : Int
+Char.ascii.lowerUpperDiff =
+ use Char toNat
+ use Int -
+ use Nat toInt
+ toInt (toNat ?a) - toInt (toNat ?A)
+
+Char.ascii.lowerUpperDiff.doc : Doc
+Char.ascii.lowerUpperDiff.doc =
+ {{
+ The numeric difference between a lowercase ascii character and its uppercase
+ equivalent.
+ }}
+
+Char.ascii.README : Doc
+Char.ascii.README = {{ Utilities for working with ASCII characters. }}
+
+test> Char.ascii.tests.propUpperLower =
+ go _ =
+ use ascii toLower toUpper
+ c = fromNat.impl natInOrder()
+ if isUpper c then expect (toUpper (toLower c) === c)
+ else
+ if isLower c then expect (toLower (toUpper c) === c)
+ else expect (toUpper c === c && toLower c === c)
+ runs 128 go
+
+Char.ascii.tests.propUpperLower.doc : Doc
+Char.ascii.tests.propUpperLower.doc =
+ {{ Test the relationship between toUpper and toLower. }}
+
+test> Char.ascii.tests.toLower = check (ascii.toLower ?A === ?a)
+
+test> Char.ascii.tests.toUpper = check (ascii.toUpper ?a === ?A)
+
+Char.ascii.toBase36Digit : Char -> Optional Nat
+Char.ascii.toBase36Digit c =
+ use Char toNat
+ use Nat + -
+ digitAlphaGap = subtractToInt (toNat ?a) (toNat ?9)
+ if isDigit c then Some (toNat c - toNat ?0)
+ else
+ if isLower c then Some (toNat c - toNat ?a + 10)
+ else if isUpper c then Some (toNat c - toNat ?A + 10) else None
+
+Char.ascii.toBase36Digit.doc : Doc
+Char.ascii.toBase36Digit.doc =
+ {{
+ Turns an alphanumeric character into a digit in base-36.
+
+ * If the caracter is numeric (see {isDigit}), then its numeric value is
+ returned.
+ * If the character is a Latin letter in the ranges `` ?A `` through `` ?Z ``
+ or `` ?a `` through ``?z``, then what's returned is 10 plus the character's
+ position in the latin alphabet.
+ * If the character is not an alphanumeric Latin character, then {None} is
+ returned.
+
+ # Examples
+
+ ```
+ toBase36Digit ?0
+ ```
+
+ ```
+ toBase36Digit ?9
+ ```
+
+ ```
+ toBase36Digit ?a
+ ```
+
+ ```
+ toBase36Digit ?Z
+ ```
+ }}
+
+Char.ascii.toLower : Char -> Char
+Char.ascii.toLower c =
+ use Int +
+ if isUpper c then
+ fromNat.impl (truncate0 (Nat.toInt (Char.toNat c) + lowerUpperDiff))
+ else c
+
+Char.ascii.toLower.doc : Doc
+Char.ascii.toLower.doc =
+ {{
+ Converts its argument to lowercase.
+
+ Does nothing if the argument is not an ascii letter.
+ }}
+
+Char.ascii.toUpper : Char -> Char
+Char.ascii.toUpper c =
+ use Int -
+ if isLower c then
+ fromNat.impl (truncate0 (Nat.toInt (Char.toNat c) - lowerUpperDiff))
+ else c
+
+Char.ascii.toUpper.doc : Doc
+Char.ascii.toUpper.doc =
+ {{
+ Converts its argument to uppercase.
+
+ Does nothing if the argument is not an ascii letter.
+ }}
+
+-- builtin Char.Class.+ : Char.Class -> Char.Class -> Char.Class
+
+Char.Class.+.doc : Doc
+Char.Class.+.doc =
+ use Class + letter number
+ {{
+ Creates a character class that contains a character if it is in either of the
+ given character classes.
+
+ # Examples
+
+ ```
+ is (letter + number) ?a
+ ```
+
+ ```
+ is (letter + number) ?.
+ ```
+
+ ```
+ Pattern.run
+ (Pattern.capture (many (patterns.char (letter + whitespace))))
+ " \t\n abc123"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ * {Class.and} for the logical AND of two character classes.
+ * {Class.not} for the logical NOT of a character class.
+ }}
+
+(Char.Class.-) : Class -> Class -> Class
+x Char.Class.- y = Class.and x (Class.not y)
+
+Char.Class.-.doc : Doc
+Char.Class.-.doc =
+ use Class - letter
+ {{
+ Subtracts one {type Class} from another. The resulting {type Class} will
+ match any {type Char} that is in the first {type Class} but not in the
+ second.
+
+ # Examples
+
+ Check if a character is a letter but not a hex digit:
+
+ ```
+ List.map (is (letter - Class.hexDigit)) (toCharList "rad")
+ ```
+
+ Capture letters up until the first occurrence of `c`.
+
+ ```
+ Pattern.run
+ (Pattern.capture (many (patterns.char (letter - fromChar ?c)))) "abcd"
+ ```
+ }}
+
+-- builtin Char.Class.alphanumeric : Char.Class
+
+Char.Class.alphanumeric.doc : Doc
+Char.Class.alphanumeric.doc =
+ use Class alphanumeric
+ {{
+ Matches alphanumeric characters. This is equivalent to
+ ``Class.and Char.Class.letter Char.Class.number``.
+
+ # Examples
+
+ ```
+ is alphanumeric ?a
+ ```
+
+ ```
+ Pattern.run
+ (Pattern.capture (many (patterns.char alphanumeric))) "abc123*xyz"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+-- builtin Char.Class.and : Char.Class -> Char.Class -> Char.Class
+
+Char.Class.and.doc : Doc
+Char.Class.and.doc =
+ use Class + and printable
+ {{
+ Creates a character class that contains a character if it is in both of the
+ given character classes.
+
+ # Examples
+
+ ```
+ is (and printable whitespace) ?\s
+ ```
+
+ ```
+ is (and printable whitespace) ?\n
+ ```
+
+ ```
+ Pattern.run
+ (Pattern.capture (many (patterns.char (and printable whitespace))))
+ " \t\n abc123"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ * {+} for the logical OR of two character classes.
+ * {Class.not} for the logical NOT of a character class.
+ }}
+
+-- builtin Char.Class.any : Char.Class
+
+Char.Class.any.doc : Doc
+Char.Class.any.doc =
+ use Class any
+ {{
+ Matches any character.
+
+ # Examples
+
+ ```
+ is any ?a
+ ```
+
+ ```
+ Pattern.run (Pattern.capture (many (patterns.char any))) "abc123"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+-- builtin Char.Class.anyOf : [Char] -> Char.Class
+
+Char.Class.anyOf.doc : Doc
+Char.Class.anyOf.doc =
+ {{
+ Creates a character class that contains a character if it is in the given
+ list of characters.
+
+ # Examples
+
+ ```
+ is (anyOf (toCharList "abc")) ?a
+ ```
+
+ ```
+ is (anyOf (toCharList "abc")) ?d
+ ```
+
+ ```
+ Pattern.run
+ (Pattern.capture (many (patterns.char (anyOf (toCharList "abc")))))
+ "abc123"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ * {in} for a version of this that takes {type Text} instead of a list.
+ }}
+
+Char.Class.ascii : Class
+Char.Class.ascii = Class.range ?\0 ?
+
+Char.Class.ascii.doc : Doc
+Char.Class.ascii.doc =
+ use Class ascii
+ use Pattern capture run
+ use patterns char
+ {{
+ Matches ASCII characters. The ASCII character set is a subset of the Unicode
+ character set. It contains the first 128 characters of Unicode.
+
+ # Examples
+
+ ```
+ is ascii ?a
+ ```
+
+ ```
+ is ascii ?€
+ ```
+
+ ```
+ run (capture (many (char ascii))) "abc123"
+ ```
+
+ ```
+ run (capture (many (char ascii))) "123 €"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+-- builtin Char.Class.control : Char.Class
+
+Char.Class.control.doc : Doc
+Char.Class.control.doc =
+ use Class control
+ use Pattern capture run
+ use patterns char
+ {{
+ Matches control characters. Control characters are non-printable characters
+ that are used to control the display of text.
+
+ # Examples
+
+ ```
+ is control ?\0
+ ```
+
+ ```
+ is control ?a
+ ```
+
+ ```
+ run (capture (many (char control))) "abc123"
+ ```
+
+ ```
+ run (capture (many (char control))) "\nabc\n123\n"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+Char.Class.digit : Class
+Char.Class.digit = Class.range ?0 ?9
+
+Char.Class.digit.doc : Doc
+Char.Class.digit.doc =
+ use Class digit
+ {{
+ Matches decimal digits. This is equivalent to ``Class.range ?0 ?9``.
+
+ # Examples
+
+ ```
+ is digit ?0
+ ```
+
+ ```
+ is digit ?a
+ ```
+
+ ```
+ Pattern.run (Pattern.capture (many (patterns.char digit))) "123abc"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+Char.Class.doc : Doc
+Char.Class.doc =
+ use Class +
+ use patterns char
+ {{
+ A {type Class} is a set of characters. It can be used to construct a
+ {type Pattern} that matches a single {type Char} in a {type Text} using
+ {char}, or a sequence of characters using {many} and
+ {{ docLink (docEmbedTermLink do some) }}.
+
+ # Provided classes
+
+ The following classes are provided:
+
+ * {Class.alphanumeric} - alphanumeric characters.
+ * {Class.any} - any character.
+ * {Class.ascii} - ASCII characters.
+ * {Class.control} - control characters.
+ * {Class.digit} - decimal digits.
+ * {Class.hexDigit} - hexadecimal digits.
+ * {Class.letter} - letters in any script.
+ * {Class.lower} - lowercase letters in any script.
+ * {mark} - marks such as accents and diacritics.
+ * {Class.number} - numbers in any script.
+ * {Class.printable} - printable characters including space.
+ * {Class.punctuation} - punctuation characters such as `!` and `?`.
+ * {separator} - spaces and paragraph separators.
+ * {symbol} - symbols such as `$` and `%`.
+ * {Class.upper} - uppercase letters in any script.
+ * {visible} - printable characters excluding space.
+ * {whitespace} - whitespace characters such as spaces, tabs, and newlines.
+ * {word} - word characters (alphanumeric or connecting punctuation).
+
+ # Class combinators
+
+ You can construct a new class from existing classes using the following
+ combinators.
+
+ Take the intersection of two classes, matching a character that is in both
+ classes:
+
+ @signature{Class.and}
+
+ Union two classes, matching a character that is in either class:
+
+ @signature{+}
+
+ Negate a class, matching a character that is not in the class:
+
+ @signature{Class.not}
+
+ You can also construct a class from a character range:
+
+ @signature{Class.range}
+
+ Or from a {type List} of characters:
+
+ @signature{anyOf}
+
+ Or from a {type Text}:
+
+ @signature{in}
+
+ # Testing for membership
+
+ Test if a character is in the given class:
+
+ @signature{is}
+
+ # Constructing a pattern from a class
+
+ Construct a {type Pattern} that matches a single character in the given
+ class:
+
+ @signature{char}
+
+ # See also
+
+ * {many} and {{ docLink (docEmbedTermLink do some) }} to match a sequence
+ of characters.
+ * [Unicode Character Classes](https://www.compart.com/en/unicode/category)
+ }}
+
+Char.Class.fromChar : Char -> Class
+Char.Class.fromChar c = Class.range c c
+
+Char.Class.fromChar.doc : Doc
+Char.Class.fromChar.doc =
+ {{
+ Creates a character class containing only the given character.
+
+ # Examples
+
+ ```
+ Pattern.run (patterns.char (fromChar ?a)) "abc"
+ ```
+
+ ```
+ is (fromChar ?a) ?a
+ ```
+
+ ```
+ is (fromChar ?a) ?b
+ ```
+ }}
+
+Char.Class.hexDigit : Class
+Char.Class.hexDigit =
+ use Class + range
+ Class.digit + range ?a ?f + range ?A ?F
+
+Char.Class.hexDigit.doc : Doc
+Char.Class.hexDigit.doc =
+ use Class + hexDigit range
+ {{
+ Matches hexadecimal digits. This is equivalent to
+ {{
+ docExample
+ 2
+ (_ Char.Class.range1 Char.Class.or1 ->
+ range ?0 ?9 + range ?a ?f + range ?A ?F)
+ }}.
+
+ # Examples
+
+ ```
+ is hexDigit ?0
+ ```
+
+ ```
+ is hexDigit ?a
+ ```
+
+ ```
+ is hexDigit ?A
+ ```
+
+ ```
+ is hexDigit ?g
+ ```
+
+ ```
+ Pattern.run (Pattern.capture (many (patterns.char hexDigit))) "123abcdefgh"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+Char.Class.in : Text -> Class
+Char.Class.in = anyOf << toCharList
+
+Char.Class.in.doc : Doc
+Char.Class.in.doc =
+ {{
+ Creates a character class that contains a character if it is in the given
+ {type Text}.
+
+ # Examples
+
+ ```
+ is (in "abc") ?a
+ ```
+
+ ```
+ is (in "abc") ?d
+ ```
+
+ ```
+ Pattern.run
+ (Pattern.capture (many (patterns.char (in "abcr")))) "abracadabra"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ * {anyOf} for a version of this that takes a list instead of {type Text}.
+ }}
+
+-- builtin Char.Class.is : Char.Class -> Char -> Boolean
+
+Char.Class.is.doc : Doc
+Char.Class.is.doc =
+ use Class letter
+ {{
+ Determines if a character is in a character class.
+
+ # Examples
+
+ ```
+ is letter ?a
+ ```
+
+ ```
+ is letter ?.
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+-- builtin Char.Class.letter : Char.Class
+
+Char.Class.letter.doc : Doc
+Char.Class.letter.doc =
+ use Class letter
+ {{
+ Matches letters in any script.
+
+ # Examples
+
+ ```
+ is letter ?a
+ ```
+
+ ```
+ is letter ?€
+ ```
+
+ ```
+ is letter ?ð
+ ```
+
+ ```
+ Pattern.run (Pattern.capture (many (patterns.char letter))) "abc123"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+-- builtin Char.Class.lower : Char.Class
+
+Char.Class.lower.doc : Doc
+Char.Class.lower.doc =
+ use Class lower
+ {{
+ Matches lowercase letters in any script.
+
+ # Examples
+
+ ```
+ is lower ?a
+ ```
+
+ ```
+ is lower ?A
+ ```
+
+ ```
+ is lower ?ð
+ ```
+
+ ```
+ Pattern.run (Pattern.capture (many (patterns.char lower))) "abcABC"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+-- builtin Char.Class.mark : Char.Class
+
+Char.Class.mark.doc : Doc
+Char.Class.mark.doc =
+ {{
+ Matches non-spacing, spacing, and enclosing marks. These are characters that
+ are used to modify the appearance of the preceding character.
+
+ # Examples
+
+ ```
+ is mark ?a
+ ```
+
+ ```
+ is mark ?̀
+ ```
+
+ If the above example looks strange, it is because the character `?\768`
+ modifies the appearance of the preceding question mark that Unison uses to
+ denote a {type Char} literal. The character `?\768` is a combining grave
+ accent, which is a non-spacing mark.
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ * [Unicode Character Classes](https://www.compart.com/en/unicode/category)
+ }}
+
+-- builtin Char.Class.not : Char.Class -> Char.Class
+
+Char.Class.not.doc : Doc
+Char.Class.not.doc =
+ use Class + letter not
+ {{
+ Creates a character class that contains a character if it is not in the given
+ character class.
+
+ # Examples
+
+ ```
+ is (not letter) ?a
+ ```
+
+ ```
+ is (not letter) ?.
+ ```
+
+ ```
+ Pattern.run (Pattern.capture (many (patterns.char (not letter)))) "123abc"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ * {Class.and} for the logical AND of two character classes.
+ * {+} for the logical OR of two character classes.
+ }}
+
+-- builtin Char.Class.number : Char.Class
+
+Char.Class.number.doc : Doc
+Char.Class.number.doc =
+ use Class number
+ {{
+ Matches numbers in any script.
+
+ # Examples
+
+ ```
+ is number ?0
+ ```
+
+ ```
+ is number ?a
+ ```
+
+ ```
+ is number ?€
+ ```
+
+ ```
+ Pattern.run (Pattern.capture (many (patterns.char number))) "123abc"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+-- builtin Char.Class.printable : Char.Class
+
+Char.Class.printable.doc : Doc
+Char.Class.printable.doc =
+ use Class printable
+ use Pattern capture run
+ use patterns char
+ {{
+ Matches printable characters. This is equivalent to
+ ``Class.not Char.Class.control``.
+
+ # Examples
+
+ ```
+ is printable ?a
+ ```
+
+ ```
+ is printable ?\0
+ ```
+
+ ```
+ run (capture (many (char printable))) "abc123"
+ ```
+
+ ```
+ run (capture (many (char printable))) "abc\n123"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+-- builtin Char.Class.punctuation : Char.Class
+
+Char.Class.punctuation.doc : Doc
+Char.Class.punctuation.doc =
+ use Class punctuation
+ {{
+ Matches punctuation characters.
+
+ This class includes the following Unicode categories:
+
+ * Connector Punctuation (Pc) (e.g. ``?_``)
+ * Dash Punctuation (Pd) (e.g. ``?-``)
+ * Open Punctuation (Ps) (e.g. ``?(``)
+ * Close Punctuation (Pe) (e.g. ``?)``)
+ * Initial Punctuation (Pi) (e.g. ``?«``)
+ * Final Punctuation (Pf) (e.g. ``?»``)
+ * Other Punctuation (Po) (e.g. ``?¿``)
+
+ # Examples
+
+ ```
+ is punctuation ?.
+ ```
+
+ ```
+ is punctuation ?a
+ ```
+
+ ```
+ Pattern.run
+ (Pattern.capture (many (patterns.char punctuation))) ".,(abc123)"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ * [Unicode Character Classes](https://www.compart.com/en/unicode/category)
+ }}
+
+-- builtin Char.Class.range : Char -> Char -> Char.Class
+
+Char.Class.range.doc : Doc
+Char.Class.range.doc =
+ use Class range
+ {{
+ Creates a character class that contains a character if it is in the given
+ range of characters.
+
+ # Examples
+
+ ```
+ is (range ?a ?z) ?a
+ ```
+
+ ```
+ is (range ?a ?z) ?A
+ ```
+
+ ```
+ Pattern.run (Pattern.capture (many (patterns.char (range ?a ?z)))) "abc123"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+-- builtin Char.Class.separator : Char.Class
+
+Char.Class.separator.doc : Doc
+Char.Class.separator.doc =
+ {{
+ Matches separator characters including spaces, no-break spaces, quads, and
+ Unicode paragraph separators.
+
+ # Examples
+
+ ```
+ is separator ?\n
+ ```
+
+ ```
+ is separator ?a
+ ```
+
+ ```
+ Pattern.run
+ (Pattern.capture (many (patterns.char separator))) " \8233 abc123"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ * [Unicode Character Classes](https://www.compart.com/en/unicode/category)
+ }}
+
+-- builtin Char.Class.symbol : Char.Class
+
+Char.Class.symbol.doc : Doc
+Char.Class.symbol.doc =
+ use Class +
+ use Pattern capture run
+ use patterns char
+ {{
+ Matches symbols. These are characters that are used to represent mathematical
+ or logical concepts, currency, etc.
+
+ # Examples
+
+ ```
+ is symbol ?+
+ ```
+
+ ```
+ is symbol ?a
+ ```
+
+ The `+` character is a mathematical symbol, but the `/` (forward slash) is
+ not:
+
+ ```
+ run (capture (many (char symbol))) "+/-10"
+ ```
+
+ However, the `∕` (division) character is a symbol:
+
+ ```
+ run (capture (many (char (Class.number + symbol)))) "1∕10"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ * [Unicode Character Classes](https://www.compart.com/en/unicode/category)
+ }}
+
+-- builtin Char.Class.upper : Char.Class
+
+Char.Class.upper.doc : Doc
+Char.Class.upper.doc =
+ use Class upper
+ {{
+ Matches uppercase letters in any script.
+
+ # Examples
+
+ ```
+ is upper ?a
+ ```
+
+ ```
+ is upper ?A
+ ```
+
+ ```
+ Pattern.run (Pattern.capture (many (patterns.char upper))) "ABCabc"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+Char.Class.visible : Class
+Char.Class.visible = Class.and Class.printable (Class.not whitespace)
+
+Char.Class.visible.doc : Doc
+Char.Class.visible.doc =
+ {{
+ Matches visible characters, which are printable characters that are not
+ whitespace.
+
+ This is equivalent to
+ ``Class.and Char.Class.printable (Char.Class.not Char.Class.whitespace)``.
+
+ # Examples
+
+ ```
+ is visible ?a
+ ```
+
+ ```
+ is visible ?\s
+ ```
+
+ ```
+ Pattern.run (Pattern.capture (many (patterns.char visible))) "abc 123"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+-- builtin Char.Class.whitespace : Char.Class
+
+Char.Class.whitespace.doc : Doc
+Char.Class.whitespace.doc =
+ {{
+ Matches whitespace characters. These are characters that are used to separate
+ text into words, sentences, and paragraphs.
+
+ This class includes the unicode Space Separator (Zs) category, as well as
+ tabs, newlines, carriage returns, form feeds, and vertical tabs.
+
+ # Examples
+
+ ```
+ is whitespace ?\s
+ ```
+
+ ```
+ is whitespace ?a
+ ```
+
+ ```
+ Pattern.run
+ (Pattern.capture (many (patterns.char whitespace))) " \t\n abc123"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ * [Unicode Character Classes](https://www.compart.com/en/unicode/category)
+ }}
+
+Char.Class.word : Class
+Char.Class.word =
+ use Class +
+ Class.alphanumeric + anyOf [?_, ?‿, ?⁀, ?⁔, ?︳, ?︴, ?﹍, ?﹎, ?﹏, ?_]
+
+Char.Class.word.doc : Doc
+Char.Class.word.doc =
+ {{
+ Matches word characters. These are characters that are used to form words.
+
+ This includes the `` Class.alphanumeric `` class, as well as the following
+ characters: ``[?_, ?‿, ?⁀, ?⁔, ?︳, ?︴, ?﹍, ?﹎, ?﹏, ?_]``.
+
+ # Examples
+
+ ```
+ is word ?a
+ ```
+
+ ```
+ is word ?.
+ ```
+
+ ```
+ Pattern.run (Pattern.capture (many (patterns.char word))) "abc_123.ABC"
+ ```
+
+ # See also
+
+ * {type Class} for more information on character classes.
+ }}
+
+Char.doc : Doc
+Char.doc =
+ use Char < <= == > >= fromNat inRange toLowercase toNat toText toUppercase
+ use Class letter
+ {{
+ {type Char} is the type of individual Unicode characters (more precisely,
+ single code points) like ``?A``, ``?b``, ``?🌈``, and ``?⭐``.
+
+ # Construction
+
+ A **character literal** is a single character preceded by a question mark,
+ like `` ?A `` or ``?🌈``. It has type {type Char}.
+
+ Use
+ [escape sequences](https://www.unison-lang.org/learn/language-reference/escape-sequences/)
+ to construct characters that are not printable Unicode characters. For
+ example, `` ?\n `` is a newline character.
+
+ You can also get a {type Char} from its code point as a {type Nat}, using
+ {fromNat}:
+
+ ```
+ fromNat 65
+ ```
+
+ ```
+ fromNat 128170
+ ```
+
+ You can get a {type Char} representing a digit in base-36 (ASCII characters
+ `` ?0 `` - `` ?9 `` and `` ?A `` to ``?Z``) from the {type Nat} value of
+ that digit, using {fromBase36Digit}:
+
+ ```
+ fromBase36Digit 2
+ ```
+
+ ```
+ fromBase36Digit 15
+ ```
+
+ ```
+ fromBase36Digit 37
+ ```
+
+ Convert a {type Text} to its list of characters using {toCharList}:
+
+ ```
+ toCharList "Hello, world!"
+ ```
+
+ ## Comparing characters
+
+ You can compare characters using the usual comparison operators: {<},
+ {<=}, {==}, {>=}, and {>}. These operators compare the code points of
+ the characters.
+
+ A character is less than another character if its code point is less
+ than the other character's code point. For example, `` ?A `` is less
+ than ``?B``, and `` ?🌈 `` is less than ``?⭐``.
+
+ Check if a {type Char} is between two other {type Char}s using
+ {inRange}:
+
+ ```
+ inRange ?A ?Z ?B
+ ```
+
+ ```
+ inRange ?A ?Z ?a
+ ```
+
+ ## Convertring to other types
+
+ You can convert a {type Char} to its code point as a {type Nat} using
+ {toNat}:
+
+ ```
+ toNat ?A
+ ```
+
+ ```
+ toNat ?🌈
+ ```
+
+ You can convert a {type Char} to a single-character {type Text} using
+ {toText}:
+
+ ```
+ toText ?A
+ ```
+
+ ## Character classes
+
+ {type Class} provides a number of functions for checking if a
+ {type Char} is in a particular class of characters and for creating new
+ character classes.
+
+ For example, one way to check if a {type Char} is a letter is to use
+ {letter}:
+
+ ```
+ is letter ?A
+ ```
+
+ Check if a {type Char} is a digit or numeral symbol:
+
+ ```
+ is Class.number ?0
+ ```
+
+ You can also use {isDigit} to check if a {type Char} is a digit between
+ 0 and 9:
+
+ ```
+ isDigit ?4
+ ```
+
+ Check if a {type Char} is a whitespace:
+
+ ```
+ is whitespace ?\s
+ ```
+
+ See {type Class} for more information on character classes.
+
+ ## Upper- and lowercase
+
+ You can transform a lowercase {type Char} to uppercase using
+ {toUppercase}:
+
+ ```
+ toUppercase ?a
+ ```
+
+ You can transform an uppercase {type Char} to lowercase using
+ {toLowercase}:
+
+ ```
+ toLowercase ?A
+ ```
+ }}
+
+Char.fromNat : Nat -> Optional Char
+Char.fromNat n =
+ use Nat >
+ if n > 1114111 then None else Some (fromNat.impl n)
+
+Char.fromNat.doc : Doc
+Char.fromNat.doc =
+ {{
+ Convert a {type Nat} value to a {type Char} value. If the {type Nat} is not
+ in the range `` 0 `` to `` 1114111 `` (inclusive), the result is {None}.
+
+ # Example
+
+ ```
+ Char.fromNat 128512
+ ```
+ }}
+
+-- builtin Char.fromNat.impl : Nat -> Char
+
+Char.inRange : Char -> Char -> Char -> Boolean
+Char.inRange lo hi c =
+ use Char toNat
+ use Universal lteq
+ code = toNat c
+ lteq (toNat lo) code && lteq code (toNat hi)
+
+Char.inRange.doc : Doc
+Char.inRange.doc =
+ {{
+ `` Char.inRange lo hi char `` returns true if and only if `char` is lexically
+ between `lo` and `hi` (inclusively).
+ }}
+
+Char.isWhitespace : Char -> Boolean
+Char.isWhitespace = is whitespace
+
+Char.isWhitespace.doc : Doc
+Char.isWhitespace.doc =
+ {{
+ Tests if a {type Char} is a whitespace character. Returns `` true `` if the
+ character is a space ``?s``, a tab ``?t``, a carriage return ``?r``, a new
+ line ``?\n``, a vertical tab (Unicode `0xb`), or any character from the
+ ["Space Separator"](https://www.compart.com/en/unicode/category/Zs) Unicode
+ category.
+
+ # Deprecation notice
+
+ This function is deprecated. Use the `` whitespace `` character class
+ instead:
+
+ ```
+ is whitespace ?\n
+ ```
+ }}
+
+Char.range : Char -> Char -> [Char]
+Char.range from to =
+ use Char toNat
+ Nat.range (toNat from) (toNat to)
+ |> List.flatMap (Char.fromNat >> Optional.toList)
+
+Char.range.doc : Doc
+Char.range.doc =
+ use Char range
+ {{
+ Returns a list of characters in the range from the first character to the
+ second character, exclusive of the second character. The characters are in
+ ascending order.
+
+ If the first character is greater than the second character, the result is an
+ empty list.
+
+ # Examples
+
+ ```
+ range ?a ?e
+ ```
+
+ ```
+ range ?e ?a
+ ```
+ }}
+
+Char.rangeClosed : Char -> Char -> [Char]
+Char.rangeClosed from to =
+ use Char toNat
+ Nat.rangeClosed (toNat from) (toNat to)
+ |> List.flatMap (Char.fromNat >> Optional.toList)
+
+Char.rangeClosed.doc : Doc
+Char.rangeClosed.doc =
+ use Char rangeClosed
+ {{
+ Returns a list of characters in the range from the first character to the
+ second character, inclusive of the second character. The characters are in
+ ascending order.
+
+ If the first character is greater than the second character, the result is an
+ empty list.
+
+ # Examples
+
+ ```
+ rangeClosed ?a ?e
+ ```
+
+ ```
+ rangeClosed ?e ?a
+ ```
+ }}
+
+Char.toLowercase : Char -> Char
+Char.toLowercase =
+ Char.toText >> Text.toLowercase >> toCharList >> List.head
+ >> getOrBug "Char.toLowercase"
+
+Char.toLowercase.doc : Doc
+Char.toLowercase.doc =
+ use Char toLowercase
+ {{
+ Convert a {type Char} to lowercase. If the character is not uppercase, it is
+ returned unchanged.
+
+ # Examples
+
+ ```
+ toLowercase ?A
+ ```
+
+ ```
+ toLowercase ?Λ
+ ```
+
+ ```
+ toLowercase ?a
+ ```
+
+ ```
+ toLowercase ?🌈
+ ```
+ }}
+
+-- builtin Char.toNat : Char -> Nat
+
+Char.toNat.doc : Doc
+Char.toNat.doc =
+ use Char toNat
+ {{
+ Convert a {type Char} to a {type Nat} representing the Unicode code point of
+ the character.
+
+ # Examples
+
+ ```
+ toNat ?a
+ ```
+
+ ```
+ toNat ?👋
+ ```
+ }}
+
+-- builtin Char.toText : Char -> Text
+
+Char.toText.doc : Doc
+Char.toText.doc =
+ use Char toText
+ {{
+ Convert a {type Char} to a one-character {type Text}.
+
+ # Examples
+
+ ```
+ toText ?a
+ ```
+
+ ```
+ toText ?👋
+ ```
+ }}
+
+Char.toUppercase : Char -> Char
+Char.toUppercase =
+ Char.toText >> Text.toUppercase >> toCharList >> List.head
+ >> getOrBug "Char.toUppercase"
+
+Char.toUppercase.doc : Doc
+Char.toUppercase.doc =
+ use Char toUppercase
+ {{
+ Convert a {type Char} to uppercase. If the character is not lowercase, it is
+ returned unchanged.
+
+ # Examples
+
+ ```
+ toUppercase ?a
+ ```
+
+ ```
+ toUppercase ?λ
+ ```
+
+ ```
+ toUppercase ?A
+ ```
+
+ ```
+ toUppercase ?🌈
+ ```
+ }}
+
+Char.toUtf8 : Char -> Bytes
+Char.toUtf8 = Text.toUtf8 << Char.toText
+
+Char.toUtf8.doc : Doc
+Char.toUtf8.doc =
+ use Char toUtf8
+ {{
+ Converts a {type Char} to its UTF-8 representation as {type Bytes}.
+
+ # Examples
+
+ ```
+ toUtf8 ?a
+ ```
+
+ ```
+ toUtf8 ?📌
+ ```
+ }}
+
+crypto.Ed25519.PrivateKey.doc : Doc
+crypto.Ed25519.PrivateKey.doc =
+ {{
+ A private key for the Ed25519 signature scheme.
+
+ # Example
+
+ ```
+ catch do
+ Ed25519.PrivateKey.PrivateKey
+ (fromBase64
+ (Text.toUtf8 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="))
+ ```
+ }}
+
+crypto.Ed25519.PublicKey.doc : Doc
+crypto.Ed25519.PublicKey.doc =
+ {{
+ A public key for the Ed25519 signature scheme.
+
+ # Example
+
+ ```
+ catch do
+ Ed25519.PublicKey.PublicKey
+ (fromBase64
+ (Text.toUtf8 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="))
+ ```
+ }}
+
+crypto.Ed25519.PublicKey.toBytes : Ed25519.PublicKey -> Bytes
+crypto.Ed25519.PublicKey.toBytes = cases Ed25519.PublicKey.PublicKey p -> p
+
+crypto.Ed25519.PublicKey.toBytes.doc : Doc
+crypto.Ed25519.PublicKey.toBytes.doc =
+ {{
+ Converts a {type Ed25519.PublicKey} to {type Bytes}.
+
+ # Example
+
+ ```
+ catch do
+ PublicKey.toBytes
+ (Ed25519.PublicKey.PublicKey
+ (fromBase64
+ (Text.toUtf8 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")))
+ ```
+ }}
+
+crypto.Ed25519.sign :
+ Ed25519.PrivateKey
+ -> Ed25519.PublicKey
+ -> Bytes
+ ->{Exception} Ed25519.Signature
+crypto.Ed25519.sign = cases
+ Ed25519.PrivateKey.PrivateKey p, Ed25519.PublicKey.PublicKey q, msg ->
+ Ed25519.Signature.Signature
+ (Either.toException (Ed25519.sign.impl p q msg))
+
+crypto.Ed25519.sign.doc : Doc
+crypto.Ed25519.sign.doc =
+ use fromList impl
+ {{
+ Signs a message with an Ed25519 key pair and returns the signature.
+
+ # Example
+
+ ```
+ catch do
+ private =
+ Ed25519.PrivateKey.PrivateKey
+ 0xs1498b5467a63dffa2dc9d9e069caf075d16fc33fdd4c3b01bfadae6433767d93
+ public =
+ Ed25519.PublicKey.PublicKey
+ 0xsb7a3c12dc0c8c748ab07525b701122b88bd78f600c76342d27f25e5f92444cde
+ Ed25519.sign private public (Text.toUtf8 "Hello, world!")
+ ```
+ }}
+
+-- builtin crypto.Ed25519.sign.impl :
+-- Bytes -> Bytes -> Bytes -> Either Failure Bytes
+
+crypto.Ed25519.Signature.doc : Doc
+crypto.Ed25519.Signature.doc =
+ {{
+ A signature for the Ed25519 signature scheme.
+
+ # Example
+
+ ```
+ catch do
+ Ed25519.Signature.Signature
+ (fromBase64
+ (Text.toUtf8 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="))
+ ```
+ }}
+
+crypto.Ed25519.verify :
+ Ed25519.PublicKey -> Bytes -> Ed25519.Signature ->{Exception} Boolean
+crypto.Ed25519.verify = cases
+ Ed25519.PublicKey.PublicKey p, msg, Ed25519.Signature.Signature s ->
+ Either.toException (Ed25519.verify.impl p msg s)
+
+crypto.Ed25519.verify.doc : Doc
+crypto.Ed25519.verify.doc =
+ use Ed25519 verify
+ use Ed25519.PublicKey PublicKey
+ use Ed25519.Signature Signature
+ use Text toUtf8
+ use fromList impl
+ {{
+ Verifies an Ed25519 signature on a message.
+
+ # Examples
+
+ ```
+ catch do
+ signature =
+ Signature
+ 0xsb3f9407deccf1e46cdddef8d10f22084a4807322499e753cab13d65974216b9c252cb82375992cfb9231c2ba2d564860f07ccfd9630e891b6aa363d9568bec0b
+ public =
+ PublicKey
+ 0xsb7a3c12dc0c8c748ab07525b701122b88bd78f600c76342d27f25e5f92444cde
+ verify public (toUtf8 "Hello, world!") signature
+ ```
+
+ ```
+ catch do
+ signature =
+ Signature
+ 0xsb3f9407deccf1e46cdddef8d10f22084a4807322499e753cab13d65974216b9c252cb82375992cfb9231c2ba2d564860f07ccfd9630e891b6aa363d9568bec0b
+ public =
+ PublicKey
+ 0xsb7a3c12dc0c8c748ab07525b701122b88bd78f600c76342d27f25e5f92444cde
+ verify public (toUtf8 "Bogus message") signature
+ ```
+ }}
+
+-- builtin crypto.Ed25519.verify.impl :
+-- Bytes -> Bytes -> Bytes -> Either Failure Boolean
+
+-- builtin crypto.hash : crypto.HashAlgorithm -> a -> Bytes
+
+crypto.hash.doc : Doc
+crypto.hash.doc =
+ use Nat +
+ {{
+ `` hash algo a `` hashes any value `a` using
+ {{ docExample 1 do algo -> (algo : HashAlgorithm) }}.
+
+ ```
+ hash Sha3_256 ("all are hashable!", [1, 2, 3, 4])
+ ```
+
+ ```
+ hash Blake2b_512 [Left "including functions!", Right do 1 + 1]
+ ```
+
+ Also see @inlineSignature{hashBytes} for hashing a {type Bytes} value.
+
+ {{
+ docCallout
+ (Some {{ ℹ️ }})
+ {{
+ While you can also pass a {type Bytes} to {hash}, the hash produced by this
+ function mixes in information about the type of the input value (and this
+ is necessary to ensure that values of different types don't coicidentally
+ hash the same). If you need the results to agree with some other system
+ that is just expecting a hash of nothing but the bytes, use {hashBytes}.
+ }} }}
+ }}
+
+-- builtin crypto.HashAlgorithm.Blake2b_256 : crypto.HashAlgorithm
+
+crypto.HashAlgorithm.Blake2b_256.doc : Doc
+crypto.HashAlgorithm.Blake2b_256.doc =
+ {{
+ The
+ [BLAKE2b 256-bit](https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2)
+ hash algorithm.
+
+ # Example
+
+ ```
+ hash Blake2b_256 (Text.toUtf8 "hello")
+ ```
+ }}
+
+-- builtin crypto.HashAlgorithm.Blake2b_512 : crypto.HashAlgorithm
+
+crypto.HashAlgorithm.Blake2b_512.doc : Doc
+crypto.HashAlgorithm.Blake2b_512.doc =
+ {{
+ The
+ [BLAKE2b 512-bit](https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2)
+ hash algorithm.
+
+ # Example
+
+ ```
+ hash Blake2b_512 (Text.toUtf8 "hello")
+ ```
+ }}
+
+-- builtin crypto.HashAlgorithm.Blake2s_256 : crypto.HashAlgorithm
+
+crypto.HashAlgorithm.Blake2s_256.doc : Doc
+crypto.HashAlgorithm.Blake2s_256.doc =
+ {{
+ The
+ [BLAKE2s 256-bit](https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2)
+ hash algorithm.
+
+ # Example
+
+ ```
+ hash Blake2s_256 (Text.toUtf8 "hello")
+ ```
+ }}
+
+crypto.HashAlgorithm.doc : Doc
+crypto.HashAlgorithm.doc =
+ {{
+ A hashing algorithm. Passed as a parameter to functions like {hash} or
+ {hashBytes}.
+
+ __Instances:__
+
+ * {Sha1}: See [SHA-1](https://en.wikipedia.org/wiki/SHA-1)
+ * {Sha2_256}: See [SHA-256](https://en.wikipedia.org/wiki/SHA-2), 256-bit
+ output
+ * {Sha2_512}: See [SHA-512](https://en.wikipedia.org/wiki/SHA-2), 512-bit
+ output
+ * {Sha3_256}: See [SHA3-256](https://en.wikipedia.org/wiki/SHA-3), 256-bit
+ output
+ * {Sha3_512}: See [SHA3-512](https://en.wikipedia.org/wiki/SHA-3), 512-bit
+ output
+ * {Blake2s_256}: See
+ [BLAKE2s-256]({{
+ docWord "https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2"
+ }}), 256-bit output
+ * {Blake2b_256}: See
+ [BLAKE2b-256]({{
+ docWord "https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2"
+ }}), 256-bit output
+ * {Blake2b_512}: See
+ [BLAKE2b-512]({{
+ docWord "https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2"
+ }}), 512-bit output
+
+ You can also use `find : HashAlgorithm` to find instances.
+ }}
+
+-- builtin crypto.HashAlgorithm.Md5 : crypto.HashAlgorithm
+
+crypto.HashAlgorithm.Md5.doc : Doc
+crypto.HashAlgorithm.Md5.doc =
+ {{
+ The [Md5](https://en.wikipedia.org/wiki/MD5) message digest algorithm.
+
+ # Example
+
+ ```
+ hash Md5 (Text.toUtf8 "hello")
+ ```
+ }}
+
+-- builtin crypto.HashAlgorithm.Sha1 : crypto.HashAlgorithm
+
+crypto.HashAlgorithm.Sha1.doc : Doc
+crypto.HashAlgorithm.Sha1.doc =
+ {{ The [SHA1](https://en.wikipedia.org/wiki/SHA-1) hash algorithm. }}
+
+-- builtin crypto.HashAlgorithm.Sha2_256 : crypto.HashAlgorithm
+
+crypto.HashAlgorithm.Sha2_256.doc : Doc
+crypto.HashAlgorithm.Sha2_256.doc =
+ {{
+ The [SHA-2 256-bit](https://en.wikipedia.org/wiki/SHA-2) hash algorithm.
+
+ # Example
+
+ ```
+ hash Sha2_256 (Text.toUtf8 "hello")
+ ```
+ }}
+
+-- builtin crypto.HashAlgorithm.Sha2_512 : crypto.HashAlgorithm
+
+crypto.HashAlgorithm.Sha2_512.doc : Doc
+crypto.HashAlgorithm.Sha2_512.doc =
+ {{
+ The [SHA-2 512-bit](https://en.wikipedia.org/wiki/SHA-2) hash algorithm.
+
+ # Example
+
+ ```
+ hash Sha2_512 (Text.toUtf8 "hello")
+ ```
+ }}
+
+-- builtin crypto.HashAlgorithm.Sha3_256 : crypto.HashAlgorithm
+
+crypto.HashAlgorithm.Sha3_256.doc : Doc
+crypto.HashAlgorithm.Sha3_256.doc =
+ {{
+ The [SHA-3 256-bit](https://en.wikipedia.org/wiki/SHA-3) hash algorithm.
+
+ # Example
+
+ ```
+ hash Sha3_256 (Text.toUtf8 "hello")
+ ```
+ }}
+
+-- builtin crypto.HashAlgorithm.Sha3_512 : crypto.HashAlgorithm
+
+crypto.HashAlgorithm.Sha3_512.doc : Doc
+crypto.HashAlgorithm.Sha3_512.doc =
+ {{
+ The [SHA-3 512-bit](https://en.wikipedia.org/wiki/SHA-3) hash algorithm.
+
+ # Example
+
+ ```
+ hash Sha3_512 (Text.toUtf8 "hello")
+ ```
+ }}
+
+-- builtin crypto.hashBytes : crypto.HashAlgorithm -> Bytes -> Bytes
+
+crypto.hashBytes.doc : Doc
+crypto.hashBytes.doc =
+ use fromList impl
+ {{
+ `` hashBytes algo bs `` hashes bytes using
+ {{ docExample 1 do algo -> (algo : HashAlgorithm) }}.
+
+ ```
+ hashBytes Sha2_256 0xs2a89d92b
+ ```
+
+ ```
+ hashBytes Blake2b_256 0xs2a89d92b
+ ```
+
+ Also see @inlineSignature{hash} for hashing an arbitrary value.
+ }}
+
+-- builtin crypto.hmac : crypto.HashAlgorithm -> Bytes -> a -> Bytes
+
+crypto.hmac.doc : Doc
+crypto.hmac.doc =
+ use Text toUtf8
+ {{
+ Compute the hash-based message authentication code (HMAC) of an arbitrary
+ value using a secret key and a cryptographic hash function. The result is a
+ {type Bytes} value.
+
+ `` hmac alg key msg `` uses the cryptographic hash function `alg` to compute
+ the HMAC of `msg` using `key` as the secret key.
+
+ Available hash functions include:
+
+ * {Sha1}
+ * {Sha2_256}
+ * {Sha2_512}
+ * {Sha3_256}
+ * {Sha3_512}
+ * {Blake2b_256}
+ * {Blake2b_512}
+ * {Blake2s_256}
+
+ {{
+ docCallout
+ (Some {{ ‼️ }})
+ {{
+ This function hashes a Unison-specific byte encoding of the message. If you
+ want fine control over how the message is encoded, first convert it to
+ {type Bytes} and then use {hmacBytes} instead. See the documentation of
+ {hmacBytes} for details.
+ }} }}
+
+ # Examples
+
+ ```
+ hmac Sha1 (toUtf8 "big secret") "hello world"
+ ```
+
+ ```
+ hmac Sha2_256 (toUtf8 "big secret") "hello world"
+ ```
+ }}
+
+crypto.hmac.verify : HashAlgorithm -> Bytes -> a -> Bytes -> Boolean
+crypto.hmac.verify algo key plaintext digest =
+ expected = hmac algo key plaintext
+ constantTimeEqual expected digest
+
+crypto.hmac.verify.doc : Doc
+crypto.hmac.verify.doc =
+ {{
+ Verify the signature of an HMAC digest created with {hmac}.
+
+ Usage:
+
+ ```
+ hmacKey = Text.toUtf8 "MY SECRET HMAC KEY"
+ value = ["Some arbitrary unison object!"]
+ algo = Sha3_256
+ digest = hmac algo hmacKey value
+ isValid = hmac.verify algo hmacKey value digest
+ isValid
+ ```
+ }}
+
+test> crypto.hmac.verify.test.happy = test.verify do
+ labeled "Test verify correctly verifies identical values" do
+ hmacKey = Text.toUtf8 "MY SECRET HMAC KEY"
+ plainText = Random.bytes 50
+ algo = Sha3_256
+ digest = hmac algo hmacKey plainText
+ isValid = hmac.verify algo hmacKey plainText digest
+ ensure isValid
+
+test> crypto.hmac.verify.test.sad =
+ test.verify do
+ labeled
+ "Test verify correctly returns false when verifying incorrect payloads"
+ do
+ use Random bytes
+ hmacKey = Text.toUtf8 "MY SECRET HMAC KEY"
+ plainText = bytes 50
+ badPlainText = once do
+ repeatForever()
+ badPlainText = bytes 50
+ guard (badPlainText !== plainText)
+ algo = Sha3_256
+ digest = hmac algo hmacKey plainText
+ isValid = hmac.verify algo hmacKey plainText digest
+ ensure isValid
+
+crypto.hmac.verifyBytes : HashAlgorithm -> Bytes -> Bytes -> Bytes -> Boolean
+crypto.hmac.verifyBytes algo key plaintext digest =
+ expected = hmacBytes algo key plaintext
+ constantTimeEqual expected digest
+
+crypto.hmac.verifyBytes.doc : Doc
+crypto.hmac.verifyBytes.doc =
+ use Text toUtf8
+ {{
+ Verify the signature of an HMAC digest created with {hmacBytes}.
+
+ Usage:
+
+ ```
+ hmacKey = toUtf8 "MY SECRET HMAC KEY"
+ plainText = toUtf8 "Hello, world!"
+ algo = Sha3_256
+ digest = hmacBytes algo hmacKey plainText
+ isValid = verifyBytes algo hmacKey plainText digest
+ isValid
+ ```
+ }}
+
+test> crypto.hmac.verifyBytes.test.happy = test.verify do
+ labeled "Test verifyBytes correctly verifies identical values" do
+ hmacKey = Text.toUtf8 "MY SECRET HMAC KEY"
+ plainText = Random.bytes 50
+ algo = Sha3_256
+ digest = hmacBytes algo hmacKey plainText
+ isValid = verifyBytes algo hmacKey plainText digest
+ ensure isValid
+
+test> crypto.hmac.verifyBytes.test.sad =
+ test.verify do
+ labeled
+ "Test verifyBytes correctly returns false when verifying incorrect payloads"
+ do
+ use Random bytes
+ hmacKey = Text.toUtf8 "MY SECRET HMAC KEY"
+ plainText = bytes 50
+ badPlainText = once do
+ repeatForever()
+ badPlainText = bytes 50
+ guard (badPlainText !== plainText)
+ algo = Sha3_256
+ digest = hmacBytes algo hmacKey plainText
+ isValid = verifyBytes algo hmacKey plainText digest
+ ensure isValid
+
+-- builtin crypto.hmacBytes : crypto.HashAlgorithm -> Bytes -> Bytes -> Bytes
+
+crypto.hmacBytes.doc : Doc
+crypto.hmacBytes.doc =
+ use Text toUtf8
+ {{
+ Compute the hash-based message authentication code (HMAC) of a {type Bytes}
+ value using a secret key and a cryptographic hash function. The result is a
+ {type Bytes} value.
+
+ `` hmacBytes alg key msg `` uses the cryptographic hash function `alg` to
+ compute the HMAC of `msg` using `key` as the secret key.
+
+ Available hash functions include:
+
+ * {Sha1}
+ * {Sha2_256}
+ * {Sha2_512}
+ * {Sha3_256}
+ * {Sha3_512}
+ * {Blake2b_256}
+ * {Blake2b_512}
+ * {Blake2s_256}
+
+ # Examples
+
+ ```
+ hmacBytes
+ Sha1
+ (toUtf8 "big secret")
+ (toUtf8 "The quick brown fox jumps over the lazy dog")
+ ```
+
+ ```
+ hmacBytes
+ Sha2_256
+ (toUtf8 "big secret")
+ (toUtf8 "The quick brown fox jumps over the lazy dog.")
+ ```
+ }}
+
+crypto.Rsa.PrivateKey.doc : Doc
+crypto.Rsa.PrivateKey.doc =
+ {{
+ A private key for the RSA signature scheme.
+
+ # Example
+
+ ```
+ catch do
+ Rsa.PrivateKey.PrivateKey
+ (fromBase64
+ (Text.toUtf8 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="))
+ ```
+ }}
+
+crypto.Rsa.PublicKey.doc : Doc
+crypto.Rsa.PublicKey.doc =
+ {{
+ A public key for the RSA signature scheme.
+
+ # Example
+
+ ```
+ catch do
+ Rsa.PublicKey.PublicKey
+ (fromBase64
+ (Text.toUtf8 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="))
+ ```
+ }}
+
+crypto.Rsa.sign : Rsa.PrivateKey -> Bytes ->{Exception} Rsa.Signature
+crypto.Rsa.sign = cases
+ Rsa.PrivateKey.PrivateKey p, msg ->
+ Rsa.Signature.Signature (Either.toException (Rsa.sign.impl p msg))
+
+crypto.Rsa.sign.doc : Doc
+crypto.Rsa.sign.doc =
+ use Rsa sign verify
+ use Rsa.PrivateKey PrivateKey
+ use Rsa.PublicKey PublicKey
+ use Text toUtf8
+ use fromList impl
+ {{
+ Signs a message with an RSA private key and returns the signature.
+
+ # Example
+
+ The private and public keys for these examples were generated with:
+
+ * `openssl genrsa -out private_key.pem 1024` to generate the key pair
+ * `openssl rsa -in private_key.pem -outform DER | xxd -p` to encode the
+ private key
+ * `openssl rsa -in private_key.pem -outform DER -pubout | xxd -p` to encode
+ the public key
+
+ ```
+ catch do
+ private =
+ PrivateKey
+ 0xs30820276020100300d06092a864886f70d0101010500048202603082025c02010002818100a7104b2f20725896076e629ccedbcd6907b16694c6e3d8768b5e0e685670b49616e796c588e5aafb92ef986c1a42c021fed0bdc99212c969cdab98087a0ee4c2f4acd4b6049a87a96afc45668329a3cf21a86fb13b488bbe9fefa1cd5a459014f0d0101378e9661e11b73acf54c8a91141ac90309e7fb6ed69b4e63230ab291502030100010281807cdc23a4fc3619d93f8293b728af848d0c0fdd603269d5bd7b99f760a9c22065d08693dbdcddf1f5863306133d694819e04d789aef4e95343b601507b8d9eac4492e6d7031b035c5d84eceaa9686b292712632d33b3303af84314d7920bc3d45f90d7818fc2587b129196d378ee4ed3e6b8d9010d504bb6470ff53e7c5fb17a1024100d67cbcf113d24325fcef12a778dc47c7060055290b68287649ef092558daccb61c4e7bc290740b75a29d4356dcbd66d18b0860dbff394cc8ff3d94d57617adbd024100c765d8261dd3d8e0d3caf11ab7b212eed181354215687ca6387283e4f0be16e79c8f298be0a70c7734dea78ea65128517d693cabfa4c0ff5328f2abb85d2023902403ca41dc347285e65c22251b2d9bfe5e7463217e1b7e0e5f7b3a58a7f6da4c6d60220ca6ad2ee8c42e10bf77afa83ee2af6551315800e52404db1ba7fb398b43d02410084877d85c0177933ddb12a554eb8edfa8b872c85d2c2d2ee8be019280696e19469ab81bab5c371f69d4e4be1f54b45d7fbda017870f1333e0eafb78051ee8689024061f694c12e934c44b7734f62d1b2a3d3624a4980e1b8e066d78dbabd2436654fbb9d9701425900daaafa1e031310e8a580520bb9e1c1288c669fce252bad1e65
+ public =
+ PublicKey
+ 0xs30819f300d06092a864886f70d010101050003818d0030818902818100a7104b2f20725896076e629ccedbcd6907b16694c6e3d8768b5e0e685670b49616e796c588e5aafb92ef986c1a42c021fed0bdc99212c969cdab98087a0ee4c2f4acd4b6049a87a96afc45668329a3cf21a86fb13b488bbe9fefa1cd5a459014f0d0101378e9661e11b73acf54c8a91141ac90309e7fb6ed69b4e63230ab29150203010001
+ signature = sign private (toUtf8 "Hello, world!")
+ verify public (toUtf8 "Hello, world!") signature
+ ```
+
+ ```
+ catch do
+ private =
+ PrivateKey
+ 0xs30820276020100300d06092a864886f70d0101010500048202603082025c02010002818100a7104b2f20725896076e629ccedbcd6907b16694c6e3d8768b5e0e685670b49616e796c588e5aafb92ef986c1a42c021fed0bdc99212c969cdab98087a0ee4c2f4acd4b6049a87a96afc45668329a3cf21a86fb13b488bbe9fefa1cd5a459014f0d0101378e9661e11b73acf54c8a91141ac90309e7fb6ed69b4e63230ab291502030100010281807cdc23a4fc3619d93f8293b728af848d0c0fdd603269d5bd7b99f760a9c22065d08693dbdcddf1f5863306133d694819e04d789aef4e95343b601507b8d9eac4492e6d7031b035c5d84eceaa9686b292712632d33b3303af84314d7920bc3d45f90d7818fc2587b129196d378ee4ed3e6b8d9010d504bb6470ff53e7c5fb17a1024100d67cbcf113d24325fcef12a778dc47c7060055290b68287649ef092558daccb61c4e7bc290740b75a29d4356dcbd66d18b0860dbff394cc8ff3d94d57617adbd024100c765d8261dd3d8e0d3caf11ab7b212eed181354215687ca6387283e4f0be16e79c8f298be0a70c7734dea78ea65128517d693cabfa4c0ff5328f2abb85d2023902403ca41dc347285e65c22251b2d9bfe5e7463217e1b7e0e5f7b3a58a7f6da4c6d60220ca6ad2ee8c42e10bf77afa83ee2af6551315800e52404db1ba7fb398b43d02410084877d85c0177933ddb12a554eb8edfa8b872c85d2c2d2ee8be019280696e19469ab81bab5c371f69d4e4be1f54b45d7fbda017870f1333e0eafb78051ee8689024061f694c12e934c44b7734f62d1b2a3d3624a4980e1b8e066d78dbabd2436654fbb9d9701425900daaafa1e031310e8a580520bb9e1c1288c669fce252bad1e65
+ public =
+ PublicKey
+ 0xs30819f300d06092a864886f70d010101050003818d0030818902818100a7104b2f20725896076e629ccedbcd6907b16694c6e3d8768b5e0e685670b49616e796c588e5aafb92ef986c1a42c021fed0bdc99212c969cdab98087a0ee4c2f4acd4b6049a87a96afc45668329a3cf21a86fb13b488bbe9fefa1cd5a459014f0d0101378e9661e11b73acf54c8a91141ac90309e7fb6ed69b4e63230ab29150203010001
+ signature = sign private (toUtf8 "Hello, world!")
+ verify public (toUtf8 "Bogus message") signature
+ ```
+
+ See also {verify}.
+ }}
+
+-- builtin crypto.Rsa.sign.impl : Bytes -> Bytes -> Either Failure Bytes
+
+test> crypto.Rsa.sign.test =
+ test.verify do
+ use fromList impl
+ actual =
+ catch do
+ private =
+ Rsa.PrivateKey.PrivateKey
+ 0xs30820276020100300d06092a864886f70d0101010500048202603082025c02010002818100a7104b2f20725896076e629ccedbcd6907b16694c6e3d8768b5e0e685670b49616e796c588e5aafb92ef986c1a42c021fed0bdc99212c969cdab98087a0ee4c2f4acd4b6049a87a96afc45668329a3cf21a86fb13b488bbe9fefa1cd5a459014f0d0101378e9661e11b73acf54c8a91141ac90309e7fb6ed69b4e63230ab291502030100010281807cdc23a4fc3619d93f8293b728af848d0c0fdd603269d5bd7b99f760a9c22065d08693dbdcddf1f5863306133d694819e04d789aef4e95343b601507b8d9eac4492e6d7031b035c5d84eceaa9686b292712632d33b3303af84314d7920bc3d45f90d7818fc2587b129196d378ee4ed3e6b8d9010d504bb6470ff53e7c5fb17a1024100d67cbcf113d24325fcef12a778dc47c7060055290b68287649ef092558daccb61c4e7bc290740b75a29d4356dcbd66d18b0860dbff394cc8ff3d94d57617adbd024100c765d8261dd3d8e0d3caf11ab7b212eed181354215687ca6387283e4f0be16e79c8f298be0a70c7734dea78ea65128517d693cabfa4c0ff5328f2abb85d2023902403ca41dc347285e65c22251b2d9bfe5e7463217e1b7e0e5f7b3a58a7f6da4c6d60220ca6ad2ee8c42e10bf77afa83ee2af6551315800e52404db1ba7fb398b43d02410084877d85c0177933ddb12a554eb8edfa8b872c85d2c2d2ee8be019280696e19469ab81bab5c371f69d4e4be1f54b45d7fbda017870f1333e0eafb78051ee8689024061f694c12e934c44b7734f62d1b2a3d3624a4980e1b8e066d78dbabd2436654fbb9d9701425900daaafa1e031310e8a580520bb9e1c1288c669fce252bad1e65
+ Rsa.sign private (Text.toUtf8 "Hello, world!")
+ expected =
+ Rsa.Signature.Signature
+ 0xs1c0cc9384fa0053b2cc2d2e9fe98b9a21ffc90a4996610934c56b101f64145f030d480cf33a9caee0cf1f1472979eda955dada655d7ae96c98183987ba7fd154f08dc281cccca3e6deb99b40d2e161ab65eba2236fa8004dba7002103b5dad5d678f832e20e552800d8722d0c34731266440b4a4ae77991c039a918d15fb8249
+ ensureEqual actual (Right expected)
+
+crypto.Rsa.Signature.doc : Doc
+crypto.Rsa.Signature.doc =
+ {{
+ A signature for the Ed25519 signature scheme.
+
+ # Example
+
+ ```
+ catch do
+ Rsa.Signature.Signature
+ (fromBase64
+ (Text.toUtf8 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="))
+ ```
+ }}
+
+crypto.Rsa.verify :
+ Rsa.PublicKey -> Bytes -> Rsa.Signature ->{Exception} Boolean
+crypto.Rsa.verify = cases
+ Rsa.PublicKey.PublicKey p, msg, Rsa.Signature.Signature s ->
+ Either.toException (Rsa.verify.impl p msg s)
+
+crypto.Rsa.verify.doc : Doc
+crypto.Rsa.verify.doc =
+ use Rsa sign verify
+ use Rsa.PrivateKey PrivateKey
+ use Rsa.PublicKey PublicKey
+ use Text toUtf8
+ use fromList impl
+ {{
+ Verifies an RSA signature on a message.
+
+ # Examples
+
+ The private and public keys for these examples were generated with:
+
+ * `openssl genrsa -out private_key.pem 1024` to generate the key pair
+ * `openssl rsa -in private_key.pem -outform DER | xxd -p` to encode the
+ private key
+ * `openssl rsa -in private_key.pem -outform DER -pubout | xxd -p` to encode
+ the public key
+
+ ```
+ catch do
+ private =
+ PrivateKey
+ 0xs30820276020100300d06092a864886f70d0101010500048202603082025c02010002818100a7104b2f20725896076e629ccedbcd6907b16694c6e3d8768b5e0e685670b49616e796c588e5aafb92ef986c1a42c021fed0bdc99212c969cdab98087a0ee4c2f4acd4b6049a87a96afc45668329a3cf21a86fb13b488bbe9fefa1cd5a459014f0d0101378e9661e11b73acf54c8a91141ac90309e7fb6ed69b4e63230ab291502030100010281807cdc23a4fc3619d93f8293b728af848d0c0fdd603269d5bd7b99f760a9c22065d08693dbdcddf1f5863306133d694819e04d789aef4e95343b601507b8d9eac4492e6d7031b035c5d84eceaa9686b292712632d33b3303af84314d7920bc3d45f90d7818fc2587b129196d378ee4ed3e6b8d9010d504bb6470ff53e7c5fb17a1024100d67cbcf113d24325fcef12a778dc47c7060055290b68287649ef092558daccb61c4e7bc290740b75a29d4356dcbd66d18b0860dbff394cc8ff3d94d57617adbd024100c765d8261dd3d8e0d3caf11ab7b212eed181354215687ca6387283e4f0be16e79c8f298be0a70c7734dea78ea65128517d693cabfa4c0ff5328f2abb85d2023902403ca41dc347285e65c22251b2d9bfe5e7463217e1b7e0e5f7b3a58a7f6da4c6d60220ca6ad2ee8c42e10bf77afa83ee2af6551315800e52404db1ba7fb398b43d02410084877d85c0177933ddb12a554eb8edfa8b872c85d2c2d2ee8be019280696e19469ab81bab5c371f69d4e4be1f54b45d7fbda017870f1333e0eafb78051ee8689024061f694c12e934c44b7734f62d1b2a3d3624a4980e1b8e066d78dbabd2436654fbb9d9701425900daaafa1e031310e8a580520bb9e1c1288c669fce252bad1e65
+ public =
+ PublicKey
+ 0xs30819f300d06092a864886f70d010101050003818d0030818902818100a7104b2f20725896076e629ccedbcd6907b16694c6e3d8768b5e0e685670b49616e796c588e5aafb92ef986c1a42c021fed0bdc99212c969cdab98087a0ee4c2f4acd4b6049a87a96afc45668329a3cf21a86fb13b488bbe9fefa1cd5a459014f0d0101378e9661e11b73acf54c8a91141ac90309e7fb6ed69b4e63230ab29150203010001
+ signature = sign private (toUtf8 "Hello, world!")
+ verify public (toUtf8 "Hello, world!") signature
+ ```
+
+ ```
+ catch do
+ private =
+ PrivateKey
+ 0xs30820276020100300d06092a864886f70d0101010500048202603082025c02010002818100a7104b2f20725896076e629ccedbcd6907b16694c6e3d8768b5e0e685670b49616e796c588e5aafb92ef986c1a42c021fed0bdc99212c969cdab98087a0ee4c2f4acd4b6049a87a96afc45668329a3cf21a86fb13b488bbe9fefa1cd5a459014f0d0101378e9661e11b73acf54c8a91141ac90309e7fb6ed69b4e63230ab291502030100010281807cdc23a4fc3619d93f8293b728af848d0c0fdd603269d5bd7b99f760a9c22065d08693dbdcddf1f5863306133d694819e04d789aef4e95343b601507b8d9eac4492e6d7031b035c5d84eceaa9686b292712632d33b3303af84314d7920bc3d45f90d7818fc2587b129196d378ee4ed3e6b8d9010d504bb6470ff53e7c5fb17a1024100d67cbcf113d24325fcef12a778dc47c7060055290b68287649ef092558daccb61c4e7bc290740b75a29d4356dcbd66d18b0860dbff394cc8ff3d94d57617adbd024100c765d8261dd3d8e0d3caf11ab7b212eed181354215687ca6387283e4f0be16e79c8f298be0a70c7734dea78ea65128517d693cabfa4c0ff5328f2abb85d2023902403ca41dc347285e65c22251b2d9bfe5e7463217e1b7e0e5f7b3a58a7f6da4c6d60220ca6ad2ee8c42e10bf77afa83ee2af6551315800e52404db1ba7fb398b43d02410084877d85c0177933ddb12a554eb8edfa8b872c85d2c2d2ee8be019280696e19469ab81bab5c371f69d4e4be1f54b45d7fbda017870f1333e0eafb78051ee8689024061f694c12e934c44b7734f62d1b2a3d3624a4980e1b8e066d78dbabd2436654fbb9d9701425900daaafa1e031310e8a580520bb9e1c1288c669fce252bad1e65
+ public =
+ PublicKey
+ 0xs30819f300d06092a864886f70d010101050003818d0030818902818100a7104b2f20725896076e629ccedbcd6907b16694c6e3d8768b5e0e685670b49616e796c588e5aafb92ef986c1a42c021fed0bdc99212c969cdab98087a0ee4c2f4acd4b6049a87a96afc45668329a3cf21a86fb13b488bbe9fefa1cd5a459014f0d0101378e9661e11b73acf54c8a91141ac90309e7fb6ed69b4e63230ab29150203010001
+ signature = sign private (toUtf8 "Hello, world!")
+ verify public (toUtf8 "Bogus message") signature
+ ```
+
+ See also {sign}.
+ }}
+
+-- builtin crypto.Rsa.verify.impl :
+-- Bytes -> Bytes -> Bytes -> Either Failure Boolean
+
+test> crypto.Rsa.verify.test =
+ test.verify do
+ use Text toUtf8
+ use fromList impl
+ result =
+ catch do
+ private =
+ Rsa.PrivateKey.PrivateKey
+ 0xs30820276020100300d06092a864886f70d0101010500048202603082025c02010002818100a7104b2f20725896076e629ccedbcd6907b16694c6e3d8768b5e0e685670b49616e796c588e5aafb92ef986c1a42c021fed0bdc99212c969cdab98087a0ee4c2f4acd4b6049a87a96afc45668329a3cf21a86fb13b488bbe9fefa1cd5a459014f0d0101378e9661e11b73acf54c8a91141ac90309e7fb6ed69b4e63230ab291502030100010281807cdc23a4fc3619d93f8293b728af848d0c0fdd603269d5bd7b99f760a9c22065d08693dbdcddf1f5863306133d694819e04d789aef4e95343b601507b8d9eac4492e6d7031b035c5d84eceaa9686b292712632d33b3303af84314d7920bc3d45f90d7818fc2587b129196d378ee4ed3e6b8d9010d504bb6470ff53e7c5fb17a1024100d67cbcf113d24325fcef12a778dc47c7060055290b68287649ef092558daccb61c4e7bc290740b75a29d4356dcbd66d18b0860dbff394cc8ff3d94d57617adbd024100c765d8261dd3d8e0d3caf11ab7b212eed181354215687ca6387283e4f0be16e79c8f298be0a70c7734dea78ea65128517d693cabfa4c0ff5328f2abb85d2023902403ca41dc347285e65c22251b2d9bfe5e7463217e1b7e0e5f7b3a58a7f6da4c6d60220ca6ad2ee8c42e10bf77afa83ee2af6551315800e52404db1ba7fb398b43d02410084877d85c0177933ddb12a554eb8edfa8b872c85d2c2d2ee8be019280696e19469ab81bab5c371f69d4e4be1f54b45d7fbda017870f1333e0eafb78051ee8689024061f694c12e934c44b7734f62d1b2a3d3624a4980e1b8e066d78dbabd2436654fbb9d9701425900daaafa1e031310e8a580520bb9e1c1288c669fce252bad1e65
+ public =
+ Rsa.PublicKey.PublicKey
+ 0xs30819f300d06092a864886f70d010101050003818d0030818902818100a7104b2f20725896076e629ccedbcd6907b16694c6e3d8768b5e0e685670b49616e796c588e5aafb92ef986c1a42c021fed0bdc99212c969cdab98087a0ee4c2f4acd4b6049a87a96afc45668329a3cf21a86fb13b488bbe9fefa1cd5a459014f0d0101378e9661e11b73acf54c8a91141ac90309e7fb6ed69b4e63230ab29150203010001
+ signature = Rsa.sign private (toUtf8 "Hello, world!")
+ Rsa.verify public (toUtf8 "Hello, world!") signature
+ ensureEqual result (Right true)
+
+data.Array.append : data.Array a -> data.Array a -> data.Array a
+data.Array.append arr1 arr2 = unsafeRun! do
+ Scope.run do
+ (Arr off1 len1 raw1) = arr1
+ (Arr off2 len2 raw2) = arr2
+ use Nat +
+ use data.Array.Raw copyTo!
+ m = Scope.Raw.array (len1 + len2)
+ copyTo! m 0 raw1 off1 len1
+ copyTo! m len1 raw2 off2 len2
+ Arr 0 (len1 + len2) (Array.Raw.freeze! m)
+
+data.Array.append.doc : Doc
+data.Array.append.doc =
+ use Array fromList
+ {{
+ Appends two arrays. This is an O(n) operation. The original arrays are not
+ modified.
+
+ # Examples
+
+ ```
+ Array.toList (Array.append (fromList [1, 2, 3]) (fromList [4, 5, 6]))
+ ```
+
+ # See also
+
+ * {Array.cons} for a version of this that prepends an element to an array.
+ * {Array.snoc} for a version of this that appends an element to an array.
+ }}
+
+data.Array.Arr.doc : Doc
+data.Array.Arr.doc =
+ {{
+ Constructs an immutable {type data.Array} from its internal representation.
+
+ `` Arr off len arr `` represents an array with `len` elements from the
+ underlying {type data.Array.Raw} array, starting at offset `off`.
+
+ # Example
+
+ @source{Array.fromList}
+ }}
+
+data.Array.ArrayFailure.doc : Doc
+data.Array.ArrayFailure.doc =
+ {{
+ A type of {type Failure} raised by {type data.Array} operations, for example
+ when an index is out of bounds.
+
+ # Example
+
+ ```
+ catch do Array.take 10 (data.Array.of 5 0)
+ ```
+ }}
+
+data.Array.ArrayFailure.raise : Text -> a ->{Exception} b
+data.Array.ArrayFailure.raise msg payload =
+ fail = Failure (typeLink ArrayFailure) msg (Any payload)
+ Exception.raise fail
+
+data.Array.ArrayFailure.raise.doc : Doc
+data.Array.ArrayFailure.raise.doc =
+ {{ Raises an exception using the {type ArrayFailure} type tag }}
+
+data.Array.at : Nat -> data.Array a -> Optional a
+data.Array.at i arr = hush do data.Array.read arr i
+
+data.Array.at.doc : Doc
+data.Array.at.doc =
+ use Array at fromList
+ {{
+ Gets the element at the given index, if it exists, otherwise returns
+ ``None``.
+
+ # Examples
+
+ ```
+ at 0 (fromList [1, 2, 3])
+ ```
+
+ ```
+ at 3 (fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {Array.at!} for a version of this that aborts if the index is out of
+ bounds.
+ * {Array.unsafeAt} simply crashes if the index is out of bounds.
+ * {data.Array.read} for a version that throws an {type Exception} if the
+ index is out of bounds.
+ }}
+
+data.Array.at! : Nat -> data.Array a ->{Abort} a
+data.Array.at! i arr = abortOnException do data.Array.read arr i
+
+data.Array.at!.doc : Doc
+data.Array.at!.doc =
+ use Array at! fromList
+ {{
+ Gets the element at the given index, calling {abort} if it does not exist.
+
+ # Examples
+
+ ```
+ toOptional! do at! 0 (fromList [1, 2, 3])
+ ```
+
+ ```
+ toOptional! do at! 3 (fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {Array.at} for a version of this that returns `` None `` if the index is
+ out of bounds.
+ * {Array.unsafeAt} simply crashes if the index is out of bounds.
+ * {data.Array.read} for a version that throws an {type Exception} if the
+ index is out of bounds.
+ }}
+
+data.Array.cons : a -> data.Array a -> data.Array a
+data.Array.cons x arr = unsafeRun! do
+ Scope.run do
+ (Arr off len raw) = arr
+ use Nat +
+ m = Scope.Raw.array (len + 1)
+ data.Array.Raw.copyTo! m 1 raw off len
+ Raw.write m 0 x
+ Arr 0 (len + 1) (Array.Raw.freeze! m)
+
+data.Array.cons.doc : Doc
+data.Array.cons.doc =
+ {{
+ Prepends an element to an array. This is an O(n) operation. The original
+ array is not modified.
+
+ # Example
+
+ ```
+ Array.toList (Array.cons 0 (Array.fromList [1, 2, 3]))
+ ```
+
+ # See also
+
+ * {Array.snoc} for a version of this that appends an element to an array.
+ * {Array.append} for a version of this that appends two arrays.
+ }}
+
+data.Array.doc : Doc
+data.Array.doc =
+ use Array toList
+ {{
+ {type data.Array} is the type of fixed size, flat arrays. The array stores
+ references to the elements rather than the elements themselves, i.e. the
+ elements are __boxed__. Boxing is less efficient for fixed size types like
+ {type Nat} or {type Int}, so for those you can use {type data.ByteArray}
+ which stores the elements directly (unboxed) at the cost of a less ergonomic
+ interface.
+
+ The values in a {type data.Array} also store an offset and length relative to
+ the underlying array, allowing for fast slicing operations.
+
+ The {type data.Array} is immutable (no destructive updates), and homogeneous
+ (all elements have the same type).
+
+ # Constructing arrays
+
+ The most common way to construct an {type data.Array} is to create a
+ mutable array, fill it with values, and then freeze it:
+
+ ```
+ catch do
+ toList
+ (Scope.run do
+ a = mutable.Array.of 0 10
+ initialize = do
+ n = Each.range 0 10
+ Array.write a n (Nat.popCount n)
+ Each.run initialize
+ Array.freeze a)
+ ```
+
+ You can also directly construct an {type data.Array} from a list of values:
+
+ ```
+ data.Array.size (Array.fromList [1, 2, 3])
+ ```
+
+ You can construct an array of a certain size where all values are the same:
+
+ ```
+ toList (data.Array.of "boing" 5)
+ ```
+ }}
+
+data.Array.drop : Nat -> data.Array a ->{Exception} data.Array a
+data.Array.drop n = cases
+ Arr off len arr| n Nat.<= len -> Arr (off Nat.+ n) (len Nat.- n) arr
+ _ -> ArrayFailure.raise "data.Array.drop: not enough elements" n
+
+data.Array.drop.doc : Doc
+data.Array.drop.doc =
+ {{
+ Returns a new {type data.Array} with some elements removed from the front.
+
+ This operation is fast, replacing only the offset and length, and keeping the
+ underlying storage. The potential downside is that more memory may be used
+ than necessary.
+
+ ```
+ catch do Array.toList (Array.drop 2 (Array.fromList [1, 2, 3, 4, 5]))
+ ```
+ }}
+
+data.Array.empty : data.Array a
+data.Array.empty = Scope.run do Arr 0 0 (Array.Raw.freeze! (Scope.Raw.array 0))
+
+data.Array.empty.doc : Doc
+data.Array.empty.doc =
+ {{
+ The empty array.
+
+ # Example
+
+ ```
+ Array.toList Array.empty
+ ```
+
+ # See also
+
+ * {Array.singleton} to create an array with a single element.
+ * {Array.fromList} to create an array from a {type List}.
+ }}
+
+data.Array.fill : Nat -> a -> data.Array a
+data.Array.fill = flip data.Array.of
+
+data.Array.fill.doc : Doc
+data.Array.fill.doc =
+ use data.Array fill
+ {{
+ `` fill sz x `` creates a new {type data.Array} of size `sz`, filled with the
+ value `x`.
+
+ # Example
+
+ ```
+ Array.toList (fill 4 0)
+ ```
+
+ # See also
+
+ * {data.Array.of} for a version of this that takes the arguments in the
+ opposite order.
+ }}
+
+data.Array.find : (a ->{g} Boolean) -> data.Array a ->{g} Optional a
+data.Array.find p arr =
+ Array.firstIndexOf p arr |> Optional.flatMap (flip Array.at arr)
+
+data.Array.find.doc : Doc
+data.Array.find.doc =
+ use Array find
+ use Nat ==
+ {{
+ `` find p arr `` returns the first element of the array `arr` that satisfies
+ the predicate `p`, or {None} if no such element exists.
+
+ # Example
+
+ ```
+ find ((==) 3) (Array.fromList [1, 2, 3, 4, 5])
+ ```
+
+ # See also
+
+ * {Array.findLast} - returns the last element that satisfies the predicate.
+ * {Array.firstIndexOf} - returns the index of the first element that
+ satisfies the predicate.
+ * {Array.lastIndexOf} - returns the index of the last element that
+ satisfies the predicate.
+ }}
+
+test> data.Array.find.tests.consistentWithList = test.verify do
+ use Nat ==
+ zeroToTen = do Random.natIn 0 10
+ list = Random.listOf zeroToTen zeroToTen
+ arr = Array.fromList list
+ p = zeroToTen()
+ ensureEqual (Array.find ((==) p) arr) (List.find ((==) p) list)
+
+data.Array.findLast : (a ->{g} Boolean) -> data.Array a ->{g} Optional a
+data.Array.findLast p arr =
+ Array.lastIndexOf p arr |> Optional.flatMap (flip Array.at arr)
+
+data.Array.findLast.doc : Doc
+data.Array.findLast.doc =
+ {{
+ `` Array.findLast p arr `` returns the last element of the array `arr` that
+ satisfies the predicate `p`, or {None} if no such element exists.
+ }}
+
+data.Array.firstIndexOf : (a ->{g} Boolean) -> data.Array a ->{g} Optional Nat
+data.Array.firstIndexOf p arr =
+ use Nat + <
+ sz = data.Array.size arr
+ go : Nat -> Optional Nat
+ go ix =
+ if ix < sz then
+ match Array.at ix arr with
+ Some x -> if p x then Some ix else go (ix + 1)
+ None -> None
+ else None
+ go 0
+
+data.Array.firstIndexOf.doc : Doc
+data.Array.firstIndexOf.doc =
+ use Array firstIndexOf lastIndexOf
+ use Nat ==
+ {{
+ `` firstIndexOf p arr `` returns the index of the first element of the array
+ `arr` that satisfies the predicate `p`, or {None} if no such element exists.
+
+ # Example
+
+ ```
+ firstIndexOf ((==) 3) (Array.fromList [1, 2, 3, 4, 5])
+ ```
+
+ # See also
+
+ * {lastIndexOf} - returns the index of the last element that satisfies the
+ predicate.
+ * {Array.find} - returns the element itself, not just the index.
+ * {Array.findLast} - returns the last element that satisfies the predicate.
+ * {lastIndexOf} - returns the index of the last element that satisfies the
+ predicate.
+ }}
+
+test> data.Array.firstIndexOf.tests.finds = test.verify do
+ use Array firstIndexOf
+ use List ++ :+ size
+ use Nat ==
+ use Random listOf natIn
+ el = do natIn 1 10
+ sz = do natIn 0 10
+ list1 = listOf el sz
+ list2 = listOf el sz
+ arr = Array.fromList (list1 :+ 0 ++ list2)
+ ensureEqual (firstIndexOf ((==) 0) arr) (Some (size list1))
+ ensureEqual (Array.lastIndexOf ((==) 0) arr) (Some (size list1))
+ ensureEqual (firstIndexOf ((==) 11) arr) None
+
+data.Array.foldLeft : (a ->{g} b ->{h} a) -> a -> data.Array b ->{g, h} a
+data.Array.foldLeft f acc arr = unsafeRun! do
+ Scope.run do
+ (Arr off len raw) = arr
+ use Nat + >=
+ go n acc =
+ if n >= len then acc
+ else
+ x = data.Array.Raw.read raw (off + n)
+ acc' = f acc x
+ go (n + 1) acc'
+ go 0 acc
+
+data.Array.foldLeft.doc : Doc
+data.Array.foldLeft.doc =
+ use Array fromList
+ use List +:
+ use Nat +
+ {{
+ Folds the elements in an array from left to right, using the given binary
+ operator and initial accumulator.
+
+ # Examples
+
+ ```
+ Array.foldLeft (+) 0 (fromList [1, 2, 3])
+ ```
+
+ ```
+ Array.foldLeft (flip (+:)) [] (fromList [1, 2, 3])
+ ```
+
+ ```
+ Array.foldLeft (+) 0 (fromList [])
+ ```
+
+ # See also
+
+ * {Array.foldRight} to fold from right to left.
+ * {List.foldLeft} for the {type List} version of this.
+ }}
+
+data.Array.foldRight : (a ->{g} b ->{h} b) -> b -> data.Array a ->{g, h} b
+data.Array.foldRight f acc arr = unsafeRun! do
+ Scope.run do
+ (Arr off len raw) = arr
+ use Nat + - ==
+ go n acc =
+ if n == 0 then acc
+ else
+ x = data.Array.Raw.read raw (off + n - 1)
+ acc' = f x acc
+ go (n - 1) acc'
+ go len acc
+
+data.Array.foldRight.doc : Doc
+data.Array.foldRight.doc =
+ use Array fromList
+ use List +:
+ use Nat +
+ {{
+ Folds the elements in an array from right to left, using the given binary
+ operator and initial accumulator.
+
+ # Examples
+
+ ```
+ Array.foldRight (+) 0 (fromList [1, 2, 3])
+ ```
+
+ ```
+ Array.foldRight (x acc -> x +: acc) [] (fromList [1, 2, 3])
+ ```
+
+ ```
+ Array.foldRight (+) 0 (fromList [])
+ ```
+
+ # See also
+
+ * {Array.foldLeft} to fold from left to right.
+ * {List.foldRight} for the {type List} version of this.
+ }}
+
+data.Array.fromList : [a] -> data.Array a
+data.Array.fromList l = Scope.run do
+ use Nat +
+ sz = List.size l
+ dst = Scope.Raw.array sz
+ go i = cases
+ [] -> ()
+ x +: xs ->
+ Raw.write dst i x
+ go (i + 1) xs
+ handle go 0 l with impossible
+ Arr 0 sz (Array.Raw.freeze! dst)
+
+data.Array.fromList.doc : Doc
+data.Array.fromList.doc =
+ {{
+ Creates a new array containing the values of the list.
+
+ ```
+ catch do
+ a = Array.fromList [?x, ?y]
+ data.Array.read a 0
+ ```
+ }}
+
+data.Array.isEmpty : data.Array a -> Boolean
+data.Array.isEmpty = cases Arr _ len _ -> len Nat.== 0
+
+data.Array.isEmpty.doc : Doc
+data.Array.isEmpty.doc =
+ use Array fromList isEmpty
+ {{
+ Checks if an array is empty.
+
+ # Examples
+
+ ```
+ isEmpty (fromList [])
+ ```
+
+ ```
+ isEmpty (fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {data.Array.size} to get the size of an array.
+ }}
+
+data.Array.lastIndexOf : (a ->{g} Boolean) -> data.Array a ->{g} Optional Nat
+data.Array.lastIndexOf p arr =
+ sz = data.Array.size arr
+ go : Nat -> Optional Nat
+ go ix =
+ use Nat - >
+ ix' = ix - 1
+ if ix > 0 then
+ match Array.at ix' arr with
+ Some x -> if p x then Some ix' else go ix'
+ None -> None
+ else None
+ go sz
+
+data.Array.lastIndexOf.doc : Doc
+data.Array.lastIndexOf.doc =
+ use Array lastIndexOf
+ use Nat ==
+ {{
+ `` lastIndexOf p arr `` returns the index of the last element of the array
+ `arr` that satisfies the predicate `p`, or {None} if no such element exists.
+
+ # Example
+
+ ```
+ lastIndexOf ((==) 3) (Array.fromList [1, 2, 3, 4, 5])
+ ```
+ }}
+
+data.Array.map : (a ->{g} b) -> data.Array a ->{g} data.Array b
+data.Array.map f arr =
+ use Nat + <
+ sz = data.Array.size arr
+ e = catch do
+ Scope.run do
+ mbarr = MArr 0 sz (Scope.Raw.array sz)
+ go n = when (n < sz) do
+ Array.write mbarr n (f (data.Array.read arr n))
+ go (n + 1)
+ go 0
+ Array.freeze mbarr
+ match e with
+ Left err -> bug ("immutable Array.map threw an exception", e)
+ Right arr -> arr
+
+data.Array.map.doc : Doc
+data.Array.map.doc =
+ use Array fromList map toList
+ use Nat toText
+ {{
+ `` map f arr `` applies the function `f` to each element of the array `arr`
+ and returns the resulting array.
+
+ # Examples
+
+ ```
+ toList (map toText (fromList [1, 2, 3]))
+ ```
+
+ ```
+ toList (map toText (fromList []))
+ ```
+ }}
+
+data.Array.new! :
+ a
+ -> Nat
+ -> (∀ s. mutable.Array {Scope s} a ->{g, Exception, Scope s} ())
+ ->{g} data.Array a
+data.Array.new! default size init = Scope.run do
+ ma = Scope.arrayOf default size
+ handle init ma
+ with cases
+ { Exception.raise fail -> _ } -> bug fail
+ { r } -> ()
+ Array.freeze! ma
+
+data.Array.new!.doc : Doc
+data.Array.new!.doc =
+ use Array new! toList
+ {{
+ Creates an array filled with a default value, passes it to the provided
+ initialization function which can mutate the array, then calls
+ {Array.freeze!} to produce an immutable {type data.Array}.
+
+ ```
+ new! "🌸" 5 const() |> toList
+ ```
+
+ ```
+ new! "🌻" 8 (arr -> Array.write arr 2 "🌹") |> toList
+ ```
+ }}
+
+test> data.Array.new!.tests =
+ test.verify do
+ use Array new! toList
+ ensure ((new! "hi" 5 const() |> toList) === List.fill 5 "hi")
+ ensure
+ ((new! "hi" 5 (arr -> Array.write arr 0 "bye") |> toList)
+ === ["bye", "hi", "hi", "hi", "hi"])
+
+data.Array.of : x -> Nat -> data.Array x
+data.Array.of x size = Scope.run do Array.freeze (Scope.arrayOf x size)
+
+data.Array.of.doc : Doc
+data.Array.of.doc =
+ {{
+ `` data.Array.of x sz `` creates a new {type data.Array} of size `sz`, filled
+ with the value `x`.
+ }}
+
+data.Array.randomChoice : data.Array a ->{Exception, Random} a
+data.Array.randomChoice array =
+ randomIndex = Random.natIn 0 (data.Array.size array)
+ Array.at randomIndex array
+ |> Optional.toException
+ "data.Array.randomChoice: empty data.Array" (typeLink data.Array)
+
+data.Array.randomChoice.doc : Doc
+data.Array.randomChoice.doc =
+ use Array fromList randomChoice
+ {{
+ Picks a random element from the given {type data.Array}. Assumes that the
+ array is not empty, so an empty array will raise an {type Exception}.
+
+ # Examples
+
+ ```
+ catch do lcg 4096 do randomChoice (fromList [0, 3, 5, 7])
+ ```
+
+ ```
+ catch do lcg 2510 do randomChoice (fromList [?x, ?y, ?z])
+ ```
+
+ ```
+ catch do lcg 128 do randomChoice (fromList [char.digit, hex]) ()
+ ```
+ }}
+
+test> data.Array.randomChoice.test = test.verify do
+ list = List.range 0 10
+ set = Set.fromList list
+ array = Array.fromList list
+ Each.repeat 1000
+ e = Array.randomChoice array
+ ensure (Set.contains e set)
+
+-- builtin data.Array.Raw.copyTo! :
+-- mutable.Array.Raw g a
+-- -> Nat
+-- -> data.Array.Raw a
+-- -> Nat
+-- -> Nat
+-- ->{g, Exception} ()
+
+data.Array.Raw.copyTo!.doc : Doc
+data.Array.Raw.copyTo!.doc =
+ {{
+ `copyTo! dst doff src soff len` copies `len` values from `src` to `dst`.
+ `doff` and `soff` are starting point offsets into the corresponding arrays.
+
+ Note that the destination array/offset comes first.
+ }}
+
+data.Array.Raw.doc : Doc
+data.Array.Raw.doc =
+ use Array fromList
+ {{
+ The type {type data.Array.Raw} represents a raw immutable array of
+ [boxed](https://https://en.wikipedia.org/wiki/Boxing_%28computer_science%29)
+ values. It's a low-level type that refers to a region of memory managed by
+ the runtime.
+
+ You should in general avoid using this type directly, and instead use the
+ higher-level {type data.Array} type.
+
+ The main use case for {type data.Array.Raw} is to serve as the underlying
+ representation for {type data.Array}:
+
+ @source{type data.Array}
+
+ # Constructing raw arrays
+
+ There is no way to construct a {type data.Array.Raw} directly. Instead, you
+ must first construct a mutable array using {type mutable.Array.Raw}, and
+ then freeze it:
+
+ @signatures{Array.Raw.freeze, Array.Raw.freeze!}
+
+ ## Example
+
+ {fromList} constructs a {type data.Array} from a list of {type Nat}, by
+ first constructing a {type mutable.Array.Raw}, copying the list into it,
+ and then freezing it into a {type data.Array.Raw}. The
+ {type data.Array.Raw} is then used as the internal representation of the
+ {type data.Array}.
+
+ @source{fromList}
+
+ # Using raw arrays
+
+ Read an element from a {type data.Array.Raw}:
+
+ @signature{data.Array.Raw.read}
+
+ Get the number of elements in a {type data.Array.Raw}:
+
+ @signature{data.Array.Raw.size}
+
+ Copy a {type data.Array.Raw} into a {type mutable.Array.Raw}:
+
+ @signature{data.Array.Raw.copyTo!}
+ }}
+
+data.Array.Raw.fromList : [a] -> data.Array.Raw a
+data.Array.Raw.fromList l = Scope.run do
+ use Nat +
+ sz = List.size l
+ dst = Scope.Raw.array sz
+ go i = cases
+ [] -> ()
+ x +: xs ->
+ Raw.write dst i x
+ go (i + 1) xs
+ handle go 0 l
+ with cases
+ { r } -> ()
+ { Exception.raise _ -> _ } -> ()
+ Array.Raw.freeze! dst
+
+-- builtin data.Array.Raw.read : data.Array.Raw a -> Nat ->{Exception} a
+
+data.Array.Raw.read.doc : Doc
+data.Array.Raw.read.doc =
+ {{
+ Reads an element from a {type data.Array.Raw} at a given index.
+
+ # Example
+
+ ```
+ catch do
+ Scope.run do
+ arr = Array.Raw.freeze! (Scope.Raw.arrayOf ?🌱 10)
+ data.Array.Raw.read arr 0
+ ```
+ }}
+
+-- builtin data.Array.Raw.size : data.Array.Raw a -> Nat
+
+data.Array.Raw.size.doc : Doc
+data.Array.Raw.size.doc =
+ {{
+ Gets the size of a {type data.Array.Raw}.
+
+ # Example
+
+ ```
+ catch do
+ Scope.run do
+ arr = Array.Raw.freeze! (Scope.Raw.arrayOf ?🌱 10)
+ data.Array.Raw.size arr
+ ```
+ }}
+
+data.Array.read : data.Array a -> Nat ->{Exception} a
+data.Array.read arr i =
+ match arr with
+ Arr off len src| i Nat.< len -> data.Array.Raw.read src (off Nat.+ i)
+ _ -> ArrayFailure.raise "data.Array.read: index out of bounds" i
+
+data.Array.read.doc : Doc
+data.Array.read.doc =
+ {{
+ Returns the value at the specified, 0-based position in the array.
+
+ An exception is thrown if the index is out of bounds.
+
+ ```
+ catch do data.Array.read (Array.fromList [1, 2]) 0
+ ```
+ }}
+
+data.Array.singleton : a -> data.Array a
+data.Array.singleton a = data.Array.of a 1
+
+data.Array.singleton.doc : Doc
+data.Array.singleton.doc =
+ use Array singleton toList
+ {{
+ Creates an array with a single element.
+
+ # Examples
+
+ ```
+ toList (singleton 1)
+ ```
+
+ ```
+ toList (singleton "hello")
+ ```
+
+ # See also
+
+ * {data.Array.of} to fill an array with multiple references to the same
+ value.
+ * {Array.fromList} to create an array from a {type List}.
+ }}
+
+data.Array.size : data.Array a -> Nat
+data.Array.size = cases Arr _ length _ -> length
+
+data.Array.size.doc : Doc
+data.Array.size.doc = {{ Gets the number of elements in the array. }}
+
+data.Array.slice : Nat -> Nat -> data.Array a ->{Exception} data.Array a
+data.Array.slice o l = cases
+ Arr off len arr| o Nat.+ l Nat.<= len -> Arr (off Nat.+ o) l arr
+ _ -> ArrayFailure.raise "data.Array.slice: not enough elements" (o Nat.+ l)
+
+data.Array.slice.doc : Doc
+data.Array.slice.doc =
+ use Array slice
+ {{
+ {slice} returns a new {type data.Array} representing a portion of the
+ original.
+
+ The first argument specifies how many values to drop from the beginning. The
+ second argument specifies how many values to retain. The original array must
+ have a length at least as long as the offset plus the new length.
+
+ This operation is fast, replacing only the offset and length, and keeping the
+ underlying storage. The potential downside is that more memory may be used
+ than necessary.
+
+ ```
+ catch do Array.toList (slice 1 2 (Array.fromList [1, 2, 3, 4, 5]))
+ ```
+ }}
+
+data.Array.snoc : data.Array a -> a -> data.Array a
+data.Array.snoc arr x = unsafeRun! do
+ Scope.run do
+ (Arr off len raw) = arr
+ use Nat +
+ m = Scope.Raw.array (len + 1)
+ data.Array.Raw.copyTo! m 0 raw off len
+ Raw.write m len x
+ Arr 0 (len + 1) (Array.Raw.freeze! m)
+
+data.Array.snoc.doc : Doc
+data.Array.snoc.doc =
+ {{
+ Appends an element to an array. This is an O(n) operation. The original array
+ is not modified.
+
+ # Examples
+
+ ```
+ Array.toList (Array.snoc (Array.fromList [1, 2, 3]) 4)
+ ```
+
+ # See also
+
+ * {Array.cons} for a version of this that prepends an element to an array.
+ * {Array.append} for a version of this that appends two arrays.
+ }}
+
+data.Array.take : Nat -> data.Array t ->{Exception} data.Array t
+data.Array.take n = Array.slice 0 n
+
+data.Array.take.doc : Doc
+data.Array.take.doc =
+ use Array take
+ {{
+ `` take n arr `` returns the first `n` elements of the array `arr`, as an
+ immutable {type data.Array}.
+
+ This does not copy the array, but merely creates a new view on the same
+ underlying array.
+
+ Raises an {type ArrayFailure} if `n` is larger than ``data.Array.size arr``.
+
+ # Example
+
+ ```
+ catch do Array.toList (take 2 (Array.fromList [?a, ?b, ?c, ?d]))
+ ```
+ }}
+
+data.Array.toList : data.Array a -> [a]
+data.Array.toList = cases
+ Arr off len arr ->
+ use List :+
+ use Nat + >=
+ max = off + len
+ go acc i =
+ if i >= max then acc else go (acc :+ data.Array.Raw.read arr i) (i + 1)
+ handle go [] off with impossible
+
+data.Array.toList.doc : Doc
+data.Array.toList.doc =
+ {{
+ Gets a list corresponding to the contents of an array
+
+ ```
+ Array.toList (Array.fromList ["hi", "bye"])
+ ```
+ }}
+
+data.Array.unsafeAt : Nat -> data.Array a -> a
+data.Array.unsafeAt i arr = unsafeRun! do data.Array.read arr i
+
+data.Array.unsafeAt.doc : Doc
+data.Array.unsafeAt.doc =
+ use Array fromList unsafeAt
+ {{
+ Gets the element at the given index, crashing by calling {bug} if it does not
+ exist.
+
+ # Examples
+
+ ```
+ unsafeAt 0 (fromList [1, 2, 3])
+ ```
+
+ ```
+ unsafeAt 3 (fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {Array.at} for a version of this that returns `` None `` if the index is
+ out of bounds.
+ * {Array.at!} for a version of this that aborts if the index is out of
+ bounds.
+ * {data.Array.read} for a version that throws an {type Exception} if the
+ index is out of bounds.
+ }}
+
+(data.Bag.*) : Bag a -> Bag b -> Bag (a, b)
+(data.Bag.*) = Bag.convolve Tuple.pair
+
+data.Bag.*.doc : Doc
+data.Bag.*.doc =
+ use Bag fromText
+ {{
+ The expression `` bx Bag.* by `` is the {type Bag} of all pairs of elements
+ `(x,y)` where `x` comes from the {type Bag} `bx` and `y` comes from the
+ {type Bag} `by`. The number of times `(x,y)` occurs in the result is
+ ``x Nat.* y``.
+
+ # Example
+
+ ```
+ use Bag *
+ bx = fromText "🎩👕👕👖👖👖"
+ by = fromText "🚗🚕🚕🚙🚙🚙"
+ Bag.occurrenceList (bx * by)
+ ```
+ }}
+
+(data.Bag.+) : Bag a -> Bag a -> Bag a
+(data.Bag.+) = cases
+ MkBag b1 -> cases MkBag b2 -> MkBag (Map.unionWith (Nat.+) b1 b2)
+
+data.Bag.+.doc : Doc
+data.Bag.+.doc =
+ use Bag + fromText
+ {{
+ `` b1 + b2 `` (or equivalently ``b1 + b2``) constructs a new {type Bag} with
+ elements from `b1` and `b2`. The number of times each element occurs in the
+ new {type Bag} is the sum of the number of times it occurs in `b1` and the
+ number of times it occurs in `b2`.
+
+ # Example
+
+ ```
+ a = fromText "🍆🌶🍏🍏"
+ b = fromText "🍏🌶🌶🍏🍏"
+ Bag.occurrenceList (a + b)
+ ```
+ }}
+
+(data.Bag.==) : Bag a -> Bag a -> Boolean
+b1 data.Bag.== b2 =
+ use Bag counts
+ use Map ==
+ counts b1 == counts b2
+
+data.Bag.add : k -> Bag k -> Bag k
+data.Bag.add k b =
+ use Bag +
+ b + Bag.singleton k
+
+data.Bag.add.doc : Doc
+data.Bag.add.doc =
+ use Bag add
+ {{
+ `` add k b `` adds one occurrence of the element `k` to the {type Bag} `b`.
+ The rule is that if `k` occurs `n` times in the {type Bag} `b` then it will
+ occur `n + 1` times in ``add k b``.
+
+ # Example
+
+ ```
+ a = Bag.fromText "🍎🍌🍓"
+ Bag.toText (add ?🍏 a)
+ ```
+ }}
+
+test> data.Bag.add.tests.adds = runs 100 do
+ use Char ascii
+ b = bagOf ascii ()
+ c = ascii()
+ expect (Bag.contains (Bag.add c b) c)
+
+test> data.Bag.addAll.tests.homomorphism = runs 100 do
+ use Bag count
+ use Char ascii
+ use Nat +
+ a = bagOf ascii ()
+ b = bagOf ascii ()
+ c = Bag.occurrenceList (Bag.addAll a b)
+ expect (List.all (cases (e, n) -> n === count e a + count e b) c)
+
+data.Bag.addN : Nat -> k -> Bag k -> Bag k
+data.Bag.addN n k b =
+ use Bag +
+ b + Bag.scale n (Bag.singleton k)
+
+data.Bag.addN.doc : Doc
+data.Bag.addN.doc =
+ use Bag addN
+ {{
+ `` addN n k b `` adds `n` occurrences of the element `k` to the {type Bag}
+ `b`.
+
+ # Example
+
+ ```
+ a = Bag.fromText "👻🎃💀"
+ b = addN 3 ?🎃 a
+ Bag.toText b
+ ```
+ }}
+
+test> data.Bag.addN.test = runs 100 do
+ use Char ascii
+ b = bagOf ascii ()
+ c = ascii()
+ n = natInOrder()
+ expect (Universal.gteq (Bag.count c (Bag.addN n c b)) n)
+
+data.Bag.all : (a ->{g} Boolean) -> Bag a ->{g} Boolean
+data.Bag.all p = Set.all p << Bag.toSet
+
+data.Bag.all.doc : Doc
+data.Bag.all.doc =
+ use Bag all
+ {{
+ Check if all elements in the {type Bag} match a query.
+
+ The expression `` all p b `` returns `` true `` if the function `p` returns
+ `true` for all elements in the {type Bag} `b`, or if `b` is empty.
+ Equivalently, it returns `` false `` only if `p` returns `` false `` for any
+ element in `b`.
+
+ # Examples
+
+ ```
+ a = Bag.fromList ["birthday", "party", "celebration"]
+ all (Nat.isOdd << Text.size) a
+ ```
+
+ ```
+ all Nat.isEven Bag.empty
+ ```
+ }}
+
+test> data.Bag.all.test = runs 100 do
+ bs = gen.listOf gen.boolean ()
+ p = yesNo()
+ expect (Bag.all p (Bag.fromList bs) === List.all p bs)
+
+data.Bag.any : (a ->{g} Boolean) -> Bag a ->{g} Boolean
+data.Bag.any p = Set.any p << Bag.toSet
+
+data.Bag.any.doc : Doc
+data.Bag.any.doc =
+ use Bag any
+ use Nat isOdd
+ {{
+ Check if any elements in the {type Bag} match a query.
+
+ The expression `` any p b `` returns `` true `` if the function `p` returns
+ `` true `` for at least one element in the {type Bag} `b`. Equivalently, it
+ returns `` false `` only if `p` returns `` false `` for all elements in `b`,
+ or if `b` is empty.
+
+ # Examples
+
+ ```
+ a = Bag.fromList ["apple", "pie", "cheese"]
+ any (isOdd << Text.size) a
+ ```
+
+ ```
+ any isOdd Bag.empty
+ ```
+ }}
+
+test> data.Bag.any.test = runs 100 do
+ bs = gen.listOf gen.boolean ()
+ p = yesNo()
+ expect (Bag.any p (Bag.fromList bs) === List.any p bs)
+
+data.Bag.contains : Bag a -> a -> Boolean
+data.Bag.contains = flip elementOf
+
+data.Bag.contains.doc : Doc
+data.Bag.contains.doc =
+ use Bag contains
+ {{
+ `` contains b k `` is `` true `` if the value `k` is an element of the bag
+ `b`, and `` false `` otherwise.
+
+ # Examples
+
+ ```
+ b = Bag.fromText "🎉🎊🎂"
+ contains b ?🎉
+ ```
+
+ ```
+ contains Bag.empty ?🎂
+ ```
+ }}
+
+test> data.Bag.contains.test =
+ runs 100 do
+ use gen listOf
+ printable = listOf Char.asciiPrintable ()
+ chars = listOf Char.ascii ()
+ bag = Bag.fromList printable
+ expect
+ (List.all (c -> Bag.contains bag c === List.contains c printable) chars)
+
+data.Bag.convolve : (i ->{g} a ->{g} t) -> Bag i -> Bag a ->{g} Bag t
+data.Bag.convolve f ba bb =
+ use Bag flatMap
+ flatMap (a -> flatMap (b -> Bag.singleton (f a b)) bb) ba
+
+data.Bag.convolve.doc : Doc
+data.Bag.convolve.doc =
+ use Bag convolve fromText
+ {{
+ `` convolve f xs ys `` applies the function `f` to every pair of elements
+ `(x,y)` where `x` comes from the {type Bag} `xs` and `y` comes from the
+ {type Bag} `ys`. This is called the __convolution__ of `xs` and `ys` with the
+ function `f`.
+
+ # Example
+
+ ```
+ bx = fromText "🎁🎈🎈"
+ by = fromText "🍏🍏🍇"
+ Bag.occurrenceList (convolve (x y -> fromCharList [x, y]) bx by)
+ ```
+ }}
+
+test> data.Bag.convolve.tests.associative = runs 100 do
+ use Bag == convolve
+ use gen boolean
+ bx = bagOf boolean ()
+ by = bagOf boolean ()
+ bz = bagOf boolean ()
+ g = logic()
+ h = logic()
+ f x y z = g x (h y z)
+ left = convolve id (convolve (x y z -> f x y z) bx by) bz
+ right = convolve (flip id) bx (convolve (y z x -> f x y z) by bz)
+ expect (left == right)
+
+test> data.Bag.convolve.tests.natural = runs 100 do
+ use Bag == convolve map
+ use gen boolean
+ bx = bagOf boolean ()
+ by = bagOf boolean ()
+ f = yesNo()
+ g = yesNo()
+ o = logic()
+ left = convolve (x y -> o (f x) (g y)) bx by
+ right = convolve o (map f bx) (map g by)
+ expect (left == right)
+
+test> data.Bag.convolve.tests.unit = runs 100 do
+ use Bag == convolve singleton
+ m = bagOf natInOrder ()
+ k = natInOrder()
+ right = convolve (const id) (singleton k) m
+ left = convolve const m (singleton k)
+ expect (left == right && left == m)
+
+data.Bag.count : a -> Bag a -> Nat
+data.Bag.count a = cases
+ MkBag counts -> Optional.getOrElse 0 (Map.get a counts)
+
+data.Bag.count.doc : Doc
+data.Bag.count.doc =
+ use Bag count
+ {{
+ `` count e b `` returns the number of times the element `e` occurs in the
+ {type Bag} `b`.
+
+ # Example
+
+ ```
+ count ?🥕 (Bag.fromText "🥒🥕🥕🥕🥔")
+ ```
+ }}
+
+test> data.Bag.count.tests.counts = runs 100 do
+ use Bag count
+ use Nat +
+ use Text ascii
+ m = bagOf ascii ()
+ k = ascii()
+ n = natInOrder()
+ c = count k m
+ expect (count k (Bag.addN n k m) === c + n)
+
+data.Bag.counts : Bag a -> Map a Nat
+data.Bag.counts b =
+ (MkBag m) = Bag.internal.normalize b
+ m
+
+data.Bag.counts.doc : Doc
+data.Bag.counts.doc =
+ use Bag counts
+ {{
+ `` counts b `` returns a {type Map} where the keys are the elements of the
+ {type Bag} `b` and the value under each key is the number of times that
+ element occurs in `b`.
+
+ # Example
+
+ ```
+ b = Bag.fromText "abracadabra"
+ Map.toList (counts b)
+ ```
+ }}
+
+data.Bag.difference : Bag a -> Bag a -> Bag a
+data.Bag.difference b1 = cases
+ MkBag b2 ->
+ List.foldRight (cases (a, n), b -> Bag.removeN n a b) b1 (Map.toList b2)
+
+data.Bag.difference.doc : Doc
+data.Bag.difference.doc =
+ use Bag difference fromText
+ use Nat -
+ {{
+ `` difference b1 b2 `` removes elements from the {type Bag} `b1` to the
+ extent that they exist in the {type Bag} `b2`. If an element occurs `n` times
+ in `b1` and `m` times in `b2`, it will occur `` n - m `` times in the result.
+
+ # Example
+
+ ```
+ b1 = fromText "🧣🧥🧤🧤"
+ b2 = fromText "🧤🧥🧥👕"
+ Bag.toList (difference b1 b2)
+ ```
+ }}
+
+test> data.Bag.difference.tests.homomorphism = runs 100 do
+ use Bag count
+ use Char ascii
+ use Nat -
+ a = bagOf ascii ()
+ b = bagOf ascii ()
+ d = Bag.occurrenceList (Bag.difference a b)
+ expect (List.all (cases (e, n) -> n === count e a - count e b) d)
+
+data.Bag.doc : Doc
+data.Bag.doc =
+ use Bag * + contains count counts fromMap fromOccurrenceList fromText occurrenceList scale subbag superbag toText
+ {{
+ {type Bag} is the type of __multisets__, or bags.
+
+ A {type Bag} is like a {type Set}, except that any given element can occur
+ more than once. In that sense it's more like a list, but unlike a list the
+ elements of a {type Bag} occur in no particular order.
+
+ The elements of a {type Bag} can be of any type, but they must all be of the
+ same type. The {type Bag} type is parameterized by the type of its elements.
+
+ # Constructing bags
+
+ {Bag.empty} is the empty bag.
+
+ `` Bag.singleton k `` is the bag with a single value `k` in it.
+
+ `` Bag.fromList xs `` constructs a bag from a list `xs`.
+
+ {fromMap} constructs a bag given a map from keys to counts:
+
+ ```
+ m = Map.fromList [(?🥒, 0), (?🥕, 3), (?🥑, 2), (?🌽, 1)]
+ toText (fromMap m)
+ ```
+
+ {fromOccurrenceList} constructs a bag from the list representation of such
+ a map:
+
+ ```
+ occurrences = [(?🍕, 1), (?🍟, 3), (?🍗, 2), (?🍔, 0)]
+ toText (fromOccurrenceList occurrences)
+ ```
+
+ {fromText} contructs a bag containing the characters of some {type Text}:
+
+ ```
+ b = fromText "🍎🍌🍇🍎🍌🍎"
+ occurrenceList b
+ ```
+
+ # Adding and removing elements
+
+ `` Bag.add k b `` adds the single element `k` to the {type Bag} `b`.
+
+ `` Bag.addN n k b `` adds `n` instances of `k` to the {type Bag} `b`.
+
+ `` Bag.remove k b `` removes one `k` from the {type Bag} `b`.
+
+ `` Bag.removeN n k b `` removes `n` occurrences of `k` from the {type Bag}
+ `b`.
+
+ `` Bag.removeAll k b `` removes all occurrences of `k` from the {type Bag}
+ `b`.
+
+ `` removeWhere p b `` removes elements from `b` where the condition `p` is
+ ``true``.
+
+ `` Bag.filter p b `` removes elements from `b` where the condition `p` is
+ ``false``.
+
+ # Querying and counting elements
+
+ `` contains b k `` is `` true `` if `k` occurs at least once in the
+ {type Bag} `b`.
+
+ {elementOf} is {contains} with its arguments flipped.
+
+ `` count k b `` counts the number of occurrences of `k` in the {type Bag}
+ `b`.
+
+ {Bag.isEmpty} checks if a {type Bag} is empty.
+
+ {Bag.size} gets the number of elements in a {type Bag}.
+
+ # Combining bags
+
+ {+} adds the elements of one {type Bag} to another.
+
+ `` Bag.convolve f xs ys `` collects all results `f x y` where `x` is from
+ the {type Bag} `xs` and `y` is from `ys`.
+
+ {Bag.difference} removes the elements of one {type Bag} from another.
+
+ `` Bag.intersect xs ys `` finds the largest {subbag} of both `xs` and `ys`.
+
+ `` xs * ys `` is the {type Bag} of all pairs of elements `(x,y)` where `x`
+ is from the {type Bag} `xs` and `y` is from `ys`.
+
+ `` Bag.union xs ys `` finds the smallest {superbag} of both `xs` and `ys`.
+
+ # Comparing bags
+
+ `` Bag.from b1 b2 `` returns `` true `` if every element in `b1` occurs at
+ least once in `b2`.
+
+ `` subbag b1 b2 `` returns `` true `` if no element occurs more times in
+ `b1` than in `b2`.
+
+ {superbag} is {subbag} with its arguments flipped.
+
+ # Conversions to other structures
+
+ `` counts b `` returns a {type Map} associating each distinct element of
+ `b` with its number of occurrences in `b`.
+
+ {occurrenceList} is the same as {counts}, but returns a {type List}.
+
+ `` Bag.toList b `` returns a {type List} of all the elements of `b`,
+ including duplicates.
+
+ `` Bag.toSet b `` returns the {type Set} of distinct elements in `b`.
+
+ `` toText b `` returns the {type Text} containing all the elements of `b`
+ as long as they are {type Char}s.
+
+ # Traversal and iteration
+
+ `` traverse f b `` iterates over the elements of `b`, applying the
+ effectful function `f` to each element, collecting the results in a new
+ {type Bag}.
+
+ `` Bag.map f b `` applies the function `f` to every element of `b`.
+
+ `` Bag.flatMap f b `` applies the bag-valued function `f` to every element
+ of `b`, collecting all results into one {type Bag}.
+
+ {Bag.foldLeft} folds a {type Bag} with a binary function, accumulating in
+ the left argument.
+
+ {Bag.foldRight} folds a {type Bag} with a binary function, accumulating in
+ the right argument.
+
+ `` Bag.modify f b `` uses `f` to modify every distinct element of `b` as
+ well as its number of occurrences.
+
+ # Operations on element counts
+
+ `` scale n b `` multiplies all occurrences in `b` by `n`:
+
+ ```
+ b = fromText "🍊🍐🍐🍑🍑🍑"
+ toText (scale 2 b)
+ ```
+
+ `` mapCounts f b `` is a new {type Bag} where every element `x` of `b`
+ occurs {{ docExample 3 do f x b -> f (count x b) }} times:
+
+ ```
+ b = fromText "🌲🌲🌳🌳🌳🌴"
+ toText (mapCounts Nat.decrement b)
+ ```
+ }}
+
+data.Bag.elementOf : a -> Bag a -> Boolean
+data.Bag.elementOf a b = Universal.gt (Bag.count a b) 0
+
+data.Bag.elementOf.doc : Doc
+data.Bag.elementOf.doc =
+ {{
+ `` elementOf k b `` is `` true `` if the value `k` is an element of the bag
+ `b`, and `false` otherwise.
+
+ # Examples
+
+ ```
+ b = Bag.fromText "abracadabra"
+ elementOf ?a b
+ ```
+
+ ```
+ elementOf 0 Bag.empty
+ ```
+ }}
+
+test> data.Bag.elementOf.test = runs 100 do
+ use gen listOf
+ printable = listOf Char.asciiPrintable ()
+ chars = listOf Char.ascii ()
+ bag = Bag.fromList printable
+ expect (List.all (c -> elementOf c bag === List.contains c printable) chars)
+
+data.Bag.empty : Bag a
+data.Bag.empty = MkBag Map.empty
+
+data.Bag.empty.doc : Doc
+data.Bag.empty.doc = {{ The empty {type Bag}. }}
+
+test> data.Bag.empty.test = runs 2 do
+ k = gen.boolean()
+ expect (Bag.count k Bag.empty === 0)
+
+data.Bag.equals.doc : Doc
+data.Bag.equals.doc =
+ use Bag count
+ use Nat ==
+ {{
+ Checks if two {type Bag}s are equal. Any two {type Bag}s `b1` and `b2` are
+ equal when for every value `k`, ``count k b1 == count k b2``.
+
+ Note that {===} does not have the correct behaviour for {type Bag}, as two
+ equal {type Bag}s may have different internal structure.
+ }}
+
+test> data.Bag.equals.tests.reflexive =
+ runs 100 do laws.reflexive (bagOf natInOrder) Bag.equals
+
+test> data.Bag.equals.tests.symmetric =
+ runs 100 do laws.commutative (bagOf natInOrder) Bag.equals
+
+test> data.Bag.equals.tests.transitive =
+ runs 100 do laws.transitive (bagOf natInOrder) Bag.equals
+
+data.Bag.filter : (i ->{e} Boolean) -> Bag i ->{e} Bag i
+data.Bag.filter p = Bag.modify cases (a, n) -> if p a then (a, n) else (a, 0)
+
+data.Bag.filter.doc : Doc
+data.Bag.filter.doc =
+ use Bag filter
+ {{
+ ``filter p b``returns a new {type Bag} with just the elements from `b` for
+ which the function `p` returns `true`.
+
+ # Example
+
+ ```
+ b = Bag.fromText "abracadabra"
+ Bag.toText (filter (Char.inRange ?b ?d) b)
+ ```
+ }}
+
+data.Bag.flatMap : (a ->{e} Bag b) -> Bag a ->{e} Bag b
+data.Bag.flatMap f =
+ use Bag +
+ k z = cases (e, n) -> z + Bag.scale n (f e)
+ List.foldLeft k Bag.empty << Bag.occurrenceList
+
+data.Bag.flatMap.doc : Doc
+data.Bag.flatMap.doc =
+ use Bag flatMap
+ {{
+ `` flatMap f b `` applies the function `f` to every element of the {type Bag}
+ `b`. The function `f` returns a whole {type Bag} each time, and {flatMap}
+ collects all their elements into one {type Bag}.
+
+ # Example
+
+ This collects into one {type Bag} all the individual characters from a
+ {type Bag} of {type Text}:
+
+ ```
+ b = Bag.fromList ["site", "real", "live"]
+ Bag.toText (flatMap (n -> Bag.fromText n) b)
+ ```
+ }}
+
+test> data.Bag.flatMap.tests.associative = runs 100 do
+ use Bag == flatMap
+ x = bagOf (bagOf (bagOf natInOrder)) ()
+ left = flatMap id (flatMap id x)
+ right = flatMap id (flatMap (Bag.singleton << flatMap id) x)
+ expect (left == right)
+
+test> data.Bag.flatMap.tests.unit = runs 100 do
+ use Bag == flatMap singleton
+ x = bagOf natInOrder ()
+ n = natInOrder()
+ expect (flatMap singleton x == x && flatMap (const x) (singleton n) == x)
+
+data.Bag.foldLeft : (b ->{g} a ->{g} b) -> b -> Bag a ->{g} b
+data.Bag.foldLeft f z = List.foldLeft f z << Bag.toList
+
+data.Bag.foldLeft.doc : Doc
+data.Bag.foldLeft.doc =
+ use Bag foldLeft
+ use Nat +
+ {{
+ `` foldLeft f z b `` iterates over the elements of the {type Bag} `b`,
+ applying the binary operator `f` to each element and the result so far. The
+ result starts as the value `z`, which is also returned in case the {type Bag}
+ is empty.
+
+ # Example
+
+ Add up all the sizes of elements in a bag:
+
+ ```
+ b = Bag.fromList ["red", "blue", "green"]
+ foldLeft (z s -> z + Text.size s) 0 b
+ ```
+ }}
+
+test> data.Bag.foldLeft.tests.asList = runs 100 do
+ use Nat +
+ x = gen.listOf natInOrder ()
+ expect (List.foldLeft (+) 0 x === Bag.foldLeft (+) 0 (Bag.fromList x))
+
+data.Bag.foldRight : (a ->{g} b ->{g} b) -> b -> Bag a ->{g} b
+data.Bag.foldRight f z = List.foldRight f z << Bag.toList
+
+data.Bag.foldRight.doc : Doc
+data.Bag.foldRight.doc =
+ use Bag foldRight
+ use Nat +
+ {{
+ `` foldRight f z b `` iterates over the elements of the {type Bag} `b`,
+ applying the binary operator `f` to each element and the result so far. The
+ result starts as the value `z`, which is also returned in case the {type Bag}
+ is empty.
+
+ # Example
+
+ Add up all the sizes of elements in a bag:
+
+ ```
+ b = Bag.fromList ["red", "blue", "green"]
+ foldRight (s z -> Text.size s + z) 0 b
+ ```
+ }}
+
+test> data.Bag.foldRight.tests.asList = runs 100 do
+ use Nat +
+ x = gen.listOf natInOrder ()
+ expect (List.foldRight (+) 0 x === Bag.foldRight (+) 0 (Bag.fromList x))
+
+data.Bag.from : Bag a -> Bag a -> Boolean
+data.Bag.from b1 b2 =
+ use Bag toSet
+ Set.subset (toSet b1) (toSet b2)
+
+data.Bag.from.doc : Doc
+data.Bag.from.doc =
+ use Bag from fromText
+ {{
+ `` from b1 b2 `` returns `` true `` if every element in `b1` occurs at least
+ once in `b2`.
+
+ # Example
+
+ ```
+ redSuits = fromText "♥♦"
+ h1 = fromText "♠♠♠♠♥♥"
+ h2 = fromText "♥♥"
+ (from h1 redSuits, from h2 redSuits)
+ ```
+ }}
+
+test> data.Bag.from.tests.subset = runs 100 do
+ use Bag toSet
+ use Char ascii
+ a = bagOf ascii ()
+ b = bagOf ascii ()
+ expect (Bag.from a b === Set.subset (toSet a) (toSet b))
+
+data.Bag.fromList : [k] -> Bag k
+data.Bag.fromList = List.foldRight Bag.add Bag.empty
+
+data.Bag.fromList.doc : Doc
+data.Bag.fromList.doc =
+ use Bag fromList
+ {{
+ `` fromList ks `` creates a new {type Bag} from the {type List} `ks`. The
+ resulting {type Bag} contains exactly as many occurrences of each element as
+ there are in the {type List} `ks`.
+
+ # Example
+
+ ```
+ Bag.occurrenceList (fromList ["apple", "banana", "cherry", "apple"])
+ ```
+ }}
+
+test> data.Bag.fromList.tests.roundtrip = runs 100 do
+ use Heap sort
+ x = sort (gen.listOf natInOrder ())
+ expect (sort (Bag.toList (Bag.fromList x)) === x)
+
+data.Bag.fromMap : Map k Nat -> Bag k
+data.Bag.fromMap m = MkBag m
+
+data.Bag.fromMap.doc : Doc
+data.Bag.fromMap.doc =
+ {{
+ Constructs a new {type Bag} from a {type Map} that associates elements to be
+ put in the {type Bag} with the number of times they should appear.
+
+ # Example
+
+ ```
+ m = Map.fromList [(?🥒, 0), (?🥕, 3), (?🥑, 2), (?🌽, 1)]
+ Bag.toText (Bag.fromMap m)
+ ```
+ }}
+
+test> data.Bag.fromMap.tests.roundTrip = runs 100 do
+ use Map ==
+ m = tests.mapOf natInOrder nonzeroNat ()
+ expect (Bag.counts (Bag.fromMap m) == m)
+
+data.Bag.fromOccurrenceList : [(a, Nat)] -> Bag a
+data.Bag.fromOccurrenceList = MkBag << Map.fromList
+
+data.Bag.fromOccurrenceList.doc : Doc
+data.Bag.fromOccurrenceList.doc =
+ {{
+ Constructs a {type Bag} from a {type List} of elements paired with the number
+ of times they should occur in the {type Bag}.
+
+ # Example
+
+ ```
+ occurrences = [(?🍕, 1), (?🍟, 3), (?🍗, 2), (?🍔, 0)]
+ Bag.toText (Bag.fromOccurrenceList occurrences)
+ ```
+ }}
+
+test> data.Bag.fromOccurrenceList.tests.roundTrip =
+ runs 100 do
+ use Heap sort
+ m =
+ List.filter
+ (cases (_, v) -> Universal.gt v 0)
+ (distinctBy at1 (sort (gen.listOf (pairOf natInOrder natInOrder) ())))
+ expect
+ (assertEquals (sort (Bag.occurrenceList (Bag.fromOccurrenceList m))) m)
+
+data.Bag.fromText.doc : Doc
+data.Bag.fromText.doc =
+ use Bag fromText
+ {{
+ `` fromText t `` is the {type Bag} of characters in the {type Text} `t`.
+
+ # Example
+
+ ```
+ b = fromText "🍎🍌🍇🍎🍌🍎"
+ Bag.occurrenceList b
+ ```
+ }}
+
+test> data.Bag.fromText.tests.anagram = runs 100 do
+ use Heap sort
+ s = Text.ascii()
+ expect (sort (Bag.toList (Bag.fromText s)) === sort (toCharList s))
+
+data.Bag.internal.compare :
+ (Nat ->{e} Nat ->{e} Boolean) -> Bag a -> Bag a ->{e} Boolean
+data.Bag.internal.compare k b1 b2 =
+ use Bag count
+ Bag.foldLeft (b e -> b && k (count e b1) (count e b2)) true (Bag.union b1 b2)
+
+data.Bag.internal.compare.doc : Doc
+data.Bag.internal.compare.doc =
+ use Bag count
+ {{
+ A utility function for comparisons of bags. `` internal.compare k b1 b2 ``
+ returns `` true `` if for all elements `e` of the {type Bag} `b1`, {{
+ docExample 4 do k b1 b2 e -> k (count e b1) (count e b2) }} returns ``true``.
+ Otherwise ``false``.
+ }}
+
+data.Bag.internal.normalize : Bag a -> Bag a
+data.Bag.internal.normalize = cases
+ MkBag b ->
+ Map.foldLeftWithKey
+ (m k n -> (if Universal.lt n 1 then m else Bag.addN n k m)) Bag.empty b
+
+data.Bag.intersect : Bag a -> Bag a -> Bag a
+data.Bag.intersect = cases
+ MkBag b1 -> cases MkBag b2 -> MkBag (Map.intersectWith Universal.min b1 b2)
+
+data.Bag.intersect.doc : Doc
+data.Bag.intersect.doc =
+ use Bag fromText intersect
+ {{
+ `` intersect b1 b2 `` constructs the intersection of `b1` and `b2`, which is
+ the largest {type Bag} that is a {Bag.subbag} of both `b1` and `b2`. This is
+ a new {type Bag} with elements from `b1` and `b2` where the number of times
+ each element occurs in the new {type Bag} is either the number of times it
+ occurs in `b1` or the number of times it occurs in `b2`, whichever is less.
+
+ ```
+ a = fromText "🐱🐭🐭🐶🐶"
+ b = fromText "🐶🐭🐶🐶🐹"
+ Bag.toText (intersect a b)
+ ```
+ }}
+
+test> data.Bag.intersect.tests.commutative = runs 100 do
+ use Bag == intersect
+ x = bagOf natInOrder ()
+ y = bagOf natInOrder ()
+ expect (intersect x y == intersect y x)
+
+test> data.Bag.intersect.tests.distributive = runs 100 do
+ use Bag == intersect union
+ x = bagOf natInOrder ()
+ y = bagOf natInOrder ()
+ z = bagOf natInOrder ()
+ expect (intersect x (union y z) == union (intersect x y) (intersect x z))
+
+test> data.Bag.intersect.tests.homomorphism =
+ runs 100 do
+ use Bag count
+ use Char ascii
+ a = bagOf ascii ()
+ b = bagOf ascii ()
+ c = Bag.occurrenceList (Bag.intersect a b)
+ expect
+ (List.all (cases (e, n) -> n === Universal.min (count e a) (count e b)) c)
+
+test> data.Bag.intersect.tests.idempotent = runs 100 do
+ use Bag ==
+ x = bagOf natInOrder ()
+ expect (Bag.intersect x x == x)
+
+test> data.Bag.intersect.tests.zero = runs 100 do
+ use Bag == empty
+ x = bagOf natInOrder ()
+ expect (Bag.intersect x empty == empty)
+
+data.Bag.isEmpty : Bag a -> Boolean
+data.Bag.isEmpty b = List.all (cases (_, n) -> n === 0) (Bag.occurrenceList b)
+
+data.Bag.isEmpty.doc : Doc
+data.Bag.isEmpty.doc =
+ use Bag isEmpty
+ {{
+ `` isEmpty b `` is `` true `` if the {type Bag} `b` has no elements, and ``
+ false `` otherwise.
+
+ # Examples
+
+ ```
+ isEmpty Bag.empty
+ ```
+
+ ```
+ isEmpty (Bag.singleton 42)
+ ```
+ }}
+
+test> data.Bag.isEmpty.test = runs 100 do
+ use Bag isEmpty
+ k = natInOrder()
+ m = Bag.add k (bagOf natInOrder ())
+ expect (isEmpty Bag.empty && Boolean.not (isEmpty m))
+
+data.Bag.map : (a -> b) -> Bag a -> Bag b
+data.Bag.map f = cases MkBag b -> MkBag (Map.mapKeysWith (Nat.+) f b)
+
+data.Bag.map.doc : Doc
+data.Bag.map.doc =
+ use Bag map
+ {{
+ `` map f b `` applies the function `f` to every element of the {type Bag}
+ `b`, constructing a new {type Bag} of the results. The function is not
+ allowed to use abilities. For a version of this that allows abilities, see
+ {traverse}.
+
+ # Example
+
+ ```
+ b = Bag.fromList [2, 3, 1, 1]
+ Bag.occurrenceList (map Nat.isEven b)
+ ```
+ }}
+
+test> data.Bag.map.tests.functor = runs 100 do
+ use Bag ==
+ m = bagOf natInOrder ()
+ expect (Bag.map id m == m)
+
+test> data.Bag.map.tests.surjection = runs 100 do
+ use Bag size
+ use Nat ==
+ b = bagOf natInOrder ()
+ c = Bag.map Nat.isEven b
+ expect (size b == size c)
+
+data.Bag.mapCounts : (Nat ->{e} Nat) -> Bag a ->{e} Bag a
+data.Bag.mapCounts f = cases MkBag b -> MkBag (Map.map f b)
+
+data.Bag.mapCounts.doc : Doc
+data.Bag.mapCounts.doc =
+ {{
+ `` mapCounts f b `` constructs a new {type Bag} with the same elements as
+ `b`, but with the number of times any given element occurs modified by the
+ function `f`.
+
+ # Example
+
+ ```
+ b = Bag.fromText "🍊🍊🍋🍋🍋🍌"
+ Bag.toText (mapCounts Nat.increment b)
+ ```
+ }}
+
+test> data.Bag.mapCounts.tests.functor = runs 100 do
+ use Bag ==
+ m = bagOf natInOrder ()
+ expect (mapCounts id m == m)
+
+data.Bag.modify : ((a, Nat) ->{e} (b, Nat)) -> Bag a ->{e} Bag b
+data.Bag.modify f =
+ Bag.fromOccurrenceList << List.flatMap
+ (p -> let
+ q@(b, n) = f p
+ if Universal.lt n 1 then [] else [q]) << Bag.occurrenceList
+
+data.Bag.modify.doc : Doc
+data.Bag.modify.doc =
+ use Bag modify
+ use Nat -
+ {{
+ `` modify f b `` passes each unique element in the {type Bag} `b`, together
+ with its count in `b`, to the function `f` which returns a modified element
+ and a new count. Can be used to modify elements, their counts, or both.
+
+ # Example
+
+ ```
+ b = Bag.fromText "abracadabra"
+ Bag.toText (modify (cases (k, c) -> (ascii.toUpper k, c - 1)) b)
+ ```
+ }}
+
+test> data.Bag.modify.tests.double = runs 100 do
+ use Bag + ==
+ use Nat *
+ m = bagOf Text.ascii ()
+ expect (Bag.modify (cases (k, n) -> (k, n * 2)) m == m + m)
+
+test> data.Bag.modify.tests.functor = runs 100 do
+ use Bag ==
+ m = bagOf natInOrder ()
+ expect (Bag.modify id m == m)
+
+test> data.Bag.modify.tests.zero = runs 100 do
+ use Bag ==
+ m = bagOf Text.ascii ()
+ expect (Bag.modify (cases (k, n) -> (k, 0)) m == Bag.empty)
+
+data.Bag.none : (a ->{g} Boolean) -> Bag a ->{g} Boolean
+data.Bag.none p = Boolean.not << Bag.any p
+
+data.Bag.none.doc : Doc
+data.Bag.none.doc =
+ use Bag fromList none
+ use Nat isEven
+ {{
+ Check if no elements in the {type Bag} match a query.
+
+ The expression `` none p b `` returns `` true `` if the function `p` returns
+ `` false `` for all elements in the {type Bag} `b`, or if `b` is empty.
+ Equivalently, it returns `` false `` only if `p` returns `` true `` for at
+ least one element in `b`.
+
+ # Example
+
+ ```
+ none isEven (fromList [1, 2, 3])
+ ```
+
+ ```
+ none isEven (fromList [1, 3, 13])
+ ```
+
+ ```
+ none Nat.isOdd (fromList [])
+ ```
+ }}
+
+test> data.Bag.none.test = runs 100 do
+ bs = gen.listOf gen.boolean ()
+ p = yesNo()
+ expect (Bag.none p (Bag.fromList bs) === List.none p bs)
+
+data.Bag.nth : Nat -> Bag a -> Optional a
+data.Bag.nth index bag =
+ use Nat + - <
+ loop : Nat -> Map a Nat -> Nat -> (Optional a, Nat)
+ loop index map visited =
+ match map with
+ internal.Tip -> (None, visited)
+ internal.Bin sz key value l r ->
+ match loop index l visited with
+ (Some k, v) -> (Some k, v)
+ (None, v) ->
+ if index - v < value then (Some key, v + value)
+ else loop index r (v + value)
+ loop index (Bag.counts bag) 0 |> at1
+
+data.Bag.nth.doc : Doc
+data.Bag.nth.doc =
+ use Bag nth
+ {{
+ {{ docExample 2 do i b -> nth i b }} returns the `i`-th smallest element in
+ `b`, where `i`=0 is the smallest element (according to {Universal.ordering}).
+
+ Is the same as {{ docExample 2 do i as -> List.at i (Bag.toList as) }} but
+ doesn't require instantiating the intermediate {type List}.
+
+ ```
+ b = Bag.fromList [3, 1, 2, 3, 4, 5, 1, 1]
+ List.map (i -> nth i b) (List.range 0 (Bag.size b))
+ ```
+ }}
+
+test> data.Bag.nth.tests =
+ test.verify do
+ use Random natIn
+ Each.repeat 100
+ s = (List.replicate (natIn 0 20) do natIn 0 10) |> Bag.fromList
+ ensure
+ (List.somes (List.map (i -> Bag.nth i s) (List.range 0 (Bag.size s)))
+ === Bag.toList s)
+
+data.Bag.occurrenceList : Bag a -> [(a, Nat)]
+data.Bag.occurrenceList = Map.toList << Bag.counts
+
+data.Bag.occurrenceList.doc : Doc
+data.Bag.occurrenceList.doc =
+ {{
+ Returns a list of all the elements of a {type Bag}, paired with the number of
+ times that element occurs.
+
+ # Example
+
+ ```
+ Bag.occurrenceList (Bag.fromText "🍕🍕🍟🍔🍔🍔")
+ ```
+ }}
+
+test> data.Bag.product.tests.spec =
+ runs 100 do
+ use Bag count occurrenceList
+ use List all
+ use Nat *
+ x = bagOf natInOrder ()
+ y = bagOf natInOrder ()
+ p = Bag.product x y
+ left =
+ all (cases ((a, b), n) -> n === count a x * count b y) (occurrenceList p)
+ right =
+ all
+ (cases ((a, m), (b, n)) -> count (a, b) p === m * n)
+ (List.flatMap
+ (a -> List.map (b -> (a, b)) (occurrenceList y)) (occurrenceList x))
+ expect (left && right)
+
+data.Bag.randomChoice : Bag a ->{Exception, Random} a
+data.Bag.randomChoice bag =
+ randomIndex = Random.natIn 0 (Bag.size bag)
+ Bag.nth randomIndex bag
+ |> Optional.toException "Bag.randomChoice: empty Bag" (typeLink Bag)
+
+data.Bag.randomChoice.doc : Doc
+data.Bag.randomChoice.doc =
+ use Bag fromList randomChoice
+ {{
+ Picks a random element from the given {type Bag}. Assumes that the bag is not
+ empty, so an empty bag will raise an {type Exception}.
+
+ # Examples
+
+ ```
+ catch do lcg 4096 do randomChoice (fromList [0, 3, 5, 7])
+ ```
+
+ ```
+ catch do lcg 2510 do randomChoice (fromList [?x, ?y, ?z])
+ ```
+
+ ```
+ catch do lcg 128 do randomChoice (fromList [char.digit, hex]) ()
+ ```
+ }}
+
+test> data.Bag.randomChoice.test = test.verify do
+ list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 1, 4, 6, 6]
+ set = Set.fromList list
+ bag = Bag.fromList list
+ Each.repeat 1000
+ e = Bag.randomChoice bag
+ ensure (Set.contains e set)
+
+data.Bag.remove : k -> Bag k -> Bag k
+data.Bag.remove k b = Bag.removeN 1 k b
+
+data.Bag.remove.doc : Doc
+data.Bag.remove.doc =
+ use Bag remove
+ {{
+ `` remove k b `` removes one occurrence of the element `k` from the
+ {type Bag} `b` if `k` occurs in the {type Bag} `b`. Otherwise it does
+ nothing.
+
+ # Example
+
+ ```
+ Bag.toText (remove ?🎅 (Bag.fromText "🎄🎅🎅🎁🎁🎁"))
+ ```
+ }}
+
+test> data.Bag.remove.tests.removes = runs 100 do
+ use Bag count
+ use Nat -
+ b = bagOf natInOrder ()
+ k = natInOrder()
+ p = count k (Bag.remove k b) === count k b - 1
+ expect p
+
+data.Bag.removeAll : k -> Bag k -> Bag k
+data.Bag.removeAll k = cases MkBag b -> MkBag (Map.delete k b)
+
+data.Bag.removeAll.doc : Doc
+data.Bag.removeAll.doc =
+ use Bag removeAll
+ {{
+ `` removeAll k b `` removes all occurrences of the element `k` from the
+ {type Bag} `b` if `k` occurs in the {type Bag} `b`. Otherwise it does
+ nothing.
+
+ # Example
+
+ ```
+ Bag.toText (removeAll ?🎅 (Bag.fromText "🎄🎅🎅🎁🎁🎁"))
+ ```
+ }}
+
+test> data.Bag.removeAll.tests.removes = runs 100 do
+ b = bagOf natInOrder ()
+ k = natInOrder()
+ p = Bag.count k (Bag.removeAll k b) === 0
+ expect p
+
+data.Bag.removeN : Nat -> k -> Bag k -> Bag k
+data.Bag.removeN n k b =
+ use Nat -
+ c = Bag.count k b
+ MkBag match b with
+ MkBag b ->
+ if Universal.lteq c n then Map.delete k b else Map.insert k (c - n) b
+
+data.Bag.removeN.doc : Doc
+data.Bag.removeN.doc =
+ use Bag fromText removeN toText
+ {{
+ `` removeN n k b `` removes `n` occurrences of the element `k` from the
+ {type Bag} `b` if `k` occurs in it. If the {type Bag} has fewer occurrences
+ of `k`, then {removeN} removes them all. If `k` doesn't occur in the
+ {type Bag}, it does nothing.
+
+ # Example
+
+ ```
+ toText (removeN 2 ?🎁 (fromText "🎄🎅🎅🎁🎁🎁"))
+ ```
+
+ ```
+ toText (removeN 9 ?🎁 (fromText "🎄🎅🎅🎁🎁🎁"))
+ ```
+ }}
+
+test> data.Bag.removeN.tests.removes = runs 100 do
+ use Bag count
+ use Nat -
+ b = bagOf natInOrder ()
+ k = natInOrder()
+ n = natInOrder()
+ p = count k (Bag.removeN n k b) === count k b - n
+ expect p
+
+data.Bag.removeWhere : (a ->{e} Boolean) -> Bag a ->{e} Bag a
+data.Bag.removeWhere p = Bag.filter (Boolean.not << p)
+
+data.Bag.removeWhere.doc : Doc
+data.Bag.removeWhere.doc =
+ {{
+ `` removeWhere p b `` removes the elements from the {type Bag} `b` for which
+ the function `p` returns ``true``.
+
+ # Example
+
+ ```
+ b = Bag.fromText "A stitch in time saves nine."
+ Bag.occurrenceList
+ (removeWhere (c -> Boolean.not (isLetter c)) (Bag.map ascii.toLower b))
+ ```
+ }}
+
+test> data.Bag.removeWhere.tests.removes = runs 100 do
+ b = bagOf gen.boolean ()
+ p = yesNo()
+ q = removeWhere p b
+ expect (Bag.none p q)
+
+data.Bag.scale : Nat -> Bag a -> Bag a
+data.Bag.scale n =
+ use Nat *
+ mapCounts (x -> x * n)
+
+data.Bag.scale.doc : Doc
+data.Bag.scale.doc =
+ use Bag fromText scale toText
+ {{
+ `` scale k b `` constructs a new {type Bag} with the same elements as `b`,
+ but where the number of times each element occurs is multiplied by `k`.
+
+ # Example
+
+ ```
+ toText (scale 2 (fromText "🍰🍩🍩🍬🍬🍬"))
+ ```
+
+ ```
+ toText (scale 0 (fromText "🍰🍩🍬"))
+ ```
+ }}
+
+test> data.Bag.scale.tests.scales = runs 10 do
+ use Bag count
+ use Nat *
+ n = natInOrder()
+ b = bagOf natInOrder ()
+ s = Bag.scale n b
+ expect (Bag.all (k -> count k s === n * count k b) b)
+
+data.Bag.similarity : Bag a -> Bag a -> Float
+data.Bag.similarity a b =
+ use Bag size
+ use Float + - / > fromNat
+ i = fromNat (size (Bag.intersect a b))
+ s = fromNat (size a) + fromNat (size b) - i
+ if s > 0.0 then i / s else 1.0
+
+data.Bag.similarity.doc : Doc
+data.Bag.similarity.doc =
+ use Bag empty fromText similarity singleton
+ {{
+ Measures the similarity of two {type Bag}s. This is the number of elements
+ they have in common, as a proportion of the total number of elements in their
+ union — a number between `` 0.0 `` (no common elements) and `` 1.0 `` (the
+ {type Bag}s are identical).
+
+ # Examples
+
+ ```
+ similarity (fromText "barbara ara") (fromText "bara rabarbara")
+ ```
+
+ ```
+ similarity (singleton 1) (singleton 2)
+ ```
+
+ ```
+ similarity empty empty
+ ```
+ }}
+
+data.Bag.singleton : k -> Bag k
+data.Bag.singleton k = MkBag (Map.singleton k 1)
+
+data.Bag.singleton.doc : Doc
+data.Bag.singleton.doc =
+ use Bag singleton
+ {{
+ `` singleton k `` constructs a {type Bag} with a single element `k` occurring
+ exactly once.
+
+ # Example
+
+ ```
+ Bag.toList (singleton 42)
+ ```
+ }}
+
+test> data.Bag.singleton.tests.one = runs 100 do
+ k = natInOrder()
+ s = Bag.singleton k
+ expect (Bag.size s === 1 && Bag.all (x -> x === k) s)
+
+data.Bag.size : Bag a -> Nat
+data.Bag.size = cases MkBag b -> List.foldLeft (Nat.+) 0 (Map.values b)
+
+data.Bag.size.doc : Doc
+data.Bag.size.doc =
+ use Bag size
+ {{
+ `` size b `` is the number of elements in the {type Bag} `b`, including
+ duplicate elements.
+
+ # Example
+
+ ```
+ size (Bag.fromText "relentlessly")
+ ```
+ }}
+
+test> data.Bag.size.test = runs 100 do
+ a = gen.listOf Char.ascii ()
+ b = Bag.fromList a
+ expect (List.size a === Bag.size b)
+
+data.Bag.subbag : Bag a -> Bag a -> Boolean
+data.Bag.subbag = internal.compare Universal.lteq
+
+data.Bag.subbag.doc : Doc
+data.Bag.subbag.doc =
+ use Bag fromText subbag
+ {{
+ `` subbag b1 b2 `` returns `` true `` if no element occurs more times in the
+ {type Bag} `b1` than in the {type Bag} `b2`.
+
+ # Example
+
+ ```
+ b1 = fromText "abracadabra"
+ b2 = fromText "abcd"
+ (subbag b2 b1, subbag b1 b2)
+ ```
+ }}
+
+test> data.Bag.subbag.tests.homomorphism = runs 100 do
+ use Bag count
+ k = natInOrder()
+ a = bagOf natInOrder ()
+ b = bagOf natInOrder ()
+ expect (implies (Bag.subbag a b) (Universal.lteq (count k a) (count k b)))
+
+data.Bag.superbag : Bag a -> Bag a -> Boolean
+data.Bag.superbag = flip Bag.subbag
+
+data.Bag.superbag.doc : Doc
+data.Bag.superbag.doc =
+ use Bag fromText superbag
+ {{
+ `` superbag b1 b2 `` returns `true` if no element occurs more times in the
+ {type Bag} `b2` than in the {type Bag} `b1`.
+
+ # Example
+
+ ```
+ b1 = fromText "abracadabra"
+ b2 = fromText "abcd"
+ (superbag b2 b1, superbag b1 b2)
+ ```
+ }}
+
+test> data.Bag.superbag.tests.homomorphism = runs 100 do
+ use Bag count
+ k = natInOrder()
+ a = bagOf natInOrder ()
+ b = bagOf natInOrder ()
+ expect (implies (Bag.superbag a b) (Universal.gteq (count k a) (count k b)))
+
+data.Bag.tests.bagOf : '{Gen} a -> '{Gen} Bag a
+data.Bag.tests.bagOf a =
+ do
+ Bag.fromOccurrenceList
+ (Map.toList (tests.mapOf a (do natInOrder() Nat.+ 1) ()))
+
+data.Bag.tests.bagOf.doc : Doc
+data.Bag.tests.bagOf.doc =
+ {{
+ Given a generator of elements, constructs a generator of {type Bag}s with
+ elements of that type.
+
+ # Example
+
+ ```
+ List.map Bag.toList (deprecated.sample 10 (bagOf gen.int))
+ ```
+ }}
+
+data.Bag.toList : Bag a -> [a]
+data.Bag.toList =
+ use List ++
+ k z = cases (e, n) -> z ++ (List.replicate n do e)
+ List.foldLeft k [] << Bag.occurrenceList
+
+data.Bag.toList.doc : Doc
+data.Bag.toList.doc =
+ use Bag toList
+ {{
+ `` toList b `` returns the list of all the elements of the {type Bag} `b`,
+ including duplicates.
+
+ # Example
+
+ ```
+ toList (Bag.fromText "🥒🥕🥔🥕")
+ ```
+ }}
+
+test> data.Bag.toList.tests.roundtrip = runs 100 do
+ use Bag ==
+ b = bagOf natInOrder ()
+ l = Bag.toList b
+ expect (Bag.fromList l == b)
+
+data.Bag.toSet : Bag a -> Set a
+data.Bag.toSet = internal.Set << Map.map const() << Bag.counts
+
+data.Bag.toSet.doc : Doc
+data.Bag.toSet.doc =
+ use Bag toSet
+ {{
+ `` toSet b `` returns the {type Set} of elements in the {type Bag} `b`.
+
+ # Example
+
+ ```
+ Set.toList (toSet (Bag.fromText "🥒🥕🥔🥕"))
+ ```
+ }}
+
+test> data.Bag.toSet.tests.roundtrip =
+ runs 100 do
+ b = bagOf natInOrder ()
+ s = Bag.toSet b
+ expect
+ (Set.all (a -> Bag.contains b a) s && Bag.all (a -> Set.contains a s) b)
+
+data.Bag.toText : Bag Char -> Text
+data.Bag.toText = fromCharList << Bag.toList
+
+data.Bag.toText.doc : Doc
+data.Bag.toText.doc =
+ use Bag toText
+ {{
+ `` toText b `` takes a {type Bag} of characters of type {type Char} and
+ returns {type Text} with those characters in an unspecified order.
+
+ # Example
+
+ ```
+ b = Bag.fromText "👗👠👗🎩👠👠"
+ toText b
+ ```
+ }}
+
+test> data.Bag.toText.tests.roundtrip = runs 100 do
+ use Bag ==
+ b = bagOf Char.ascii ()
+ t = Bag.toText b
+ expect (Bag.fromText t == b)
+
+data.Bag.traverse : (a ->{e} b) -> Bag a ->{e} Bag b
+data.Bag.traverse f = Bag.flatMap (Bag.singleton << f)
+
+data.Bag.traverse.doc : Doc
+data.Bag.traverse.doc =
+ {{
+ `` traverse f b `` iterates over the elements of the {type Bag} `b`, applying
+ the function `f` to each element, collecting the results in a new {type Bag}.
+
+ # Example
+
+ ```
+ b = Bag.fromList [5, 2, 4, 4, 2, 6]
+ randoms = do traverse (x -> Random.natIn 0 x) b
+ Bag.toList (lcg 3735928559 randoms)
+ ```
+ }}
+
+test> data.Bag.traverse.tests.functor = runs 100 do
+ use Bag ==
+ b = bagOf natInOrder ()
+ expect (traverse id b == b)
+
+data.Bag.union : Bag a -> Bag a -> Bag a
+data.Bag.union = cases
+ MkBag b1 -> cases MkBag b2 -> MkBag (Map.unionWith Universal.max b1 b2)
+
+data.Bag.union.doc : Doc
+data.Bag.union.doc =
+ use Bag fromText union
+ {{
+ `` union b1 b2 `` constructs the union of `b1` and `b2`, which is the
+ smallest {type Bag} that is a {Bag.superbag} of both `b1` and `b2`. This is a
+ new {type Bag} with elements from `b1` and `b2` where the number of times
+ each element occurs in the new {type Bag} is either the number of times it
+ occurs in `b1` or the number of times it occurs in `b2`, whichever is
+ greater.
+
+ # Example
+
+ ```
+ b1 = fromText "🥒🥕🥔🥕"
+ b2 = fromText "🥒🥕🥔🥑🥒"
+ Bag.toText (union b1 b2)
+ ```
+ }}
+
+test> data.Bag.union.tests.commutative = runs 100 do
+ use Bag == union
+ x = bagOf natInOrder ()
+ y = bagOf natInOrder ()
+ expect (union x y == union y x)
+
+test> data.Bag.union.tests.distributive = runs 100 do
+ use Bag == intersect union
+ x = bagOf natInOrder ()
+ y = bagOf natInOrder ()
+ z = bagOf natInOrder ()
+ expect (union x (intersect y z) == intersect (union x y) (union x z))
+
+test> data.Bag.union.tests.homomorphism =
+ runs 100 do
+ use Bag count
+ use Char ascii
+ a = bagOf ascii ()
+ b = bagOf ascii ()
+ c = Bag.occurrenceList (Bag.union a b)
+ expect
+ (List.all (cases (e, n) -> n === Universal.max (count e a) (count e b)) c)
+
+test> data.Bag.union.tests.idempotent = runs 100 do
+ use Bag ==
+ x = bagOf natInOrder ()
+ expect (Bag.union x x == x)
+
+test> data.Bag.union.tests.unit = runs 100 do
+ use Bag ==
+ x = bagOf natInOrder ()
+ expect (Bag.union x Bag.empty == x)
+
+(data.ByteArray.++) : data.ByteArray -> data.ByteArray -> data.ByteArray
+(data.ByteArray.++) = cases
+ BArr o1 l1 a1, BArr o2 l2 a2 ->
+ use Nat +
+ use data.ByteArray.Raw copyTo!
+ sz = l1 + l2
+ ByteArray.new! sz cases
+ MBArr _ _ ma ->
+ copyTo! ma 0 a1 o1 l1
+ copyTo! ma l1 a2 o2 l2
+
+data.ByteArray.++.doc : Doc
+data.ByteArray.++.doc =
+ use ByteArray ++ fromList
+ {{
+ Joins together two byte arrays. This requires copying both arrays to a new
+ array, so it has a cost proportional to the combined lengths of both arrays.
+
+ ```
+ fromList [1, 2, 3] ++ fromList [4, 5, 6]
+ ```
+ }}
+
+data.ByteArray.append.doc : Doc
+data.ByteArray.append.doc =
+ use ByteArray fromBytes
+ use fromList impl
+ {{
+ Constructs a new {type data.ByteArray} with the contents of both the given
+ {type data.ByteArray}s, in order.
+
+ # Example
+
+ ```
+ ByteArray.toList
+ (ByteArray.append (fromBytes 0xs01020304) (fromBytes 0xs05060708))
+ ```
+ }}
+
+test> data.ByteArray.append.test = test.verify do
+ use ByteArray fromList
+ use List ++ replicate
+ use Random natIn
+ Each.repeat 100
+ l1 = replicate (natIn 0 20) do natIn 0 255
+ l2 = replicate (natIn 0 20) do natIn 0 255
+ l3 = l1 ++ l2
+ a1 : data.ByteArray
+ a1 = fromList l1
+ a2 = fromList l2
+ ensureEqual (fromList l3) (ByteArray.append a1 a2)
+
+data.ByteArray.BArr.doc : Doc
+data.ByteArray.BArr.doc =
+ {{
+ The constructor of {type data.ByteArray} values. You should in general not
+ use this constructor directly. Instead, use {ByteArray.fromBytes},
+ {ByteArray.fromList}, or {ByteArray.freeze!}.
+
+ Takes an offset and a length, and a {type data.ByteArray.Raw} that contains
+ the bytes. The offset and length are used to specify a slice of the raw array
+ that the {type data.ByteArray} value represents.
+ }}
+
+data.ByteArray.base32Hex : data.ByteArray -> Text
+data.ByteArray.base32Hex = cases
+ BArr off len arr -> handle Raw.base32Hex arr off len with impossible
+
+data.ByteArray.base32Hex.doc : Doc
+data.ByteArray.base32Hex.doc =
+ {{
+ Encodes an array using the base 32 hex encoding.
+
+ ```
+ ByteArray.base32Hex (ByteArray.fromList [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
+ ```
+ }}
+
+test> data.ByteArray.base32Hex.test = test.verify do
+ use Text ==
+ _ = Each.repeat 100
+ tx = text.base32Hex()
+ match fromBase32Hex (Text.toUtf8 tx) with
+ Right bs ->
+ ba = ByteArray.fromBytes bs
+ ensure (tx == ByteArray.base32Hex ba)
+ Left _ -> bug "falied to decode base32Hex"
+
+data.ByteArray.doc : Doc
+data.ByteArray.doc =
+ use ByteArray ++ toList
+ {{
+ The type {type data.ByteArray} represents a raw immutable array of
+ [unboxed](https://en.wikipedia.org/wiki/Boxing_%28computer_science%29) bytes.
+
+ # Constructing byte arrays
+
+ Copy {type Bytes} into to a new {type data.ByteArray}:
+
+ @signature{ByteArray.fromBytes}
+
+ Copy a {type List} of {type Nat} into a new {type data.ByteArray}. The
+ {type Nat} values must be in the range 0 to 255 (inclusive).
+
+ @signature{ByteArray.fromList}
+
+ Freeze a {type mutable.ByteArray} into a {type data.ByteArray}:
+
+ @signature{ByteArray.freeze!}
+
+ You can also construct a {type data.ByteArray} fom a function that mutates
+ a {type mutable.ByteArray} using `data.ByteArray.new!`. The function will
+ be passed a {type mutable.ByteArray} of the requested size:
+
+ ```
+ toList
+ (ByteArray.new!
+ 3 (arr -> let
+ use mutable.ByteArray write8
+ write8 arr 0 42
+ write8 arr 1 43
+ write8 arr 2 44))
+ ```
+
+ The low-level {BArr} constructor can be used to construct a
+ {type data.ByteArray} from its internal representation:
+
+ @source{type data.ByteArray}
+
+ # Querying and indexing byte arrays
+
+ Read a single byte at a given index:
+
+ @signature{data.ByteArray.read8}
+
+ Read two bytes in [big-endian](https://en.wikipedia.org/wiki/Endianness)
+ order at a given index:
+
+ @signature{ByteArray.read16be}
+
+ Read four bytes in [big-endian](https://en.wikipedia.org/wiki/Endianness)
+ order at a given index:
+
+ @signature{ByteArray.read32be}
+
+ Read eight bytes in [big-endian](https://en.wikipedia.org/wiki/Endianness)
+ order at a given index:
+
+ @signature{ByteArray.read64be}
+
+ Get the number of bytes in a {type data.ByteArray}:
+
+ @signature{data.ByteArray.size}
+
+ # Writing to byte arrays
+
+ Write a single byte to a {type data.ByteArray}. Note that this will create
+ a new {type data.ByteArray} with the updated value and leave the original
+ {type data.ByteArray} unchanged:
+
+ @signature{data.ByteArray.write8}
+
+ # Combining byte arrays
+
+ Concatenate two {type data.ByteArray}s:
+
+ @signature{++}
+
+ Concatenate a list of {type data.ByteArray}s:
+
+ @signature{ByteArray.join}
+
+ # Slicing byte arrays
+
+ Get a prefix of a {type data.ByteArray}:
+
+ @signature{ByteArray.take}
+
+ Drop a prefix of a {type data.ByteArray}:
+
+ @signature{ByteArray.drop}
+
+ Get a slice of a {type data.ByteArray}:
+
+ @signature{ByteArray.slice}
+
+ # Converting to other types
+
+ Convert a {type data.ByteArray} to a {type List} of {type Nat}:
+
+ @signature{toList}
+
+ Convert a {type data.ByteArray} to {type Text} using the
+ [base32hex](https://en.wikipedia.org/wiki/Base32#base32hex) encoding:
+
+ @signature{ByteArray.base32Hex}
+ }}
+
+data.ByteArray.drop : Nat -> data.ByteArray -> data.ByteArray
+data.ByteArray.drop n = cases
+ ba@(BArr o l a)
+ | n Nat.== 0 -> ba
+ | n Nat.>= l -> BArr (o Nat.+ l) 0 a
+ | otherwise -> BArr (o Nat.+ n) (l Nat.- n) a
+
+data.ByteArray.drop.doc : Doc
+data.ByteArray.drop.doc =
+ {{
+ Returns a new {type data.ByteArray} representing the portion of the original
+ with an initial segment removed.
+
+ This operation is fast, but shares the underlying storage with the original
+ array. The potential downside is that more memory may be used than is
+ necessary.
+ }}
+
+test> data.ByteArray.drop.test = test.verify do
+ use ByteArray fromList toList
+ use Random natIn
+ Each.repeat 100
+ l = List.replicate (natIn 0 30) do natIn 0 255
+ a = fromList l
+ n = natIn 0 60
+ ensuring do toList (fromList (List.drop n l)) === toList (ByteArray.drop n a)
+
+data.ByteArray.find :
+ (Nat ->{g} Boolean) -> data.ByteArray ->{g, Exception} Optional Nat
+data.ByteArray.find p arr =
+ ByteArray.firstIndexOf (p << data.ByteArray.read8 arr) arr
+
+data.ByteArray.find.doc : Doc
+data.ByteArray.find.doc =
+ use ByteArray find
+ use Nat ==
+ {{
+ `` find p arr `` returns the index of the first element of the byte array
+ `arr` that satisfies the predicate `p`, or {None} if no such element exists.
+
+ # Example
+
+ ```
+ catch do find ((==) 3) (ByteArray.fromList [1, 2, 3, 4, 5])
+ ```
+ }}
+
+data.ByteArray.findLast :
+ (Nat ->{g} Boolean) -> data.ByteArray ->{g, Exception} Optional Nat
+data.ByteArray.findLast p arr =
+ ByteArray.lastIndexOf (p << data.ByteArray.read8 arr) arr
+
+data.ByteArray.findLast.doc : Doc
+data.ByteArray.findLast.doc =
+ use ByteArray findLast
+ use Nat ==
+ {{
+ `` findLast p arr `` returns the index of the last element of the byte array
+ `arr` that satisfies the predicate `p`, or {None} if no such element exists.
+
+ # Example
+
+ ```
+ catch do findLast ((==) 3) (ByteArray.fromList [1, 2, 3, 4, 5])
+ ```
+ }}
+
+data.ByteArray.firstIndexOf :
+ (Nat ->{g} Boolean) -> data.ByteArray ->{g, Exception} Optional Nat
+data.ByteArray.firstIndexOf p arr =
+ go : Nat -> Optional Nat
+ go ix =
+ use Nat + <
+ sz = data.ByteArray.size arr
+ if ix < sz then
+ if p (data.ByteArray.read8 arr ix) then Some ix else go (ix + 1)
+ else None
+ go 0
+
+data.ByteArray.firstIndexOf.doc : Doc
+data.ByteArray.firstIndexOf.doc =
+ use ByteArray firstIndexOf
+ use Nat ==
+ {{
+ `` firstIndexOf p arr `` returns the index of the first element of the byte
+ array `arr` that satisfies the predicate `p`, or {None} if no such element
+ exists.
+
+ # Example
+
+ ```
+ catch do firstIndexOf ((==) 3) (ByteArray.fromList [1, 2, 3, 4, 5])
+ ```
+ }}
+
+data.ByteArray.fromBytes : Bytes -> data.ByteArray
+data.ByteArray.fromBytes bs = BArr 0 (Bytes.size bs) (Raw.fromBytes bs)
+
+data.ByteArray.fromBytes.doc : Doc
+data.ByteArray.fromBytes.doc =
+ {{
+ Creates a {type data.ByteArray} from the contents of a {type Bytes}.
+
+ ```
+ ByteArray.toList (ByteArray.fromBytes 0xsffff)
+ ```
+ }}
+
+data.ByteArray.fromList : [Nat] -> data.ByteArray
+data.ByteArray.fromList l = Scope.run do
+ use Nat +
+ sz = List.size l
+ dst = Scope.Raw.byteArray sz
+ go i = cases
+ [] -> ()
+ n +: ns ->
+ Raw.write8 dst i n
+ go (i + 1) ns
+ handle go 0 l with impossible
+ BArr 0 sz (ByteArray.Raw.freeze! dst)
+
+data.ByteArray.fromList.doc : Doc
+data.ByteArray.fromList.doc =
+ {{
+ Creates a new array containing the bytes in the list.
+
+ ```
+ ByteArray.toList (ByteArray.fromList [1, 2, 3])
+ ```
+ }}
+
+data.ByteArray.join : [data.ByteArray] -> data.ByteArray
+data.ByteArray.join l =
+ use List foldLeft
+ use Nat +
+ f0 : mutable.ByteArray.Raw g -> Nat -> data.ByteArray ->{g, Exception} Nat
+ f0 ma mo = cases
+ BArr o l a ->
+ data.ByteArray.Raw.copyTo! ma mo a o l
+ mo + l
+ f : mutable.ByteArray g -> Nat -> data.ByteArray ->{g, Exception} Nat
+ f = cases MBArr _ _ ma -> f0 ma
+ sz = foldLeft (n a -> n + data.ByteArray.size a) 0 l
+ ByteArray.new!
+ sz (ma -> let
+ _ = foldLeft (f ma) 0 l
+ ())
+
+data.ByteArray.join.doc : Doc
+data.ByteArray.join.doc =
+ use ByteArray fromList
+ {{
+ Joins several [byte arrays]({type data.ByteArray}) into a single array. Each
+ of the input arrays is copied to the output, so no storage is shared.
+
+ ```
+ ByteArray.join [fromList [1, 2], fromList [3], fromList [4, 5, 6]]
+ ```
+ }}
+
+test> data.ByteArray.join.test = test.verify do
+ use ByteArray fromList
+ use List replicate
+ use Random natIn
+ Each.repeat 100
+ by = do natIn 0 255
+ ls = replicate (natIn 0 5) do replicate (natIn 0 20) by
+ as = List.map fromList ls
+ ensureEqual (fromList (List.join ls)) (ByteArray.join as)
+
+data.ByteArray.lastIndexOf :
+ (Nat ->{g} Boolean) -> data.ByteArray ->{g, Exception} Optional Nat
+data.ByteArray.lastIndexOf p arr =
+ sz = data.ByteArray.size arr
+ go : Nat -> Optional Nat
+ go ix =
+ use Nat - >
+ ix' = ix - 1
+ if ix > 0 then
+ if p (data.ByteArray.read8 arr ix') then Some ix' else go ix'
+ else None
+ go sz
+
+data.ByteArray.lastIndexOf.doc : Doc
+data.ByteArray.lastIndexOf.doc =
+ use ByteArray lastIndexOf
+ use Nat ==
+ {{
+ `` lastIndexOf p arr `` returns the index of the last element of the byte
+ array `arr` that satisfies the predicate `p`, or {None} if no such element
+ exists.
+
+ # Example
+
+ ```
+ catch do lastIndexOf ((==) 3) (ByteArray.fromList [1, 2, 3, 4, 5])
+ ```
+ }}
+
+data.ByteArray.new! :
+ Nat
+ -> (∀ s. mutable.ByteArray (Scope s) ->{g, Exception, Scope s} ())
+ ->{g} data.ByteArray
+data.ByteArray.new! n k =
+ ba = Raw.new! n (ma -> k (MBArr 0 n ma))
+ BArr 0 n ba
+
+data.ByteArray.new!.doc : Doc
+data.ByteArray.new!.doc =
+ use mutable.ByteArray write8
+ {{
+ Given a size and a function that initializes a
+ [mutable byte array]({type mutable.ByteArray}), creates an immutable
+ {type data.ByteArray} that is initialized in the way specified by the
+ function.
+
+ # Example
+
+ ```
+ ByteArray.toList
+ (ByteArray.new!
+ 5 (arr -> let
+ write8 arr 0 1
+ write8 arr 1 2
+ write8 arr 2 3
+ write8 arr 3 4
+ write8 arr 4 5))
+ ```
+ }}
+
+data.ByteArray.randomChoice : data.ByteArray ->{Exception, Random} Nat
+data.ByteArray.randomChoice byteArray =
+ randomIndex = Random.natIn 0 (data.ByteArray.size byteArray)
+ match catch do data.ByteArray.read8 byteArray randomIndex with
+ Right v -> v
+ Left _ ->
+ Exception.raise
+ (Failure
+ (typeLink data.ByteArray)
+ "data.ByteArray.randomChoice: index out of bounds"
+ (Any byteArray))
+
+data.ByteArray.randomChoice.doc : Doc
+data.ByteArray.randomChoice.doc =
+ use ByteArray fromList randomChoice
+ {{
+ Picks a random byte (as a {type Nat}) from the given {type data.ByteArray}.
+ Assumes that the {type data.ByteArray} is not empty, so an empty
+ {type data.ByteArray} will raise an {type Exception}.
+
+ # Examples
+
+ ```
+ catch do lcg 4096 do randomChoice (fromList [0, 3, 5, 7])
+ ```
+
+ ```
+ catch do lcg 2510 do randomChoice (fromList [0, 3, 5, 7])
+ ```
+ }}
+
+test> data.ByteArray.randomChoice.test = test.verify do
+ list = List.range 0 10
+ set = Set.fromList list
+ byteArray = ByteArray.fromList list
+ Each.repeat 1000
+ e = ByteArray.randomChoice byteArray
+ ensure (Set.contains e set)
+
+data.ByteArray.Raw.base32Hex : data.ByteArray.Raw -> Nat -> Nat -> Text
+data.ByteArray.Raw.base32Hex arr off0 len =
+ use Nat + <= shiftLeft
+ readBytes off = cases
+ 0 -> (0, 8)
+ 1 -> (shiftLeft (data.ByteArray.Raw.read8 arr off) 2, 6)
+ 2 -> (shiftLeft (data.ByteArray.Raw.read16be arr off) 4, 4)
+ 3 -> (shiftLeft (data.ByteArray.Raw.read24be arr off) 1, 3)
+ 4 -> (shiftLeft (data.ByteArray.Raw.read32be arr off) 3, 1)
+ _ -> (data.ByteArray.Raw.read40be arr off, 0)
+ go acc off =
+ use Nat - >
+ use Text ++
+ csz = len - off
+ match readBytes off csz with
+ (_, 8) -> acc
+ (n, p)
+ | p > 0 -> acc ++ encodeChunk n p
+ | otherwise -> go (acc ++ encodeChunk n p) (off + 5)
+ if off0 + len <= data.ByteArray.Raw.size arr then
+ handle go "" off0 with impossible
+ else ""
+
+data.ByteArray.Raw.base32Hex.doc : Doc
+data.ByteArray.Raw.base32Hex.doc =
+ {{
+ Encodes a portion of a byte array using the base 32 hex encoding.
+
+ The first argument is an offset. The second is the number of bytes to encode.
+
+ ```
+ (BArr off len arr) = ByteArray.fromList [0, 10, 15]
+ Raw.base32Hex arr off len
+ ```
+ }}
+
+-- builtin data.ByteArray.Raw.copyTo! :
+-- mutable.ByteArray.Raw g
+-- -> Nat
+-- -> data.ByteArray.Raw
+-- -> Nat
+-- -> Nat
+-- ->{g, Exception} ()
+
+data.ByteArray.Raw.copyTo!.doc : Doc
+data.ByteArray.Raw.copyTo!.doc =
+ {{
+ `` data.ByteArray.Raw.copyTo! dst doff src soff len `` copies `len` bytes
+ from `src` to `dst`. `doff` and `soff` are byte offsets into the
+ corresponding arrays.
+
+ Note that the destination array/offset comes first.
+ }}
+
+data.ByteArray.Raw.doc : Doc
+data.ByteArray.Raw.doc =
+ use Raw toBytes
+ {{
+ {type data.ByteArray.Raw} is the type of raw, immutable, unboxed arrays of
+ bytes. It's a low-level type that refers to a region of memory managed by the
+ runtime.
+
+ It is similar to {type mutable.ByteArray.Raw}, but cannot be modified in
+ place. It's also similar to {type data.Array.Raw}, but is unboxed and thus
+ more efficient.
+
+ The main use case for {type data.ByteArray.Raw} is to serve as the internal
+ representation of {type data.ByteArray}, and you should generally use that
+ type instead. A {type data.ByteArray} is implemented as a slice of an
+ underlying {type data.ByteArray.Raw}.
+
+ @source{type data.ByteArray}
+
+ # Constructing raw byte arrays
+
+ Construct a {type data.ByteArray.Raw} from {type Bytes}:
+
+ @signature{Raw.fromBytes}
+
+ Freeze a portion of a {type mutable.ByteArray.Raw}. The mutable array can
+ still be safely modified after this operation:
+
+ @signature{ByteArray.Raw.freeze}
+
+ Freeze a {type mutable.ByteArray.Raw} in place. The mutable array can no
+ longer be modified after this operation:
+
+ @signature{ByteArray.Raw.freeze!}
+
+ You can also construct a {type data.ByteArray.Raw} from a function that
+ takes a {type mutable.ByteArray.Raw} and fills it with data:
+
+ ```
+ toBytes
+ (Scope.run do
+ Raw.new!
+ 10 (buf -> let
+ Raw.write64be buf 0 1311768467294899695
+ Raw.write16be buf 8 4660))
+ ```
+
+ # Using raw byte arrays
+
+ Get the size of a {type data.ByteArray.Raw}:
+
+ @signature{data.ByteArray.Raw.size}
+
+ Read a byte from a {type data.ByteArray.Raw} at a given index:
+
+ @signature{data.ByteArray.Raw.read8}
+
+ Read two bytes from a {type data.ByteArray.Raw}, in big-endian order, at a
+ given index:
+
+ @signature{data.ByteArray.Raw.read16be}
+
+ Read three bytes from a {type data.ByteArray.Raw}, in big-endian order, at
+ a given index:
+
+ @signature{data.ByteArray.Raw.read24be}
+
+ Read four bytes from a {type data.ByteArray.Raw}, in big-endian order, at a
+ given index:
+
+ @signature{data.ByteArray.Raw.read32be}
+
+ Read five bytes from a {type data.ByteArray.Raw}, in big-endian order, at a
+ given index:
+
+ @signature{data.ByteArray.Raw.read40be}
+
+ Read eight bytes from a {type data.ByteArray.Raw}, in big-endian order, at
+ a given index:
+
+ @signature{data.ByteArray.Raw.read64be}
+
+ Copy a {type data.ByteArray.Raw} into a {type mutable.ByteArray.Raw}:
+
+ @signature{data.ByteArray.Raw.copyTo!}
+
+ # Converting raw byte arrays to other types
+
+ Convert a {type data.ByteArray.Raw} to a {type Bytes}:
+
+ @signature{toBytes}
+
+ Convert a {type data.ByteArray.Raw} to a {type Text} in
+ [base32hex](https://tools.ietf.org/html/rfc4648#section-6):
+
+ @signature{Raw.base32Hex}
+
+ Convert a {type data.ByteArray.Raw} to a {type List} of {type Nat} in the
+ range 0 to 255:
+
+ @signature{toByteList}
+ }}
+
+data.ByteArray.Raw.fromBytes : Bytes -> data.ByteArray.Raw
+data.ByteArray.Raw.fromBytes bs = Scope.run do
+ use Nat +
+ sz = Bytes.size bs
+ arr = Scope.Raw.byteArray sz
+ fill i = match Bytes.at i bs with
+ Some b ->
+ Raw.write8 arr i b
+ fill (i + 1)
+ None -> ()
+ handle fill 0
+ with cases
+ { _ } -> ()
+ { Exception.raise _ -> _ } -> ()
+ ByteArray.Raw.freeze! arr
+
+data.ByteArray.Raw.fromBytes.doc : Doc
+data.ByteArray.Raw.fromBytes.doc =
+ {{
+ Creates a {type data.ByteArray.Raw} from the contents of a {type Bytes}.
+ }}
+
+data.ByteArray.Raw.new! :
+ Nat
+ -> (∀ s. mutable.ByteArray.Raw (Scope s) ->{g, Exception, Scope s} ())
+ ->{g} data.ByteArray.Raw
+data.ByteArray.Raw.new! n k = Scope.run do
+ ma = Scope.Raw.byteArray n
+ handle k ma
+ with cases
+ { Exception.raise fail -> _ } -> bug fail
+ { a } -> a
+ ByteArray.Raw.freeze! ma
+
+data.ByteArray.Raw.new!.doc : Doc
+data.ByteArray.Raw.new!.doc =
+ use Raw write8
+ {{
+ Given a size and a function that initializes a
+ [mutable byte array]({type mutable.ByteArray.Raw}), creates an immutable
+ {type data.ByteArray.Raw} that is initialized in the way specified by the
+ function.
+
+ # Example
+
+ ```
+ Raw.toBytes
+ (Raw.new!
+ 5 (arr -> let
+ write8 arr 0 1
+ write8 arr 1 2
+ write8 arr 2 3
+ write8 arr 3 4
+ write8 arr 4 5))
+ ```
+ }}
+
+-- builtin data.ByteArray.Raw.read16be :
+-- data.ByteArray.Raw -> Nat ->{Exception} Nat
+
+data.ByteArray.Raw.read16be.doc : Doc
+data.ByteArray.Raw.read16be.doc =
+ {{
+ Reads a 16-bit big-endian unsigned integer from a {type data.ByteArray.Raw}
+ at the given offset. Raises an {type Exception} if the offset is out of
+ bounds.
+ }}
+
+-- builtin data.ByteArray.Raw.read24be :
+-- data.ByteArray.Raw -> Nat ->{Exception} Nat
+
+data.ByteArray.Raw.read24be.doc : Doc
+data.ByteArray.Raw.read24be.doc =
+ {{
+ `read24be arr ix` yields a 24-bit big endian value starting at __byte__
+ offset `ix` in the array.
+ }}
+
+-- builtin data.ByteArray.Raw.read32be :
+-- data.ByteArray.Raw -> Nat ->{Exception} Nat
+
+data.ByteArray.Raw.read32be.doc : Doc
+data.ByteArray.Raw.read32be.doc =
+ {{
+ Reads a 32-bit big-endian unsigned integer from a {type data.ByteArray.Raw}
+ at the given offset. Raises an {type Exception} if the offset is out of
+ bounds.
+ }}
+
+-- builtin data.ByteArray.Raw.read40be :
+-- data.ByteArray.Raw -> Nat ->{Exception} Nat
+
+data.ByteArray.Raw.read40be.doc : Doc
+data.ByteArray.Raw.read40be.doc =
+ {{
+ `read24be arr ix` yields a 40-bit big endian value starting at __byte__
+ offset `ix` in the array.
+ }}
+
+-- builtin data.ByteArray.Raw.read64be :
+-- data.ByteArray.Raw -> Nat ->{Exception} Nat
+
+data.ByteArray.Raw.read64be.doc : Doc
+data.ByteArray.Raw.read64be.doc =
+ {{
+ Reads a 64-bit big-endian unsigned integer from a {type data.ByteArray.Raw}
+ at the given offset. Raises an {type Exception} if the offset is out of
+ bounds.
+ }}
+
+-- builtin data.ByteArray.Raw.read8 :
+-- data.ByteArray.Raw -> Nat ->{Exception} Nat
+
+data.ByteArray.Raw.read8.doc : Doc
+data.ByteArray.Raw.read8.doc =
+ use Raw fromBytes
+ use data.ByteArray.Raw read8
+ use fromList impl
+ {{
+ Reads an 8-bit unsigned integer from the given raw byte array at the given
+ index, returning it as a {type Nat}. Throws an {type ArrayFailure}
+ {type Exception} if the index is out of bounds.
+
+ # Example
+
+ ```
+ catch do read8 (fromBytes 0xs01020304) 2
+ ```
+
+ ```
+ catch do read8 (fromBytes 0xs01020304) 42
+ ```
+ }}
+
+-- builtin data.ByteArray.Raw.size : data.ByteArray.Raw -> Nat
+
+data.ByteArray.Raw.size.doc : Doc
+data.ByteArray.Raw.size.doc = {{ Gets the size of the given raw byte array. }}
+
+data.ByteArray.Raw.toByteList : data.ByteArray.Raw -> [Nat]
+data.ByteArray.Raw.toByteList arr =
+ use List :+
+ use Nat + <
+ sz = data.ByteArray.Raw.size arr
+ f acc = cases
+ i
+ | i < sz -> f (acc :+ data.ByteArray.Raw.read8 arr i) (i + 1)
+ | otherwise -> acc
+ unsafeRun! do f [] 0
+
+data.ByteArray.Raw.toByteList.doc : Doc
+data.ByteArray.Raw.toByteList.doc =
+ {{
+ Produces a list of bytes in the array.
+
+ ```
+ toByteList (Raw.fromBytes 0xs01020304)
+ ```
+ }}
+
+data.ByteArray.Raw.toBytes : data.ByteArray.Raw -> Bytes
+data.ByteArray.Raw.toBytes arr = fromList.impl (toByteList arr)
+
+data.ByteArray.Raw.toBytes.doc : Doc
+data.ByteArray.Raw.toBytes.doc =
+ {{
+ Produces a {type Bytes} values containing the same bytes as the array.
+
+ ```
+ Raw.toBytes (Raw.fromBytes 0xs10203040)
+ ```
+ }}
+
+data.ByteArray.read16be : data.ByteArray -> Nat ->{Exception} Nat
+data.ByteArray.read16be = cases
+ ba@(BArr o l a), n
+ | n Nat.+ 1 Nat.< l -> data.ByteArray.Raw.read16be a (o Nat.+ n)
+ | otherwise -> ArrayFailure.raise "read16be" (ba, n)
+
+data.ByteArray.read16be.doc : Doc
+data.ByteArray.read16be.doc =
+ {{
+ Reads a 16-bit big endian value starting at the **byte** offset given by the
+ second argument.
+
+ Throws an exception if there are insufficient bytes in the array, past the
+ offset.
+ }}
+
+data.ByteArray.read32be : data.ByteArray -> Nat ->{Exception} Nat
+data.ByteArray.read32be = cases
+ ba@(BArr o l a), n
+ | n Nat.+ 3 Nat.< l -> data.ByteArray.Raw.read32be a (o Nat.+ n)
+ | otherwise -> ArrayFailure.raise "read32be" (ba, n)
+
+data.ByteArray.read32be.doc : Doc
+data.ByteArray.read32be.doc =
+ {{
+ Reads a 32-bit big endian value starting at the **byte** offset given by the
+ second argument.
+
+ Throws an exception if there are insufficient bytes in the array, past the
+ offset.
+ }}
+
+data.ByteArray.read64be : data.ByteArray -> Nat ->{Exception} Nat
+data.ByteArray.read64be = cases
+ ba@(BArr o l a), n
+ | n Nat.+ 7 Nat.< l -> data.ByteArray.Raw.read64be a (o Nat.+ n)
+ | otherwise -> ArrayFailure.raise "read64be" (ba, n)
+
+data.ByteArray.read64be.doc : Doc
+data.ByteArray.read64be.doc =
+ {{
+ Reads a 64-bit big endian value starting at the **byte** offset given by the
+ second argument.
+
+ Throws an exception if there are insufficient bytes in the array, past the
+ offset.
+ }}
+
+data.ByteArray.read8 : data.ByteArray -> Nat ->{Exception} Nat
+data.ByteArray.read8 arr i =
+ match arr with
+ BArr off len src| i Nat.< len ->
+ data.ByteArray.Raw.read8 src (off Nat.+ i)
+ _ -> ArrayFailure.raise "data.ByteArray.read8: index out of bounds" i
+
+data.ByteArray.read8.doc : Doc
+data.ByteArray.read8.doc =
+ {{
+ Gets a single byte from an array.
+
+ Throws an exception if the index is out of bounds.
+
+ ```
+ catch do data.ByteArray.read8 (ByteArray.fromList [0, 1, 2, 3]) 5
+ ```
+ }}
+
+data.ByteArray.size : data.ByteArray -> Nat
+data.ByteArray.size = cases BArr _ length _ -> length
+
+data.ByteArray.size.doc : Doc
+data.ByteArray.size.doc =
+ {{
+ Gets the size of a {type data.ByteArray}, in bytes.
+
+ # Example
+
+ ```
+ data.ByteArray.size (ByteArray.fromBytes 0xs0102030405)
+ ```
+ }}
+
+test> data.ByteArray.size.test = test.verify do
+ use Random natIn
+ Each.repeat 100
+ l = List.replicate (natIn 0 40) do natIn 0 255
+ ensureEqual (List.size l) (data.ByteArray.size (ByteArray.fromList l))
+
+data.ByteArray.slice :
+ Nat -> Nat -> data.ByteArray ->{Exception} data.ByteArray
+data.ByteArray.slice o l = cases
+ BArr off len arr| o Nat.+ l Nat.<= len -> BArr (off Nat.+ o) l arr
+ _ ->
+ ArrayFailure.raise "data.ByteArray.slice: not enough elements" (o Nat.+ l)
+
+data.ByteArray.slice.doc : Doc
+data.ByteArray.slice.doc =
+ use ByteArray slice
+ {{
+ {slice} returns a new {type data.ByteArray} representing a portion of the
+ original.
+
+ The first argument specifies how many bytes to drop from the beginning of the
+ array. The second argument specifies how many values to retain. The original
+ array must have a length at least as long as the offset plus the new length.
+
+ This operation is fast, replacing only the offset and length, and retaining
+ the underlying storage. The potential downside is that more memory may be
+ used than necessary.
+
+ ```
+ catch do ByteArray.toList (slice 1 2 (ByteArray.fromList [1, 2, 3, 4, 5]))
+ ```
+ }}
+
+data.ByteArray.take : Nat -> data.ByteArray -> data.ByteArray
+data.ByteArray.take n = cases
+ ba@(BArr o l a)
+ | l Nat.<= n -> ba
+ | otherwise -> BArr o n a
+
+data.ByteArray.take.doc : Doc
+data.ByteArray.take.doc =
+ {{
+ Returns a new {type data.ByteArray} representing an initial portion of the
+ original.
+
+ This operation is fast, but shares the underlying storage with the original
+ array. The potential downside is that more memory may be used than is
+ necessary.
+ }}
+
+test> data.ByteArray.take.test =
+ test.verify do
+ use ByteArray fromList toList
+ use Random natIn
+ Each.repeat 100
+ l = List.replicate (natIn 0 30) do natIn 0 255
+ n = natIn 0 60
+ ensureEqual
+ (toList (fromList (List.take n l)))
+ (toList (ByteArray.take n (fromList l)))
+
+data.ByteArray.toList : data.ByteArray -> [Nat]
+data.ByteArray.toList = cases
+ BArr off len arr ->
+ use List :+
+ use Nat + >=
+ max = off + len
+ go acc i =
+ if i >= max then acc
+ else go (acc :+ data.ByteArray.Raw.read8 arr i) (i + 1)
+ handle go [] off with impossible
+
+data.ByteArray.toList.doc : Doc
+data.ByteArray.toList.doc =
+ {{
+ Gets the list of bytes in a byte array.
+
+ ```
+ ByteArray.toList (ByteArray.fromList [1, 2, 3])
+ ```
+ }}
+
+test> data.ByteArray.toList.test = test.verify do
+ use Random natIn
+ Each.repeat 100
+ l = List.replicate (natIn 0 40) do natIn 0 255
+ ensureEqual l (ByteArray.toList (ByteArray.fromList l))
+
+data.ByteArray.write8 :
+ data.ByteArray -> Nat -> Nat ->{Exception} data.ByteArray
+data.ByteArray.write8 arr i v =
+ match arr with
+ BArr off len src| i Nat.< len ->
+ Scope.run do
+ dst = Scope.Raw.byteArray len
+ data.ByteArray.Raw.copyTo! dst 0 src off len
+ Raw.write8 dst i v
+ BArr 0 len (ByteArray.Raw.freeze! dst)
+ _ -> ArrayFailure.raise "data.ByteArray.write8: index out of bounds" i
+
+data.ByteArray.write8.doc : Doc
+data.ByteArray.write8.doc =
+ use ByteArray fromList
+ {{
+ Writes a single byte to an array.
+
+ Throws an exception if the index is out of bounds.
+
+ Note: this operation is slow (O(length)), because it must copy the array's
+ contents to a new array.
+
+ ```
+ catch do
+ arr1 = data.ByteArray.write8 (fromList [0, 1, 2, 3]) 1 5
+ arr1 === fromList [0, 5, 2, 3]
+ ```
+ }}
+
+data.deprecated.Heap.breakOffMax : Heap k v -> (k, v, Optional (Heap k v))
+data.deprecated.Heap.breakOffMax h =
+ (k, v) = Heap.max h
+ (k, v, Heap.pop h)
+
+data.deprecated.Heap.breakOffMax.doc : Doc
+data.deprecated.Heap.breakOffMax.doc =
+ use Heap breakOffMax
+ {{
+ `` breakOffMax h `` returns the largest element of the heap `h` and the heap
+ with that element removed.
+
+ # Example
+
+ ```
+ (k, v, h) =
+ breakOffMax
+ (Heap.fromList ((1, "a") +| [(2, "b"), (3, "c"), (4, "d"), (5, "e")]))
+ (k, v, Optional.fold (do []) (List.Nonempty.toList << Heap.toList) h)
+ ```
+ }}
+
+data.deprecated.Heap.children : Heap k v -> [Heap k v]
+data.deprecated.Heap.children = cases Heap _ _ _ cs -> cs
+
+data.deprecated.Heap.children.doc : Doc
+data.deprecated.Heap.children.doc =
+ {{
+ Returns the children of a {type Heap} as a {type List} of {type Heap}s.
+
+ # Example
+
+ ```
+ List.map
+ Heap.toList (children (Heap.fromList ((1, "a") +| [(2, "b"), (3, "c")])))
+ ```
+ }}
+
+data.deprecated.Heap.doc : Doc
+data.deprecated.Heap.doc =
+ {{
+ A {type Heap} is a
+ [max-heap](https://en.wikipedia.org/wiki/Heap_(data_structure)) of key-value
+ pairs, with the property that the key of each node is greater than or equal
+ to the keys of its children. The maximum key is always at the root of the
+ heap.
+
+ {{
+ docCallout
+ (Some {{ ⚠️ }})
+ {{
+ This type is deprecated and will be removed in a future release.
+
+ Please use the
+ [Heap](https://share.unison-lang.org/@stew/p/code/latest/types/public/projects/dataextra/latest/heap/Heap)
+ type from the
+ [data-extra](https://share.unison-lang.org/@stew/p/code/latest/types/public/projects/dataextra/latest/)
+ library instead.
+ }} }}
+
+ The {type Heap} type is parameterized by the key type and the value type.
+
+ # Constructing a heap
+
+ Construct a heap from a list of key-value pairs:
+
+ @signature{mayFromList}
+
+ Construct a heap from a list of keys only. The values will be the keys
+ themselves:
+
+ @signature{fromKeys}
+
+ Construct a heap with a single key-value pair:
+
+ @signature{Heap.singleton}
+
+ # Adding and removing elements
+
+ Insert a key-value pair into a heap:
+
+ @signature{Heap.insert}
+
+ Remove the maximum key-value pair from a heap:
+
+ @signature{Heap.pop}
+
+ Add a heap to another heap:
+
+ @signature{Heap.union}
+
+ # Querying a heap
+
+ Get the maximum key-value pair from a heap:
+
+ @signature{Heap.max}
+
+ Get the child heaps of a heap:
+
+ @signature{children}
+
+ Get the size of a heap:
+
+ @signature{Heap.size}
+ }}
+
+data.deprecated.Heap.fromKeys : [a] -> Optional (Heap a a)
+data.deprecated.Heap.fromKeys as = mayFromList (List.map (a -> (a, a)) as)
+
+data.deprecated.Heap.fromKeys.doc : Doc
+data.deprecated.Heap.fromKeys.doc =
+ {{
+ Constructs a {type Heap} from a {type List} of keys. The values are set to
+ the keys themselves.
+
+ # Example
+
+ ```
+ Optional.map Heap.toList (fromKeys [1, 2, 3])
+ ```
+
+ # See also
+
+ {Heap.fromList}
+ }}
+
+data.deprecated.Heap.fromList : List.Nonempty (k, v) -> Heap k v
+data.deprecated.Heap.fromList kvs =
+ List.foldLeft
+ (cases h, (k, v) -> Heap.insert k v h)
+ (uncurry Heap.singleton (Nonempty.head kvs))
+ (Nonempty.tail kvs)
+
+data.deprecated.Heap.fromList.doc : Doc
+data.deprecated.Heap.fromList.doc =
+ {{
+ Create a {type Heap} from a non-empty list of key-value pairs.
+
+ # Example
+
+ ```
+ Heap.fromList ((1, "one") +| [(2, "two"), (3, "three")])
+ ```
+ }}
+
+data.deprecated.Heap.insert : k -> v -> Heap k v -> Heap k v
+data.deprecated.Heap.insert k v h = Heap.union h (Heap.singleton k v)
+
+data.deprecated.Heap.insert.doc : Doc
+data.deprecated.Heap.insert.doc =
+ {{ Puts a new key (priority) and value in the [Heap]({type Heap}). }}
+
+data.deprecated.Heap.max : Heap k v -> (k, v)
+data.deprecated.Heap.max = cases Heap _ k v _ -> (k, v)
+
+data.deprecated.Heap.max.doc : Doc
+data.deprecated.Heap.max.doc =
+ {{
+ Gets the value under the maximum key in a {type Heap} together with the key
+ itself.
+
+ # Example
+
+ ```
+ Optional.map
+ Heap.max (mayFromList [(1, "Lions"), (2, "Tigers"), (3, "Bears")])
+ ```
+ }}
+
+data.deprecated.Heap.maxKey : Heap k v -> k
+data.deprecated.Heap.maxKey = cases Heap _ k _ _ -> k
+
+data.deprecated.Heap.maxKey.doc : Doc
+data.deprecated.Heap.maxKey.doc =
+ {{
+ Returns the maximum key in the heap.
+
+ # Example
+
+ ```
+ Heap.maxKey (Heap.fromList ((1, "a") +| [(2, "b"), (3, "c")]))
+ ```
+ }}
+
+data.deprecated.Heap.mayFromList : [(k, v)] -> Optional (Heap k v)
+data.deprecated.Heap.mayFromList kvs =
+ op a b = match a with
+ None -> b
+ Some a ->
+ match b with
+ None -> Some a
+ Some b -> Some (Heap.union a b)
+ single = cases (k, v) -> Some (Heap.singleton k v)
+ List.foldBalanced single op None kvs
+
+data.deprecated.Heap.pop : Heap k v -> Optional (Heap k v)
+data.deprecated.Heap.pop h =
+ use Heap union
+ use List size unsafeAt
+ go h subs =
+ if size subs === 0 then h
+ else
+ if size subs === 1 then union h (unsafeAt 0 subs)
+ else
+ union
+ (union h (unsafeAt 0 subs)) (go (unsafeAt 1 subs) (List.drop 2 subs))
+ match List.uncons (children h) with
+ None -> None
+ Some (s0, subs) -> Some (go s0 subs)
+
+data.deprecated.Heap.pop.doc : Doc
+data.deprecated.Heap.pop.doc =
+ use Optional toAbort
+ {{
+ Removes the maximum key and its associated value from a {type Heap} and
+ returns the rest of the {type Heap}, or {None} if there are no more elements.
+
+ # Example
+
+ ```
+ toOptional! do
+ h = toAbort (mayFromList [(1, "Lions"), (2, "Tigers"), (3, "Bears")])
+ h' = toAbort (Heap.pop h)
+ Heap.max h'
+ ```
+ }}
+
+data.deprecated.Heap.singleton : k -> v -> Heap k v
+data.deprecated.Heap.singleton k v = Heap 1 k v []
+
+data.deprecated.Heap.singleton.doc : Doc
+data.deprecated.Heap.singleton.doc =
+ {{
+ Creates a {type Heap} with a single element.
+
+ # Example
+
+ ```
+ Heap.singleton ?k 1
+ ```
+ }}
+
+data.deprecated.Heap.size : Heap k v -> Nat
+data.deprecated.Heap.size = cases Heap n _ _ _ -> n
+
+data.deprecated.Heap.size.doc : Doc
+data.deprecated.Heap.size.doc =
+ {{
+ Get the number of elements in a {type Heap}.
+
+ # Example
+
+ ```
+ Heap.size (Heap.fromList ((1, ?a) +| [(2, ?b), (3, ?c)]))
+ ```
+ }}
+
+data.deprecated.Heap.sort : [a] -> [a]
+data.deprecated.Heap.sort as = sortDescending as |> List.reverse
+
+data.deprecated.Heap.sort.doc : Doc
+data.deprecated.Heap.sort.doc =
+ use Heap sort
+ {{
+ Heap-sort a {type List}. This is a stable sort, meaning that it preserves the
+ order of elements that are equal.
+
+ The time complexity is `O(n log n)` and the space usage is `O(n)`.
+
+ # Examples
+
+ ```
+ sort [5, 2, 4, 4, 2, 6]
+ ```
+
+ ```
+ sort []
+ ```
+ }}
+
+data.deprecated.Heap.sortDescending : [a] -> [a]
+data.deprecated.Heap.sortDescending as =
+ step = cases
+ None -> None
+ Some h -> Some (Heap.max h, Heap.pop h)
+ List.unfold (fromKeys as) step |> List.map at1
+
+data.deprecated.Heap.sortDescending.doc : Doc
+data.deprecated.Heap.sortDescending.doc =
+ {{
+ Sorts the given {type List} in descending order, using heap-sort.
+
+ # Example
+
+ ```
+ sortDescending [5, 3, 1, 4, 2]
+ ```
+ }}
+
+data.deprecated.Heap.take : Nat -> Heap k v -> [(k, v)]
+data.deprecated.Heap.take n h =
+ use List :+
+ use Nat - ==
+ go acc n h =
+ if n == 0 then acc
+ else
+ (k, v, h') = Heap.breakOffMax h
+ match h' with
+ Some h' -> go (acc :+ (k, v)) (n - 1) h'
+ _ -> acc :+ (k, v)
+ go [] n h
+
+data.deprecated.Heap.take.doc : Doc
+data.deprecated.Heap.take.doc =
+ use Heap take
+ {{
+ `` take n h `` returns the largest `n` elements of the heap `h`.
+
+ # Example
+
+ ```
+ take
+ 3 (Heap.fromList ((1, "a") +| [(2, "b"), (3, "c"), (4, "d"), (5, "e")]))
+ ```
+ }}
+
+data.deprecated.Heap.toList : Heap k v -> List.Nonempty (k, v)
+data.deprecated.Heap.toList = cases
+ Heap _ k v tail ->
+ (k, v)
+ +| List.flatMap
+ (List.Nonempty.toList << data.deprecated.Heap.toList) tail
+
+data.deprecated.Heap.toList.doc : Doc
+data.deprecated.Heap.toList.doc =
+ {{
+ Convert a {type Heap} to a non-empty list of key-value pairs, sorted by key
+ in descending order.
+
+ # Example
+
+ ```
+ Heap.toList (Heap.fromList ((1, "one") +| [(2, "two"), (3, "three")]))
+ ```
+ }}
+
+data.deprecated.Heap.union : Heap k v -> Heap k v -> Heap k v
+data.deprecated.Heap.union h1 h2 =
+ use List +:
+ use Nat +
+ (Heap n k1 v1 hs1, Heap m k2 v2 hs2) = (h1, h2)
+ if Universal.gteq k1 k2 then Heap (n + m) k1 v1 (h2 +: hs1)
+ else Heap (n + m) k2 v2 (h1 +: hs2)
+
+data.deprecated.Heap.union.doc : Doc
+data.deprecated.Heap.union.doc =
+ use Heap fromList
+ {{
+ Returns the union of two {type Heap}s. If a key is present in both
+ {type Heap}s, the value from the first {type Heap} is used.
+
+ # Example
+
+ ```
+ Heap.union
+ (fromList ((1, "a") +| [(2, "b")])) (fromList ((2, "c") +| [(3, "d")]))
+ ```
+
+ ```
+ fromList ((1, "a") +| [(2, "b"), (3, "d")])
+ ```
+ }}
+
+(data.deprecated.Weighted.<|>) : Weighted a -> Weighted a -> Weighted a
+(data.deprecated.Weighted.<|>) = cases
+ Weighted.Fail, n -> n
+ Yield x m, n -> Yield x (m data.deprecated.Weighted.<|> n)
+ Weight w m, Weighted.Fail -> Weight w m
+ Weight w m, Yield x n -> Yield x (Weight w m data.deprecated.Weighted.<|> n)
+ Weight w m, Weight w' n ->
+ if Universal.lt w w' then
+ Weight w do m() data.deprecated.Weighted.<|> Weight (w' Nat.- w) n
+ else
+ if w === w' then Weight w do m() data.deprecated.Weighted.<|> n()
+ else Weight w do Weight (w Nat.- w') m data.deprecated.Weighted.<|> n()
+
+data.deprecated.Weighted.append : Weighted a -> Weighted a -> Weighted a
+data.deprecated.Weighted.append w1 w2 = match w1 with
+ Weight n k -> Weight n do data.deprecated.Weighted.append k() w2
+ Yield a k -> Yield a (data.deprecated.Weighted.append k w2)
+ Weighted.Fail -> w2
+
+data.deprecated.Weighted.append.doc : Doc
+data.deprecated.Weighted.append.doc =
+ use Weighted append fromList
+ {{
+ `` append w1 w2 `` first yields values from `w1`, then `w2`. For example:
+
+ ```
+ Weighted.sample 5 (append (fromList [1, 2, 3]) (fromList [4, 5, 6]))
+ ```
+ }}
+
+data.deprecated.Weighted.append.examples.ex : [Nat]
+data.deprecated.Weighted.append.examples.ex =
+ use Weighted fromList
+ Weighted.sample
+ 10 (Weighted.append (fromList [1, 2, 3]) (fromList [4, 5, 6]))
+
+data.deprecated.Weighted.dedupe : Weighted a -> Weighted a
+data.deprecated.Weighted.dedupe =
+ use Weighted Fail
+ go seen = cases
+ Yield a w ->
+ if Set.contains a seen then go seen w
+ else Yield a (go (Set.insert a seen) w)
+ Weight n w -> Weight n do go seen w()
+ Fail -> Fail
+ go Set.empty
+
+data.deprecated.Weighted.dedupe.doc : Doc
+data.deprecated.Weighted.dedupe.doc =
+ {{ Removes duplicate elements from a {type Weighted}. }}
+
+data.deprecated.Weighted.doc : Doc
+data.deprecated.Weighted.doc =
+ {{
+ {type Weighted} is a lazy sequence with weights, such that the lowest-weight
+ elements are returned first. This allows you to search infinite spaces
+ productively, by guarding recursive calls with weights.
+
+ It is used by the {type Gen} ability's {Gen.sample} function to generate
+ values, prioritizing lower-weight values.
+ }}
+
+data.deprecated.Weighted.drop : Nat -> Weighted a -> Weighted a
+data.deprecated.Weighted.drop = cases
+ 0, ws -> ws
+ n, Weighted.Fail -> Weighted.Fail
+ n, Weight w ws -> data.deprecated.Weighted.drop n ws()
+ n, Yield _ ws -> data.deprecated.Weighted.drop (n Nat.- 1) ws
+
+data.deprecated.Weighted.drop.doc : Doc
+data.deprecated.Weighted.drop.doc =
+ use Weighted drop fromList
+ {{
+ Drops the first `n` elements from a {type Weighted} value. If the provided
+ {type Weighted} value contains fewer than `n` elements, the result is
+ {Weighted.Fail}.
+
+ # Examples
+
+ ```
+ drop 3 (fromList [1, 2, 3, 4, 5])
+ ```
+
+ ```
+ drop 4 (fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {Weighted.take}
+ }}
+
+data.deprecated.Weighted.Fail.doc : Doc
+data.deprecated.Weighted.Fail.doc =
+ {{ A {type Weighted} value that contains no elements. }}
+
+data.deprecated.Weighted.filter : (a -> Boolean) -> Weighted a -> Weighted a
+data.deprecated.Weighted.filter p = cases
+ Yield a k ->
+ (if p a then Yield a else id) (data.deprecated.Weighted.filter p k)
+ Weight n k -> Weight n do data.deprecated.Weighted.filter p k()
+ w -> w
+
+data.deprecated.Weighted.filter.doc : Doc
+data.deprecated.Weighted.filter.doc =
+ {{
+ Takes a predicate and a {type Weighted} and returns a new {type Weighted}
+ containing only the elements that satisfy the predicate.
+
+ # Example
+
+ ```
+ Weighted.sample 10 (Weighted.filter Nat.isEven natsInOrder)
+ ```
+ }}
+
+data.deprecated.Weighted.flatMap :
+ (a -> Weighted b) -> Weighted a -> Weighted b
+data.deprecated.Weighted.flatMap f = cases
+ Weighted.Fail -> Weighted.Fail
+ Yield x m -> f x Weighted.<|> data.deprecated.Weighted.flatMap f m
+ Weight w m -> Weight w do data.deprecated.Weighted.flatMap f m()
+
+data.deprecated.Weighted.flatMap.doc : Doc
+data.deprecated.Weighted.flatMap.doc =
+ use Nat +
+ {{
+ Maps the given function over the values of the {type Weighted} and
+ interleaves the result of each application with the rest of the
+ {type Weighted} from that point forward.
+
+ # Example
+
+ ```
+ Weighted.sample
+ 20
+ (Weighted.flatMap
+ (old ->
+ Weighted.fromList
+ (List.map (new -> (old, new + 100)) (List.range 0 old)))
+ natsInOrder)
+ ```
+ }}
+
+data.deprecated.Weighted.floats : Weighted Float
+data.deprecated.Weighted.floats =
+ use Weighted <|>
+ is = Weighted.drop 1 Weighted.nats
+ Weighted.append
+ (yield 0.0 <|> yield -0.0 <|> yield minFloat <|> yield maxFloat)
+ (Weighted.map (x -> Float.fromRepresentation x) is)
+
+data.deprecated.Weighted.floats.doc : Doc
+data.deprecated.Weighted.floats.doc =
+ {{
+ A {type Weighted} of {type Float}s. Generates pseudo-random {type Float}s in
+ the range {minFloat} to {maxFloat}.
+
+ # Example
+
+ ```
+ Weighted.sample 12 Weighted.floats
+ ```
+ }}
+
+data.deprecated.Weighted.fromList : [a] -> Weighted a
+data.deprecated.Weighted.fromList = cases
+ [] -> Weighted.Fail
+ a +: as ->
+ yield a Weighted.<|> (weight 1 do data.deprecated.Weighted.fromList as)
+
+data.deprecated.Weighted.fromList.doc : Doc
+data.deprecated.Weighted.fromList.doc =
+ {{
+ Creates a {type Weighted} from a {type List} of elements. Each element has a
+ weight one higher than the previous element. The first element has a weight
+ of ``0``.
+ }}
+
+data.deprecated.Weighted.ints : Weighted Int
+data.deprecated.Weighted.ints =
+ use Int negate
+ use Weighted <|>
+ go n =
+ yield n
+ <|> (weight 1 do
+ go
+ (if Universal.gt n +0 then negate n
+ else Int.increment (negate n)))
+ List.foldLeft
+ (a n -> a <|> yield n) Weighted.Fail [+0, +1, -1, maxInt, minInt]
+ <|> go +2
+
+data.deprecated.Weighted.ints.doc : Doc
+data.deprecated.Weighted.ints.doc =
+ {{
+ A {type Weighted} that generates {type Int} values starting with ``+0``, and
+ then alternating positive and negative values.
+
+ ```
+ Weighted.sample 10 Weighted.ints
+ ```
+
+ # See also
+
+ * {Weighted.nats}
+ * {natsInOrder}
+ }}
+
+data.deprecated.Weighted.lists : Weighted a -> Weighted [a]
+data.deprecated.Weighted.lists w =
+ use List +:
+ use Weighted <|>
+ yield []
+ <|> (weight 1 do
+ Weighted.mergeWith (+:) w (data.deprecated.Weighted.lists w))
+
+data.deprecated.Weighted.lists.doc : Doc
+data.deprecated.Weighted.lists.doc =
+ use Weighted <|> lists sample
+ {{
+ Takes a {type Weighted} value and yields lists of elements from it. The lists
+ are generated in order of increasing combined weight with the empty list
+ first. Each consecutive list has a weight one higher than the weight of its
+ elements combined, plus the weight of the previous list.
+
+ # Examples
+
+ ```
+ sample 10 (lists natsInOrder)
+ ```
+
+ If the weights of later elements are much larger than the weights of
+ earlier elements, then longer lists of the earlier elements will be
+ generated before the later elements start appearing.
+
+ ```
+ sample
+ 13
+ (lists (yield ?a <|> (weight 2 do yield ?b <|> (weight 2 do yield ?c))))
+ ```
+
+ Note that weights are cumulative, so in the above example the weight of the
+ element `` ?c `` is 4, not 2.
+ }}
+
+data.deprecated.Weighted.map : (a -> b) -> Weighted a -> Weighted b
+data.deprecated.Weighted.map f = cases
+ Weighted.Fail -> Weighted.Fail
+ Yield x w -> Yield (f x) (data.deprecated.Weighted.map f w)
+ Weight a w -> weight a do data.deprecated.Weighted.map f w()
+
+data.deprecated.Weighted.map.doc : Doc
+data.deprecated.Weighted.map.doc =
+ {{
+ Transforms the values of a {type Weighted} using the given function.
+
+ # Example
+
+ ```
+ Weighted.sample 10 (Weighted.map List.size (Weighted.lists Weighted.nats))
+ ```
+ }}
+
+data.deprecated.Weighted.mergeWith :
+ (a -> b -> c) -> Weighted a -> Weighted b -> Weighted c
+data.deprecated.Weighted.mergeWith f as bs =
+ Weighted.flatMap (a -> Weighted.map (b -> f a b) bs) as
+
+data.deprecated.Weighted.mergeWith.doc : Doc
+data.deprecated.Weighted.mergeWith.doc =
+ use Nat +
+ use Weighted fromList
+ {{
+ Merges two {type Weighted} values, using the given function to combine the
+ values of elements. For every pair of elements in the two {type Weighted}
+ values, the function is called with the two values. The result of the
+ function is used as the value of the element in the merged {type Weighted}
+ value, and the weight of the element is the sum of the weights of the two
+ elements in the original {type Weighted} values.
+
+ # Example
+
+ ```
+ Weighted.sample
+ 100
+ (Weighted.mergeWith (+) (fromList [1, 2, 3]) (fromList [100, 200, 300]))
+ ```
+ }}
+
+data.deprecated.Weighted.nats : Weighted Nat
+data.deprecated.Weighted.nats =
+ use Weighted <|>
+ go n = yield n <|> (weight 1 do go (lsfr n))
+ yield 0 <|> (weight 1 do go maxNat)
+
+data.deprecated.Weighted.nats.doc : Doc
+data.deprecated.Weighted.nats.doc =
+ {{
+ A {type Weighted} that generates {type Nat} values starting with ``+0``,
+ ``maxNat``, and then {type Nat} values in a pseudo-random order.
+
+ ```
+ Weighted.sample 10 Weighted.nats
+ ```
+
+ # See also
+
+ * {Weighted.ints}
+ * {natsInOrder}
+ }}
+
+data.deprecated.Weighted.natsInOrder : Weighted Nat
+data.deprecated.Weighted.natsInOrder =
+ use Nat +
+ use Weighted <|>
+ go n = yield n <|> (weight 1 do go (n + 1))
+ go 0
+
+data.deprecated.Weighted.normalFloats : Weighted Float
+data.deprecated.Weighted.normalFloats =
+ use Float / fromNat
+ Weighted.map (x -> fromNat x / fromNat maxNat) Weighted.nats
+
+data.deprecated.Weighted.normalFloats.doc : Doc
+data.deprecated.Weighted.normalFloats.doc =
+ {{
+ A {type Weighted} of {type Float}s. Generates pseudo-random {type Float}s in
+ the range `` 0.0 `` to ``1.0``.
+
+ # Example
+
+ ```
+ Weighted.sample 12 normalFloats
+ ```
+ }}
+
+data.deprecated.Weighted.sample : Nat -> Weighted a -> [a]
+data.deprecated.Weighted.sample n wsa =
+ use List +:
+ use Nat -
+ use data.deprecated.Weighted sample
+ if Universal.gt n 0 then
+ match wsa with
+ Weighted.Fail -> []
+ Yield a ms -> a +: sample (n - 1) ms
+ Weight _ w -> sample n w()
+ else []
+
+data.deprecated.Weighted.sample.doc : Doc
+data.deprecated.Weighted.sample.doc =
+ {{
+ Returns a {type List} of of the given length, sampled from the given
+ {type Weighted}.
+
+ # Example
+
+ ```
+ Weighted.sample 12 natsInOrder
+ ```
+ }}
+
+data.deprecated.Weighted.take : Nat -> Weighted a -> Weighted a
+data.deprecated.Weighted.take n = cases
+ Weighted.Fail -> Weighted.Fail
+ Yield x tail ->
+ if n Nat.== 0 then Weighted.Fail
+ else Yield x (data.deprecated.Weighted.take (n Nat.- 1) tail)
+ Weight w tail -> Weight w do data.deprecated.Weighted.take n tail()
+
+data.deprecated.Weighted.take.doc : Doc
+data.deprecated.Weighted.take.doc =
+ {{
+ Construct a new {type Weighted} that yields the first `n` elements from the
+ given {type Weighted} and then stops.
+
+ # Examples
+
+ ```
+ Weighted.sample 10 (Weighted.take 5 natsInOrder)
+ ```
+ }}
+
+data.deprecated.Weighted.weight : Nat ->{e} '{e} Weighted a ->{e} Weighted a
+data.deprecated.Weighted.weight w ws = Weight w ws
+
+data.deprecated.Weighted.weight.doc : Doc
+data.deprecated.Weighted.weight.doc =
+ {{ Adds the given weight to the elements of the given {type Weighted}. }}
+
+data.deprecated.Weighted.yield : a -> Weighted a
+data.deprecated.Weighted.yield a = Yield a Weighted.Fail
+
+data.deprecated.Weighted.Yield.doc : Doc
+data.deprecated.Weighted.Yield.doc =
+ {{
+ A {type Weighted} value that yields the given element and continues with the
+ given {type Weighted} value.
+ }}
+
+data.deprecated.Weighted.yield.doc : Doc
+data.deprecated.Weighted.yield.doc =
+ use Weighted <|> sample
+ {{
+ A {type Weighted} value that yields the given element and then stops.
+
+ # Examples
+
+ ```
+ sample 10 (yield 1)
+ ```
+
+ ```
+ sample 10 (yield ?a <|> (weight 2 do yield ?b))
+ ```
+ }}
+
+data.Graph.build : [(v, k, [k])] -> Graph v
+data.Graph.build input =
+ use Array.Raw freeze!
+ use List :+
+ use Map get
+ use Nat +
+ use Raw write
+ use Scope.Raw array
+ prepro i = cases
+ acc, [] -> acc
+ acc, (d, k, es) +: rest -> prepro (i + 1) (acc :+ (k, (i, d, es))) rest
+ data = Map.fromList <| prepro 0 [] input
+ Scope.run do
+ n = Map.size data
+ adj = array n
+ ann = array n
+ fill = cases
+ [] -> AdjLists (freeze! adj) (freeze! ann)
+ k +: ks ->
+ (i, d, es) = getOrBug "Graph.build: fill" <| get k data
+ is = List.filterMap (e -> Optional.map at1 <| get e data) es
+ write adj i is
+ write ann i d
+ fill ks
+ unsafeRun! do fill (Map.keys data)
+
+data.Graph.build.doc : Doc
+data.Graph.build.doc =
+ {{
+ Creates a graph from a list of information about the nodes.
+
+ The triples in the input represent the following:
+
+ 1. Arbitrary data associated with each node
+ 2. A key used to identify the node
+ 3. A list of keys specifying the out edges the node
+
+ Edges with targets that do not exist as sources in the list are simply
+ removed.
+ }}
+
+data.Graph.doc : Doc
+data.Graph.doc =
+ {{
+ A representation of a directed graph as adjacency lists stored in an array.
+ The nodes correspond to the array indices, and each index lists the indices
+ of other nodes with an edge from the given index.
+
+ The graph also stores data associated with each node.
+ }}
+
+data.Graph.edgeCount : Graph v -> Nat
+data.Graph.edgeCount = cases
+ AdjLists es _ ->
+ use Nat + <
+ go cnt = cases
+ i
+ | i < data.Array.Raw.size es ->
+ go (cnt + List.size (data.Array.Raw.read es i)) (i + 1)
+ | otherwise -> cnt
+ unsafeRun! do go 0 0
+
+data.Graph.edgeCount.doc : Doc
+data.Graph.edgeCount.doc = {{ Yields the number of edges in a {type Graph}. }}
+
+data.Graph.edges : Graph v -> Nat ->{Exception} [Nat]
+data.Graph.edges = cases AdjLists es _ -> data.Array.Raw.read es
+
+data.Graph.edges.doc : Doc
+data.Graph.edges.doc =
+ {{
+ Gives the vertices with an edge from the given vertex.
+
+ Note: can throw an exception if an invalid vertex number is supplied.
+ }}
+
+data.Graph.isReachable : Graph v -> Nat -> Nat ->{Exception} Boolean
+data.Graph.isReachable gr m n =
+ use Graph edges
+ use List any
+ use Nat ==
+ search seen k =
+ if NatSet.contains k seen then false
+ else
+ seen' = NatSet.insert k seen
+ n == k || any (search seen') (edges gr k)
+ any (search NatSet.empty) (edges gr m)
+
+data.Graph.isReachable.doc : Doc
+data.Graph.isReachable.doc =
+ {{
+ Checks if the second vertex is reachable from the first by following one or
+ more edges.
+
+ If the two vertices are equal, this will check that there is actually a cycle
+ in the graph with those vertices as endpoints.
+ }}
+
+data.Graph.reverse : Graph v ->{Exception} Graph v
+data.Graph.reverse = cases
+ AdjLists es no ->
+ use List ++
+ use Nat + <
+ n = data.Array.Raw.size es
+ f i n = (n, [i])
+ backs : [(Nat, [Nat])] -> Nat -> [(Nat, [Nat])]
+ backs acc = cases
+ i
+ | i < n ->
+ backs (List.map (f i) (data.Array.Raw.read es i) ++ acc) (i + 1)
+ | otherwise -> acc
+ nm = NatMap.fromListWith (++) (backs [] 0)
+ nes = Scope.run do
+ arr = Scope.Raw.array n
+ fill i =
+ if i < n then
+ Raw.write arr i (NatMap.getOrElse i [] nm)
+ fill (i + 1)
+ else ()
+ fill 0
+ Array.Raw.freeze! arr
+ AdjLists nes no
+
+data.Graph.reverse.doc : Doc
+data.Graph.reverse.doc =
+ {{
+ Yields the {type Graph} obtained by reversing all the edges in the given
+ graph.
+ }}
+
+data.Graph.SCC.add : a -> SCC a -> SCC a
+data.Graph.SCC.add a = cases
+ Acyclic b -> Cyclic [a, b]
+ Cyclic as -> Cyclic (a List.+: as)
+
+data.Graph.SCC.add.doc : Doc
+data.Graph.SCC.add.doc =
+ {{
+ Adds a value to an existing {type SCC}. Any {type SCC} with multiple values
+ will necessarily be {Cyclic}.
+ }}
+
+data.Graph.SCC.augment : Boolean -> a -> Optional (SCC a) -> SCC a
+data.Graph.SCC.augment cyc a = cases
+ None
+ | cyc -> Cyclic [a]
+ | otherwise -> Acyclic a
+ Some cc -> SCC.add a cc
+
+data.Graph.SCC.augment.doc : Doc
+data.Graph.SCC.augment.doc =
+ {{
+ A helper function for building an {type SCC} in a map, like {type Map} or
+ {type NatMap}. If no {type SCC} is supplied, a new one is created, with the
+ {type Boolean} argument specifying whether the new component should be
+ {Cyclic} or not. Otherwise the value is added to the given {type SCC}.
+ }}
+
+data.Graph.SCC.doc : Doc
+data.Graph.SCC.doc =
+ {{
+ {type SCC} represents the strongly connected components of a {type Graph}. An
+ {type SCC} contains the data associated to the vertices in the component.
+
+ {Cyclic} represents a cycle of one or more elements in the graph, while
+ {Acyclic} represents a component with a solitary node with no path to itself.
+ }}
+
+data.Graph.SCC.map : (a ->{g1} b) -> SCC a ->{g1} SCC b
+data.Graph.SCC.map f = cases
+ Acyclic x -> Acyclic (f x)
+ Cyclic xs -> Cyclic (List.map f xs)
+
+data.Graph.SCC.map.doc : Doc
+data.Graph.SCC.map.doc =
+ {{
+ {type SCC} forms a functor in the obvious way, so we can map over the stored
+ values.
+ }}
+
+data.Graph.sccs : Graph v -> [SCC v]
+data.Graph.sccs graph =
+ n = vertexCount graph
+ unsafeRun! do
+ (_, stack) = sccs.crawls graph (NatSet.empty, []) (List.range 0 n)
+ rgraph = Graph.reverse graph
+ f st no = uncurry (classify rgraph no) st no
+ let
+ (_, sccm) = List.foldLeft f (NatSet.empty, NatMap.empty) stack
+ NatMap.values sccm
+
+data.Graph.sccs.classifies :
+ Graph v
+ -> Nat
+ -> (NatSet, NatMap (SCC v))
+ -> [Nat]
+ ->{Exception} (NatSet, NatMap (SCC v))
+data.Graph.sccs.classifies graph root =
+ List.foldLeft (uncurry <| data.Graph.sccs.classify graph root)
+
+data.Graph.sccs.classifies.doc : Doc
+data.Graph.sccs.classifies.doc =
+ {{ Applies {classify} to several vertices in sequence. }}
+
+data.Graph.sccs.classify :
+ Graph v
+ -> Nat
+ -> NatSet
+ -> NatMap (SCC v)
+ -> Nat
+ ->{Exception} (NatSet, NatMap (SCC v))
+data.Graph.sccs.classify graph root seen groups node =
+ use Nat ==
+ if NatSet.contains node seen then (seen, groups)
+ else
+ seen' = NatSet.insert node seen
+ nexts = Graph.edges graph node
+ cyc = root == node && List.any ((==) node) nexts
+ annot = vertex graph node
+ groups' = NatMap.alter (Some << augment cyc annot) root groups
+ data.Graph.sccs.classifies graph root (seen', groups') nexts
+
+data.Graph.sccs.classify.doc : Doc
+data.Graph.sccs.classify.doc =
+ {{
+ Assigns vertices of a graph to connected components. The connected components
+ are identified by (numbers for) vertices in the graph, and the `root`
+ argument is used as the identifier that `node` should be assigned to.
+ }}
+
+data.Graph.sccs.crawl :
+ Graph v -> NatSet -> [Nat] -> Nat ->{Exception} (NatSet, [Nat])
+data.Graph.sccs.crawl graph seen stk node =
+ use List +:
+ if NatSet.contains node seen then (seen, stk)
+ else
+ seen' = NatSet.insert node seen
+ nexts = Graph.edges graph node
+ Tuple.mapRight ((+:) node)
+ <| data.Graph.sccs.crawls graph (seen', stk) nexts
+
+data.Graph.sccs.crawl.doc : Doc
+data.Graph.sccs.crawl.doc =
+ {{
+ Traverses a portion of a graph from a given node, pushing the encountered
+ vertices onto a stack. The {type NatSet} is used to keep track of which nodes
+ have already been traversed.
+ }}
+
+data.Graph.sccs.crawls :
+ Graph v -> (NatSet, [Nat]) -> [Nat] ->{Exception} (NatSet, [Nat])
+data.Graph.sccs.crawls graph =
+ List.foldLeft (uncurry <| data.Graph.sccs.crawl graph)
+
+data.Graph.sccs.crawls.doc : Doc
+data.Graph.sccs.crawls.doc =
+ {{ {sccs.crawl}s multiple vertices in a graph in order. }}
+
+data.Graph.sccs.doc : Doc
+data.Graph.sccs.doc =
+ {{
+ Finds the strongly connected components of a graph using
+ [Kosaraju's algorithm](https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm).
+
+ The algorithm works by first traversing the graph and placing vertices onto a
+ stack in post-order. Then, the post-order stack is used in conjunction with a
+ reversed graph to calculate the connected components.
+ }}
+
+data.Graph.stronglyConnectedComponents : [(v, k, [k])] -> [SCC v]
+data.Graph.stronglyConnectedComponents graph = Graph.sccs (Graph.build graph)
+
+data.Graph.stronglyConnectedComponents.doc : Doc
+data.Graph.stronglyConnectedComponents.doc =
+ {{
+ Finds the strongly connected components of a graph presentation.
+
+ The graph input is represented as a list of triples. The data in the triples
+ is:
+
+ 1. Arbitrary data associated with each vertex, yielded in the output
+ 2. A key used to identify the vertex
+ 3. A list of keys specifying the out edges the vertex
+
+ Any edges targeting nodes that do not occur in the second component of a
+ triple are just omitted from the graph, and will not show up in the output.
+ }}
+
+test> data.Graph.tests.build =
+ test.verify do
+ use List all
+ use Nat <
+ use Random listOf nat natIn
+ Each.repeat 50
+ gt = do (nat(), nat(), listOf nat do natIn 0 4)
+ gd = listOf gt do natIn 0 10
+ gr = Graph.build gd
+ n = vertexCount gr
+ ensureWith "build well-formed"
+ <| all (k -> all (j -> j < n) (Graph.edges gr k)) (List.range 0 n)
+
+data.Graph.tests.checkAcyclic : Graph v -> Nat ->{Exception} Boolean
+data.Graph.tests.checkAcyclic gr v = Boolean.not (isReachable gr v v)
+
+data.Graph.tests.checkCyclic : Graph v -> [Nat] ->{Exception} Boolean
+data.Graph.tests.checkCyclic gr vs =
+ use List all
+ all (v -> all (isReachable gr v) vs) vs
+
+data.Graph.tests.checkTopOrder : Graph v -> [v] ->{Exception} ()
+data.Graph.tests.checkTopOrder gr vs =
+ use List all
+ vn v = match vertexNum gr v with
+ None -> test.raiseFailure "checkTopOrder: vertex not found" v
+ Some n -> n
+ edg m n =
+ use Nat !=
+ es = Graph.edges gr n
+ all ((!=) m) es && all (edg m) es
+ go = cases
+ [] -> ()
+ v +: vs ->
+ n = vn v
+ ensureWith "checkTopOrder: no edge"
+ <| all (Boolean.not << flip (isReachable gr) n << vn) vs
+ go vs
+ go vs
+
+data.Graph.tests.checkTopOrder.doc : Doc
+data.Graph.tests.checkTopOrder.doc =
+ {{
+ Checks that, in a topological order, if v precedes u, then v does not occur
+ by following edges from u.
+ }}
+
+data.Graph.tests.randomDAG : (Nat -> a) ->{Random} Graph a
+data.Graph.tests.randomDAG f =
+ use List +:
+ use Nat - ==
+ vtxs = Random.natIn 0 21
+ edg acc n =
+ if n == 0 then acc
+ else
+ m = n - 1
+ acc' = if Random.boolean() then m +: acc else acc
+ edg acc' m
+ nod n = (f n, n, edg [] n)
+ Graph.build (shuffle <| List.map nod (List.range 0 vtxs))
+
+data.Graph.tests.randomGraph : (Nat -> a) ->{Random} Graph a
+data.Graph.tests.randomGraph f =
+ use Nat -
+ vtxs = Random.natIn 0 21
+ edg acc n =
+ use List +:
+ use Nat >
+ acc' = if Random.boolean() then n +: acc else acc
+ if n > 0 then edg acc' (n - 1) else acc'
+ nod n = (f n, n, edg [] (vtxs - 1))
+ Graph.build (List.map nod (List.range 0 vtxs))
+
+data.Graph.tests.randomGraph.doc : Doc
+data.Graph.tests.randomGraph.doc =
+ {{ Constructs a random {type Graph} for testing purposes. }}
+
+test> data.Graph.tests.reverse = test.verify do
+ Each.repeat 50
+ gr = randomGraph Nat.toText
+ rgr = Graph.reverse gr
+ ensureEqual (vertexCount gr) (vertexCount rgr)
+ ensureEqual (edgeCount gr) (edgeCount rgr)
+ matchEdges gr rgr
+
+data.Graph.tests.reverse.matchEdges : Graph v1 -> Graph v ->{Exception} ()
+data.Graph.tests.reverse.matchEdges gr rgr =
+ use Nat - ==
+ go i =
+ use Graph edges
+ use List all any
+ to = all (j -> any ((==) i) (edges rgr j)) (edges gr i)
+ fro = all (j -> any ((==) i) (edges gr j)) (edges rgr i)
+ ensureWith "reverse.matchEdges" (to && fro)
+ n = vertexCount gr
+ if n == 0 then () else go (n - 1)
+
+data.Graph.tests.reverse.matchEdges.doc : Doc
+data.Graph.tests.reverse.matchEdges.doc =
+ {{
+ Checks that for every edge in each graph, there is a reversed edge in the
+ other graph.
+ }}
+
+test> data.Graph.tests.sccs = test.verify do
+ Each.repeat 50
+ gr = randomGraph id
+ p = cases
+ Acyclic v -> checkAcyclic gr v
+ Cyclic vs -> checkCyclic gr vs
+ ensureWith "sccs" <| List.all p (Graph.sccs gr)
+
+test> data.Graph.tests.topSort.test = test.verify do
+ Each.repeat 50
+ gr = randomDAG id
+ vs = Abort.toGenericException "topSort failed" () do topSort gr
+ checkTopOrder gr vs
+
+data.Graph.topSort : Graph v ->{Abort} [v]
+data.Graph.topSort graph =
+ use NatSet empty
+ n = vertexCount graph
+ result =
+ unsafeRun! do topSort.crawls graph empty (empty, []) (List.range 0 n)
+ at2 result
+
+data.Graph.topSort.crawl :
+ Graph v -> NatSet -> (NatSet, [v]) -> Nat ->{Exception, Abort} (NatSet, [v])
+data.Graph.topSort.crawl graph loop0 state n =
+ use List +:
+ use NatSet contains insert
+ if contains n (at1 state) then state
+ else
+ if contains n loop0 then abort
+ else
+ loop = insert n loop0
+ let
+ (seen, sort) =
+ data.Graph.topSort.crawls graph loop state (Graph.edges graph n)
+ (insert n seen, vertex graph n +: sort)
+
+data.Graph.topSort.crawl.doc : Doc
+data.Graph.topSort.crawl.doc =
+ {{
+ Traverses a graph from a given node, pushing vertex information onto a
+ topological sorting.
+
+ The {type NatSet} paired with the sort stores the vertex numbers that have
+ already been successfully visited, so that they are not repeated.
+
+ The other {type NatSet} stores the vertex numbers that are part of the
+ current path through the graph, which allows for detecting cycles.
+
+ The strategy is to do depth-first traversal of the graph. The loop detection
+ is modified before following edges, while the sort and 'success' maps are
+ modified post-order. This means that the resulting list should contain a
+ given vertex at an earlier position than any vertex reachable from it.
+ }}
+
+data.Graph.topSort.crawls :
+ Graph v
+ -> NatSet
+ -> (NatSet, [v])
+ -> [Nat]
+ ->{Exception, Abort} (NatSet, [v])
+data.Graph.topSort.crawls graph loop =
+ List.foldLeft (data.Graph.topSort.crawl graph loop)
+
+data.Graph.topSort.doc : Doc
+data.Graph.topSort.doc =
+ {{
+ Calculates a topological sorting the vertices of a {type Graph}. Such
+ sortings are not necessarily unique, and depend on the representation details
+ of the graph.
+
+ If the {type Graph} contains cycles, there can be no meaningful ordering of
+ the vertices in the cycle, so this function will {type Abort} in that
+ scenario.
+
+ The algorithm repeatedly {topSort.crawl}s from each vertex in the graph,
+ which results in post-order traversals of the graph, with the exact
+ representation determining which relatively unordered pieces are listed
+ first.
+ }}
+
+data.Graph.vertex : Graph v -> Nat ->{Exception} v
+data.Graph.vertex = cases AdjLists _ vs -> data.Array.Raw.read vs
+
+data.Graph.vertex.doc : Doc
+data.Graph.vertex.doc =
+ {{
+ Gives the data associated with a vertex in the graph.
+
+ Note: can throw an exception if an invalid vertex number is supplied.
+ }}
+
+data.Graph.vertexCount : Graph v -> Nat
+data.Graph.vertexCount = cases AdjLists es _ -> data.Array.Raw.size es
+
+data.Graph.vertexCount.doc : Doc
+data.Graph.vertexCount.doc =
+ {{ Yields the number of vertices in a {type Graph}. }}
+
+data.Graph.vertexNum : Graph v -> v ->{Exception} Optional Nat
+data.Graph.vertexNum gr v =
+ use Nat + >=
+ (AdjLists _ vs) = gr
+ n = data.Array.Raw.size vs
+ go i =
+ if i >= n then None
+ else if data.Array.Raw.read vs i === v then Some i else go (i + 1)
+ go 0
+
+data.Graph.vertexNum.doc : Doc
+data.Graph.vertexNum.doc =
+ {{
+ Finds the vertex number associated with some vertex data. If the initial
+ vertex data was not unique, then an arbitrary matching number may be
+ returned.
+ }}
+
+data.Id.apply : Id i -> Id (i ->{g} t) ->{g} Id t
+data.Id.apply x = cases Id f -> Id.map f x
+
+data.Id.apply.doc : Doc
+data.Id.apply.doc = {{ Apply the wrapped function to the wrapped value. }}
+
+data.Id.doc : Doc
+data.Id.doc =
+ {{
+ The {type Id} type is a trivial type that simply wraps another type without
+ adding any additional structure.
+
+ This type is useful as a base case for recursive types, or as a trivial type
+ argument where a type constructor is required.
+
+ # Example
+
+ A branching tree type that is abstract in the branching functor can be
+ defined as follows:
+
+ ``` unison
+ structural type Tree f a = Empty | Branch a (f (Tree f a))
+ ```
+
+ The {type Id} type can be used as the `f` argument to the `Tree` type
+ constructor, in which case the tree is simply a list:
+
+ ``` unison
+ structural type MyList a = List (Tree Id a)
+ ```
+ }}
+
+data.Id.flatMap : (i ->{g} o) -> Id i ->{g} o
+data.Id.flatMap f = cases Id x -> f x
+
+data.Id.flatMap.doc : Doc
+data.Id.flatMap.doc =
+ {{
+ Transform the wrapped value with a function that returns its argument in
+ {type Id}.
+ }}
+
+data.Id.map : (i ->{g} o) -> Id i ->{g} Id o
+data.Id.map f = cases Id x -> Id (f x)
+
+data.Id.map.doc : Doc
+data.Id.map.doc = {{ Transform the wrapped value. }}
+
+-- builtin data.List.++ : [a] -> [a] -> [a]
+
+data.List.++.doc : Doc
+data.List.++.doc =
+ use List ++
+ {{
+ Append two {type List} values.
+
+ # Examples
+
+ ```
+ [1, 2, 3] ++ [4, 5, 6]
+ ```
+
+ ```
+ [1, 2, 3] ++ []
+ ```
+
+ ```
+ [] ++ [4, 5, 6]
+ ```
+
+ ```
+ [] ++ []
+ ```
+ }}
+
+-- builtin data.List.+: : a -> [a] -> [a]
+
+data.List.+:.doc : Doc
+data.List.+:.doc =
+ use List +:
+ {{
+ Prepend an element to a {type List} value.
+
+ # Examples
+
+ ```
+ 1 +: [2, 3]
+ ```
+
+ ```
+ 1 +: []
+ ```
+ }}
+
+-- builtin data.List.:+ : [a] -> a -> [a]
+
+data.List.:+.doc : Doc
+data.List.:+.doc =
+ use List :+
+ {{
+ Append an element to a {type List} value.
+
+ # Examples
+
+ ```
+ [1, 2] :+ 3
+ ```
+
+ ```
+ [] :+ 1
+ ```
+ }}
+
+data.List.align : [a] -> [b] -> [OneOrBoth a b]
+data.List.align = List.alignWith id
+
+data.List.align.doc : Doc
+data.List.align.doc =
+ use List align
+ {{
+ Aligns two lists into a list of {type OneOrBoth} values.
+
+ The result will have the same length as the longer of the two lists, and each
+ element will be a {type OneOrBoth} containing the corresponding elements from
+ the two input lists. If one of the lists is shorter than the other, the
+ result will contain {This} or {That} values accordingly.
+
+ # Examples
+
+ ```
+ align [1, 2, 3] ["a", "b"]
+ ```
+
+ ```
+ align [1, 2] ["a", "b", "c"]
+ ```
+
+ ```
+ align [1, 2, 3] ["a", "b", "c"]
+ ```
+
+ # See also
+
+ * {List.alignWith} - a variant where you can specify a function to apply to
+ the aligned elements.
+ }}
+
+data.List.alignWith : (OneOrBoth a b ->{g} c) -> [a] -> [b] ->{g} [c]
+data.List.alignWith f =
+ use List :+
+ go acc = cases
+ [], [] -> acc
+ [], y +: ys -> go (acc :+ f (That y)) [] ys
+ x +: xs, [] -> go (acc :+ f (This x)) xs []
+ x +: xs, y +: ys -> go (acc :+ f (Both x y)) xs ys
+ go []
+
+data.List.alignWith.doc : Doc
+data.List.alignWith.doc =
+ use List alignWith
+ use Nat +
+ use OneOrBoth fold
+ use Text size
+ {{
+ Aligns two lists into a list of values using a function.
+
+ The result will have the same length as the longer of the two lists, and each
+ element will be the result of applying the given function to the
+ corresponding elements from the two input lists. If one of the lists is
+ shorter than the other, the result will contain values accordingly.
+
+ # Examples
+
+ ```
+ alignWith (fold id size (x y -> x + size y)) [1, 2, 3] ["a", "b"]
+ ```
+
+ ```
+ alignWith (fold id size (x y -> x + size y)) [1, 2] ["a", "b", "c"]
+ ```
+
+ ```
+ alignWith (fold id size (x y -> x + size y)) [1, 2, 3] ["a", "b", "c"]
+ ```
+
+ # See also
+
+ * {List.align} - a variant that returns a list of {type OneOrBoth} values.
+ }}
+
+data.List.all : (a ->{e} Boolean) -> [a] ->{e} Boolean
+data.List.all predicate = cases
+ [] -> true
+ x +: rest -> predicate x && data.List.all predicate rest
+
+data.List.all.doc : Doc
+data.List.all.doc =
+ use List all
+ use Nat isEven
+ {{
+ `` all predicate list `` returns `` true `` if `predicate` is true for every
+ element of `list`.
+
+ # Examples
+
+ ```
+ all isEven [2, 4, 6]
+ ```
+
+ ```
+ all Nat.isOdd [1, 2, 3]
+ ```
+
+ {all} returns `` true `` for the empty list:
+
+ ```
+ all isEven []
+ ```
+ }}
+
+test> data.List.all.tests.deMorgan = runs 100 do
+ bs = gen.listOf gen.boolean ()
+ expect (List.all id bs === List.none Boolean.not bs)
+
+test> data.List.all.tests.homomorphism =
+ runs 100 do
+ deprecated.laws.homomorphism
+ (gen.listOf gen.boolean) (List.all id) (List.++) (a b -> a && b)
+
+data.List.allPairs : [a] -> [(a, a)]
+data.List.allPairs xs =
+ Each.toList do match each (dropRight 1 (List.tails xs)) with
+ x +: rest ->
+ y = each rest
+ (x, y)
+ _ -> bug "empty tails"
+
+data.List.allPairs.doc : Doc
+data.List.allPairs.doc =
+ {{
+ Returns all pairs of elements from a {type List}. The order of elements in
+ each pair is always the order in which they appear in the original list. That
+ is, if `x` only appears before `y` in the original list, then the pair
+ `(x, y)` will appear in the result, but not `(y, x)`.
+
+ If an element appears more than once in the original list, then pairs with
+ that element will be repeated in the result.
+
+ If the input contains fewer than two elements, the result is empty.
+
+ Considering the elements of the list as vertices of a graph, this function
+ returns all the undirected edges of the graph.
+
+ # Examples
+
+ ```
+ allPairs [1, 2, 3]
+ ```
+
+ ```
+ allPairs [1, 2, 2, 3]
+ ```
+
+ ```
+ allPairs [1]
+ ```
+
+ # See also
+
+ * {slidingPairs} for all pairs of adjacent elements.
+ * {List.tails} for all suffixes of a list.
+ * {inits} for all prefixes of a list.
+ * {subsequences} for all subsequences of a list.
+ }}
+
+data.List.any : (a ->{e} Boolean) -> [a] ->{e} Boolean
+data.List.any predicate = cases
+ [] -> false
+ x +: rest -> predicate x || data.List.any predicate rest
+
+data.List.any.doc : Doc
+data.List.any.doc =
+ use List any
+ use Nat isEven
+ {{
+ `` any predicate list `` returns `` true `` if `predicate` is true for at
+ least one element of `list`.
+
+ # Examples
+
+ ```
+ any isEven [1, 2, 3]
+ ```
+
+ ```
+ any Nat.isOdd [2, 4, 6]
+ ```
+
+ ```
+ any isEven []
+ ```
+ }}
+
+test> data.List.any.tests.deMorgan = runs 100 do
+ use Boolean not
+ bs = gen.listOf gen.boolean ()
+ expect (List.any id bs === not (List.all not bs))
+
+test> data.List.any.tests.homomorphism =
+ runs 100 do
+ deprecated.laws.homomorphism
+ (gen.listOf gen.boolean) (List.any id) (List.++) (a b -> a || b)
+
+data.List.anyIndexOf : a -> [a] -> Optional Nat
+data.List.anyIndexOf a s =
+ ao = Some a
+ search (i -> Universal.compare ao (List.at i s)) 0 (List.size s)
+
+data.List.anyIndexOf.doc : Doc
+data.List.anyIndexOf.doc =
+ {{
+ `` anyIndexOf e xs `` returns the first index where the element `e` occurs in
+ the list `xs` using a binary search approach, provided the input list is
+ sorted in ascending order, or {None} if the element is not found.
+
+ {anyIndexOf} is not guaranteed to find the index of the element unless the
+ input is sorted in ascending order according to {Universal.ordering}. In
+ certain circumstances the logic of the binary search will exit early and not
+ return an index even if the element exists in the list.
+
+ # Examples:
+
+ ```
+ anyIndexOf 0 []
+ ```
+
+ ```
+ anyIndexOf 1 [1, 2, 3]
+ ```
+
+ ```
+ anyIndexOf 6 [5, 5, 6, 6, 7, 7, 9]
+ ```
+
+ ## Corner cases
+
+ ```
+ anyIndexOf 1 [1, 2, 1, 2, 1]
+ ```
+
+ ```
+ anyIndexOf 2 [1, 2, 1, 2, 1]
+ ```
+
+ ```
+ anyIndexOf 2 [2, 1, 1, 2, 1]
+ ```
+ }}
+
+data.List.anyIndexOf.evaluated.empty : Optional Nat
+data.List.anyIndexOf.evaluated.empty = anyIndexOf 0 []
+
+data.List.anyIndexOf.evaluated.lower : Optional Nat
+data.List.anyIndexOf.evaluated.lower = anyIndexOf 2 input.lower
+
+data.List.anyIndexOf.evaluated.notSorted : Optional Nat
+data.List.anyIndexOf.evaluated.notSorted = anyIndexOf 1 input.lower
+
+data.List.anyIndexOf.evaluated.sorted : Optional Nat
+data.List.anyIndexOf.evaluated.sorted = anyIndexOf 1 input.sorted
+
+data.List.anyIndexOf.evaluated.upper : Optional Nat
+data.List.anyIndexOf.evaluated.upper = anyIndexOf 2 input.upper
+
+data.List.anyIndexOf.examples.input.lower : [Nat]
+data.List.anyIndexOf.examples.input.lower = [1, 2, 1, 2, 1]
+
+data.List.anyIndexOf.examples.input.upper : [Nat]
+data.List.anyIndexOf.examples.input.upper = [2, 1, 1, 2, 1]
+
+test> data.List.anyIndexOf.tests.empty = check (anyIndexOf 0 [] === None)
+
+test> data.List.anyIndexOf.tests.lower =
+ check (anyIndexOf 2 input.lower === None)
+
+test> data.List.anyIndexOf.tests.notSorted =
+ check (anyIndexOf 1 input.lower === Some 2)
+
+test> data.List.anyIndexOf.tests.sorted =
+ check (anyIndexOf 1 input.sorted === Some 0)
+
+test> data.List.anyIndexOf.tests.upper =
+ check (anyIndexOf 2 input.upper === None)
+
+data.List.apply : [a ->{e} b] -> [a] ->{e} [b]
+data.List.apply fs xs = Each.toList do
+ f = each fs
+ x = each xs
+ f x
+
+data.List.apply.doc : Doc
+data.List.apply.doc =
+ use List apply
+ {{
+ Applies every function in the first list to every element in the second list.
+ The result is a list whose length is the product of the lengths of the two
+ lists.
+
+ # Examples
+
+ ```
+ apply [Nat.increment, Nat.decrement] [1, 2, 3]
+ ```
+
+ ```
+ apply [Nat.isOdd, Nat.isEven] [1, 2, 3]
+ ```
+ }}
+
+-- builtin data.List.at : Nat -> [a] -> Optional a
+
+data.List.at.doc : Doc
+data.List.at.doc =
+ use List at
+ {{
+ `` at n `` gets the element at the position `n` in the list (using
+ [zero-based indexing](https://en.wikipedia.org/wiki/Zero-based_numbering)),
+ or returns {None} if the list has fewer than `n+1` elements.
+
+ # Examples
+
+ ```
+ at 0 [10, 20, 30]
+ ```
+
+ ```
+ at 2 [10, 20, 30]
+ ```
+
+ ```
+ at 3 [10, 20, 30]
+ ```
+ }}
+
+test> data.List.at.tests.index = runs 100 do
+ use Nat *
+ as = gen.listOf natInOrder ()
+ n = gen.natIn 0 (List.size as * 2) ()
+ expect (List.at n as === List.head (List.drop n as))
+
+data.List.at! : Nat -> [a] ->{Abort} a
+data.List.at! n as = match List.at n as with
+ Some a -> a
+ None -> abort
+
+data.List.at!.doc : Doc
+data.List.at!.doc =
+ use Abort toBug
+ use List at!
+ {{
+ `` at! n `` gets the element at the position `n` in the list (using
+ [zero-based indexing](https://en.wikipedia.org/wiki/Zero-based_numbering)),
+ or aborts with {abort} if the list has fewer than `n+1` elements.
+
+ # Examples
+
+ ```
+ toBug do at! 0 [10, 20, 30]
+ ```
+
+ ```
+ toBug do at! 2 [10, 20, 30]
+ ```
+
+ ```
+ toBug do at! 3 [10, 20, 30]
+ ```
+ }}
+
+data.List.chunk : Nat -> [a] -> [[a]]
+data.List.chunk n xs =
+ use List ++
+ use Nat ==
+ go acc = cases
+ [] -> acc
+ xs -> go (acc ++ [List.take n xs]) (List.drop n xs)
+ if n == 0 then [] else go [] xs
+
+data.List.chunk.doc : Doc
+data.List.chunk.doc =
+ use List chunk
+ {{
+ `` chunk n xs `` splits the list `xs` into chunks of size `n`. The final
+ chunk may be smaller than `n`.
+
+ # Examples
+
+ ```
+ chunk 2 [1, 2, 3, 4, 5, 6, 7, 8, 9]
+ ```
+
+ ```
+ chunk 3 [1, 2, 3, 4, 5, 6, 7, 8, 9]
+ ```
+ }}
+
+data.List.compare : (a ->{f} b ->{g} Ordering) -> [a] -> [b] ->{f, g} Ordering
+data.List.compare ord xs ys =
+ use List size
+ compared = List.zipWith ord xs ys
+ match List.find (x -> Boolean.not (x === Equal)) compared with
+ None -> Universal.ordering (size xs) (size ys)
+ Some o -> o
+
+data.List.compare.doc : Doc
+data.List.compare.doc =
+ use List compare
+ use Universal ordering
+ {{
+ Compares two lists for {type Ordering}. The given function is used to compare
+ the elements.
+
+ # Examples
+
+ ```
+ compare ordering [1, 2, 3] [1, 2, 3]
+ ```
+
+ ```
+ compare ordering [1, 2, 3] [1, 2, 4]
+ ```
+
+ ```
+ compare ordering [1, 2, 3] [1, 2]
+ ```
+
+ ```
+ compare ordering [1, 2, 3] [1, 2, 3, 4]
+ ```
+ }}
+
+data.List.concatOptional : Optional [a] -> [a]
+data.List.concatOptional = cases
+ Some xs -> xs
+ None -> []
+
+data.List.concatOptional.doc : Doc
+data.List.concatOptional.doc =
+ {{
+ Unwraps an {type Optional} {type List} to just a {type List}.
+
+ # Examples
+
+ ```
+ concatOptional None
+ ```
+
+ ```
+ concatOptional (Some [1, 2, 3])
+ ```
+ }}
+
+test> data.List.concatOptional.tests.prop1 =
+ go _ =
+ xs = gen.listOf natInOrder ()
+ expect (concatOptional (Some xs) === xs)
+ runs 100 go
+
+test> data.List.concatOptional.tests.test1 = check (concatOptional None === [])
+
+data.List.contains : a -> [a] -> Boolean
+data.List.contains a = cases
+ hd +: tl
+ | hd === a -> true
+ | otherwise -> data.List.contains a tl
+ [] -> false
+
+data.List.contains.doc : Doc
+data.List.contains.doc =
+ {{ {List.contains} returns `` true `` if the element is found in the list. }}
+
+test> data.List.contains.tests.negative1 = check (List.contains 0 [] === false)
+
+test> data.List.contains.tests.negative2 =
+ check (List.contains 0 [1, 2, 3] === false)
+
+test> data.List.contains.tests.negative3 =
+ check (List.contains 1 [0, 0, 0] === false)
+
+test> data.List.contains.tests.positive =
+ go _ =
+ use List ++ :+
+ use gen listOf
+ a = listOf natInOrder ()
+ b = natInOrder()
+ c = listOf natInOrder ()
+ expect (List.contains b (a :+ b ++ c))
+ runs 100 go
+
+data.List.containsNot : a -> [a] -> Boolean
+data.List.containsNot a as = Boolean.not <| List.contains a as
+
+data.List.containsNot.doc : Doc
+data.List.containsNot.doc =
+ {{
+ `` containsNot x xs `` returns `` true `` if the element `x` is __not__ found
+ in the list `xs`.
+ }}
+
+data.List.count : (a ->{e} Boolean) -> [a] ->{e} Nat
+data.List.count f as =
+ use Nat +
+ List.foldLeft (acc a -> (if f a then acc + 1 else acc)) 0 as
+
+data.List.count.doc : Doc
+data.List.count.doc =
+ use List count
+ {{
+ @signature{count}
+
+ {List.count f as} returns the number of times the function `f` returns `true`
+ for elements in list `as`.
+
+ # Example
+
+ ```
+ count Nat.isEven [1, 2, 3, 4, 5]
+ ```
+ }}
+
+data.List.countElement : a -> [a] -> Nat
+data.List.countElement a = List.count ((===) a)
+
+data.List.countElement.doc : Doc
+data.List.countElement.doc =
+ {{
+ @signature{countElement}
+
+ {List.countElement a as} returns the number of times the element `a` appears
+ in the list `as`.
+
+ # Example
+
+ ```
+ countElement 2 [1, 2, 3, 2, 3, 3, 4]
+ ```
+ }}
+
+data.List.deleteAt : Nat -> [a] -> [a]
+data.List.deleteAt n as =
+ use List ++ slice
+ use Nat +
+ slice 0 n as ++ slice (n + 1) (List.size as) as
+
+data.List.deleteAt.doc : Doc
+data.List.deleteAt.doc =
+ {{
+ Deletes from the list the element at the given (0-based) index.
+
+ # Examples
+
+ ```
+ deleteAt 1 [1, 2, 3]
+ ```
+
+ Does nothing if the requested index is past the end of the list:
+
+ ```
+ deleteAt 9 [1, 2, 3]
+ ```
+ }}
+
+test> data.List.deleteAt.test = runs 100 do
+ use List ++
+ use gen listOf
+ xs = listOf natInOrder ()
+ ys = listOf natInOrder ()
+ x = natInOrder()
+ expect (deleteAt (List.size xs) (xs ++ [x] ++ ys) === (xs ++ ys))
+
+data.List.deleteFirst : (a ->{g} Boolean) -> [a] ->{g} [a]
+data.List.deleteFirst p = cases
+ x +: xs -> if p x then xs else x List.+: data.List.deleteFirst p xs
+ [] -> []
+
+data.List.deleteFirst.doc : Doc
+data.List.deleteFirst.doc =
+ {{
+ Deletes the first element for which the predicate holds true. If no such
+ element is found, leaves the list unchanged
+ }}
+
+test> data.List.deleteFirst.tests.ex1 =
+ use Nat ==
+ check (deleteFirst (n -> n == 1) [3, 2, 1, 2, 3] === [3, 2, 2, 3])
+
+test> data.List.deleteFirst.tests.ex2 =
+ use Nat ==
+ check (deleteFirst (n -> n == 1) [3, 2, 2, 3] === [3, 2, 2, 3])
+
+test> data.List.deleteFirst.tests.ex3 =
+ use Nat ==
+ check (deleteFirst (n -> n == 1) [] === [])
+
+data.List.deprecated.lubIndexOf : a -> [a] -> Nat
+data.List.deprecated.lubIndexOf a s = lubIndexOf' a 0 s
+
+data.List.deprecated.lubIndexOf.doc : Doc
+data.List.deprecated.lubIndexOf.doc =
+ {{
+ `` lubIndexOf x xs `` returns the index in the {type List} `xs` where the
+ element `x` occurs, using a head-to-tail scan of the {type List}. It returns
+ the size of the {type List} if `x` is not found.
+
+ {lubIndexOf} is only guaranteed to find the index if the elements are sorted
+ in ascending order according to {Universal.ordering}.
+
+ # Examples
+
+ ```
+ lubIndexOf 0 []
+ ```
+
+ ```
+ lubIndexOf 0 [1, 2, 3]
+ ```
+
+ ```
+ lubIndexOf 2 [1, 2, 3]
+ ```
+
+ {lubIndexOf} returns the size of the input if it's not ordered:
+
+ ```
+ lubIndexOf 3 [1, 3, 2, 1]
+ ```
+ }}
+
+data.List.deprecated.lubIndexOf.evaluated.empty : Nat
+data.List.deprecated.lubIndexOf.evaluated.empty = lubIndexOf 0 []
+
+data.List.deprecated.lubIndexOf.evaluated.notSorted : Nat
+data.List.deprecated.lubIndexOf.evaluated.notSorted = lubIndexOf 3 [1, 3, 2, 1]
+
+data.List.deprecated.lubIndexOf.evaluated.sorted : Nat
+data.List.deprecated.lubIndexOf.evaluated.sorted = lubIndexOf 1 [1, 2, 3]
+
+test> data.List.deprecated.lubIndexOf.tests.empty =
+ check (lubIndexOf 0 [] === 0)
+
+test> data.List.deprecated.lubIndexOf.tests.notSorted =
+ check (lubIndexOf 3 [1, 3, 2, 1] === 4)
+
+test> data.List.deprecated.lubIndexOf.tests.sorted =
+ check (lubIndexOf 1 [1, 2, 3] === 0)
+
+data.List.deprecated.lubIndexOf' : a -> Nat -> [a] -> Nat
+data.List.deprecated.lubIndexOf' a start s =
+ ao = Some a
+ findLowestZero (i -> Universal.compare ao (List.at i s)) start (List.size s)
+
+data.List.deprecated.lubIndexOf'.doc : Doc
+data.List.deprecated.lubIndexOf'.doc =
+ {{
+ `` lubIndexOf' x n xs `` returns the index in the {type List} `xs` where the
+ element `x` occurs, using a scan of the {type List} starting from index `n`.
+ It returns the size of the {type List} if `x` is not found after index `n`.
+
+ {lubIndexOf'} is only guaranteed to find the index if the elements are sorted
+ in ascending order according to {Universal.ordering}.
+
+ # Examples
+
+ ```
+ lubIndexOf' 0 0 []
+ ```
+
+ ```
+ lubIndexOf' 2 0 [1, 2, 3]
+ ```
+
+ ```
+ lubIndexOf' 2 2 [3, 2, 1, 2, 3]
+ ```
+
+ {lubIndexOf'} returns the size of the {type List} if the portion of the
+ input after the specified index is not ordered in ascending order:
+
+ ```
+ lubIndexOf' 3 1 [1, 3, 2, 1]
+ ```
+ }}
+
+data.List.diagonal : [[a]] -> [a]
+data.List.diagonal = List.join << diagonals
+
+data.List.diagonal.doc : Doc
+data.List.diagonal.doc =
+ {{
+ Returns a diagonalization of a {type List} of {type List}s. The result begins
+ with the first element of the first list, then the first element of the
+ second list, the second element of the first list, the first element of the
+ third list, the second element of the second list, the third element of the
+ first list, etc.
+
+ # Examples
+
+ ```
+ diagonal [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+ ```
+
+ ```
+ diagonal [[], [1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]
+ ```
+
+ ```
+ fromCharList
+ (List.take
+ 21
+ (diagonal
+ (List.map
+ toCharList
+ [ "🟥🟧🟨🟩🟦🟪"
+ , "🟧🟨🟩🟦🟪🟫"
+ , "🟨🟩🟦🟪🟫🔴"
+ , "🟩🟦🟪🟫🔴🟠"
+ , "🟦🟪🟫🔴🟠🟡"
+ , "🟪🟫🔴🟠🟡🟢"
+ , "🟫🔴🟠🟡🟢🟣"
+ , "🔴🟠🟡🟢🟣🟤"
+ ])))
+ ```
+
+ # See also
+
+ * {diagonals}
+ * {transpose}
+ }}
+
+data.List.diagonals : [[a]] -> [[a]]
+data.List.diagonals =
+ use List tail
+ go b es =
+ use List +:
+ ts = List.filterMap tail b
+ deprecated.mapOptional List.head b +: (match es with
+ [] -> transpose ts
+ e +: es -> go (e +: ts) es)
+ List.join << Optional.toList << tail << go []
+
+data.List.diagonals.doc : Doc
+data.List.diagonals.doc =
+ use List map
+ {{
+ Returns the diagonals of a {type List} of {type List}s. Considered as a 2D
+ arrangement with the first list as the top row, the second list as the second
+ row, etc., the diagonals are the lists of elements that run from bottom left
+ to top right. The first diagonal is just the first element of the first list,
+ the second diagonal is the first element of the second list and the second
+ element of the first list, the third diagonal is the first element of the
+ third list and the second element of the second list and the third element of
+ the first list, etc.
+
+ # Examples
+
+ ```
+ diagonals [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+ ```
+
+ ```
+ diagonals [[], [1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]
+ ```
+
+ ```
+ map
+ fromCharList
+ (diagonals
+ (map
+ toCharList
+ [ "🟥🟧🟨🟩🟦🟪"
+ , "🟧🟨🟩🟦🟪🟫"
+ , "🟨🟩🟦🟪🟫🔴"
+ , "🟩🟦🟪🟫🔴🟠"
+ , "🟦🟪🟫🔴🟠🟡"
+ , "🟪🟫🔴🟠🟡🟢"
+ , "🟫🔴🟠🟡🟢🟣"
+ , "🔴🟠🟡🟢🟣🟤"
+ ]))
+ ```
+
+ # See also
+
+ * {diagonal}
+ * {transpose}
+ }}
+
+data.List.distinct : [a] -> [a]
+data.List.distinct =
+ use List :+
+ go seen acc = cases
+ a +: as
+ | Set.contains a seen -> go seen acc as
+ | otherwise -> go (Set.insert a seen) (acc :+ a) as
+ [] -> acc
+ go Set.empty []
+
+data.List.distinct.doc : Doc
+data.List.distinct.doc =
+ {{
+ Returns a {type List} of the distinct elements of a {type List}, according to
+ the universal equality function {===}. The first occurrence of each element
+ is retained.
+
+ # Examples
+
+ ```
+ distinct [1, 2, 3, 2, 1]
+ ```
+
+ With emoji
+
+ ```
+ distinct [?🍓, ?🍅, ?🍓, ?🍎, ?🍅, ?🍎, ?🍅]
+ ```
+
+ # See also
+
+ * {distinctBy}
+ * {groupBy}
+ * {groupConsecutive}
+ }}
+
+test> data.List.distinct.tests.preservation = runs 1000 do
+ use List all at
+ use Nat <= ==
+ use Optional toAbort
+ l = gen.listOf gen.nat ()
+ d = distinct l
+ dr = List.range 0 (List.size d)
+ allUnique = all (i -> all (j -> i == j || at i d !== at j d) dr) dr
+ orderPreserved =
+ all
+ (i ->
+ Optional.getOrElse
+ false (toOptional! do
+ di = toAbort (at i d)
+ ix = toAbort (List.firstIndexOf di l)
+ i <= ix)) dr
+ allPresent = all (e -> List.contains e d) l
+ expect (allUnique && orderPreserved && allPresent)
+
+data.List.distinctBy : (a ->{g} b) -> [a] ->{g} [a]
+data.List.distinctBy f =
+ use List :+
+ go seen acc = cases
+ a +: as ->
+ b = f a
+ if Set.contains b seen then go seen acc as
+ else go (Set.insert b seen) (acc :+ a) as
+ [] -> acc
+ go Set.empty []
+
+data.List.distinctBy.doc : Doc
+data.List.distinctBy.doc =
+ {{
+ The expression `` distinctBy f xs `` removes duplicate elements from the
+ {type List} `xs`, but only considers any two elements to be the same if the
+ function `f` returns the same value for both. For any group of duplicate
+ elements, only the first such element in the {type List} is retained.
+
+ # Example
+
+ ```
+ xs = ["the", "cat", "is", "on", "the", "roof"]
+ distinctBy Text.size xs
+ ```
+ }}
+
+test> data.List.distinctBy.tests.preservation = runs 1000 do
+ use List all at
+ use Nat <= ==
+ use Optional toAbort
+ l = gen.listOf gen.nat ()
+ f = flip Nat.mod 10
+ d = distinctBy f l
+ dr = List.range 0 (List.size d)
+ allUnique = all (i -> all (j -> i == j || at i d !== at j d) dr) dr
+ orderPreserved =
+ all
+ (i ->
+ Optional.getOrElse
+ false (toOptional! do
+ di = toAbort (at i d)
+ ix = toAbort (List.firstIndexOf di l)
+ i <= ix)) dr
+ allPresent = all (e -> List.contains e d) l
+ expect (allUnique && orderPreserved && allPresent)
+
+test> data.List.distinctBy.tests.removesDuplicates = runs 100 do
+ xs = gen.listOf natInOrder ()
+ distinct = distinctBy id xs
+ isDistinct ys =
+ s = Heap.sort ys
+ go = cases
+ [] -> true
+ x +: xs -> List.dropWhile (y -> y === x) xs === xs && go xs
+ go s
+ expect (List.all (x -> List.contains x distinct) xs && isDistinct distinct)
+
+data.List.doc : Doc
+data.List.doc =
+ use List.docs constructing converting
+ use docs elements nonempty
+ {{
+ The {type List} type is a general-purpose data type that represents finite
+ sequences of elements. {type List} is built into Unison.
+
+ Lists are immutable (no destructive updates), finitite (no infinite lists),
+ and homogenous (every element must have the same type). {type List} is
+ parameterized on the type of elements of the lists. For example, in a type
+ expression like `List t` (or equivalently `[t]`), the type `t` is the type of
+ the elements.
+
+ {{
+ docAside
+ {{
+ The internal representation of the {type List} type is a
+ [Finger Tree](http://www.staff.city.ac.uk/~ross/papers/FingerTree.html).
+ This provides fast (constant-time) access to the first and last element, as
+ well as relatively efficient (logarithmic time) concatenation, insertion,
+ and deletion.
+ }} }}
+
+ This document provides a high-level overview of the main operations on lists,
+ organized into the following categories:
+
+ * [Constructing lists]({constructing})
+ * [Adding and removing elements]({addingAndRemoving})
+ * [Accessing and querying elements]({elements})
+ * [Combining lists]({combining})
+ * [Splitting and rearranging lists]({splittingAndRearranging})
+ * [Traversing and transforming lists]({traversing})
+ * [Nonempty lists]({nonempty})
+ * [Converting lists to other types]({converting})
+
+ There are many more operations on lists than are enumerated here. See the
+ docs for individual functions for more details.
+
+ {{ constructing }}
+
+ {{ addingAndRemoving }}
+
+ {{ elements }}
+
+ {{ combining }}
+
+ {{ splittingAndRearranging }}
+
+ {{ traversing }}
+
+ {{ nonempty }}
+
+ {{ converting }}
+ }}
+
+data.List.docs.addingAndRemoving : Doc
+data.List.docs.addingAndRemoving =
+ use List +: :+ intersperse
+ {{
+ # Adding, removing, and replacing elements of lists
+
+ {+:} adds an element to the front of a list:
+
+ ```
+ 1 +: [2, 3, 4]
+ ```
+
+ {:+} adds an element to the end of a list:
+
+ ```
+ [1, 2, 3] :+ 4
+ ```
+
+ {insertAt} inserts an element at any point in a list:
+
+ ```
+ insertAt 1 "green" ["red", "blue"]
+ ```
+
+ {intersperse} inserts an element between all the elements of a list:
+
+ ```
+ intersperse 0 [1, 2, 3]
+ ```
+
+ {deleteAt} removes a specific element from a list:
+
+ ```
+ deleteAt 2 [5, 3, 8, 9]
+ ```
+
+ {replace} replaces a specific element with another one:
+
+ ```
+ replace 1 "cheese" ["flour", "butter", "eggs"]
+ ```
+ }}
+
+data.List.docs.combining : Doc
+data.List.docs.combining =
+ use List ++ join zip
+ {{
+ # Combining lists
+
+ {++} concatenates two lists.
+
+ ```
+ [1, 2, 3] ++ [4, 5, 6]
+ ```
+
+ {join} concatenates a whole list of lists.
+
+ ```
+ join [[1, 2, 3], [], [4, 5], [6]]
+ ```
+
+ {intercalate} inserts a list between other lists.
+
+ ```
+ intercalate [10, 20] [[1, 2], [], [3]]
+ ```
+
+ {zip} makes a list of pairs from two lists, each with elements of both
+ lists occuring at the same position.
+
+ ```
+ zip ["red", "green", "blue"] [5, 8, 2]
+ ```
+ }}
+
+data.List.docs.constructing : Doc
+data.List.docs.constructing =
+ use List empty fill replicate singleton
+ use Nat *
+ {{
+ # Constructing lists
+
+ A __list literal__ is an expression of the form {{
+ docExample 3 do a b c -> [a, b, c] }} (in that case the list contains three
+ elements, `a`, `b`, and `c`).
+
+ {empty} is the empty list (also written ``[]``):
+
+ ```
+ empty
+ ```
+
+ {singleton} creates a list with one element:
+
+ ```
+ singleton 4
+ ```
+
+ `` fill n expr `` creates a list of `n` copies of `expr`:
+
+ ```
+ fill 3 "boing"
+ ```
+
+ {fill'} is like {fill} but allows the value to be computed lazily:
+
+ ```
+ lcg 1 do fill' 8 do Random.natIn 1 7
+ ```
+
+ `` initialize n f `` creates a list of length `n`, filled with the values
+ of `f i` as `i` runs from `0` up to `n`:
+
+ ```
+ initialize 4 (x -> x * 2)
+ ```
+
+ `` replicate n op `` performs the (possibly effectful) computation `op` `n`
+ times and collects the results in a list:
+
+ ```
+ deprecated.sample 4 do replicate 3 natInOrder
+ ```
+
+ {List.range} and {List.rangeClosed} create lists of all natural numbers (of
+ type {type Nat}) in the specified range:
+
+ ```
+ List.range 1 4
+ ```
+
+ ```
+ List.rangeClosed 1 4
+ ```
+
+ {Int.range} and {Int.rangeClosed} create lists of all integers (of type
+ {type Int}) in the specified range:
+
+ ```
+ Int.range -3 +3
+ ```
+
+ ```
+ Int.rangeClosed -3 +3
+ ```
+ }}
+
+data.List.docs.converting : Doc
+data.List.docs.converting =
+ use List toMap toSet
+ use fromList impl
+ {{
+ # Conversions to/from lists
+
+ {mayNonempty} attempts to convert a list to a {type List.Nonempty}:
+
+ ```
+ mayNonempty [1, 2, 3]
+ ```
+
+ ```
+ mayNonempty []
+ ```
+
+ {toSet} constructs a {type Set} from a {type List}, and {Set.toList}
+ converts the other way:
+
+ ```
+ Set.toList (toSet [5, 8, 8, 5, 5, 2, 2])
+ ```
+
+ {toMap} converts a list of key-value pairs to a {type Map}, and
+ {Map.toList} converts the other way:
+
+ ```
+ Map.toList (toMap [(5, 8), (8, 5), (5, 2), (2, 0)])
+ ```
+
+ {fromCharList} converts a list of characters to {type Text}, and
+ {toCharList} converts the other way:
+
+ ```
+ fromCharList [?a, ?b, ?c]
+ ```
+
+ ```
+ toCharList "abc"
+ ```
+
+ {impl} converts a list of {type Nat} numbers to {type Bytes}, and
+ {Bytes.toList} converts the other way:
+
+ ```
+ impl (List.range 0 4)
+ ```
+
+ ```
+ Bytes.toList 0xsdeadbeef
+ ```
+
+ {Stream.toList} enumerates all elements on a {type Stream}, as a list:
+
+ ```
+ Stream.toList do
+ emit 1
+ emit 2
+ emit 3
+ ```
+ }}
+
+data.List.docs.elements : Doc
+data.List.docs.elements =
+ use List all any at contains drop dropWhile filter head init last maximum minimum size tail take takeWhile
+ use Nat isEven
+ {{
+ # Accessing and querying elements
+
+ {size} gets the number of elements in the list.
+
+ ```
+ size [5, 8, 2]
+ ```
+
+ {head} gets the first element of the list.
+
+ ```
+ head [1, 2, 3]
+ ```
+
+ {last} gets the last element.
+
+ ```
+ last [1, 2, 3]
+ ```
+
+ {tail} gets all but the first element.
+
+ ```
+ tail [1, 2, 3]
+ ```
+
+ {init} gets all but the last element.
+
+ ```
+ init [1, 2, 3]
+ ```
+
+ {at} gets the element at a given position in the list.
+
+ ```
+ at 1 ["a", "b", "c"]
+ ```
+
+ {take} gets a specified number of elements from the front of the list.
+
+ ```
+ take 2 ["a", "b", "c"]
+ ```
+
+ {drop} removes a specified number of elements from the front of the list.
+
+ ```
+ drop 1 ["a", "b", "c"]
+ ```
+
+ {takeWhile} gets elements from the front of the list until a given function
+ returns `false`.
+
+ ```
+ takeWhile isEven [2, 4, 6, 7, 8, 9]
+ ```
+
+ {dropWhile} removes elements from the front of the list until a given
+ function returns `false`.
+
+ ```
+ dropWhile isEven [2, 4, 6, 7, 8, 9]
+ ```
+
+ {filter} finds all the elements that satisfy some {type Boolean} function.
+
+ ```
+ filter isEven [2, 4, 6, 7, 8, 9]
+ ```
+
+ {any} checks if any elements satisfy some {type Boolean} function.
+
+ ```
+ any isEven [2, 4, 6, 7, 8, 9]
+ ```
+
+ {all} checks if all elements satisfy some {type Boolean} function.
+
+ ```
+ all isEven [2, 4, 6, 7, 8, 9]
+ ```
+
+ {{ docLink (docEmbedTermLink do contains) }} checks if an element is in the
+ list.
+
+ ```
+ contains 2 [1, 2, 3]
+ ```
+
+ {maximum} finds the largest element in the list.
+
+ ```
+ maximum [5, 8, 2]
+ ```
+
+ {minimum} finds the smallest element in the list.
+
+ ```
+ minimum [5, 8, 2]
+ ```
+ }}
+
+data.List.docs.nonempty : Doc
+data.List.docs.nonempty =
+ {{
+ # Nonempty lists
+
+ The {type List} type allows lists to potentially be empty. For lists that
+ should not be allowed to be empty, use the type {type List.Nonempty}. A
+ number of operations on {type List} produce values of type
+ {type List.Nonempty} to ensure that the result has at least one element.
+ For example:
+
+ @signature{List.scanLeft} @signature{List.scanRight}
+ @signature{nonEmptySubsequences} @signature{groupBy}
+ @signature{groupConsecutive} @signature{groupMap}
+ @signature{groupSublistsBy}
+ }}
+
+data.List.docs.splittingAndRearranging : Doc
+data.List.docs.splittingAndRearranging =
+ use Heap sort
+ use List reverse slice span split splitAt tails
+ use Nat == isEven
+ {{
+ # Splitting, slicing, and rearranging lists
+
+ {reverse} flips the order of the elements of a list:
+
+ ```
+ reverse [5, 8, 2]
+ ```
+
+ {sort} puts the elements of a list in ascending order:
+
+ ```
+ sort [5, 8, 2]
+ ```
+
+ {sortDescending} puts the elements of a list in descending order:
+
+ ```
+ sortDescending [5, 8, 2]
+ ```
+
+ {sortBy} sorts a list on some specified property of the elements:
+
+ ```
+ sortBy Text.size ["four", "six", "three"]
+ ```
+
+ {split} breaks a list into sub-lists on a delimiter matching a condition:
+
+ ```
+ split (x -> x == 0) [1, 2, 3, 0, 4, 0, 2, 1, 0, 0, 1]
+ ```
+
+ {splitAt} breaks a list into two lists, at a specific index:
+
+ ```
+ splitAt 2 [1, 2, 3, 4, 5]
+ ```
+
+ {span} breaks a list into two lists, at the first element for which a
+ condition returns ``false``.
+
+ ```
+ span (x -> Universal.lt x 3) [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ span isEven [1, 2, 3]
+ ```
+
+ {halve} splits a list into two lists of roughly equal size:
+
+ ```
+ halve [1, 2, 3, 4]
+ ```
+
+ ```
+ halve [1, 2, 3]
+ ```
+
+ {distinct} and {distinctBy} remove any duplicate elements from the list.
+
+ ```
+ distinct [5, 8, 8, 5, 5, 2, 2]
+ ```
+
+ ```
+ distinctBy isEven [5, 8, 8, 5, 5, 2, 2]
+ ```
+
+ {slice} extracts a sub-list from a list:
+
+ ```
+ slice 2 5 [5, 8, 8, 5, 5, 2, 2]
+ ```
+
+ {powerslice} returns all contiguous sub-lists of a list:
+
+ ```
+ powerslice [1, 2, 3]
+ ```
+
+ {tails} returns all suffixes of a list, longest first:
+
+ ```
+ tails [1, 2, 3]
+ ```
+
+ {inits} returns all prefixes of a list, shortest first:
+
+ ```
+ inits [1, 2, 3]
+ ```
+ }}
+
+data.List.docs.traversing : Doc
+data.List.docs.traversing =
+ use List flatMap foldBalanced foldLeft foldRight foreach map scanLeft scanRight
+ use Nat +
+ use Text ++ size
+ {{
+ # Traversing and transforming lists
+
+ {map} applies a function to every element of a list, collecting the
+ results:
+
+ ```
+ map size ["one", "two", "three"]
+ ```
+
+ {foreach} applies a function to every element of a list only for its
+ effects, ignoring the results:
+
+ ```
+ Stream.toList do ["one", "two", "three"] |> foreach (x -> emit (size x))
+ ```
+
+ {flatMap} applies a list-valued function to every element of a list,
+ collecting the results in one list:
+
+ ```
+ flatMap (n -> List.fill n "boing") [0, 1, 2]
+ ```
+
+ {mapIndexed} applies a function to every element of a list together with
+ the index of the element in the list:
+
+ ```
+ mapIndexed (n x -> (n, x)) ["rip", "rap", "rup"]
+ ```
+
+ {foldLeft} iterates over a list from left to right, accumulating elements
+ into a result using a given function:
+
+ ```
+ foldLeft (++) "It's " ["Super", "Duper", "Awesome"]
+ ```
+
+ {foldRight} iterates over a list from right to left, accumulating elements
+ into a result using the given function:
+
+ ```
+ foldRight (++) " Cool" ["Really", "Rather", "Super"]
+ ```
+
+ {foldBalanced} applies a function to every element of a list and then
+ combines the results using a binary function:
+
+ ```
+ foldBalanced size (+) 0 ["abc", "def", "ghi"]
+ ```
+
+ {scanLeft} iterates over a list just like {foldLeft}, but returning all
+ intermediate results:
+
+ ```
+ scanLeft (+) 0 [1, 2, 3, 4, 5]
+ ```
+
+ {scanRight} iterates over a list just like {foldRight}, but returning all
+ intermediate results:
+
+ ```
+ scanRight (+) 0 [1, 2, 3, 4, 5]
+ ```
+ }}
+
+-- builtin data.List.drop : Nat -> [a] -> [a]
+
+data.List.drop.doc : Doc
+data.List.drop.doc =
+ use List drop
+ {{
+ Drops the first `n` elements of a {type List}.
+
+ # Examples
+
+ ```
+ drop 2 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ drop 0 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ drop 5 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ drop 6 [1, 2, 3, 4, 5]
+ ```
+ }}
+
+data.List.dropLast : [a] -> [a]
+data.List.dropLast = cases
+ [] -> []
+ xs :+ _ -> xs
+
+data.List.dropLast.doc : Doc
+data.List.dropLast.doc =
+ {{
+ `` dropLast xs `` drops the last element of the list `xs`. If the list is
+ empty it returns an empty list.
+
+ ```
+ dropLast [1, 2, 3]
+ ```
+
+ ```
+ dropLast []
+ ```
+ }}
+
+data.List.dropRight : Nat -> [a] -> [a]
+data.List.dropRight n xs =
+ use Nat -
+ List.take (List.size xs - n) xs
+
+data.List.dropRight.doc : Doc
+data.List.dropRight.doc =
+ {{
+ `` dropRight n xs `` removes the last (rightmost) `n` elements of `xs`, or if
+ `n` is greater than or equal to ``List.size xs``, returns the empty
+ {type List}.
+
+ ```
+ dropRight 3 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ dropRight 0 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ dropRight 11 [1, 2, 3, 4, 5]
+ ```
+ }}
+
+data.List.dropRightWhile : (a ->{g} Boolean) -> [a] ->{g} [a]
+data.List.dropRightWhile f xs = match xs with
+ [] -> []
+ init :+ last -> if f last then data.List.dropRightWhile f init else xs
+
+data.List.dropRightWhile.doc : Doc
+data.List.dropRightWhile.doc =
+ use List dropRightWhile
+ use Nat >
+ {{
+ `` dropRightWhile p xs `` removes elements from the end (i.e. from the right)
+ of the {type List} `xs` as long as the function `p` returns ``true``. It
+ removes the longest suffix whose elements all result in `` true `` when `p`
+ is applied to them.
+
+ ```
+ dropRightWhile (x -> x > 10) [1, 2, 3, 4, 500, 600, 700]
+ ```
+
+ Here, the last element doesn't match the predicate, so the list is left
+ alone:
+
+ ```
+ dropRightWhile (x -> x > 10) [1, 2, 3, 4, 500, 600, 700, 0]
+ ```
+ }}
+
+test> data.List.dropRightWhile.tests = test.verify do
+ use List reverse
+ use Nat <
+ use Random natIn
+ Each.repeat 50
+ x = natIn 0 5
+ xs = List.replicate (natIn 0 20) do natIn 0 10
+ xs1 = List.dropRightWhile (n -> n < x) xs
+ xs2 = List.dropWhile (n -> n < x) (reverse xs) |> reverse
+ ensureEqual xs1 xs2
+
+test> data.List.dropRightWhile.tests.all =
+ use Nat <
+ check (List.dropRightWhile (flip (<) 9) [1, 2, 3] === [])
+
+test> data.List.dropRightWhile.tests.last =
+ use Nat <
+ check
+ (List.dropRightWhile (flip (<) 3) [1, 2, 3, 4, 5, 1, 2] === [1, 2, 3, 4, 5])
+
+test> data.List.dropRightWhile.tests.none =
+ use Nat <
+ check (List.dropRightWhile (flip (<) 0) [1, 2, 3] === [1, 2, 3])
+
+data.List.dropUntil : (a ->{e} Boolean) -> [a] ->{e} [a]
+data.List.dropUntil f xs =
+ go = cases
+ x +: xs
+ | f x -> xs
+ | otherwise -> go xs
+ _ -> []
+ go xs
+
+data.List.dropUntil.doc : Doc
+data.List.dropUntil.doc =
+ use List dropUntil
+ {{
+ Drops elements from a list until a predicate is satisfied. The element that
+ satisfies the predicate is dropped as well.
+
+ # Examples
+
+ ```
+ dropUntil Nat.isEven [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ dropUntil Nat.isOdd [1, 2, 3, 4, 5]
+ ```
+ }}
+
+data.List.dropWhile : (a ->{e} Boolean) -> [a] ->{e} [a]
+data.List.dropWhile f = cases
+ [] -> []
+ x +: xs' | f x -> data.List.dropWhile f xs'
+ xs -> xs
+
+data.List.dropWhile.doc : Doc
+data.List.dropWhile.doc =
+ use List dropWhile
+ use Nat <=
+ {{
+ `` dropWhile p xs `` constructs a new {type List} from `xs`, removing the
+ longest prefix whose elements all match the predicate `p`. The result will be
+ a suffix of the input list, where the first element in the result is the
+ first element in the input for which the predicate is false.
+
+ # Example
+
+ ```
+ dropWhile (x -> x <= 3) [1, 2, 3, 4, 5]
+ ```
+ }}
+
+data.List.dropWhile.examples.ex1 : [Nat]
+data.List.dropWhile.examples.ex1 =
+ List.dropWhile (x -> Universal.lt x 3) [1, 2, 3, 4, 5]
+
+test> data.List.dropWhile.tests.all =
+ check (List.dropWhile (flip Universal.lt 9) [1, 2, 3] === [])
+
+test> data.List.dropWhile.tests.middle =
+ check
+ (List.dropWhile (flip Universal.lt 3) [1, 2, 3, 4, 5, 1, 2]
+ === [3, 4, 5, 1, 2])
+
+test> data.List.dropWhile.tests.none =
+ check (List.dropWhile (flip Universal.lt 0) [1, 2, 3] === [1, 2, 3])
+
+data.List.empty : [a]
+data.List.empty = []
+
+data.List.empty.doc : Doc
+data.List.empty.doc = {{ The empty {type List}, or ``[]``. }}
+
+data.List.equals : (a ->{f} b ->{g} Boolean) -> [a] -> [b] ->{f, g} Boolean
+data.List.equals eq xs ys =
+ use List size
+ size xs === size ys && List.all id (List.zipWith eq xs ys)
+
+data.List.equals.doc : Doc
+data.List.equals.doc =
+ use List equals
+ use Nat ==
+ {{
+ Compares two lists for equality. The given function is used to compare the
+ elements.
+
+ # Examples
+
+ ```
+ equals (==) [1, 2, 3] [1, 2, 3]
+ ```
+
+ ```
+ equals (==) [1, 2, 3] [1, 2, 4]
+ ```
+
+ ```
+ equals (==) [1, 2, 3] [1, 2]
+ ```
+
+ ```
+ equals (==) [1, 2, 3] [1, 2, 3, 4]
+ ```
+ }}
+
+data.List.every : Nat -> [a] -> [a]
+data.List.every n as = skip n (List.drop n as)
+
+data.List.every.doc : Doc
+data.List.every.doc =
+ {{
+ Returns every `n`th element of a {type List} (0-based indexing).
+
+ If `n` is 0, returns the original {type List}.
+
+ # Examples
+
+ ```
+ every 2 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ every 1 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ every 0 [1, 2, 3, 4, 5]
+ ```
+ }}
+
+data.List.fill : Nat -> a -> [a]
+data.List.fill length value = initialize length (const value)
+
+data.List.fill.doc : Doc
+data.List.fill.doc =
+ use List fill
+ {{
+ `` fill length value `` creates a {type List} of length `length` where every
+ element in the {type List} is the given `value`.
+
+ # Example
+
+ ```
+ fill 4 "a"
+ ```
+ }}
+
+data.List.fill.examples.ex1 : [Text]
+data.List.fill.examples.ex1 = List.fill 4 "a"
+
+test> data.List.fill.tests.ex1 =
+ actual = List.fill 4 "a"
+ expected = ["a", "a", "a", "a"]
+ check (actual === expected)
+
+data.List.fill' : Nat -> '{g} a ->{g} [a]
+data.List.fill' length value = initialize length (_ -> value())
+
+data.List.fill'.doc : Doc
+data.List.fill'.doc =
+ {{
+ `` fill' length value `` evaluates `value` `length` times and returns a list
+ of the results. Because evaluating `value` can perform an effect, this can
+ result in varying elements in the output.
+
+ # Examples:
+
+ If `value` doesn't perform any effects, then the output will be a list of
+ the same value `length` times.
+
+ ```
+ fill' 4 do "hi"
+ ```
+
+ If `value` performs an effect, then the elements in the output won't
+ necessarily be the same.
+
+ ```
+ (do fill' 3 Random.int) |> lcg 42
+ ```
+ }}
+
+test> data.List.fill'.tests = test.verify do
+ ensureEqual (fill' 3 do 42) [42, 42, 42]
+ Scope.run do
+ n = Scope.ref 0
+ incrementAndGet = do
+ Ref.modify n Nat.increment
+ Ref.read n
+ ensureEqual (fill' 3 incrementAndGet) [1, 2, 3]
+
+data.List.filter : (a ->{𝕖} Boolean) -> [a] ->{𝕖} [a]
+data.List.filter pred as =
+ use List :+
+ List.foldLeft (acc a -> (if pred a then acc :+ a else acc)) [] as
+
+data.List.filter.doc : Doc
+data.List.filter.doc =
+ use List filter
+ {{
+ Given a predicate and a list of elements, {filter} constructs a list
+ containing only the elements that satisfy the predicate.
+
+ # Examples
+
+ ```
+ filter Nat.isEven []
+ ```
+
+ ```
+ filter Nat.isOdd [2, 3, 4, 5]
+ ```
+ }}
+
+test> data.List.filter.tests.empty =
+ check (List.filter (x -> Universal.gt x 3) [] === [])
+
+test> data.List.filter.tests.negative =
+ check (List.filter (x -> Universal.gt x 3) [0, 1, 2] === [])
+
+test> data.List.filter.tests.positive =
+ check (List.filter (x -> Universal.gt x 3) [3, 4, 5] === [4, 5])
+
+data.List.filterMap : (a ->{e} Optional b) -> [a] ->{e} [b]
+data.List.filterMap f as =
+ use List :+
+ go acc = cases
+ a +: as ->
+ b = f a
+ match b with
+ Some b -> go (acc :+ b) as
+ None -> go acc as
+ [] -> acc
+ go [] as
+
+data.List.filterMap.doc : Doc
+data.List.filterMap.doc =
+ use List filterMap
+ {{
+ {filterMap} maps an {type Optional}-valued function over a {type List}, then
+ accumulates and returns all {Some} values.
+
+ # Example
+
+ Use {filterMap} to look up multiple values in a map at once:
+
+ ```
+ dict = List.toMap [("apple", ?🍎), ("pear", ?🍐), ("orange", ?🍊)]
+ filterMap (k -> Map.get k dict) ["apple", "orange", "banana"]
+ ```
+ }}
+
+data.List.filterMap.examples.ex1 : [Nat]
+data.List.filterMap.examples.ex1 =
+ dict = List.toMap [("Foo", 42), ("Bar", 100), ("Baz", 40)]
+ List.filterMap (k -> Map.get k dict) ["Foo", "Bar", "Qux"]
+
+test> data.List.filterMap.tests.identity = runs 1000 do
+ l = gen.listOf gen.nat ()
+ expect (List.filterMap Some l === l)
+
+test> data.List.filterMap.tests.mapsAndFilters = runs 1000 do
+ use Nat +
+ l = gen.listOf gen.nat ()
+ f n = if Nat.isEven n then Some (n + 1) else None
+ expect (List.filterMap f l === List.somes (List.map f l))
+
+data.List.find : (a ->{g} Boolean) -> [a] ->{g} Optional a
+data.List.find p = cases
+ [] -> None
+ x +: rest -> if p x then Some x else data.List.find p rest
+
+data.List.find.doc : Doc
+data.List.find.doc =
+ {{
+ Look for a item that matches the predicate, returning the value if found, or
+ None otherwise
+ }}
+
+test> data.List.find.tests.ex1 =
+ check match ((List.find (const true) []) : Optional Nat) with
+ None -> true
+ _ -> false
+
+test> data.List.find.tests.ex2 =
+ check match List.find (n -> n Nat.== 2) [1, 2, 3] with
+ Some 2 -> true
+ _ -> false
+
+data.List.find! : (a ->{g} Boolean) -> [a] ->{g, Abort} a
+data.List.find! p list = Optional.toAbort (List.find p list)
+
+data.List.find!.doc : Doc
+data.List.find!.doc =
+ {{
+ Look for a item that matches the predicate, returning the value if found, or
+ aborting otherwise
+ }}
+
+data.List.findFirstIndex : (a ->{e} Boolean) -> [a] ->{e} Optional Nat
+data.List.findFirstIndex f xs =
+ use Nat +
+ go i = cases
+ x +: xs
+ | f x -> Some i
+ | otherwise -> go (i + 1) xs
+ _ -> None
+ go 0 xs
+
+data.List.findLastIndex : (a ->{e} Boolean) -> [a] ->{e} Optional Nat
+data.List.findLastIndex f xs =
+ use Nat -
+ go i = cases
+ xs :+ x
+ | f x -> Some i
+ | otherwise -> go (i - 1) xs
+ _ -> None
+ go (List.size xs - 1) xs
+
+data.List.findMap : (a ->{g} Optional b) -> [a] ->{g} Optional b
+data.List.findMap f = cases
+ h +: tail ->
+ match f h with
+ Some b -> Some b
+ None -> data.List.findMap f tail
+ [] -> None
+
+data.List.findMap.doc : Doc
+data.List.findMap.doc =
+ use Nat fromText
+ {{
+ `` findMap f xs `` calls the function `f` for every element in the list until
+ `f` returns a {Some}.
+
+ # Examples
+
+ ```
+ findMap fromText ["alice", "bob", "32", "64"]
+ ```
+
+ ```
+ findMap fromText ["alice", "bob", "juan"]
+ ```
+
+ # See also
+
+ * @inlineSignature{List.filterMap}
+ * @inlineSignature{List.find}
+ }}
+
+test> data.List.findMap.tests.ex1 =
+ check (findMap Nat.fromText ["alice", "bob", "32", "64"] === Some 32)
+
+test> data.List.findMap.tests.ex2 =
+ check (findMap Nat.fromText ["alice", "bob"] === None)
+
+data.List.firstIndexOf : a -> [a] -> Optional Nat
+data.List.firstIndexOf a as =
+ use Nat +
+ go : Nat -> [a] -> Optional Nat
+ go index = cases
+ [] -> None
+ head +: tail | head === a -> Some index
+ _ +: tail -> go (index + 1) tail
+ go 0 as
+
+data.List.firstIndexOf.doc : Doc
+data.List.firstIndexOf.doc =
+ use List firstIndexOf
+ {{
+ `` firstIndexOf x xs `` returns the first index in the {type List} `xs` where
+ the element `x` occurs, using a head-to-tail scan of the {type List}, or
+ {None} if `x` is not found.
+
+ # Examples
+
+ ```
+ firstIndexOf 0 []
+ ```
+
+ ```
+ firstIndexOf 0 [1, 2, 3]
+ ```
+
+ ```
+ firstIndexOf 2 [1, 2, 3]
+ ```
+ }}
+
+test> data.List.firstIndexOf.tests.negative1 =
+ check (List.firstIndexOf 0 [] === None)
+
+test> data.List.firstIndexOf.tests.negative2 =
+ check (List.firstIndexOf 0 [1, 2, 3] === None)
+
+test> data.List.firstIndexOf.tests.negative3 =
+ check (List.firstIndexOf 1 [0, 0, 0] === None)
+
+test> data.List.firstIndexOf.tests.positive1 =
+ check (List.firstIndexOf 1 [1, 2, 3] === Some 0)
+
+test> data.List.firstIndexOf.tests.positive2 =
+ check (List.firstIndexOf 3 [3, 1, 2, 3] === Some 0)
+
+test> data.List.firstIndexOf.tests.positive3 =
+ check (List.firstIndexOf 1 [3, 1, 2, 3] === Some 1)
+
+data.List.flatMap : (a ->{e} [b]) -> [a] ->{e} [b]
+data.List.flatMap f =
+ use List ++
+ List.foldLeft (acc e -> acc ++ f e) []
+
+data.List.flatMap.doc : Doc
+data.List.flatMap.doc =
+ use List flatMap
+ use Nat *
+ {{
+ `` flatMap `` maps over a {type List} and concatenates the results together.
+
+ # Example
+
+ ```
+ flatMap (x -> [x, x * x]) [1, 2, 3, 4]
+ ```
+ }}
+
+data.List.flatMap.examples.ex1 : [Nat]
+data.List.flatMap.examples.ex1 =
+ use Nat *
+ List.flatMap (x -> [x, x * x]) [1, 2, 3, 4]
+
+data.List.flatMap.examples.ex2 : [Nat]
+data.List.flatMap.examples.ex2 =
+ xs = [(1, 2), (2, 3), (3, 4)]
+ List.flatMap
+ (cases (amount, element) -> List.map (do element) (List.range 0 amount)) xs
+
+test> data.List.flatMap.tests.associative = runs 100 do
+ use Int * +
+ use List flatMap
+ m = gen.listOf gen.int ()
+ f x = [x + +1, x + +2]
+ g x = [x * +2, x * +3]
+ expect (flatMap g (flatMap f m) === flatMap (x -> flatMap g (f x)) m)
+
+test> data.List.flatMap.tests.flatMapIdentityIsJoin = runs 100 do
+ use gen listOf
+ xs = listOf (listOf natInOrder) ()
+ expect (List.join xs === List.flatMap id xs)
+
+test> data.List.flatMap.tests.identity = runs 100 do
+ x = gen.listOf gen.int ()
+ expect (List.flatMap (x -> [x]) x === x)
+
+data.List.flatMapRight : (a ->{e} [b]) -> [a] ->{e} [b]
+data.List.flatMapRight f =
+ use List ++
+ List.foldRight (e acc -> f e ++ acc) []
+
+data.List.flatMapRight.doc : Doc
+data.List.flatMapRight.doc =
+ use List flatMapRight
+ use Nat *
+ {{
+ `` flatMapRight `` maps over a {type List} and concatenates the results
+ together, working right to left.
+
+ # Examples
+
+ ```
+ flatMapRight (x -> [x, x * x]) [1, 2, 3, 4]
+ ```
+
+ The direction of traversal matters only for effects. This example emits the
+ elements of a {type List} onto a {type Stream}, and the elements are
+ emitted in reverse order.
+
+ ```
+ Stream.toList do
+ flatMapRight
+ (x -> let
+ emit x
+ []) [1, 2, 3, 4, 5]
+ ```
+ }}
+
+data.List.flatMapRight.examples.ex1 : [Nat]
+data.List.flatMapRight.examples.ex1 =
+ use Nat *
+ List.flatMapRight (x -> [x, x * x]) [1, 2, 3, 4]
+
+data.List.flatMapRight.examples.ex2 : [Nat]
+data.List.flatMapRight.examples.ex2 =
+ xs = [(1, 2), (2, 3), (3, 4)]
+ List.flatMapRight
+ (cases (amount, element) -> List.map (do element) (List.range 0 amount)) xs
+
+test> data.List.flatMapRight.tests.flatMapIdentityIsJoin = runs 100 do
+ use gen listOf
+ xs = listOf (listOf natInOrder) ()
+ expect (List.join xs === List.flatMapRight id xs)
+
+data.List.foldBalanced : (a ->{e} b) -> (b ->{e} b ->{e} b) -> b -> [a] ->{e} b
+data.List.foldBalanced f op z as =
+ use List size
+ use data.List foldBalanced
+ if size as === 0 then z
+ else
+ if size as === 1 then f (List.unsafeAt 0 as)
+ else
+ (left, right) = halve as
+ op (foldBalanced f op z left) (foldBalanced f op z right)
+
+data.List.foldBalanced.doc : Doc
+data.List.foldBalanced.doc =
+ use List foldBalanced range
+ use Nat +
+ use Text ++
+ {{
+ Folds a {type List} in a balanced manner by recursively folding the first
+ half of the list, then the second half, and finally combining the results.
+ Otherwise works like {foldBalanced}.
+
+ # Example
+
+ ```
+ foldBalanced id (x y -> x + y) 0 (range 0 10)
+ ```
+
+ ```
+ pair t1 t2 = "(" ++ t1 ++ ", " ++ t2 ++ ")"
+ foldBalanced Nat.toText pair "" (range 0 10)
+ ```
+ }}
+
+data.List.foldDelimited :
+ (b ->{g2} b ->{g1} b) -> (a ->{g} b) -> b -> b -> b -> [a] ->{g, g1, g2} b
+data.List.foldDelimited combine map prefix delimiter suffix input =
+ go : b -> [a] -> b
+ go acc = cases
+ [] -> acc
+ l +: [] -> combine acc (map l)
+ h +: t -> go (combine (combine acc (map h)) delimiter) t
+ combine (go prefix input) suffix
+
+data.List.foldDelimited.doc : Doc
+data.List.foldDelimited.doc =
+ use Text ++
+ {{
+ Folds a list, inserting a delimiter between each element, and adding a prefix
+ and suffix.
+
+ Arguments:
+
+ 1. `combine` - a function that combines the prefix, delimiter, and suffix,
+ and each mapped element.
+ 2. `map` - a function that maps each element of the list to a value of the
+ result type.
+ 3. `prefix` - the prefix to add to the result.
+ 4. `delimiter` - the delimiter to add between each element.
+ 5. `suffix` - the suffix to add to the result.
+ 6. `input` - the list to fold.
+
+ # Example
+
+ This example folds a list of numbers into a {type Text}, with a prefix of
+ "(" and a suffix of ")" and a delimiter of ", ":
+
+ ```
+ foldDelimited (++) Nat.toText "(" ", " ")" [1, 2, 3]
+ ```
+
+ See also: {List.foldBalanced}.
+ }}
+
+data.List.foldLeft : (b ->{𝕖} a ->{𝕖} b) -> b -> [a] ->{𝕖} b
+data.List.foldLeft f b =
+ go b = cases
+ a +: as -> go (f b a) as
+ [] -> b
+ go b
+
+data.List.foldLeft.doc : Doc
+data.List.foldLeft.doc =
+ use Text ++
+ {{
+ Fold the elements in the list, associating to the left, using the given
+ binary operator and initial value.
+
+ # Example
+
+ ```
+ List.foldLeft (++) "x" ["a", "b", "c"]
+ ```
+ }}
+
+test> data.List.foldLeft.tests.dual = runs 1000 do
+ use gen boolean
+ l = gen.listOf boolean ()
+ f = logic()
+ z = boolean()
+ expect (List.foldLeft f z l === List.foldRight (flip f) z (List.reverse l))
+
+test> data.List.foldLeft.tests.endomorphic = runs 1000 do
+ use List foldLeft
+ use gen boolean
+ l = gen.listOf boolean ()
+ f = logic()
+ z = boolean()
+ expect (foldLeft f z l === (List.map (flip f) l |> foldLeft (>>) id <| z))
+
+test> data.List.foldLeft.tests.length = runs 1000 do
+ l = gen.listOf gen.boolean ()
+ expect (List.foldLeft (x y -> Nat.increment x) 0 l === List.size l)
+
+data.List.foldMap.doc : Doc
+data.List.foldMap.doc =
+ use List foldMap
+ use Nat * +
+ {{
+ `` foldMap f op z xs `` transforms every value of a list `xs` with a unary
+ function `f`, then applies a binary operator `op` to the resulting values.
+ The initial value `z` is used for the empty list.
+
+ The operation `op` is assumed to be associative, and the order in which the
+ values are transformed and combined is unspecified.
+
+ # Examples
+
+ ```
+ foldMap (x -> x * 2) (+) 0 [1, 2, 3]
+ ```
+
+ ```
+ foldMap (x -> [x]) (flip (List.++)) [] [1, 2, 3]
+ ```
+
+ If we use a non-associative operation, we can reveal the order in which the
+ values are actually combined:
+
+ ```
+ foldMap
+ (x -> Nat.toText x)
+ (a b -> "(" Text.++ a Text.++ " + " Text.++ b Text.++ ")")
+ ""
+ (List.range 0 10)
+ ```
+ }}
+
+data.List.foldRight : (a ->{e} b ->{e} b) -> b -> [a] ->{e} b
+data.List.foldRight f b =
+ go b = cases
+ as :+ a -> go (f a b) as
+ [] -> b
+ go b
+
+data.List.foldRight.doc : Doc
+data.List.foldRight.doc =
+ use Text ++
+ {{
+ Fold the elements in the list, associating to the right, using the given
+ binary operator.
+
+ # Example
+
+ ```
+ List.foldRight (++) "x" ["a", "b", "c"]
+ ```
+ }}
+
+test> data.List.foldRight.tests.dual = runs 1000 do
+ use gen boolean
+ l = gen.listOf boolean ()
+ f = logic()
+ z = boolean()
+ expect (List.foldRight f z l === List.foldLeft (flip f) z (List.reverse l))
+
+test> data.List.foldRight.tests.endomorphic = runs 1000 do
+ use List foldRight
+ use gen boolean
+ l = gen.listOf boolean ()
+ f = logic()
+ z = boolean()
+ expect (foldRight f z l === (List.map f l |> foldRight (<<) id <| z))
+
+test> data.List.foldRight.tests.homomorphism = runs 100 do
+ use List +:
+ xs = gen.listOf natInOrder ()
+ expect (List.foldRight (+:) [] xs === xs)
+
+test> data.List.foldRight.tests.length = runs 1000 do
+ l = gen.listOf gen.boolean ()
+ expect (List.foldRight (const Nat.increment) 0 l === List.size l)
+
+data.List.foreach : (a ->{g} ()) -> [a] ->{g} ()
+data.List.foreach f = cases
+ [] -> ()
+ h +: t ->
+ ignore (f h)
+ data.List.foreach f t
+
+data.List.foreach.deprecated : (a ->{g} b) -> [a] ->{g} ()
+data.List.foreach.deprecated f = cases
+ [] -> ()
+ h +: t ->
+ ignore (f h)
+ data.List.foreach.deprecated f t
+
+data.List.foreach.deprecated.doc : Doc
+data.List.foreach.deprecated.doc =
+ use Text ++
+ use foreach deprecated
+ {{
+ `` deprecated f xs `` traverses the {type List} `xs` and applies the function
+ `f` to each element in turn, ignoring the results. Returns {Unit} as the
+ function is applied only for its effects.
+
+ # Examples
+
+ ```
+ Stream.toList do deprecated emit ["duck", "rabbit", "beaver"]
+ ```
+
+ ```
+ Scope.run do
+ r = Scope.ref ""
+ deprecated (x -> Ref.modify r (v -> v ++ x)) ["cat", "hat", "saw"]
+ Ref.read r
+ ```
+ }}
+
+test> data.List.foreach.deprecated.tests = test.verify do
+ n = Each.range 0 20
+ ns = List.replicate n do Random.natIn 0 1000
+ ensure ((Stream.toList do foreach.deprecated (n -> emit n) ns) === ns)
+
+data.List.foreach.doc : Doc
+data.List.foreach.doc =
+ use List foreach
+ use Text ++
+ {{
+ {{ docExample 2 do f xs -> foreach f xs }} traverses the {type List} `xs` and
+ applies the function `f` to each element in turn, ignoring the results.
+ Returns {Unit} as the function is applied only for its effects.
+
+ It's equivalent to ``ignore (List.map f xs)``, but does not create an
+ intermediate list.
+
+ # Examples
+
+ ```
+ Stream.toList do foreach emit ["duck", "rabbit", "beaver"]
+ ```
+
+ ```
+ Scope.run do
+ r = Scope.ref ""
+ ["cat", "hat", "saw"] |> foreach (x -> Ref.modify r (v -> v ++ x))
+ Ref.read r
+ ```
+ }}
+
+data.List.foreach.flipped : [a] -> (a ->{g} ()) ->{g} ()
+data.List.foreach.flipped list f = match list with
+ [] -> ()
+ x +: xs ->
+ ignore (f x)
+ data.List.foreach.flipped xs f
+
+data.List.foreach.flipped.deprecated : [a] -> (a ->{g} b) ->{g} ()
+data.List.foreach.flipped.deprecated list f = match list with
+ [] -> ()
+ x +: xs ->
+ ignore (f x)
+ data.List.foreach.flipped.deprecated xs f
+
+data.List.foreach.flipped.deprecated.doc : Doc
+data.List.foreach.flipped.deprecated.doc =
+ use Nat +
+ use foreach flipped
+ {{
+ {{ docExample 2 do xs f -> flipped xs f }} applies the function `f` to every
+ element of the {type List} `xs`, ignoring the results, but combining the
+ effects.
+
+ It's equivalent to ``ignore (List.map f xs)``.
+
+ See also {foreach.deprecated} which takes its arguments in the opposite
+ order.
+
+ # Example
+
+ ```
+ withInitialValue 0 do
+ flipped [10, 20, 30] (x -> Store.modify (s -> s + x))
+ Store.get
+ ```
+ }}
+
+data.List.foreach.flipped.doc : Doc
+data.List.foreach.flipped.doc =
+ use Nat +
+ use foreach flipped
+ {{
+ {{ docExample 2 do xs f -> flipped xs f }} applies the function `f` to every
+ element of the {type List} `xs`, ignoring the results, but combining the
+ effects.
+
+ It's equivalent to ``ignore (List.map f xs)``, but does not create an
+ intermediate list.
+
+ See also {{ docLink (docEmbedTermLink do List.foreach) }} which takes its
+ arguments in the opposite order.
+
+ # Example
+
+ ```
+ withInitialValue 0 do
+ flipped [10, 20, 30] (x -> Store.modify (s -> s + x))
+ Store.get
+ ```
+ }}
+
+data.List.groupBy : (v ->{e} k) -> [v] ->{e} Map k (List.Nonempty v)
+data.List.groupBy f =
+ List.foldLeft
+ (m v ->
+ Map.alter
+ (cases
+ None -> Some (List.Nonempty.singleton v)
+ Some vs -> Some (Nonempty.cons v vs)) (f v) m) Map.empty
+
+data.List.groupBy.doc : Doc
+data.List.groupBy.doc =
+ {{
+ Partitions the {type List} into a {type Map} according to a discriminator
+ function.
+
+ The expression `` groupBy d list `` applies the function `d` to every element
+ in the {type List} `list`, constructing a {type Map} keyed by the results,
+ and under each key a {type List.Nonempty} of the elements of the {type List}
+ `list` for which the function `d` returned that key.
+
+ # Example
+
+ ```
+ xs = [1, 2, 3, 4]
+ Map.toList (groupBy Nat.isEven xs)
+ ```
+ }}
+
+data.List.groupBy.examples.ex1 : [[Nat]]
+data.List.groupBy.examples.ex1 =
+ List.map List.Nonempty.toList (groupSublistsBy (===) [5, 4, 4, 6])
+
+data.List.groupBy.examples.ex2 : [[Nat]]
+data.List.groupBy.examples.ex2 =
+ List.map
+ List.Nonempty.toList (groupSublistsBy Universal.lt [5, 6, 7, 5, 9, 7, 6])
+
+test> data.List.groupBy.tests.groups =
+ runs 100 do
+ use Heap sort
+ use List Nonempty.toList all
+ xs = gen.listOf gen.boolean ()
+ f = yesNo()
+ groups = groupBy f xs
+ list = Map.toList groups
+ allCorrect =
+ all (cases (k, v) -> all (a -> k === f a) (Nonempty.toList v)) list
+ noJunk =
+ sort (List.flatMap Nonempty.toList (Map.values groups)) === sort xs
+ expect (allCorrect && noJunk)
+
+data.List.groupConsecutive : [a] -> [List.Nonempty a]
+data.List.groupConsecutive = groupSublistsBy (===)
+
+data.List.groupConsecutive.doc : Doc
+data.List.groupConsecutive.doc =
+ {{
+ {groupConsecutive} is equivalent to ``groupSublistsBy (==)``.
+
+ # Example
+
+ ```
+ groupConsecutive [?🐱, ?🐱, ?🐹, ?🐰, ?🐹, ?🐹, ?🐹]
+ ```
+ }}
+
+test> data.List.groupConsecutive.tests.base =
+ use List.Nonempty singleton
+ check
+ (groupConsecutive [1, 2, 2, 3, 2]
+ === [singleton 1, 2 +| [2], singleton 3, singleton 2])
+
+data.List.groupMap :
+ (a ->{e} k) -> (a ->{e} v) -> [a] ->{e} Map k (List.Nonempty v)
+data.List.groupMap key value =
+ List.foldLeft
+ (m a ->
+ Map.alter
+ (cases
+ None -> Some (List.Nonempty.singleton (value a))
+ Some as -> Some (Nonempty.cons (value a) as)) (key a) m) Map.empty
+
+data.List.groupMap.doc : Doc
+data.List.groupMap.doc =
+ {{
+ Partitions the {type List} into a {type Map} according to a pair of
+ functions, one that generates the key and another that generates the value.
+
+ The expression `` groupMap key val list `` applies the functions `key` and
+ `val` to every element in the {type List} `list`, constructing an entry in
+ the resulting {type Map}. If `key` takes any two values in the {type List} to
+ the same key, the last value is used.
+
+ # Example
+
+ ```
+ xs = ["the", "early", "bird", "gets", "the", "worm"]
+ Map.toList
+ (Map.map
+ (Set.toList << List.Nonempty.toSet)
+ (groupMap Text.size (Text.map ascii.toUpper) xs))
+ ```
+ }}
+
+test> data.List.groupMap.tests.groups = runs 100 do
+ use List map toSet
+ use Set ==
+ xs = gen.listOf gen.boolean ()
+ f = yesNo()
+ g = yesNo()
+ groups = groupMap f g xs
+ list = Map.toList groups
+ vals = List.flatMap (List.Nonempty.toList << at2) list
+ keyProp = toSet (map f xs) == toSet (map at1 list)
+ valueProp = List.all (x -> List.contains (g x) vals) xs
+ expect (keyProp && valueProp)
+
+data.List.groupMapReduce :
+ (a ->{e} k) -> (a ->{e} v) -> (v ->{e} v ->{e} v) -> [a] ->{e} Map k v
+data.List.groupMapReduce key value combine =
+ List.foldLeft
+ (m a ->
+ Map.alter
+ (cases
+ None -> Some (value a)
+ Some v -> Some (combine v (value a))) (key a) m) Map.empty
+
+data.List.groupMapReduce.doc : Doc
+data.List.groupMapReduce.doc =
+ use List ++
+ {{
+ Partitions the {type List} into a {type Map} according to a pair of functions
+ — one that generates the key and another that generates the value — combining
+ duplicates with a third function.
+
+ The expression `` List.groupMapReduce k v f xs `` applies the functions `k`
+ and `v` to every element in the {type List} `xs`, constructing an entry in
+ the resulting {type Map}. If `k` takes any two values in the {type List} to
+ the same key, they're combined with the function `f` with the semantics of a
+ left fold.
+
+ That is,
+
+ @typecheck ```
+ groupMapReduce key val combine list =
+ Map.map (reduceLeft combine) (groupMap key val list)
+ ```
+
+ # Example
+
+ ```
+ xs = [5, 4, 4, 6]
+ Map.toList (List.groupMapReduce Nat.isEven List.singleton (++) xs)
+ ```
+ }}
+
+test> data.List.groupMapReduce.tests.groups = runs 100 do
+ xs = gen.listOf gen.boolean ()
+ f = yesNo()
+ g = yesNo()
+ h = logic()
+ expect (groupMapReduce f g h xs === Map.map (reduceLeft h) (groupMap f g xs))
+
+data.List.groupSublistsBy :
+ (a ->{e} a ->{e} Boolean) -> [a] ->{e} [List.Nonempty a]
+data.List.groupSublistsBy p' = cases
+ [] -> []
+ x' +: xs' ->
+ use List +:
+ go p z = cases
+ [] -> ([], [])
+ x +: xs ->
+ (ys, zs) = go p x xs
+ if p z x then (x +: ys, zs) else ([], x +| ys +: zs)
+ let
+ (ys', zs') = go p' x' xs'
+ x' +| ys' +: zs'
+
+data.List.groupSublistsBy.doc : Doc
+data.List.groupSublistsBy.doc =
+ use List map
+ use Nat < ==
+ {{
+ `` groupSublistsBy f xs `` groups elements of `xs` into a list of sublists.
+ Consecutive elements of `xs` appear in the same group if `f` applied to them
+ is ``true``.
+
+ Note that `` List.join (Nonempty.toList (List.groupSublistsBy f xs)) `` is
+ always `xs`, regardless of what `f` is.
+
+ # Examples
+
+ Grouping by an equality condition will group together consecutive runs of
+ the same element:
+
+ ```
+ map List.Nonempty.toList (groupSublistsBy (==) [5, 4, 4, 6])
+ ```
+
+ Grouping by an order on the elements will result in ordered sublists:
+
+ ```
+ map List.Nonempty.toList (groupSublistsBy (<) [5, 6, 7, 5, 9, 7, 6])
+ ```
+ }}
+
+data.List.groupWith : (a ->{e} a ->{f} Boolean) -> [a] ->{e, f} [[a]]
+data.List.groupWith p xs =
+ use List +:
+ f x a q =
+ (ys, zs) = a (p x)
+ if q x then (x +: ys, zs) else ([], x +: ys +: zs)
+ at2 (List.foldRight f (const ([], [])) xs (const false))
+
+data.List.groupWith.doc : Doc
+data.List.groupWith.doc =
+ use Nat <=
+ {{
+ `` groupWith p xs `` groups the elements of the list `xs` into sublists where
+ each sublist contains successive elements that compare `` true `` under the
+ comparison function `p`.
+
+ # Examples
+
+ We can use {groupWith} to group equal elements of a sorted list:
+
+ ```
+ groupWith (Nat.==) [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
+ ```
+
+ On an unordered list, this will group elements that are equal and adjacent:
+
+ ```
+ List.map fromCharList (groupWith (Char.==) (toCharList "hello"))
+ ```
+
+ We can group runs of elements that are less than or equal to the next
+ element:
+
+ ```
+ groupWith (<=) [1, 2, 2, 3, 1, 2, 0, 4, 5, 2]
+ ```
+
+ We can also use abilities in the comparison function. Here we use the
+ {type Each} ability to generate all partitions of a list:
+
+ ```
+ Each.toList do groupWith (do do each [true, false]) [1, 2, 3]
+ ```
+ }}
+
+data.List.halve : [a] -> ([a], [a])
+data.List.halve s =
+ use Nat /
+ n = List.size s / 2
+ (List.take n s, List.drop n s)
+
+data.List.halve.doc : Doc
+data.List.halve.doc =
+ {{
+ Splits a {type List} into a prefix and suffix of equal length. If the
+ {type List} has an odd number of elements, the suffix will have one more
+ element than the prefix.
+
+ # Examples
+
+ ```
+ halve [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ halve [1, 2, 3, 4]
+ ```
+ }}
+
+data.List.head : [a] -> Optional a
+data.List.head = cases
+ [] -> None
+ a +: _ -> Some a
+
+data.List.head.doc : Doc
+data.List.head.doc =
+ use List head
+ {{
+ Returns the first element of a {type List}, or {None} if it's empty. The
+ complexity is O(1).
+
+ # Examples
+
+ ```
+ head List.empty
+ ```
+
+ ```
+ head [4]
+ ```
+
+ ```
+ head [1, 2, 3]
+ ```
+ }}
+
+data.List.head.examples.evaluated.elems : Optional Nat
+data.List.head.examples.evaluated.elems = List.head examples.elems
+
+data.List.head.examples.evaluated.empty : Optional a
+data.List.head.examples.evaluated.empty = List.head List.empty
+
+data.List.head.examples.evaluated.single : Optional Nat
+data.List.head.examples.evaluated.single = List.head examples.single
+
+test> data.List.head.test = runs 100 do
+ use List +: head
+ x = natInOrder()
+ xs = gen.listOf natInOrder ()
+ expect (head (x +: xs) === Some x && head [] === None)
+
+data.List.indexed : [a] -> [(a, Nat)]
+data.List.indexed =
+ use List :+
+ use Nat +
+ go acc i = cases
+ a +: as -> go (acc :+ (a, i)) (i + 1) as
+ [] -> acc
+ go [] 0
+
+data.List.indexed.doc : Doc
+data.List.indexed.doc =
+ {{
+ Returns a {type List} of pairs of the form `(element, index)` for each
+ element in the given {type List}, where the `index` is the position of the
+ `element` in the {type List}.
+
+ # Example
+
+ ```
+ List.indexed [?a, ?b, ?c]
+ ```
+ }}
+
+test> data.List.indexed.tests.isIndexed = runs 1000 do
+ l = gen.listOf gen.boolean ()
+ expect (List.indexed l === List.zip l (List.range 0 (List.size l)))
+
+data.List.indexOfSublist : [a] -> [a] -> Optional Nat
+data.List.indexOfSublist needle haystack =
+ use Nat +
+ use Optional toAbort
+ if List.isPrefixOf needle haystack then Some 0
+ else
+ toOptional! do
+ rest = toAbort (List.tail haystack)
+ toAbort (data.List.indexOfSublist needle rest) + 1
+
+data.List.indexOfSublist.doc : Doc
+data.List.indexOfSublist.doc =
+ {{
+ `` indexOfSublist needle haystack `` finds the index of the first occurrence
+ of 'needle' as a sublist of 'haystack', or {None} if 'needle' doesn't occur
+ in 'haystack'.
+
+ # Examples
+
+ ```
+ indexOfSublist (toCharList "ca") (toCharList "abcab")
+ ```
+
+ ```
+ indexOfSublist (toCharList "ab") (toCharList "abab")
+ ```
+
+ ```
+ indexOfSublist (toCharList "pq") (toCharList "abab")
+ ```
+ }}
+
+data.List.init : [a] -> Optional [a]
+data.List.init = cases
+ [] -> None
+ xs :+ _ -> Some xs
+
+data.List.init.doc : Doc
+data.List.init.doc =
+ use List init
+ {{
+ Returns all but the last element of a {type List}.
+
+ # Examples
+
+ ```
+ init [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ init [1]
+ ```
+
+ ```
+ init []
+ ```
+ }}
+
+data.List.initialize : Nat -> (Nat ->{e} a) ->{e} [a]
+data.List.initialize length initializer =
+ use List :+
+ use Nat +
+ go : Nat -> [a] -> [a]
+ go i acc =
+ if Universal.gteq i length then acc else go (i + 1) (acc :+ initializer i)
+ go 0 []
+
+data.List.initialize.doc : Doc
+data.List.initialize.doc =
+ use Nat *
+ {{
+ `` initialize n f `` makes a list of length `n` by calling `f` on the numbers
+ `0` to `n` (exclusive).
+
+ # Examples
+
+ ```
+ initialize 10 (x -> x)
+ ```
+
+ ```
+ initialize 0 (x -> "hello")
+ ```
+
+ ```
+ initialize 10 (x -> x * 10)
+ ```
+ }}
+
+data.List.initialize.examples.ex1 : [Nat]
+data.List.initialize.examples.ex1 =
+ use Nat *
+ initialize 4 (i -> i * 2)
+
+test> data.List.initialize.tests.tests.ex1 =
+ use Nat *
+ actual = initialize 4 (i -> i * 2)
+ expected = [0, 2, 4, 6]
+ check (actual === expected)
+
+data.List.inits : [a] -> [[a]]
+data.List.inits l =
+ use List :+
+ is = match l with
+ [] -> []
+ xs :+ _ -> data.List.inits xs
+ is :+ l
+
+data.List.inits.doc : Doc
+data.List.inits.doc =
+ {{
+ Returns a {type List} of all initial segments of the input. The first element
+ of the result is the empty {type List}, and the last element is the input
+ {type List} itself.
+
+ # Examples
+
+ ```
+ inits [1, 2, 3]
+ ```
+ }}
+
+data.List.insertAfter : (a ->{e} a ->{f} Boolean) -> a -> [a] ->{e, f} [a]
+data.List.insertAfter p = cases
+ x, [] -> [x]
+ x, yys@(y +: ys) ->
+ if p x y then x List.+: yys else y List.+: data.List.insertAfter p x ys
+
+data.List.insertAfter.doc : Doc
+data.List.insertAfter.doc =
+ use Nat <=
+ {{
+ `` insertAfter p x ys `` inserts the element `x` into the list `ys` before
+ the first element `y` where `p x y` returns ``false``. If no such element is
+ found, `x` is appended to the end of the list.
+
+ # Examples
+
+ {insertAfter} can be used to insert an element into a sorted list:
+
+ ```
+ insertAfter (<=) 3 [1, 2, 4, 5]
+ ```
+
+ We can implement a simple insertion sort using {insertAfter}:
+
+ ```
+ List.foldRight (insertAfter (<=)) [] [3, 1, 4, 1, 5, 9, 2, 6]
+ ```
+
+ We can use abilities in the comparison function. Here we generate an
+ identity matrix by inserting `1` after each element in the list `[0,0,0]`:
+
+ ```
+ Each.toList do insertAfter (do do each [true, false]) 1 [0, 0, 0]
+ ```
+ }}
+
+data.List.insertAt : Nat -> a -> [a] -> [a]
+data.List.insertAt i a as =
+ use List ++
+ List.take i as ++ [a] ++ List.drop i as
+
+data.List.insertAt.doc : Doc
+data.List.insertAt.doc =
+ {{
+ Inserts an element into a list at a given index (0-based). If the index is
+ out of bounds, the element is inserted at the end of the list.
+
+ # Examples
+
+ ```
+ insertAt 2 "c" ["a", "b", "d"]
+ ```
+
+ ```
+ insertAt 42 "e" ["a", "b", "c", "d"]
+ ```
+ }}
+
+data.List.insertSortedDistinct : a -> [a] -> [a]
+data.List.insertSortedDistinct a = cases
+ [] -> [a]
+ [a2]
+ | a === a2 -> [a2]
+ | Universal.lteq a a2 -> [a, a2]
+ | otherwise -> [a2, a]
+ as ->
+ match halve as with
+ (l, r@(mid +: _)) ->
+ if Universal.lt a mid then data.List.insertSortedDistinct a l List.++ r
+ else l List.++ data.List.insertSortedDistinct a r
+ _ -> bug "insertSortedDistinct: halve returned a list of size < 2"
+
+data.List.insertSortedDistinct.doc : Doc
+data.List.insertSortedDistinct.doc =
+ {{
+ `` insertSortedDistinct a as `` inserts `a` into `as`, assuming `as` is
+ ordered ascending via {Universal.ordering}, using binary search to find the
+ insertion point.
+
+ # Examples
+
+ ```
+ insertSortedDistinct 4 [0, 1, 2, 3, 5]
+ ```
+
+ If `a` already exists in `as`, this will just return `as`:
+
+ ```
+ insertSortedDistinct 1 [0, 1, 2, 3, 4]
+ ```
+
+ If `as` is not sorted in ascending order, this function will insert the
+ element in a spot you might not expect:
+
+ ```
+ insertSortedDistinct 3 [6, 5, 4, 2, 1, 0]
+ ```
+ }}
+
+test> data.List.insertSortedDistinct.tests = test.verify do
+ use Nat ==
+ size = Each.range 0 32
+ i = Each.range 0 size
+ ns = List.range 0 size
+ ns' = deleteFirst (n -> n == i) ns
+ ignore "make sure insertion works"
+ ensure (insertSortedDistinct i ns' === ns)
+ ignore "and make sure insertion never produces dupes"
+ ensure (insertSortedDistinct i ns === ns)
+
+data.List.intercalate : [a] -> [[a]] -> [a]
+data.List.intercalate between = cases
+ [] -> []
+ xs +: xxs -> xs List.++ List.flatMap (as -> between List.++ as) xxs
+
+data.List.intercalate.doc : Doc
+data.List.intercalate.doc =
+ {{
+ `` intercalate xs xss `` is equivalent to
+ ``List.join (List.intersperse xs xss)``. It inserts the list `xs` in between
+ the lists in `xss` and concatenates the result.
+
+ # Example
+
+ ```
+ intercalate
+ ["cha", "cha", "cha"] [["one", "two"], ["three", "four"], ["..."]]
+ ```
+ }}
+
+test> data.List.intercalate.tests.empty = check (intercalate [] [] === [])
+
+test> data.List.intercalate.tests.empty1 =
+ check (intercalate [] [[1], [2]] === [1, 2])
+
+test> data.List.intercalate.tests.empty2 = check (intercalate [1] [] === [])
+
+test> data.List.intercalate.tests.simple =
+ check (intercalate [3, 4] [[1, 2], [5, 6]] === [1, 2, 3, 4, 5, 6])
+
+test> data.List.intercalate.tests.size =
+ use List size
+ use Nat * +
+ use gen listOf
+ go _ =
+ expect
+ let
+ l = listOf natInOrder ()
+ v = listOf (listOf natInOrder) ()
+ i = intercalate l v
+ actual = size i
+ expected =
+ size (List.join v) + size l * truncate0 (subtractToInt (size v) 1)
+ if Boolean.not (expected === actual) then
+ bug ("Not equal!", expected, actual, l, v)
+ else true
+ runs 100 go
+
+data.List.interleave : [a] -> [a] -> [a]
+data.List.interleave xs ys =
+ use List ++ +: :+
+ go : [a] -> [a] -> [a] -> [a]
+ go acc = cases
+ [], ys -> acc ++ ys
+ xs, [] -> acc ++ xs
+ x +: xs, y +: ys -> go (acc :+ x) (y +: ys) xs
+ go [] xs ys
+
+data.List.interleave.doc : Doc
+data.List.interleave.doc =
+ use List interleave
+ {{
+ Combines two lists into a single list, alternating elements from each list.
+ If the two lists have different lengths, a suffix of the longer list will
+ appear at the end of the result.
+
+ # Examples
+
+ ```
+ interleave [1, 2, 3] [4, 5, 6, 7]
+ ```
+
+ ```
+ interleave [1, 2, 3, 4] [5, 6, 7]
+ ```
+
+ ```
+ interleave [1, 2, 3, 4] [5]
+ ```
+
+ # Relationships and properties
+
+ {interleave} is the inverse of {uncollate}:
+
+ ```
+ uncurry interleave (uncollate [1, 2, 3, 4, 5])
+ ```
+
+ However, {uncollate} is not the exact inverse of {interleave}:
+
+ ```
+ uncollate (interleave [1, 2] [3, 5, 7, 9])
+ ```
+ }}
+
+data.List.intersperse : a -> [a] -> [a]
+data.List.intersperse sep ls =
+ use List ++ +:
+ prependToAll : a -> [a] -> [a]
+ prependToAll sep = cases
+ [] -> []
+ a +: as -> [sep, a] ++ prependToAll sep as
+ match ls with
+ [] -> []
+ a +: as -> a +: prependToAll sep as
+
+data.List.intersperse.doc : Doc
+data.List.intersperse.doc =
+ {{
+ Takes an element and a {type List} and intersperses that element between the
+ elements of the list.
+
+ # Example
+
+ ```
+ List.intersperse "potato" ["one", "two", "three", "four"]
+ ```
+ }}
+
+test> data.List.intersperse.tests.base =
+ sep = ?,
+ list = [?a, ?b, ?c, ?d]
+ check (List.intersperse sep list === [?a, ?,, ?b, ?,, ?c, ?,, ?d])
+
+test> data.List.intersperse.tests.empty =
+ sep = 0
+ list = []
+ check (List.intersperse sep list === [])
+
+data.List.isEmpty : [a] -> Boolean
+data.List.isEmpty xs = xs === []
+
+data.List.isEmpty.doc : Doc
+data.List.isEmpty.doc =
+ use List isEmpty
+ {{
+ Returns `` true `` if the list is empty, `` false `` otherwise.
+
+ # Examples
+
+ ```
+ isEmpty []
+ ```
+
+ ```
+ isEmpty [1, 2, 3]
+ ```
+ }}
+
+data.List.isInfixOf : [a] -> [a] -> Boolean
+data.List.isInfixOf infix list =
+ List.isPrefixOf infix list || (match list with
+ _ +: xs -> data.List.isInfixOf infix xs
+ [] -> false)
+
+data.List.isInfixOf.doc : Doc
+data.List.isInfixOf.doc =
+ {{
+ `` isInfixOf is xs `` checks whether `xs` contains `is`, as a contiguous
+ sub-list.
+
+ # Examples
+
+ ```
+ isInfixOf [2, 3] [1, 2, 3, 4]
+ ```
+
+ ```
+ isInfixOf [1, 2] [1, 2, 3, 4]
+ ```
+
+ ```
+ isInfixOf [3, 4] [1, 2, 3, 4]
+ ```
+
+ ```
+ isInfixOf [3] [1, 2, 3, 4]
+ ```
+
+ ```
+ isInfixOf [1, 2] [1]
+ ```
+
+ The empty list is a sub-list of any list:
+
+ ```
+ isInfixOf [] [1, 2, 3, 4]
+ ```
+
+ Note that the sub-list needs to appear as a contiguous block, so:
+
+ ```
+ isInfixOf [1, 3] [1, 2, 3, 4]
+ ```
+ }}
+
+test> data.List.isInfixOf.tests.prop1 =
+ go _ =
+ use List ++
+ use gen listOf
+ a = listOf natInOrder ()
+ b = listOf natInOrder ()
+ c = listOf natInOrder ()
+ expect (isInfixOf b (a ++ b ++ c))
+ runs 100 go
+
+data.List.isPrefixOf : [a] -> [a] -> Boolean
+data.List.isPrefixOf prefix list = match prefix with
+ p +: ps ->
+ match list with
+ x +: xs -> p === x && data.List.isPrefixOf ps xs
+ [] -> false
+ [] -> true
+
+data.List.isPrefixOf.doc : Doc
+data.List.isPrefixOf.doc =
+ use List isPrefixOf
+ {{
+ `` isPrefixOf ps xs `` checks whether `ps` is a prefix of `xs`.
+
+ # Examples
+
+ ```
+ isPrefixOf [1, 2] [1, 2, 3]
+ ```
+
+ ```
+ isPrefixOf [1, 2] [1, 2]
+ ```
+
+ ```
+ isPrefixOf [1, 2] [1, 3, 4]
+ ```
+
+ ```
+ isPrefixOf [1, 2] [1]
+ ```
+
+ The empty list is a prefix of any list:
+
+ ```
+ isPrefixOf [] [1, 2, 3]
+ ```
+ }}
+
+test> data.List.isPrefixOf.tests.prop1 =
+ go _ =
+ use List ++
+ use gen listOf
+ a = listOf natInOrder ()
+ b = listOf natInOrder ()
+ expect (List.isPrefixOf a (a ++ b))
+ runs 100 go
+
+test> data.List.isPrefixOf.tests.prop2 =
+ go _ =
+ use List ++ size
+ use gen listOf
+ a = listOf natInOrder ()
+ b = listOf natInOrder ()
+ c = listOf natInOrder ()
+ expect (size a !== size b || a === b === List.isPrefixOf a (b ++ c))
+ runs 100 go
+
+data.List.isSortedBy : (a -> a ->{e} Boolean) -> [a] ->{e} Boolean
+data.List.isSortedBy f = cases
+ [] -> true
+ [x] -> true
+ x +: (y +: rest) -> f x y && data.List.isSortedBy f (y List.+: rest)
+
+data.List.isSortedBy.doc : Doc
+data.List.isSortedBy.doc =
+ use Nat <=
+ {{
+ Returns `` true `` if the given {type List} is sorted according to the given
+ comparison function, `` false `` otherwise.
+
+ # Example
+
+ ```
+ isSortedBy (<=) [1, 2, 3]
+ ```
+
+ ```
+ isSortedBy (<=) [1, 3, 2]
+ ```
+ }}
+
+data.List.isSuffixOf : [a] -> [a] -> Boolean
+data.List.isSuffixOf suffix list = match suffix with
+ ss :+ s ->
+ match list with
+ xs :+ x -> s === x && data.List.isSuffixOf ss xs
+ [] -> false
+ [] -> true
+
+data.List.isSuffixOf.doc : Doc
+data.List.isSuffixOf.doc =
+ use List isSuffixOf
+ {{
+ `` isSuffixOf ss xs `` checks whether `ss` is a suffix of `xs`.
+
+ Examples:
+
+ ```
+ isSuffixOf [2, 3] [1, 2, 3]
+ ```
+
+ ```
+ isSuffixOf [2, 3] [2, 3]
+ ```
+
+ ```
+ isSuffixOf [1, 3] [1, 2, 3]
+ ```
+
+ ```
+ isSuffixOf [2, 3] [3]
+ ```
+
+ The empty list is a suffix of any list:
+
+ ```
+ isSuffixOf [] [1, 2, 3]
+ ```
+ }}
+
+test> data.List.isSuffixOf.tests.prop1 =
+ go _ =
+ use List ++
+ use gen listOf
+ a = listOf natInOrder ()
+ b = listOf natInOrder ()
+ expect (List.isSuffixOf b (a ++ b))
+ runs 100 go
+
+test> data.List.isSuffixOf.tests.prop2 =
+ go _ =
+ use List ++ size
+ use gen listOf
+ a = listOf natInOrder ()
+ b = listOf natInOrder ()
+ c = listOf natInOrder ()
+ expect (size b !== size c || b === c === List.isSuffixOf c (a ++ b))
+ runs 100 go
+
+data.List.iterate : (a ->{Abort} a) -> a -> [a]
+data.List.iterate f init = unfold! init do
+ s = Store.get
+ Store.put (f s)
+ s
+
+data.List.iterate.doc : Doc
+data.List.iterate.doc =
+ use Nat + <
+ {{
+ Generate a list of values from a seed value and a function, by repeatedly
+ applying the function to the seed. For a seed value x and a function f, the
+ output will be
+
+ `[x, f x, f (f x), f (f (f x))]`
+
+ and so on, until f calls abort.
+
+ For example:
+
+ ```
+ List.iterate (x -> (if x < 5 then x + 1 else abort)) 0
+ ```
+ }}
+
+test> data.List.iterate.tests.ex1 =
+ use Nat + <
+ check
+ (List.iterate (n -> (if n < 10 then n + 1 else abort)) 1
+ === [1, 2, 3, 4, 5, 6, 7, 8, 9])
+
+data.List.join : [[a]] -> [a]
+data.List.join =
+ use List ++
+ List.foldLeft (++) []
+
+data.List.join.doc : Doc
+data.List.join.doc =
+ use List join
+ {{
+ {join} flattens a list of lists into a single list.
+
+ # Examples
+
+ ```
+ join [[1, 2], [3, 4], [5]]
+ ```
+
+ ```
+ join [[]]
+ ```
+ }}
+
+test> data.List.join.tests.associative = runs 100 do
+ use List join
+ use gen listOf
+ x = listOf (listOf (listOf natInOrder)) ()
+ expect (join (join x) === join (List.map join x))
+
+test> data.List.join.tests.homomorphism = runs 100 do
+ use List ++
+ use gen listOf
+ x = listOf natInOrder ()
+ y = listOf natInOrder ()
+ expect (List.join [x, y] === (x ++ y))
+
+test> data.List.join.tests.unit = runs 100 do
+ use List join
+ x = gen.listOf natInOrder ()
+ expect (join [x] === join (List.map (a -> [a]) x))
+
+data.List.last : [a] -> Optional a
+data.List.last = cases
+ _ :+ x -> Some x
+ [] -> None
+
+data.List.last.doc : Doc
+data.List.last.doc =
+ use List last
+ {{
+ Returns the last element of a list, or {None} if it's empty. The complexity
+ is O(1).
+
+ # Examples
+
+ ```
+ last []
+ ```
+
+ ```
+ last [4]
+ ```
+
+ ```
+ last [1, 2, 3]
+ ```
+ }}
+
+data.List.last.examples.elems : [Nat]
+data.List.last.examples.elems = [1, 2, 3]
+
+data.List.last.examples.evaluated.elems : Optional Nat
+data.List.last.examples.evaluated.elems = List.last examples.elems
+
+data.List.last.examples.evaluated.empty : Optional a
+data.List.last.examples.evaluated.empty = List.last examples.empty
+
+data.List.last.examples.evaluated.single : Optional Nat
+data.List.last.examples.evaluated.single = List.last examples.single
+
+data.List.last.examples.single : [Nat]
+data.List.last.examples.single = [1]
+
+test> data.List.last.tests.elems = check (List.last examples.elems === Some 3)
+
+test> data.List.last.tests.empty = check (List.last examples.empty === None)
+
+test> data.List.last.tests.isHeadOnReversedList = runs 100 do
+ xs = gen.listOf natInOrder ()
+ expect ((xs |> List.last) === (xs |> List.reverse |> List.head))
+
+test> data.List.last.tests.single =
+ check (List.last examples.single === Some 1)
+
+data.List.lefts : [Either a b] -> [a]
+data.List.lefts = at1 << partitionEithers
+
+data.List.lefts.doc : Doc
+data.List.lefts.doc =
+ {{
+ Accumulate all of the elements that are {Left} in a list of {type Either}s.
+
+ # Example
+
+ ```
+ lefts [Left 2, Right "bump", Left 10, Right "thump"]
+ ```
+ }}
+
+test> data.List.lefts.tests.ex1 =
+ check
+ let
+ actual = lefts [Left 2, Right "Hello", Left 10, Right "Hello again"]
+ expected = [2, 10]
+ assert (actual === expected) ("Not equal!", actual, expected) true
+
+data.List.map : (a ->{𝕖} b) -> [a] ->{𝕖} [b]
+data.List.map f =
+ use List :+
+ go acc = cases
+ a +: as -> go (acc :+ f a) as
+ [] -> acc
+ go []
+
+data.List.map.doc : Doc
+data.List.map.doc =
+ use List map
+ use Nat increment
+ {{
+ Apply a function to each element of a {type List}.
+
+ # Examples
+
+ ```
+ map increment [1, 2, 3]
+ ```
+
+ ```
+ map increment []
+ ```
+ }}
+
+test> data.List.map.tests.functor = runs 1000 do
+ use List map
+ l = gen.listOf gen.boolean ()
+ f = yesNo()
+ g = yesNo()
+ expect (map id l === l && map (f >> g) l === map g (map f l))
+
+data.List.map2 : (a ->{e} b ->{e} c) -> [a] -> [b] ->{e} [c]
+data.List.map2 f xs ys = Each.toList do
+ x = each xs
+ y = each ys
+ f x y
+
+data.List.map2.doc : Doc
+data.List.map2.doc =
+ use List map2
+ use Nat +
+ {{
+ Applies a function to every pair of elements from two lists. The result is a
+ list whose length is the product of the lengths of the two lists.
+
+ # Examples
+
+ ```
+ map2 (+) [1, 2, 3] [4, 5, 6]
+ ```
+
+ ```
+ map2 Tuple.pair [1, 2, 3] [4, 5, 6]
+ ```
+ }}
+
+data.List.mapIndexed : (Nat ->{𝕖} a ->{𝕖} b) -> [a] ->{𝕖} [b]
+data.List.mapIndexed f =
+ use List :+
+ use Nat +
+ go acc i = cases
+ a +: as -> go (acc :+ f i a) (i + 1) as
+ [] -> acc
+ go [] 0
+
+data.List.mapIndexed.doc : Doc
+data.List.mapIndexed.doc =
+ {{
+ Like {List.map}, applies a function over a {type List} of elements, but gets
+ passed the index to the element in the list.
+
+ # Examples
+
+ Replace the list elements with their index:
+
+ ```
+ mapIndexed const ["one", "two", "three"]
+ ```
+
+ Add indexes to the elements in pairs:
+
+ ```
+ mapIndexed Tuple.pair [5, 3, 1]
+ ```
+ }}
+
+test> data.List.mapIndexed.tests.ex1 =
+ check (mapIndexed const ["Foo", "Bar", "Baz"] === [0, 1, 2])
+
+test> data.List.mapIndexed.tests.ex2 =
+ check (mapIndexed (i v -> (i, v)) [5, 3, 1] === [(0, 5), (1, 3), (2, 1)])
+
+test> data.List.mapIndexed.tests.mapsAndIndexes = runs 1000 do
+ use Nat +
+ l = gen.listOf gen.nat ()
+ f n i = n + i
+ expect (mapIndexed f l === List.zipWith f l (List.range 0 (List.size l)))
+
+data.List.mapRight : (a ->{g} b) -> [a] ->{g} [b]
+data.List.mapRight f xs =
+ use List +:
+ g a bs = f a +: bs
+ List.foldRight g [] xs
+
+data.List.mapRight.doc : Doc
+data.List.mapRight.doc =
+ use List map mapRight
+ use Nat +
+ use Store get put
+ {{
+ `` mapRight f xs `` is the same as `` map f xs `` except that the effects of
+ `f` will happen in reverse order.
+
+ # Examples
+
+ This uses the {type Store} ability with {map} to number the elements of the
+ list:
+
+ ```
+ withInitialValue 0 do
+ map
+ (x -> let
+ n = get
+ put (n + 1)
+ (n, x)) ["a", "b", "c"]
+ ```
+
+ Using {mapRight} will number the elements of the list in reverse order:
+
+ ```
+ withInitialValue 0 do
+ mapRight
+ (x -> let
+ n = get
+ put (n + 1)
+ (n, x)) ["a", "b", "c"]
+ ```
+
+ This throws the last element of the list:
+
+ ```
+ toEither do mapRight throw [1, 2, 3]
+ ```
+
+ Whereas using {map} will throw the first element of the list:
+
+ ```
+ toEither do map throw [1, 2, 3]
+ ```
+ }}
+
+data.List.maximum : [a] -> Optional a
+data.List.maximum list =
+ go x = cases
+ [] -> Some x
+ y +: ys | Universal.gt y x -> go y ys
+ _ +: ys -> go x ys
+ match list with
+ x +: xs -> go x xs
+ [] -> None
+
+data.List.maximum.doc : Doc
+data.List.maximum.doc = {{ Finds the largest element in the list. }}
+
+test> data.List.maximum.tests.base =
+ check (List.maximum [1, 2, 90, 3, 5] === Some 90)
+
+test> data.List.maximum.tests.empty = check (List.maximum [] === None)
+
+data.List.maximumBy : (a ->{e} a ->{e} Ordering) -> [a] ->{e} Optional a
+data.List.maximumBy f xs =
+ go = cases
+ None, x +: xs -> go (Some x) xs
+ Some y, x +: xs
+ | f x y === Greater -> go (Some x) xs
+ | otherwise -> go (Some y) xs
+ y, _ -> y
+ go None xs
+
+data.List.maximumOn :
+ (b ->{f} b ->{g} Ordering) -> (a ->{e} b) -> [a] ->{e, f, g} Optional a
+data.List.maximumOn ord p as =
+ f a e =
+ g = cases
+ None, q -> Some (q, e)
+ b@(Some (o, y)), q
+ | ord o q === Less -> Some (q, e)
+ | otherwise -> b
+ g a (p e)
+ Optional.map at2 (List.foldLeft f None as)
+
+data.List.maximumOn.doc : Doc
+data.List.maximumOn.doc =
+ use Universal ordering
+ {{
+ `` maximumOn ord p as `` returns the element of the list `as` that has the
+ maximum value under the function `p` according to the ordering function
+ `ord`. If the list is empty, this function returns {None}.
+
+ If there are multiple elements with the maximum value, the first one is
+ returned.
+
+ # Examples
+
+ We can find the longest {type Text} in a {type List}:
+
+ ```
+ maximumOn ordering Text.size ["the", "quick", "brown", "fox"]
+ ```
+
+ We can use abilities in the comparison function. Here we find the most
+ frequent element in a list by using the {type Store} ability to keep track
+ of the frequency of each element:
+
+ ```
+ withInitialValue Bag.empty do
+ maximumOn
+ ordering (x -> let
+ b = Store.get
+ c = Bag.count x b
+ Store.put (Bag.add x b)
+ c) [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
+ ```
+ }}
+
+data.List.mayNonempty : [a] -> Optional (List.Nonempty a)
+data.List.mayNonempty = cases
+ [] -> None
+ x +: xs -> Some (Nonempty.Nonempty x xs)
+
+data.List.mayNonempty.doc : Doc
+data.List.mayNonempty.doc =
+ {{
+ Returns the given list as a {type List.Nonempty} if it has at least one
+ element, otherwise {None}.
+ }}
+
+data.List.minimum : [a] -> Optional a
+data.List.minimum list =
+ go x = cases
+ [] -> Some x
+ y +: ys | Universal.lt y x -> go y ys
+ _ +: ys -> go x ys
+ match list with
+ x +: xs -> go x xs
+ [] -> None
+
+data.List.minimum.doc : Doc
+data.List.minimum.doc = {{ Finds the smallest element in the list. }}
+
+test> data.List.minimum.tests.base =
+ check (List.minimum [+1, +20, -30, +4] === Some -30)
+
+data.List.minimumBy : (a ->{e} a ->{e} Ordering) -> [a] ->{e} Optional a
+data.List.minimumBy f xs =
+ go = cases
+ None, x +: xs -> go (Some x) xs
+ Some y, x +: xs
+ | f x y === Less -> go (Some x) xs
+ | otherwise -> go (Some y) xs
+ y, _ -> y
+ go None xs
+
+data.List.modifyAt : Nat -> (a ->{g} a) -> [a] ->{g} Optional [a]
+data.List.modifyAt i f xs = match List.splitAt i xs with
+ (before, x +: xs) -> Some (before List.++ (f x List.+: xs))
+ _ -> None
+
+data.List.modifyAt.doc : Doc
+data.List.modifyAt.doc =
+ use Nat +
+ {{
+ `` modifyAt i f xs `` updates the `i`-th (0-based) element of the list `xs`
+ using the function `f`.
+
+ It returns {None} if the index is out of bounds.
+
+ ```
+ modifyAt 0 (x -> x + 10) [0, 1, 2, 3]
+ ```
+
+ ```
+ modifyAt 2 (x -> x + 10) [0, 1, 2, 3]
+ ```
+
+ ```
+ modifyAt 1000 (x -> x + 10) [0, 1, 2, 3]
+ ```
+ }}
+
+test> data.List.modifyAt.tests =
+ use Nat +
+ check
+ (List.map
+ (i -> modifyAt i (x -> x + 10) [0, 1, 2, 3, 4, 5]) [0, 1, 2, 3, 4, 5, 6]
+ === [ Some [10, 1, 2, 3, 4, 5]
+ , Some [0, 11, 2, 3, 4, 5]
+ , Some [0, 1, 12, 3, 4, 5]
+ , Some [0, 1, 2, 13, 4, 5]
+ , Some [0, 1, 2, 3, 14, 5]
+ , Some [0, 1, 2, 3, 4, 15]
+ , None
+ ])
+
+data.List.mostFrequent : [a] -> Optional a
+data.List.mostFrequent as = withInitialValue Bag.empty do
+ maximumOn
+ Universal.ordering (x -> let
+ b = Store.get
+ c = Bag.count x b
+ Store.put (Bag.add x b)
+ c) as
+
+data.List.mostFrequent.doc : Doc
+data.List.mostFrequent.doc =
+ {{
+ Returns the most frequent element in a list. If there are multiple elements
+ with the same frequency, the first one is returned.
+
+ # Example
+
+ ```
+ mostFrequent [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
+ ```
+ }}
+
+data.List.none : (i ->{e} Boolean) -> [i] ->{e} Boolean
+data.List.none predicate = List.all (x -> Boolean.not (predicate x))
+
+data.List.none.doc : Doc
+data.List.none.doc =
+ {{
+ `` List.none p xs `` returns whether the predicate `p` is false for every
+ element of the {type List} `xs`. Returns `` true `` if `xs` is empty.
+ }}
+
+data.List.nonempty : [a] ->{Abort} List.Nonempty a
+data.List.nonempty = cases
+ x +: xs -> x +| xs
+ [] -> abort
+
+(data.List.Nonempty.++) : List.Nonempty a -> List.Nonempty a -> List.Nonempty a
+(data.List.Nonempty.++) = cases
+ Nonempty.Nonempty x xs ->
+ cases
+ Nonempty.Nonempty y ys -> Nonempty.Nonempty x (xs List.:+ y List.++ ys)
+
+data.List.Nonempty.++.doc : Doc
+data.List.Nonempty.++.doc =
+ use Nonempty ++
+ {{
+ Appends one nonempty list to another. The list `` x ++ y `` contains all the
+ elements from `x` followed by all the elements from `y`.
+ }}
+
+(data.List.Nonempty.+|) : a -> [a] -> List.Nonempty a
+(data.List.Nonempty.+|) = Nonempty.Nonempty
+
+data.List.Nonempty.+|.doc : Doc
+data.List.Nonempty.+|.doc =
+ {{
+ Constructs a {type List.Nonempty} from an element for the {Nonempty.head} and
+ a {type List} for the {Nonempty.tail}.
+
+ # Example
+
+ ```
+ 1 +| [2, 3]
+ ```
+ }}
+
+data.List.Nonempty.align :
+ List.Nonempty a -> List.Nonempty b -> List.Nonempty (OneOrBoth a b)
+data.List.Nonempty.align = List.Nonempty.alignWith id
+
+data.List.Nonempty.align.doc : Doc
+data.List.Nonempty.align.doc =
+ use List.Nonempty align
+ {{
+ Aligns two non-empty lists into a non-empty list of {type OneOrBoth} values.
+
+ The result will have the same length as the longer of the two lists, and each
+ element will be a {type OneOrBoth} containing the corresponding elements from
+ the two input lists. If one of the lists is shorter than the other, the
+ result will contain {This} or {That} values accordingly.
+
+ # Examples
+
+ ```
+ align (1 +| [2, 3]) ("a" +| ["b"])
+ ```
+
+ ```
+ align (1 +| [2]) ("a" +| ["b", "c"])
+ ```
+
+ ```
+ align (1 +| [2, 3]) ("a" +| ["b", "c"])
+ ```
+
+ # See also
+
+ * {List.Nonempty.alignWith} - a variant where you can specify a function to
+ apply to the aligned elements.
+ }}
+
+test> data.List.Nonempty.align.tests = test.verify do
+ use List.Nonempty align
+ labeled "more elements in the first list" do
+ actual = align (1 +| [2, 3]) ("a" +| ["b"])
+ ensureEqual actual (Both 1 "a" +| [Both 2 "b", This 3])
+ labeled "more elements in the second list" do
+ actual = align (1 +| [2]) ("a" +| ["b", "c"])
+ ensureEqual actual (Both 1 "a" +| [Both 2 "b", That "c"])
+ labeled "same number of elements" do
+ actual = align (1 +| [2, 3]) ("a" +| ["b", "c"])
+ ensureEqual actual (Both 1 "a" +| [Both 2 "b", Both 3 "c"])
+
+data.List.Nonempty.alignWith :
+ (OneOrBoth a b ->{g} c)
+ -> List.Nonempty a
+ -> List.Nonempty b
+ ->{g} List.Nonempty c
+data.List.Nonempty.alignWith f = cases
+ Nonempty.Nonempty x xs, Nonempty.Nonempty y ys ->
+ f (Both x y) +| List.alignWith f xs ys
+
+data.List.Nonempty.alignWith.doc : Doc
+data.List.Nonempty.alignWith.doc =
+ use List.Nonempty alignWith
+ use Nat +
+ use OneOrBoth fold
+ use Text size
+ {{
+ Aligns two non-empty lists into a non-empty list of values using a function.
+
+ The result will have the same length as the longer of the two lists, and each
+ element will be the result of applying the given function to the
+ corresponding elements from the two input lists. If one of the lists is
+ shorter than the other, the result will contain values accordingly.
+
+ # Examples
+
+ ```
+ alignWith (fold id size (x y -> x + size y)) (1 +| [2, 3]) ("a" +| ["b"])
+ ```
+
+ ```
+ alignWith (fold id size (x y -> x + size y)) (1 +| [2]) ("a" +| ["b", "c"])
+ ```
+
+ ```
+ alignWith
+ (fold id size (x y -> x + size y)) (1 +| [2, 3]) ("a" +| ["b", "c"])
+ ```
+
+ # See also
+
+ * {List.Nonempty.align} - a variant that returns a list of {type OneOrBoth}
+ values.
+ }}
+
+test> data.List.Nonempty.alignWith.tests = test.verify do
+ use List.Nonempty alignWith
+ use Nat +
+ use Text size
+ f = OneOrBoth.fold id size (x y -> x + size y)
+ labeled "more elements in the first list" do
+ actual = alignWith f (1 +| [2, 3]) ("a" +| ["b"])
+ ensureEqual actual (2 +| [3, 3])
+ labeled "more elements in the second list" do
+ actual = alignWith f (1 +| [2]) ("a" +| ["b", "c"])
+ ensureEqual actual (2 +| [3, 1])
+ labeled "same number of elements" do
+ actual = alignWith f (1 +| [2, 3]) ("a" +| ["b", "c"])
+ ensureEqual actual (2 +| [3, 4])
+
+test> data.List.Nonempty.append.tests.associative = runs 100 do
+ use Nonempty append
+ x = atLeastOne natInOrder ()
+ y = atLeastOne natInOrder ()
+ z = atLeastOne natInOrder ()
+ expect (append x (append y z) === append (append x y) z)
+
+test> data.List.Nonempty.append.tests.homomorphism =
+ runs 100 do
+ use List size
+ use List.Nonempty toList
+ use Nat +
+ x = atLeastOne natInOrder ()
+ y = atLeastOne natInOrder ()
+ expect
+ (size (toList x) + size (toList y)
+ === size (toList (Nonempty.append x y)))
+
+data.List.Nonempty.appendList : List.Nonempty a -> [a] -> List.Nonempty a
+data.List.Nonempty.appendList = cases
+ Nonempty.Nonempty a as -> bs -> Nonempty.Nonempty a (as List.++ bs)
+
+data.List.Nonempty.appendList.doc : Doc
+data.List.Nonempty.appendList.doc =
+ {{
+ Appends a {type List} to the end of a {type List.Nonempty} list.
+
+ # Examples
+
+ ```
+ appendList (Nonempty.Nonempty 1 [2, 3]) [4, 5]
+ ```
+ }}
+
+data.List.Nonempty.at : Nat -> List.Nonempty a -> Optional a
+data.List.Nonempty.at index = cases
+ Nonempty.Nonempty head tail ->
+ match index with
+ 0 -> Some head
+ _ -> List.at (index Nat.- 1) tail
+
+data.List.Nonempty.at.doc : Doc
+data.List.Nonempty.at.doc =
+ use Nonempty Nonempty at
+ {{
+ `` at n `` gets the element at the position `n` in the list (using
+ [zero-based indexing](https://en.wikipedia.org/wiki/Zero-based_numbering)),
+ or returns {None} if the list has fewer than `n+1` elements.
+
+ # Examples
+
+ ```
+ at 0 (Nonempty 10 [20, 30])
+ ```
+
+ ```
+ at 2 (Nonempty 10 [20, 30])
+ ```
+
+ ```
+ at 3 (Nonempty 10 [20, 30])
+ ```
+ }}
+
+data.List.Nonempty.cons : a -> List.Nonempty a -> List.Nonempty a
+data.List.Nonempty.cons a = Nonempty.Nonempty a << List.Nonempty.toList
+
+data.List.Nonempty.cons.doc : Doc
+data.List.Nonempty.cons.doc =
+ {{
+ `` Nonempty.cons x xs `` adds the element `x` to the front of the nonempty
+ list `xs`.
+ }}
+
+data.List.Nonempty.doc : Doc
+data.List.Nonempty.doc =
+ use Nat +
+ use Nonempty ++
+ {{
+ {type List.Nonempty} is a type of list that always has at least one element.
+ Otherwise this type is identical to {type List}.
+
+ The following is a summary of operations available on {type List.Nonempty}.
+
+ # Constructing nonempty lists
+
+ A {type List.Nonempty} list consists of a single element followed by a
+ regular {type List}:
+
+ ```
+ Nonempty.Nonempty 1 [2, 3]
+ ```
+
+ You can use the {+|} function to add an element to the front of a
+ {type List}, constructing a {type List.Nonempty}:
+
+ ```
+ 1 +| [2, 3]
+ ```
+
+ You can also put the element at the end, using {|+}:
+
+ ```
+ [1, 2] |+ 3
+ ```
+
+ `` List.Nonempty.singleton x `` is a {type List.Nonempty} with just the
+ element `x`.
+
+ # Adding and removing elements
+
+ `` Nonempty.cons x xs `` adds the element `x` at the front of the
+ {type List.Nonempty} `xs`.
+
+ `` Nonempty.snoc xs x `` adds the element `x` at the end of the
+ {type List.Nonempty} `xs`.
+
+ `` appendList xs ys `` appends the {type List} ys onto the end of the
+ {type List.Nonempty} `xs`.
+
+ `` Nonempty.tail xs `` removes the first element of `xs`, returning a
+ {type List}.
+
+ `` Nonempty.init xs `` removes the last element of `xs`, returning a
+ {type List}.
+
+ # Accessing and querying elements
+
+ `` List.Nonempty.size xs `` gets the number of elements in the
+ {type List.Nonempty} `xs`.
+
+ `` Nonempty.head xs `` gets the first element of `xs`.
+
+ `` Nonempty.last xs `` gets the last element of `xs`.
+
+ # Combining nonempty lists
+
+ `` xs ++ ys `` appends `ys` to the end of `xs`.
+
+ `` Nonempty.join xs `` concatenates a whole {type List.Nonempty} list full
+ of {type List.Nonempty} lists.
+
+ # Traversals
+
+ {type List.Nonempty} allows combining elements with a binary function
+ without regard to the empty case or a default value, using {reduceLeft} and
+ {reduceRight}:
+
+ @signature{reduceLeft} @signature{reduceRight}
+
+ ```
+ reduceLeft (+) (5 +| [4, 6])
+ ```
+
+ Other traversal functions are equivalent to their {type List} counterparts:
+
+ @signature{Nonempty.scanLeft} @signature{Nonempty.scanRight}
+ @signature{List.Nonempty.map} @signature{List.Nonempty.flatMap}
+ @signature{List.Nonempty.foldLeft}
+ @signature{List.Nonempty.foldRight}
+ @signature{List.Nonempty.foldMap} @signature{Nonempty.reverse}
+
+ # Conversions to/from other types
+
+ @signature{List.Nonempty.toList} @signature{List.Nonempty.toSet}
+ }}
+
+data.List.nonempty.doc : Doc
+data.List.nonempty.doc =
+ {{
+ Turns a {type List} into a {type List.Nonempty} if it is non-empty, otherwise
+ calls {abort}.
+ }}
+
+data.List.Nonempty.examples.construction.ex1 : List.Nonempty Nat
+data.List.Nonempty.examples.construction.ex1 = Nonempty.Nonempty 1 [2, 3]
+
+data.List.Nonempty.examples.construction.ex2 : Optional (List.Nonempty Nat)
+data.List.Nonempty.examples.construction.ex2 = mayNonempty [1, 2, 3]
+
+data.List.Nonempty.filterMap :
+ (a ->{g} Optional b) -> List.Nonempty a ->{g} Optional (List.Nonempty b)
+data.List.Nonempty.filterMap f = cases
+ Nonempty.Nonempty a [] -> Optional.map List.Nonempty.singleton (f a)
+ Nonempty.Nonempty a (a1 +: as) ->
+ match f a with
+ None -> data.List.Nonempty.filterMap f (Nonempty.Nonempty a1 as)
+ Some b ->
+ match data.List.Nonempty.filterMap f (Nonempty.Nonempty a1 as) with
+ Some bs -> Some (Nonempty.cons b bs)
+ None -> Some (List.Nonempty.singleton b)
+
+data.List.Nonempty.filterMap.doc : Doc
+data.List.Nonempty.filterMap.doc =
+ use Nat >
+ {{
+ Maps a function over the values of a non-empty list, possibly removing some
+ values. It is equivalent to running ``map f |> somes``.
+
+ # Example
+
+ ```
+ f a = if Text.size a > 5 then Some a else None
+ List.Nonempty.filterMap f ("Circuit" +| ["Quasar", "Voyage"])
+ ```
+
+ # See also
+
+ * {Nonempty.traverseOptional} - where `` None `` is returned if any `f a`
+ returns `` None ``
+ }}
+
+test> data.List.Nonempty.filterMap.test.example = test.verify do
+ use List.Nonempty filterMap
+ use Nat >
+ use Text size
+ labeled "with all Nones" do
+ f a = if size a > 6 then Some a else None
+ actual = filterMap f ("Circuit" +| ["Quasar", "Voyage"])
+ ensureEqual actual (Some ("Circuit" +| []))
+ labeled "with some Somes" do
+ f a = if size a > 10 then Some a else None
+ actual = filterMap f ("Circuit" +| ["Quasar", "Voyage"])
+ ensureEqual actual None
+
+data.List.Nonempty.flatMap :
+ (a ->{e} List.Nonempty b) -> List.Nonempty a ->{e} List.Nonempty b
+data.List.Nonempty.flatMap f = cases
+ Nonempty.Nonempty a as ->
+ match List.flatMap (List.Nonempty.toList << f) as with
+ x +: xs -> f a Nonempty.++ Nonempty.Nonempty x xs
+ [] -> f a
+
+data.List.Nonempty.flatMap.doc : Doc
+data.List.Nonempty.flatMap.doc =
+ {{
+ Builds a new nonempty list which contains all the elements that result from
+ applying a function to each of the elements of an existing list.
+ }}
+
+test> data.List.Nonempty.flatMap.tests.flatMapIdIsjoin = runs 100 do
+ xs = atLeastOne (atLeastOne natInOrder) ()
+ expect (List.Nonempty.flatMap id xs === Nonempty.join xs)
+
+data.List.Nonempty.foldLeft :
+ (b ->{e} a ->{e} b) -> b -> List.Nonempty a ->{e} b
+data.List.Nonempty.foldLeft f b = cases
+ Nonempty.Nonempty h t -> List.foldLeft f (f b h) t
+
+data.List.Nonempty.foldLeft.doc : Doc
+data.List.Nonempty.foldLeft.doc =
+ use Nat +
+ {{
+ Folds a {type List.Nonempty} from left to right using the given function and
+ initial accumulator value.
+
+ # Example
+
+ ```
+ List.Nonempty.foldLeft (+) 0 (1 +| [2, 3])
+ ```
+ }}
+
+test> data.List.Nonempty.foldLeft.tests.listConsistency =
+ runs 100 do
+ use Int -
+ use gen int
+ nel = atLeastOne int ()
+ z = int()
+ op = (-)
+ expect
+ (assertEquals
+ (List.Nonempty.foldLeft op z nel)
+ (List.foldLeft op z (List.Nonempty.toList nel)))
+
+test> data.List.Nonempty.foldLeft.tests.multiple =
+ use Nat +
+ check
+ (assertEquals
+ (List.Nonempty.foldLeft (+) 1 (Nonempty.Nonempty 2 [3, 4])) 10)
+
+test> data.List.Nonempty.foldLeft.tests.single =
+ use Nat +
+ check
+ (assertEquals (List.Nonempty.foldLeft (+) 1 (Nonempty.Nonempty 2 [])) 3)
+
+data.List.Nonempty.foldMap :
+ (b ->{e} b ->{e} b) -> (a ->{e} b) -> List.Nonempty a ->{e} b
+data.List.Nonempty.foldMap semigroup f = cases
+ Nonempty.Nonempty a as ->
+ List.foldLeft (acc x -> semigroup acc (f x)) (f a) as
+
+data.List.Nonempty.foldMap.doc : Doc
+data.List.Nonempty.foldMap.doc =
+ use Text ++
+ {{
+ Transform every value of a nonempty list with a unary function, then apply an
+ associative binary operator to all the results.
+
+ # Example
+
+ ```
+ List.Nonempty.foldMap (++) Nat.toText (1 +| [2, 3])
+ ```
+ }}
+
+data.List.Nonempty.foldMap.examples.ex1 : Text
+data.List.Nonempty.foldMap.examples.ex1 =
+ use Text ++
+ List.Nonempty.foldMap (++) Nat.toText (1 +| [2, 3])
+
+test> data.List.Nonempty.foldMap.test =
+ runs 100 do
+ use Nat toText
+ use Text ++
+ x = natInOrder()
+ y = natInOrder()
+ z = natInOrder()
+ f = List.Nonempty.foldMap (++) toText
+ expect
+ (f (x +| [y, z]) === (toText x ++ toText y ++ toText z)
+ && f (x +| [y]) === (toText x ++ toText y)
+ && f (List.Nonempty.singleton x) === toText x)
+
+data.List.Nonempty.foldRight :
+ (a ->{e} b ->{e} b) -> b -> List.Nonempty a ->{e} b
+data.List.Nonempty.foldRight f b = cases
+ Nonempty.Nonempty h t -> f h (List.foldRight f b t)
+
+data.List.Nonempty.foldRight.doc : Doc
+data.List.Nonempty.foldRight.doc =
+ use Nat +
+ {{
+ Folds a {type List.Nonempty} from right to left using the given function and
+ initial accumulator value.
+
+ # Example
+
+ ```
+ List.Nonempty.foldRight (+) 0 (1 +| [2, 3])
+ ```
+ }}
+
+test> data.List.Nonempty.foldRight.tests.listConsistency =
+ runs 100 do
+ use Int -
+ use gen int
+ nel = atLeastOne int ()
+ z = int()
+ op = (-)
+ expect
+ (assertEquals
+ (List.Nonempty.foldRight op z nel)
+ (List.foldRight op z (List.Nonempty.toList nel)))
+
+test> data.List.Nonempty.foldRight.tests.multiple =
+ use Int -
+ check
+ (assertEquals
+ (List.Nonempty.foldRight (-) +1 (Nonempty.Nonempty +5 [+4, +2]))
+ (+5 - +4 - +2 - +1))
+
+test> data.List.Nonempty.foldRight.tests.single =
+ use Int -
+ check
+ (assertEquals (List.Nonempty.foldRight (-) +1 (Nonempty.Nonempty +3 [])) +2)
+
+data.List.Nonempty.head : List.Nonempty a -> a
+data.List.Nonempty.head = cases Nonempty.Nonempty a _ -> a
+
+data.List.Nonempty.head.doc : Doc
+data.List.Nonempty.head.doc =
+ {{ Returns the first element of a nonempty list. }}
+
+test> data.List.Nonempty.head.test = runs 100 do
+ a = natInOrder()
+ as = gen.listOf natInOrder ()
+ expect (Nonempty.head (Nonempty.Nonempty a as) === a)
+
+data.List.Nonempty.init : List.Nonempty a -> [a]
+data.List.Nonempty.init = cases
+ Nonempty.Nonempty a [] -> []
+ Nonempty.Nonempty a (xs :+ _) -> a List.+: xs
+
+data.List.Nonempty.init.doc : Doc
+data.List.Nonempty.init.doc =
+ {{ Returns all but the last element of a nonempty list. }}
+
+test> data.List.Nonempty.init.test = runs 100 do
+ a = natInOrder()
+ as = gen.listOf natInOrder ()
+ expect (Nonempty.init (as |+ a) === as)
+
+data.List.Nonempty.join : List.Nonempty (List.Nonempty a) -> List.Nonempty a
+data.List.Nonempty.join = cases
+ Nonempty.Nonempty (Nonempty.Nonempty a as) nels ->
+ Nonempty.Nonempty a (as List.++ List.flatMap List.Nonempty.toList nels)
+
+data.List.Nonempty.join.doc : Doc
+data.List.Nonempty.join.doc =
+ {{
+ Concatenates all the nonempty lists in a nonempty list, so that the resulting
+ list contains all the elements of those lists, in the order they appear in
+ the input.
+
+ # Example
+
+ ```
+ Nonempty.join (1 +| [2] +| [3 +| [4, 5]])
+ ```
+ }}
+
+data.List.Nonempty.join.examples.ex1 : List.Nonempty Nat
+data.List.Nonempty.join.examples.ex1 =
+ use Nonempty Nonempty
+ Nonempty.join (Nonempty (Nonempty 1 [2]) [Nonempty 3 [4, 5]])
+
+test> data.List.Nonempty.join.tests.associative = runs 100 do
+ use Nonempty join
+ nels = atLeastOne (atLeastOne (atLeastOne natInOrder)) ()
+ expect (join (join nels) === join (List.Nonempty.map join nels))
+
+test> data.List.Nonempty.join.tests.unit = runs 100 do
+ use List.Nonempty singleton
+ use Nonempty join
+ nats = atLeastOne natInOrder ()
+ right = join (List.Nonempty.map singleton nats)
+ left = join (singleton nats)
+ expect ((left === right && left === nats) === right === nats)
+
+data.List.Nonempty.last : List.Nonempty a -> a
+data.List.Nonempty.last = cases
+ Nonempty.Nonempty a [] -> a
+ Nonempty.Nonempty a (xs :+ x) -> x
+
+data.List.Nonempty.last.doc : Doc
+data.List.Nonempty.last.doc =
+ {{ Returns the last element of a nonempty list. }}
+
+test> data.List.Nonempty.last.test = runs 100 do
+ a = natInOrder()
+ as = gen.listOf natInOrder ()
+ expect (Nonempty.last (as |+ a) === a)
+
+data.List.Nonempty.map : (a ->{e} b) -> List.Nonempty a ->{e} List.Nonempty b
+data.List.Nonempty.map f = cases
+ Nonempty.Nonempty a as -> Nonempty.Nonempty (f a) (List.map f as)
+
+data.List.Nonempty.map.doc : Doc
+data.List.Nonempty.map.doc =
+ {{ Applies the given function to every element of the nonempty list. }}
+
+test> data.List.Nonempty.map.tests.functor = runs 100 do
+ nats = atLeastOne natInOrder ()
+ nats' = List.Nonempty.map id nats
+ expect (nats' === nats)
+
+data.List.Nonempty.maximum : List.Nonempty a -> a
+data.List.Nonempty.maximum list =
+ use Nonempty Nonempty
+ go = cases
+ y, xs ->
+ match List.maximum xs with
+ Some x
+ | Universal.gt y x -> y
+ | otherwise -> x
+ _ -> y
+ match list with
+ Nonempty x [] -> x
+ Nonempty x xs -> go x xs
+
+data.List.Nonempty.maximum.doc : Doc
+data.List.Nonempty.maximum.doc =
+ {{
+ Returns the maximum element of a nonempty list.
+
+ # Examples
+
+ ```
+ Nonempty.maximum (1 +| [2, 3, 4, 5])
+ ```
+
+ ```
+ Nonempty.maximum (5 +| [4, 3, 2, 1])
+ ```
+
+ ```
+ Nonempty.maximum (List.Nonempty.singleton 1)
+ ```
+
+ ```
+ Nonempty.maximum (1 +| [2, 3, 4, 5, 4, 3, 2, 1])
+ ```
+
+ # See also
+
+ * {Nonempty.maximumBy} - Returns the maximum element of a nonempty list
+ using a custom comparison function.
+ * {Nonempty.minimum} - Returns the minimum element of a nonempty list.
+ * {List.maximum} - Returns the maximum element of a (possibly empty) list.
+ }}
+
+test> data.List.Nonempty.maximum.test = test.verify do
+ use List +:
+ _ = Each.range 0 100
+ x = Random.nat!
+ xs = Random.listOf Random.nat do 10
+ f = compareOn id
+ lhs = Nonempty.maximum (x +| xs) |> Some
+ rhs = List.maximum (x +: xs)
+ ensure (lhs === rhs)
+
+data.List.Nonempty.maximumBy :
+ (a ->{e} a ->{e} Ordering) -> List.Nonempty a ->{e} a
+data.List.Nonempty.maximumBy f list =
+ use Nonempty Nonempty
+ go = cases
+ y, xs ->
+ match List.maximumBy f xs with
+ None -> y
+ Some x
+ | f x y === Greater -> x
+ | otherwise -> y
+ match list with
+ Nonempty x [] -> x
+ Nonempty x xs -> go x xs
+
+data.List.Nonempty.maximumBy.doc : Doc
+data.List.Nonempty.maximumBy.doc =
+ use Universal ordering
+ {{
+ Returns the maximum element of a nonempty list using a custom comparison
+ function.
+
+ # Examples
+
+ ```
+ Nonempty.maximumBy ordering (1 +| [2, 3, 4, 5])
+ ```
+
+ ```
+ Nonempty.maximumBy (compose2 Ordering.inverse ordering) (1 +| [2, 3, 4, 5])
+ ```
+
+ ```
+ Nonempty.maximumBy ordering (List.Nonempty.singleton 1)
+ ```
+
+ ```
+ Nonempty.maximumBy ordering (1 +| [2, 3, 4, 5, 4, 3, 2, 1])
+ ```
+
+ # See also
+
+ * {Nonempty.maximum} - Returns the maximum element of a nonempty list.
+ * {Nonempty.minimumBy} - Returns the minimum element of a nonempty list
+ using a custom comparison function.
+ * {List.maximumBy} - Returns the maximum element of a (possibly empty) list
+ using a custom comparison function.
+ }}
+
+test> data.List.Nonempty.maximumBy.test = test.verify do
+ use List +:
+ _ = Each.range 0 100
+ x = Random.nat!
+ xs = Random.listOf Random.nat do 10
+ f = compareOn id
+ lhs = Nonempty.maximumBy f (x +| xs) |> Some
+ rhs = List.maximumBy f (x +: xs)
+ ensure (lhs === rhs)
+
+data.List.Nonempty.minimum : List.Nonempty a -> a
+data.List.Nonempty.minimum list =
+ use Nonempty Nonempty
+ go = cases
+ y, xs ->
+ match List.minimum xs with
+ Some x
+ | Universal.lt y x -> y
+ | otherwise -> x
+ _ -> y
+ match list with
+ Nonempty x [] -> x
+ Nonempty x xs -> go x xs
+
+data.List.Nonempty.minimum.doc : Doc
+data.List.Nonempty.minimum.doc =
+ {{
+ Returns the minimum element of a nonempty list.
+
+ # Examples
+
+ ```
+ Nonempty.minimum (1 +| [2, 3, 4, 5])
+ ```
+
+ ```
+ Nonempty.minimum (5 +| [4, 3, 2, 1])
+ ```
+
+ ```
+ Nonempty.minimum (List.Nonempty.singleton 1)
+ ```
+
+ ```
+ Nonempty.minimum (1 +| [2, 3, 4, 5, 4, 3, 2, 1])
+ ```
+
+ # See also
+
+ * {Nonempty.maximum} - Returns the maximum element of a nonempty list.
+ * {Nonempty.minimumBy} - Returns the minimum element of a nonempty list
+ using a custom comparison function.
+ * {List.minimum} - Returns the minimum element of a (possibly empty) list.
+ }}
+
+test> data.List.Nonempty.minimum.test = test.verify do
+ use List +:
+ _ = Each.range 0 100
+ x = Random.nat!
+ xs = Random.listOf Random.nat do 10
+ f = compareOn id
+ lhs = Nonempty.minimum (x +| xs) |> Some
+ rhs = List.minimum (x +: xs)
+ ensure (lhs === rhs)
+
+data.List.Nonempty.minimumBy :
+ (a ->{e} a ->{e} Ordering) -> List.Nonempty a ->{e} a
+data.List.Nonempty.minimumBy f list =
+ use Nonempty Nonempty
+ go = cases
+ y, xs ->
+ match List.minimumBy f xs with
+ None -> y
+ Some x
+ | f x y === Less -> x
+ | otherwise -> y
+ match list with
+ Nonempty x [] -> x
+ Nonempty x xs -> go x xs
+
+data.List.Nonempty.minimumBy.doc : Doc
+data.List.Nonempty.minimumBy.doc =
+ use Universal ordering
+ {{
+ Returns the minimum element of a nonempty list using a custom comparison
+ function.
+
+ # Examples
+
+ ```
+ Nonempty.minimumBy ordering (1 +| [2, 3, 4, 5])
+ ```
+
+ ```
+ Nonempty.minimumBy (compose2 Ordering.inverse ordering) (1 +| [2, 3, 4, 5])
+ ```
+
+ ```
+ Nonempty.minimumBy ordering (List.Nonempty.singleton 1)
+ ```
+
+ ```
+ Nonempty.minimumBy ordering (1 +| [2, 3, 4, 5, 4, 3, 2, 1])
+ ```
+
+ # See also
+
+ * {Nonempty.maximumBy} - Returns the maximum element of a nonempty list
+ using a custom comparison function.
+ * {Nonempty.minimum} - Returns the minimum element of a nonempty list.
+ * {List.minimumBy} - Returns the minimum element of a (possibly empty) list
+ using a custom comparison function.
+ }}
+
+test> data.List.Nonempty.minimumBy.test = test.verify do
+ use List +:
+ _ = Each.range 0 100
+ x = Random.nat!
+ xs = Random.listOf Random.nat do 10
+ f = compareOn id
+ lhs = Nonempty.minimumBy f (x +| xs) |> Some
+ rhs = List.minimumBy f (x +: xs)
+ ensure (lhs === rhs)
+
+data.List.Nonempty.prependList : [a] -> List.Nonempty a -> List.Nonempty a
+data.List.Nonempty.prependList = cases
+ [], xs -> xs
+ x +: xs, Nonempty.Nonempty y ys ->
+ Nonempty.Nonempty x (xs List.++ (y List.+: ys))
+
+data.List.Nonempty.prependList.doc : Doc
+data.List.Nonempty.prependList.doc =
+ {{
+ Prepends a list to a non-empty list.
+
+ # Examples
+
+ ```
+ prependList [1, 2] (3 +| [4])
+ ```
+
+ ```
+ prependList [] (3 +| [4])
+ ```
+ }}
+
+data.List.Nonempty.randomChoice : List.Nonempty a ->{Random} a
+data.List.Nonempty.randomChoice l =
+ randomIndex = Random.natIn 0 (List.Nonempty.size l)
+ Nonempty.at randomIndex l
+ |> getOrBug "List.Nonempty.randomChoice: index out of bounds"
+
+data.List.Nonempty.randomChoice.doc : Doc
+data.List.Nonempty.randomChoice.doc =
+ use List.Nonempty randomChoice
+ use Nonempty Nonempty
+ {{
+ Returns a random element from the given {type List.Nonempty}.
+
+ # Examples
+
+ ```
+ lcg 4096 do randomChoice (Nonempty 0 [1, 3, 5, 7, 9])
+ ```
+
+ ```
+ lcg 2510 do randomChoice (Nonempty 0 [1, 3, 5, 7, 9])
+ ```
+ }}
+
+test> data.List.Nonempty.randomChoice.test = test.verify do
+ list = Nonempty.Nonempty 0 [1, 2, 3, 4, 5, 6, 7, 8, 9]
+ set = List.toSet [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ Each.repeat 1000
+ e = List.Nonempty.randomChoice list
+ ensure (Set.contains e set)
+
+data.List.Nonempty.reduceLeft : (a ->{e} a ->{e} a) -> List.Nonempty a ->{e} a
+data.List.Nonempty.reduceLeft f = cases
+ Nonempty.Nonempty a as -> List.foldLeft f a as
+
+data.List.Nonempty.reduceLeft.doc : Doc
+data.List.Nonempty.reduceLeft.doc =
+ use Nat /
+ {{
+ Applies a binary operator to all elements of the nonempty list, associating
+ to the left.
+
+ Contrast with {reduceRight} which associates to the right.
+
+ # Example
+
+ ```
+ reduceLeft (/) (10 +| [5, 2])
+ ```
+
+ This is equivalent to:
+
+ ```
+ 10 / 5 / 2
+ ```
+ }}
+
+test> data.List.Nonempty.reduceLeft.test =
+ runs 100 do
+ use Nat -
+ x = natInOrder()
+ y = natInOrder()
+ z = natInOrder()
+ expect
+ (reduceLeft (-) (x +| [y, z]) === x - y - z
+ && reduceLeft (-) (x +| [y]) === x - y
+ && reduceLeft (-) (List.Nonempty.singleton x) === x)
+
+data.List.Nonempty.reduceRight : (a ->{e} a ->{e} a) -> List.Nonempty a ->{e} a
+data.List.Nonempty.reduceRight f as =
+ List.foldRight f (Nonempty.last as) (Nonempty.init as)
+
+data.List.Nonempty.reduceRight.doc : Doc
+data.List.Nonempty.reduceRight.doc =
+ use Nat /
+ {{
+ Applies a binary operator to all elements of the nonempty list, associating
+ to the right.
+
+ Contrast with {reduceLeft} which associates to the left.
+
+ # Example
+
+ ```
+ reduceRight (/) (10 +| [5, 2])
+ ```
+
+ This is equivalent to:
+
+ ```
+ 10 / 5 / 2
+ ```
+ }}
+
+test> data.List.Nonempty.reduceRight.test =
+ runs 100 do
+ use Nat -
+ x = natInOrder()
+ y = natInOrder()
+ z = natInOrder()
+ expect
+ (reduceRight (-) (x +| [y, z]) === x - y - z
+ && reduceRight (-) (x +| [y]) === x - y
+ && reduceRight (-) (List.Nonempty.singleton x) === x)
+
+data.List.Nonempty.reverse : List.Nonempty a -> List.Nonempty a
+data.List.Nonempty.reverse = cases
+ Nonempty.Nonempty a as -> List.reverse as |+ a
+
+data.List.Nonempty.reverse.doc : Doc
+data.List.Nonempty.reverse.doc =
+ {{
+ Reverses a {type List.Nonempty} list.
+
+ # Example
+
+ ```
+ Nonempty.reverse (Nonempty.Nonempty 1 [2, 3, 4])
+ ```
+ }}
+
+data.List.Nonempty.scanLeft :
+ (a ->{e} a ->{e} a) -> List.Nonempty a ->{e} List.Nonempty a
+data.List.Nonempty.scanLeft f = cases
+ Nonempty.Nonempty x xs -> List.scanLeft f x xs
+
+data.List.Nonempty.scanLeft.doc : Doc
+data.List.Nonempty.scanLeft.doc =
+ use Nat + / == >
+ use Nonempty scanLeft
+ {{
+ `` scanLeft f xs `` applies the function `f` to the first two elements of
+ `xs`, then applies `f` to the result and the next element of `xs`, and so on.
+ Returns the list of intermediate results, with the final result as the last
+ element of the output. The first element of the output list is always the
+ same as the first element of the input list.
+
+ Note that the last element of the output list will equal ``reduceLeft f xs``.
+
+ See also {scanLeft} which scans the list in the other direction.
+
+ # Examples
+
+ ```
+ scanLeft (+) (1 +| [2, 3, 4])
+ ```
+
+ ```
+ scanLeft (/) (64 +| [4, 2, 8])
+ ```
+
+ ```
+ scanLeft (/) (List.Nonempty.singleton 12)
+ ```
+
+ ```
+ scanLeft Boolean.and (3 > 1 +| [3 > 2, 4 > 6, 5 == 5])
+ ```
+
+ ```
+ scanLeft Universal.max (3 +| [6, 12, 4, 55, 11])
+ ```
+
+ ```
+ scanLeft (x y -> (x + y) / 2) (3 +| [5, 10, 5])
+ ```
+ }}
+
+test> data.List.Nonempty.scanLeft.test =
+ runs 100 do
+ use Int -
+ use Nonempty Nonempty
+ use gen int
+ x = int()
+ y = int()
+ z = int()
+ expect
+ (Nonempty.scanLeft (-) (Nonempty x [y, z])
+ === Nonempty x [x - y, x - y - z])
+
+data.List.Nonempty.scanRight :
+ (a ->{e} a ->{e} a) -> List.Nonempty a ->{e} List.Nonempty a
+data.List.Nonempty.scanRight f = cases
+ Nonempty.Nonempty x (ys :+ y) -> List.scanRight f y (x List.+: ys)
+ xs -> xs
+
+data.List.Nonempty.scanRight.doc : Doc
+data.List.Nonempty.scanRight.doc =
+ use Nat + / == >
+ use Nonempty scanRight
+ {{
+ `` scanRight f xs `` applies the function `f` to the last two elements of
+ `xs`, then applies `f` to the result and the previous element of `xs`, and so
+ on. Returns the list of intermediate results, with the final result at the
+ head. The last element of the output list is always the same as the last
+ element of the input list.
+
+ Note that the first element of the output list will equal
+ ``reduceRight f xs``.
+
+ See also {scanRight} which scans the list in the other direction.
+
+ # Examples
+
+ ```
+ scanRight (+) (1 +| [2, 3, 4])
+ ```
+
+ ```
+ scanRight (/) (32 +| [16, 8, 2])
+ ```
+
+ ```
+ scanRight (/) (List.Nonempty.singleton 12)
+ ```
+
+ ```
+ scanRight Boolean.and (3 > 1 +| [3 > 2, 4 > 6, 5 == 5])
+ ```
+
+ ```
+ scanRight Universal.max (3 +| [6, 12, 4, 55, 11])
+ ```
+
+ ```
+ scanRight (x y -> (x + y) / 2) (3 +| [5, 10, 5])
+ ```
+ }}
+
+test> data.List.Nonempty.scanRight.test =
+ runs 100 do
+ use Int -
+ use Nonempty Nonempty
+ use gen int
+ x = int()
+ y = int()
+ z = int()
+ expect
+ (Nonempty.scanRight (-) (Nonempty x [y, z])
+ === Nonempty (x - y - z) [y - z, z])
+
+data.List.Nonempty.sequenceOptional :
+ List.Nonempty (Optional a) -> Optional (List.Nonempty a)
+data.List.Nonempty.sequenceOptional = cases
+ Nonempty.Nonempty None _ -> None
+ Nonempty.Nonempty (Some a) as ->
+ Optional.map2 (+|) (Some a) (List.sequenceOptional as)
+
+data.List.Nonempty.sequenceOptional.doc : Doc
+data.List.Nonempty.sequenceOptional.doc =
+ use Nonempty sequenceOptional
+ {{
+ {sequenceOptional} transforms a {type List.Nonempty} of {type Optional}
+ values into an {type Optional} of {type List.Nonempty} of values so that:
+
+ * If one of the values is {None} then the whole result is {None}.
+ * Otherwise all the values are collected into ``Some values``.
+
+ # Examples
+
+ ```
+ sequenceOptional (Some 1 +| [None, Some 3])
+ ```
+
+ ```
+ sequenceOptional (Some 1 +| [Some 2, Some 3])
+ ```
+ }}
+
+test> data.List.Nonempty.sequenceOptional.test.example = test.verify do
+ use Nonempty sequenceOptional
+ labeled "with some Nones" do
+ actual = sequenceOptional (Some 1 +| [None, Some 3])
+ ensureEqual actual None
+ labeled "with no Nones" do
+ actual = sequenceOptional (Some 1 +| [Some 2, Some 3])
+ ensureEqual actual (Some (1 +| [2, 3]))
+
+data.List.Nonempty.singleton : a -> List.Nonempty a
+data.List.Nonempty.singleton a = Nonempty.Nonempty a []
+
+data.List.Nonempty.singleton.doc : Doc
+data.List.Nonempty.singleton.doc =
+ {{ Constructs a nonempty list with just the given element. }}
+
+test> data.List.Nonempty.singleton.test = runs 100 do
+ n = natInOrder()
+ expect (List.Nonempty.size (List.Nonempty.singleton n) === 1)
+
+data.List.Nonempty.size : List.Nonempty a -> Nat
+data.List.Nonempty.size = cases Nonempty.Nonempty _ as -> List.size as Nat.+ 1
+
+data.List.Nonempty.size.doc : Doc
+data.List.Nonempty.size.doc =
+ {{ Returns the number of elements in the given nonempty list. }}
+
+test> data.List.Nonempty.size.test =
+ runs 100 do
+ use Nat +
+ units = atLeastOne (do ()) ()
+ expect
+ (List.Nonempty.size units
+ === List.foldLeft
+ (+) 0 (List.Nonempty.toList (List.Nonempty.map (const 1) units)))
+
+data.List.Nonempty.snoc : List.Nonempty a -> a -> List.Nonempty a
+data.List.Nonempty.snoc = cases
+ Nonempty.Nonempty head middle ->
+ last -> Nonempty.Nonempty head (middle List.:+ last)
+
+data.List.Nonempty.snoc.doc : Doc
+data.List.Nonempty.snoc.doc =
+ {{ Adds the given element to the end of the nonempty list. }}
+
+data.List.Nonempty.somes :
+ List.Nonempty (Optional a) -> Optional (List.Nonempty a)
+data.List.Nonempty.somes = cases
+ Nonempty.Nonempty None [] -> None
+ Nonempty.Nonempty None (a +: as) ->
+ data.List.Nonempty.somes (Nonempty.Nonempty a as)
+ Nonempty.Nonempty (Some a) as -> Some (Nonempty.Nonempty a (List.somes as))
+
+data.List.Nonempty.somes.doc : Doc
+data.List.Nonempty.somes.doc =
+ use Nonempty somes
+ {{
+ @signature{somes}
+
+ `` somes xs `` flattens a {type List.Nonempty} of {type Optional} values
+ returning a non-empty list of the values present within the {type Optional}
+ values in `xs`. If the initial list contains only {None} values then {None}
+ is returned.
+
+ # Example
+
+ ```
+ somes (Some 1 +| [Some 2, None, Some 3, None])
+ ```
+ }}
+
+test> data.List.Nonempty.somes.tests.ex1 =
+ test.verify do
+ use Nonempty somes
+ ensureEqual
+ (somes (Some 1 +| [Some 2, None, Some 3, None])) (Some (1 +| [2, 3]))
+ ensureEqual (somes (None +| [None, None])) None
+
+data.List.Nonempty.tail : List.Nonempty a -> [a]
+data.List.Nonempty.tail = cases Nonempty.Nonempty _ as -> as
+
+data.List.Nonempty.tail.doc : Doc
+data.List.Nonempty.tail.doc =
+ {{ Returns all but the first element of a nonempty list. }}
+
+test> data.List.Nonempty.tail.test = runs 100 do
+ a = natInOrder()
+ as = gen.listOf natInOrder ()
+ expect (Nonempty.tail (Nonempty.Nonempty a as) === as)
+
+data.List.Nonempty.toList : List.Nonempty a -> [a]
+data.List.Nonempty.toList = cases Nonempty.Nonempty a as -> a List.+: as
+
+data.List.Nonempty.toList.doc : Doc
+data.List.Nonempty.toList.doc =
+ {{ Turns a nonempty list into an ordinary {type List}. }}
+
+data.List.Nonempty.toSet : List.Nonempty a -> Set a
+data.List.Nonempty.toSet = cases
+ Nonempty.Nonempty x xs -> Set.insert x (List.toSet xs)
+
+data.List.Nonempty.toSet.doc : Doc
+data.List.Nonempty.toSet.doc =
+ {{ Converts a {type List.Nonempty} to its {type Set} of distinct elements. }}
+
+data.List.Nonempty.traverseOptional :
+ (a ->{g} Optional b) -> List.Nonempty a ->{g} Optional (List.Nonempty b)
+data.List.Nonempty.traverseOptional f = cases
+ Nonempty.Nonempty a [] -> Optional.map List.Nonempty.singleton (f a)
+ Nonempty.Nonempty a (a1 +: as) ->
+ Optional.map2
+ Nonempty.cons
+ (f a)
+ (data.List.Nonempty.traverseOptional f (Nonempty.Nonempty a1 as))
+
+data.List.Nonempty.traverseOptional.doc : Doc
+data.List.Nonempty.traverseOptional.doc =
+ use Nat + isEven
+ use Nonempty traverseOptional
+ {{
+ {traverseOptional} applies a function to each element of a
+ {type List.Nonempty}. If any of the results are {None} then the whole result
+ is {None}. Otherwise all the results are collected into ``Some results``.
+
+ # Examples
+
+ ```
+ traverseOptional
+ (n -> (if isEven n then Some (n + 1) else None)) (1 +| [2, 3])
+ ```
+
+ ```
+ traverseOptional
+ (n -> (if isEven n then Some (n + 1) else None)) (2 +| [4, 6])
+ ```
+ }}
+
+test> data.List.Nonempty.traverseOptional.test.example = test.verify do
+ use Nat +
+ use Nonempty traverseOptional
+ f n = if Nat.isEven n then Some (n + 1) else None
+ labeled "with some Nones" do
+ actual = traverseOptional f (1 +| [2, 3])
+ ensureEqual actual None
+ labeled "with no Nones" do
+ actual = traverseOptional f (2 +| [4, 6])
+ ensureEqual actual (Some (3 +| [5, 7]))
+
+data.List.Nonempty.zipWith :
+ (a ->{g1} b ->{g} c)
+ -> List.Nonempty a
+ -> List.Nonempty b
+ ->{g1, g} List.Nonempty c
+data.List.Nonempty.zipWith f = cases
+ Nonempty.Nonempty x xs, Nonempty.Nonempty y ys ->
+ f x y +| List.zipWith f xs ys
+
+data.List.Nonempty.zipWith.doc : Doc
+data.List.Nonempty.zipWith.doc =
+ use Nat +
+ use Nonempty zipWith
+ use Tuple pair
+ {{
+ Apply a function to corresponding elements of two nonempty lists, producing a
+ nonempty list of the results. The output list is the same length as the
+ shorter of the two input lists. Each element of the output list is the result
+ of applying the function to the elements of the input lists at the same
+ position. If one of the input lists is shorter than the other, the extra
+ elements of the longer list are ignored.
+
+ # Examples
+
+ ```
+ xs = 1 +| [2, 3, 4, 5]
+ ys = 7 +| [8, 9, 10, 11]
+ zipWith (+) xs ys
+ ```
+
+ The length of the output list is the length of the shorter input list:
+
+ ```
+ xs = 1 +| [2, 3]
+ ys = 7 +| [8, 9, 10, 11]
+ zipWith pair xs ys
+ ```
+
+ ```
+ xs = 1 +| [2, 3, 4, 5]
+ ys = 7 +| [8, 9]
+ zipWith pair xs ys
+ ```
+ }}
+
+(data.List.Nonempty.|+) : [a] -> a -> List.Nonempty a
+(data.List.Nonempty.|+) = cases
+ [] -> a -> Nonempty.Nonempty a []
+ x +: xs -> a -> Nonempty.Nonempty x (xs List.:+ a)
+
+data.List.Nonempty.|+.doc : Doc
+data.List.Nonempty.|+.doc =
+ {{
+ Constructs a {type List.Nonempty} from a {type List} for the {Nonempty.init}
+ and an element for the {Nonempty.last}.
+
+ # Example
+
+ ```
+ [1, 2] |+ 3
+ ```
+ }}
+
+data.List.nonEmptySubsequences : [a] -> [List.Nonempty a]
+data.List.nonEmptySubsequences list =
+ use List +: Nonempty
+ f : a -> Nonempty a -> [Nonempty a] -> [Nonempty a]
+ f x ys r = ys +: (Nonempty.cons x ys +: r)
+ match list with
+ [] -> []
+ x +: xs ->
+ List.Nonempty.singleton x
+ +: List.foldRight (f x) [] (data.List.nonEmptySubsequences xs)
+
+data.List.nonEmptySubsequences.doc : Doc
+data.List.nonEmptySubsequences.doc =
+ {{
+ Returns the list of all subsequences of the argument, which have at least one
+ element.
+ }}
+
+test> data.List.nonEmptySubsequences.tests.base =
+ use List.Nonempty singleton
+ check
+ (nonEmptySubsequences [1, 2, 3]
+ === [ singleton 1
+ , singleton 2
+ , 1 +| [2]
+ , singleton 3
+ , 1 +| [3]
+ , 2 +| [3]
+ , 1 +| [2, 3]
+ ])
+
+data.List.of : x -> Nat -> [x]
+data.List.of = flip List.fill
+
+data.List.of.doc : Doc
+data.List.of.doc =
+ use List of
+ {{
+ `` of x sz `` creates a new {type List} of size `sz`, filled with the value
+ `x`.
+
+ # Example
+
+ ```
+ of 0 4
+ ```
+
+ # See also
+
+ * {List.fill} for a version of this that takes the arguments in the
+ opposite order.
+ }}
+
+data.List.partition : (a ->{g} Boolean) -> [a] ->{g} ([a], [a])
+data.List.partition p =
+ use List +:
+ go = cases
+ (accTrue, accFalse) ->
+ cases
+ [] -> (accTrue, accFalse)
+ x +: xs ->
+ if p x then go (x +: accTrue, accFalse) xs
+ else go (accTrue, x +: accFalse) xs
+ go ([], [])
+
+data.List.partition.doc : Doc
+data.List.partition.doc =
+ {{
+ Partitions a list into two lists, one containing the elements that satisfy
+ the given predicate, and one containing the elements that don't.
+
+ # Example
+
+ ```
+ List.partition Nat.isEven [1, 2, 3, 4, 5]
+ ```
+ }}
+
+data.List.partitionBy : (a ->{g} Boolean) -> [a] ->{g} [List.Nonempty a]
+data.List.partitionBy f = groupSublistsBy (a b -> iff (f a) (f b))
+
+data.List.partitionBy.doc : Doc
+data.List.partitionBy.doc =
+ {{
+ Partitions a list into sublists, where each sublist contains elements that
+ are equal according to the given function.
+
+ # Example
+
+ ```
+ List.map
+ List.Nonempty.toList (partitionBy Nat.isEven [5, 2, 4, 4, 2, 6, 7, 7, 8])
+ ```
+ }}
+
+data.List.partitionEithers : [Either a b] -> ([a], [b])
+data.List.partitionEithers =
+ use List +:
+ List.foldRight
+ (cases
+ Left a, (lefts, rights) -> (a +: lefts, rights)
+ Right b, (lefts, rights) -> (lefts, b +: rights)) ([], [])
+
+data.List.partitionEithers.doc : Doc
+data.List.partitionEithers.doc =
+ {{
+ Partition the elements in a list, accumulating them into a tuple containing a
+ list of the {Left}s and a list of the {Right}s
+
+ # Example
+
+ ```
+ partitionEithers [Left 2, Right "Hello", Left 10, Right "Hi again"]
+ ```
+ }}
+
+test> data.List.partitionEithers.tests.ex1 =
+ check
+ let
+ actual =
+ partitionEithers [Left 2, Right "Hello", Left 10, Right "Hello again"]
+ expected = ([2, 10], ["Hello", "Hello again"])
+ assert (actual === expected) ("Not equal!", actual, expected) true
+
+data.List.partitionMap : (a ->{g} Either b c) -> [a] ->{g} ([b], [c])
+data.List.partitionMap f =
+ use List +:
+ go = cases
+ (accLeft, accRight) ->
+ cases
+ [] -> (accLeft, accRight)
+ x +: xs ->
+ match f x with
+ Left l -> go (l +: accLeft, accRight) xs
+ Right r -> go (accLeft, r +: accRight) xs
+ go ([], [])
+
+data.List.partitionMap.doc : Doc
+data.List.partitionMap.doc =
+ {{
+ Partitions a list into two lists, one containing the elements that are {Left}
+ values, and one containing the elements that are {Right} values (after
+ applying the given function).
+
+ # Example
+
+ ```
+ partitionMap
+ (cases
+ 0 -> Left "zero"
+ n -> Right n) [0, 1, 2, 3, 4, 5]
+ ```
+ }}
+
+data.List.powerslice : [a] -> [[a]]
+data.List.powerslice l =
+ use List +:
+ [] +: List.flatMap (dropLast << List.tails) (inits l)
+
+data.List.powerslice.doc : Doc
+data.List.powerslice.doc =
+ {{
+ `` powerslice xs `` returns a list of all contiguous sub-lists of the list
+ `xs`.
+
+ # Examples
+
+ ```
+ powerslice [1, 2, 3]
+ ```
+
+ ```
+ powerslice []
+ ```
+ }}
+
+data.List.powerslice.examples.ex1 : [[Nat]]
+data.List.powerslice.examples.ex1 = powerslice [1, 2, 3]
+
+data.List.powerslice.examples.ex2 : [[a]]
+data.List.powerslice.examples.ex2 = powerslice []
+
+test> data.List.powerslice.tests.prop1 =
+ go _ =
+ use List ++
+ use gen listOf
+ a = listOf natInOrder ()
+ b = listOf natInOrder ()
+ c = listOf natInOrder ()
+ expect (List.contains b (powerslice (a ++ b ++ c)))
+ runs 100 go
+
+test> data.List.powerslice.tests.test1 =
+ check
+ (powerslice [1, 2, 3] === [[], [1], [1, 2], [2], [1, 2, 3], [2, 3], [3]])
+
+data.List.randomChoice : [a] ->{Exception, Random} a
+data.List.randomChoice list =
+ randomIndex = Random.natIn 0 (List.size list)
+ List.at randomIndex list
+ |> Optional.toException "List.randomChoice: empty List" (typeLink List)
+
+data.List.randomChoice.doc : Doc
+data.List.randomChoice.doc =
+ use List randomChoice
+ {{
+ Picks a random element from the given {type List}. Assumes that the
+ {type List} is not empty, so an empty {type List} will cause a runtime
+ exception.
+
+ # Examples
+
+ ```
+ catch do lcg 4096 do randomChoice [0, 3, 5, 7]
+ ```
+
+ ```
+ catch do lcg 2510 do randomChoice [?x, ?y, ?z]
+ ```
+
+ ```
+ catch do lcg 128 do randomChoice [char.digit, hex] ()
+ ```
+ }}
+
+test> data.List.randomChoice.test = test.verify do
+ list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ set = List.toSet list
+ Each.repeat 1000
+ e = List.randomChoice list
+ ensure (Set.contains e set)
+
+data.List.range.doc : Doc
+data.List.range.doc =
+ {{
+ {{ docExample 2 do min max -> List.range min max }} creates a list of
+ {type Nat} values from `min` to `max` (excluding `max`).
+
+ Returns the empty list if the `min` value is less than or equal to the `max`
+ value.
+
+ # Examples
+
+ ```
+ List.range 1 10
+ ```
+
+ ```
+ List.range 0 0
+ ```
+
+ ## See also
+
+ {List.rangeClosed} generates a range inclusive of the upper limit.
+
+ {Int.range} and {Int.rangeClosed} create ranges on {type Int}.
+ }}
+
+test> data.List.rangeClosed.tests.empty = _checkRangeClosed 1 0 []
+
+test> data.List.rangeClosed.tests.empty2 = _checkRangeClosed 2 1 []
+
+test> data.List.rangeClosed.tests.empty3 = _checkRangeClosed 10 9 []
+
+test> data.List.rangeClosed.tests.fromBig =
+ _checkRangeClosed 99 101 [99, 100, 101]
+
+test> data.List.rangeClosed.tests.fromOne = _checkRangeClosed 1 3 [1, 2, 3]
+
+test> data.List.rangeClosed.tests.fromZero = _checkRangeClosed 0 2 [0, 1, 2]
+
+test> data.List.rangeClosed.tests.rangeSize = runs 100 do
+ use Int >=
+ use List rangeClosed
+ use Nat + == >
+ x = natInOrder()
+ y = natInOrder()
+ if x > y then expect (List.size (rangeClosed x y) == 0)
+ else
+ diff = subtractToInt y x
+ size = Int.toRepresentation diff + 1
+ expect (List.size (rangeClosed x y) == size && diff >= +0)
+
+test> data.List.rangeClosed.tests.single0 = _checkRangeClosed 0 0 [0]
+
+test> data.List.rangeClosed.tests.single1 = _checkRangeClosed 1 1 [1]
+
+test> data.List.rangeClosed.tests.single99 = _checkRangeClosed 99 99 [99]
+
+data.List.rangeClosed.tests._checkRangeClosed : Nat -> Nat -> [Nat] -> [Result]
+data.List.rangeClosed.tests._checkRangeClosed low high res =
+ check (List.rangeClosed low high === res)
+
+data.List.replace : Nat -> a -> [a] -> [a]
+data.List.replace i a as =
+ use List ++
+ use Nat +
+ List.take i as ++ [a] ++ List.drop (i + 1) as
+
+data.List.replace.doc : Doc
+data.List.replace.doc =
+ {{
+ Replaces the element at the given index in the {type List} with the given
+ element.
+
+ # Examples
+
+ ```
+ replace 1 ?b [?a, ?c, ?d]
+ ```
+
+ ```
+ replace 0 ?a [?b, ?c, ?d]
+ ```
+
+ ```
+ replace 2 ?d [?a, ?b, ?c]
+ ```
+
+ If the index is out of bounds, the element is added to the end of the
+ {type List}:
+
+ ```
+ replace 9 ?d [?a, ?b, ?c]
+ ```
+ }}
+
+data.List.replicate : Nat -> '{e} a ->{e} [a]
+data.List.replicate n op =
+ use Nat -
+ List.unfold n (n -> (if n === 0 then None else Some (op(), n - 1)))
+
+data.List.replicate.doc : Doc
+data.List.replicate.doc =
+ use List replicate
+ use Random nat
+ {{
+ `` replicate n op `` does `!op` `n` times, returning a list of the results.
+
+ # Examples
+
+ ```
+ lcg 0 do replicate 5 nat
+ ```
+
+ ```
+ lcg 0 do replicate 0 nat
+ ```
+ }}
+
+test> data.List.replicate.test = runs 100 do
+ n = natInOrder()
+ h = cases
+ { ask -> k } -> handle k() with h
+ { a } -> a
+ units : [()]
+ units = handle List.replicate n do ask with h
+ expect (List.size units === n)
+
+data.List.replicate.test.doc : Doc
+data.List.replicate.test.doc =
+ {{
+ Tests that {List.replicate} produces effects correctly and results in a list
+ of the appropriate length.
+ }}
+
+data.List.reverse : [a] -> [a]
+data.List.reverse as =
+ use List +:
+ List.foldLeft (acc a -> a +: acc) [] as
+
+data.List.reverse.doc : Doc
+data.List.reverse.doc =
+ use List reverse
+ {{
+ Returns a {type List} with the elements of the given {type List} in reverse
+ order.
+
+ # Examples
+
+ ```
+ reverse [1, 2, 3]
+ ```
+
+ ```
+ reverse []
+ ```
+ }}
+
+data.List.rights : [Either a b] -> [b]
+data.List.rights = at2 << partitionEithers
+
+data.List.rights.doc : Doc
+data.List.rights.doc =
+ {{
+ Accumulate all of the elements that are {Right} in a list of {type Either}s
+
+ # Example
+
+ ```
+ rights [Left 2, Right "Hello", Left 10, Right "Hi again"]
+ ```
+ }}
+
+test> data.List.rights.tests.ex1 =
+ check
+ let
+ actual = rights [Left 2, Right "Hello", Left 10, Right "Hello again"]
+ expected = ["Hello", "Hello again"]
+ assert (actual === expected) ("Not equal!", actual, expected) true
+
+data.List.scanLeft : (b ->{e} a ->{e} b) -> b -> [a] ->{e} List.Nonempty b
+data.List.scanLeft f initial values =
+ go acc f q = cases
+ [] -> acc
+ x +: xs ->
+ next = f q x
+ go (Nonempty.snoc acc next) f next xs
+ go (List.Nonempty.singleton initial) f initial values
+
+data.List.scanLeft.doc : Doc
+data.List.scanLeft.doc =
+ use List scanLeft
+ use Nat * + /
+ {{
+ `` scanLeft f z xs `` applies the function `f` to `z` and the first element
+ of `xs`, then applies `f` to the result and the next element of `xs`, and so
+ on. Returns the list of intermediate results.
+
+ See also {List.scanRight} which scans the list in the other direction.
+
+ # Properties
+
+ * The final result (the last element of the output) is
+ ``List.foldLeft f z xs``.
+ * The first element of the output list is always `z`.
+ * The output list (a {type List.Nonempty}) is always 1 longer than the
+ input list.
+ * `` scanLeft f 0 [1, 2, 3] `` = {{
+ docExample 1 do f -> 0 +| [f 0 1, f (f 0 1) 2, f (f (f 0 1) 2) 3] }}
+
+ # Examples
+
+ ```
+ scanLeft (/) 128 [8, 4, 2]
+ ```
+
+ ```
+ scanLeft (/) 3 []
+ ```
+
+ ```
+ scanLeft Nat.min 5 [5, 2, 4, 4, 2, 6]
+ ```
+
+ ```
+ scanLeft Nat.max 5 [1, 2, 3, 4, 5, 6, 7]
+ ```
+
+ ```
+ scanLeft (x y -> 2 * x + y) 4 [1, 2, 3]
+ ```
+ }}
+
+data.List.scanLeft.examples.ex1 : List.Nonempty Nat
+data.List.scanLeft.examples.ex1 =
+ use Nat /
+ List.scanLeft (/) 64 [4, 2, 4]
+
+data.List.scanLeft.examples.ex2 : List.Nonempty Nat
+data.List.scanLeft.examples.ex2 =
+ use Nat /
+ List.scanLeft (/) 3 []
+
+data.List.scanLeft.examples.ex3 : List.Nonempty Nat
+data.List.scanLeft.examples.ex3 = List.scanLeft Universal.max 5 [1, 2, 3, 4]
+
+data.List.scanLeft.examples.ex4 : List.Nonempty Nat
+data.List.scanLeft.examples.ex4 =
+ List.scanLeft Universal.max 5 [1, 2, 3, 4, 5, 6, 7]
+
+data.List.scanLeft.examples.ex5 : List.Nonempty Nat
+data.List.scanLeft.examples.ex5 =
+ use Nat * +
+ List.scanLeft (x y -> 2 * x + y) 4 [1, 2, 3]
+
+test> data.List.scanLeft.test =
+ runs 100 do
+ use Nat +
+ xs = gen.listOf natInOrder ()
+ expect
+ (List.scanLeft (x y -> y + 1) 0 xs === (0 +| List.map (x -> x + 1) xs))
+
+data.List.scanRight : (a ->{e} b ->{e} b) -> b -> [a] ->{e} List.Nonempty b
+data.List.scanRight f initial values =
+ use List Nonempty
+ go : Nonempty b -> (a ->{e} b ->{e} b) -> b -> [a] -> Nonempty b
+ go acc f q = cases
+ [] -> acc
+ xs :+ x ->
+ next = f x q
+ go (Nonempty.cons next acc) f next xs
+ go (List.Nonempty.singleton initial) f initial values
+
+data.List.scanRight.doc : Doc
+data.List.scanRight.doc =
+ use List scanRight
+ use Nat * + /
+ {{
+ `` scanRight f z xs `` applies the function `f` to the last element of `xs`
+ and `z`, then applies `f` to the result and the previous element of `xs`, and
+ so on. Returns the list of intermediate results.
+
+ See also {List.scanLeft} which scans the list in the other direction.
+
+ # Properties
+
+ * The final result (the first element of the output) is
+ ``List.foldRight f z xs``.
+ * The last element of the output list is always `z`.
+ * The output list (a {type List.Nonempty}) is always 1 longer than the
+ input list.
+ * `` scanRight f 0 [1, 2, 3] `` = {{
+ docExample 1 do f -> f 1 (f 2 (f 3 0)) +| [f 2 (f 3 0), f 3 0, 0] }}
+
+ # Examples
+
+ ```
+ scanRight (/) 2 [128, 32, 8]
+ ```
+
+ ```
+ scanRight (/) 3 []
+ ```
+
+ ```
+ scanRight Nat.min 5 [5, 2, 4, 4, 2, 6]
+ ```
+
+ ```
+ scanRight Nat.max 5 [1, 2, 3, 4, 5, 6, 7]
+ ```
+
+ ```
+ scanRight (x y -> 2 * x + y) 4 [1, 2, 3]
+ ```
+ }}
+
+data.List.scanRight.examples.ex1 : List.Nonempty Nat
+data.List.scanRight.examples.ex1 =
+ use Nat +
+ List.scanRight (+) 5 [1, 2, 3, 4]
+
+data.List.scanRight.examples.ex2 : List.Nonempty Nat
+data.List.scanRight.examples.ex2 =
+ use Nat /
+ List.scanRight (/) 2 [8, 12, 24, 4]
+
+data.List.scanRight.examples.ex3 : List.Nonempty Nat
+data.List.scanRight.examples.ex3 =
+ use Nat /
+ List.scanRight (/) 3 []
+
+data.List.scanRight.examples.ex4 : List.Nonempty Boolean
+data.List.scanRight.examples.ex4 =
+ use Universal gt
+ List.scanRight Boolean.and true [gt 1 2, gt 3 2, 5 === 5]
+
+data.List.scanRight.examples.ex5 : List.Nonempty Nat
+data.List.scanRight.examples.ex5 =
+ List.scanRight Universal.max 18 [3, 6, 12, 4, 55, 11]
+
+test> data.List.scanRight.test =
+ runs 100 do
+ use Nat +
+ xs = gen.listOf natInOrder ()
+ expect
+ (List.scanRight (x y -> x + 1) 0 xs === (List.map (x -> x + 1) xs |+ 0))
+
+data.List.sequenceOptional : [Optional a] -> Optional [a]
+data.List.sequenceOptional = cases
+ [] -> None
+ [Some a] -> Some [a]
+ [None] -> None
+ Some a +: as -> Some (as -> a List.+: as) <*> data.List.sequenceOptional as
+ None +: as -> None
+
+data.List.sequenceOptional.doc : Doc
+data.List.sequenceOptional.doc =
+ use List sequenceOptional
+ {{
+ {sequenceOptional} transforms a {type List} of {type Optional} values into an
+ {type Optional} of {type List} of values so that:
+
+ * If one of the values is {None} then the whole result is {None}.
+ * Otherwise all the values are collected into ``Some values``.
+
+ # Examples
+
+ ```
+ sequenceOptional [Some 1, None, Some 3]
+ ```
+
+ ```
+ sequenceOptional [Some 1, Some 2, Some 3]
+ ```
+ }}
+
+data.List.singleton : a -> [a]
+data.List.singleton x = [x]
+
+data.List.singleton.doc : Doc
+data.List.singleton.doc =
+ {{
+ `` List.singleton x `` is equivalent to `[x]`, a list with the single element
+ `x`.
+ }}
+
+-- builtin data.List.size : [a] -> Nat
+
+data.List.size.doc : Doc
+data.List.size.doc =
+ use List size
+ {{
+ Get the number of elements in a {type List}.
+
+ # Examples
+
+ ```
+ size [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ size []
+ ```
+ }}
+
+data.List.skip : Nat -> [a] -> [a]
+data.List.skip n as =
+ use List :+
+ go acc = cases
+ x +: xs -> go (acc :+ x) (List.drop n xs)
+ [] -> acc
+ go [] as
+
+data.List.skip.doc : Doc
+data.List.skip.doc =
+ {{
+ Skips elements of a {type List}. Returns the first element, then skips `n`
+ elements, then returns the next element, and so on.
+
+ # Examples
+
+ ```
+ skip 2 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ skip 1 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ skip 0 [1, 2, 3, 4, 5]
+ ```
+ }}
+
+data.List.slice : Nat -> Nat -> [a] -> [a]
+data.List.slice start stopExclusive s =
+ use Nat -
+ List.take (stopExclusive - start) (List.drop start s)
+
+data.List.slice.doc : Doc
+data.List.slice.doc =
+ use List slice
+ {{
+ Returns a slice of a {type List} from the given start index (inclusive) to
+ the given end index (exclusive). If the start index is greater than the end
+ index, the result is the empty {type List}.
+
+ # Examples
+
+ ```
+ slice 1 3 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ slice 3 1 [1, 2, 3, 4, 5]
+ ```
+ }}
+
+data.List.slidingPairs : [a] -> [(a, a)]
+data.List.slidingPairs as =
+ pair = cases
+ x +: rest@(y +: _) -> Some ((x, y), rest)
+ _ -> None
+ List.unfold as pair
+
+data.List.slidingPairs.doc : Doc
+data.List.slidingPairs.doc =
+ {{
+ Returns a list of all adjacent character pairs in the {type List}, in the
+ order that they appear.
+
+ For example:
+
+ ```
+ slidingPairs [1, 2, 3, 4, 5]
+ ```
+
+ # See also
+
+ * {slidingWindow}
+ }}
+
+test> data.List.slidingPairs.tests.concat = runs 100 do
+ use List map
+ use Nat -
+ use Text ==
+ t = Text.ascii()
+ s = Text.size t
+ ps = slidingPairs (toCharList t)
+ fst = fromCharList (map at1 ps)
+ snd = fromCharList (map at2 ps)
+ expect (fst == Text.take (s - 1) t && snd == Text.drop 1 t)
+
+data.List.slidingWindow : Nat -> [a] -> [[a]]
+data.List.slidingWindow length xs =
+ use List :+
+ use Nat + - ==
+ if length == 0 then bug "Length of window must not be zero."
+ else
+ count = List.size xs + 1 - length
+ go acc xs = cases
+ 0 -> acc
+ n ->
+ window = List.take length xs
+ rest = List.drop 1 xs
+ go (acc :+ window) rest (n - 1)
+ go [] xs count
+
+data.List.slidingWindow.doc : Doc
+data.List.slidingWindow.doc =
+ {{
+ Returns a list of sliding windows containing elements drawn from the input
+ list. Each window is a list with the given length.
+
+ {{ docCallout None {{ The length must not be zero. }} }}
+
+ # Examples
+
+ ```
+ slidingWindow 1 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ slidingWindow 3 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ slidingWindow 5 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ slidingWindow 6 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ slidingWindow 1 []
+ ```
+
+ # See also
+
+ * {slidingPairs}
+ }}
+
+test> data.List.slidingWindow.tests.ex1 =
+ check (slidingWindow 1 [1, 2, 3, 4, 5] === [[1], [2], [3], [4], [5]])
+
+test> data.List.slidingWindow.tests.ex2 =
+ check (slidingWindow 3 [1, 2, 3, 4, 5] === [[1, 2, 3], [2, 3, 4], [3, 4, 5]])
+
+test> data.List.slidingWindow.tests.ex3 =
+ check (slidingWindow 5 [1, 2, 3, 4, 5] === [[1, 2, 3, 4, 5]])
+
+test> data.List.slidingWindow.tests.ex4 =
+ check (slidingWindow 6 [1, 2, 3, 4, 5] === [])
+
+test> data.List.slidingWindow.tests.ex5 = check (slidingWindow 1 [] === [])
+
+test> data.List.slidingWindow.tests.isSlidingPairsForLength2 = runs 100 do
+ x = natInOrder()
+ xs = gen.listOf natInOrder ()
+ pairToList = cases (x, y) -> [x, y]
+ expect (slidingWindow 2 xs === (slidingPairs xs |> List.map pairToList))
+
+test> data.List.snoc.tests.isReversedConsOnReversedList = runs 100 do
+ use List +: reverse
+ x = natInOrder()
+ xs = gen.listOf natInOrder ()
+ expect (List.snoc xs x === (x +: (xs |> reverse) |> reverse))
+
+data.List.somes : [Optional a] -> [a]
+data.List.somes xs =
+ use List +:
+ List.foldRight
+ (v a -> (match v with
+ Some x -> x +: a
+ None -> a)) [] xs
+
+data.List.somes.doc : Doc
+data.List.somes.doc =
+ use List somes
+ {{
+ @signature{somes}
+
+ {List.somes as} flattens a {type List} of {type Optional}s returning a list
+ of the values present within the {type Optional}s in `as`.
+
+ # Example
+
+ ```
+ somes [Some 1, Some 2, None, Some 3, None]
+ ```
+ }}
+
+test> data.List.somes.tests.ex1 =
+ check
+ let
+ actual = List.somes [None, None, Some 2, None, None, None, Some 10]
+ expected = [2, 10]
+ assert (actual === expected) ("Not equal!", actual, expected) true
+
+data.List.sort : [a] -> [a]
+data.List.sort = sortWith Universal.lteq
+
+data.List.sort.doc : Doc
+data.List.sort.doc =
+ use List sort
+ {{
+ Sort a list of values using the universal ordering {Universal.lteq}.
+
+ # Examples
+
+ ```
+ sort [5, 2, 4, 4, 2, 6]
+ ```
+
+ ```
+ fromCharList (sort (toCharList "abracadabra"))
+ ```
+ }}
+
+test> data.List.sort.test = test.verify do
+ use List all contains size
+ use Nat ==
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 0 100
+ sorted = List.sort xs
+ ensure (isSortedBy Universal.lteq sorted)
+ ensure (all (x -> contains x xs) sorted)
+ ensure (all (x -> contains x sorted) xs)
+ ensure (size sorted == size xs)
+
+data.List.sortBy : (a ->{e} b) -> [a] ->{e} [a]
+data.List.sortBy f as =
+ use List map
+ tweak = cases (p1, p2) -> (f p1, p2, p1)
+ List.sort (map tweak (List.indexed as)) |> map at3
+
+data.List.sortBy.doc : Doc
+data.List.sortBy.doc =
+ {{
+ Sorts a list by the result of a function applied to each element.
+
+ This function is stable, meaning that the relative order of elements that
+ compare equal is preserved.
+
+ # Examples
+
+ ```
+ sortBy id [3, 2, 1]
+ ```
+
+ ```
+ sortBy
+ Text.size
+ ["the", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]
+ ```
+ }}
+
+data.List.sortWith : (a -> a ->{e} Boolean) -> [a] ->{e} [a]
+data.List.sortWith f list =
+ use List ++ :+
+ use data.List sortWith
+ merge acc = cases
+ [], ys -> acc ++ ys
+ xs, [] -> acc ++ xs
+ xss@(x +: xs), yss@(y +: ys) ->
+ if f x y then merge (acc :+ x) xs yss else merge (acc :+ y) xss ys
+ match list with
+ [] -> []
+ [x] -> [x]
+ xs ->
+ (left, right) = halve xs
+ merge [] (sortWith f left) (sortWith f right)
+
+data.List.sortWith.doc : Doc
+data.List.sortWith.doc =
+ use Nat <= >=
+ {{
+ Sorts a {type List} according to the given comparison function.
+
+ # Example
+
+ ```
+ sortWith (<=) [1, 3, 2]
+ ```
+
+ ```
+ sortWith (>=) [1, 2, 3]
+ ```
+ }}
+
+test> data.List.sortWith.tests.isOrdered = runs 100 do
+ use Nat <=
+ xs = gen.listOf natInOrder ()
+ sorted = sortWith (<=) xs
+ expect (isSortedBy (<=) sorted)
+
+test> data.List.sortWith.tests.length = runs 100 do
+ use List size
+ use Nat <= ==
+ xs = gen.listOf natInOrder ()
+ sorted = sortWith (<=) xs
+ expect (size sorted == size xs)
+
+test> data.List.sortWith.tests.permutation = runs 100 do
+ use Nat <=
+ xs = gen.listOf natInOrder ()
+ sorted = sortWith (<=) xs
+ expect (List.all (x -> List.contains x sorted) xs)
+
+data.List.span : (a ->{e} Boolean) -> [a] ->{e} ([a], [a])
+data.List.span f xs = (List.takeWhile f xs, List.dropWhile f xs)
+
+data.List.span.doc : Doc
+data.List.span.doc =
+ use List span
+ use Nat <
+ {{
+ `` span f xs `` returns a tuple where the first element is the longest prefix
+ of `xs` of elements that satisfy `f`, and the second element is the remainder
+ of the list.
+
+ Equivalent to ``(List.takeWhile f xs, List.dropWhile f xs)``.
+
+ # Example
+
+ ```
+ span (x -> x < 9) [5, 6, 7, 5, 9, 7, 6]
+ ```
+ }}
+
+test> data.List.span.tests.alle =
+ check (List.span (flip Universal.lt 0) [1, 2, 3] === ([], [1, 2, 3]))
+
+test> data.List.span.tests.allf =
+ check (List.span (flip Universal.lt 9) [1, 2, 3] === ([1, 2, 3], []))
+
+test> data.List.span.tests.middle =
+ check
+ (List.span (flip Universal.lt 3) [1, 2, 3, 4, 1, 2, 3, 4]
+ === ([1, 2], [3, 4, 1, 2, 3, 4]))
+
+data.List.split : (a ->{e} Boolean) -> [a] ->{e} [[a]]
+data.List.split f =
+ use List :+
+ go acc = cases
+ [] -> acc
+ x +: xs
+ | f x -> go (acc :+ []) xs
+ | otherwise ->
+ match acc with
+ [] -> bug "splitTailRec: impossible"
+ ys :+ y -> go (ys :+ (y :+ x)) xs
+ go [[]]
+
+data.List.split.doc : Doc
+data.List.split.doc =
+ use List split
+ {{
+ `` split p xs `` splits the {type List} `xs` into components delimited by
+ separators, where a separator is any element for which `p` returns ``true``.
+ The resulting components do not contain the separators. Two adjacent
+ separators result in an empty component in the output, as does a separator at
+ the end of the input.
+
+ # Examples
+
+ ```
+ split isSpace (toCharList "one two three")
+ ```
+
+ ```
+ split Nat.isEven [5, 6, 7, 5, 9, 7, 6]
+ ```
+ }}
+
+test> data.List.split.tests.base =
+ check
+ (List.split ((===) 0) [1, 2, 0, 3, 4, 5, 0, 6, 7, 8]
+ === [[1, 2], [3, 4, 5], [6, 7, 8]])
+
+test> data.List.split.tests.double =
+ check
+ (List.split ((===) 0) [0, 0, 1, 2, 3, 0, 4, 5, 0, 0, 1, 2, 3, 0, 0]
+ === [[], [], [1, 2, 3], [4, 5], [], [1, 2, 3], [], []])
+
+test> data.List.split.tests.empty = check (List.split ((===) 0) [] === [[]])
+
+data.List.splitAt : Nat -> [a] -> ([a], [a])
+data.List.splitAt i xs = (List.take i xs, List.drop i xs)
+
+data.List.splitAt.doc : Doc
+data.List.splitAt.doc =
+ use List splitAt
+ {{
+ `` splitAt n xs `` returns a tuple where the first element is the prefix of
+ length `n` and the second element is the remainder of `xs`.
+
+ # Examples
+
+ ```
+ splitAt 0 [?a, ?b, ?c, ?d]
+ ```
+
+ ```
+ splitAt 2 [?a, ?b, ?c, ?d]
+ ```
+
+ ```
+ splitAt 9 [?a, ?b, ?c, ?d]
+ ```
+ }}
+
+test> data.List.splitAt.tests.base =
+ check (List.splitAt 3 [1, 2, 3, 4, 5] === ([1, 2, 3], [4, 5]))
+
+test> data.List.splitAt.tests.ob =
+ check (List.splitAt 10 [1, 2] === ([1, 2], []))
+
+test> data.List.splitAt.tests.zero =
+ check (List.splitAt 0 [1, 2] === ([], [1, 2]))
+
+data.List.stripPrefix : [a] -> [a] -> Optional [a]
+data.List.stripPrefix prefix list =
+ (p, s) = List.splitAt (List.size prefix) list
+ if p === prefix then Some s else None
+
+data.List.stripPrefix.doc : Doc
+data.List.stripPrefix.doc =
+ {{
+ `` stripPrefix prefix list `` drops the given prefix from the list. It
+ returns {None} if the list does not start with the prefix, or {Some}
+ containing the elements after the prefix.
+
+ # Examples
+
+ ```
+ stripPrefix [1, 2, 3] [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ stripPrefix [9, 0, 2] [1, 2, 3, 4, 5]
+ ```
+ }}
+
+test> data.List.stripPrefix.tests.all =
+ check (stripPrefix [1, 2, 3] [1, 2, 3] === Some [])
+
+test> data.List.stripPrefix.tests.base =
+ check (stripPrefix [1, 2] [1, 2, 3] === Some [3])
+
+test> data.List.stripPrefix.tests.none =
+ check (stripPrefix [1, 2] [3, 1, 2] === None)
+
+data.List.subsequences : [a] -> [[a]]
+data.List.subsequences list =
+ use List +:
+ [] +: List.map List.Nonempty.toList (nonEmptySubsequences list)
+
+data.List.subsequences.doc : Doc
+data.List.subsequences.doc =
+ {{
+ Returns the list of all subsequences of the argument.
+
+ # Examples
+
+ ```
+ subsequences [1, 2, 3]
+ ```
+
+ ```
+ subsequences []
+ ```
+ }}
+
+test> data.List.subsequences.tests.base =
+ check (subsequences [1, 2] === [[], [1], [2], [1, 2]])
+
+data.List.tail : [a] -> Optional [a]
+data.List.tail = cases
+ _ +: xs -> Some xs
+ [] -> None
+
+data.List.tail.doc : Doc
+data.List.tail.doc =
+ use List tail
+ {{
+ Returns all elements of the list except the first one, or {None} if it's
+ empty.
+
+ # Examples
+
+ ```
+ tail [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ tail [1]
+ ```
+
+ ```
+ tail []
+ ```
+ }}
+
+test> data.List.tail.tests.isReversedInitOnReversedList =
+ runs 100 do
+ use List reverse
+ xs = gen.listOf natInOrder ()
+ expect
+ ((xs |> List.tail)
+ === (xs |> reverse |> List.init |> Optional.map reverse))
+
+test> data.List.tail.tests.prop1 =
+ go _ =
+ use List +:
+ xs = gen.listOf natInOrder ()
+ expect (List.tail (1 +: xs) === Some xs)
+ runs 100 go
+
+test> data.List.tail.tests.test1 = check (List.tail [1, 2, 3] === Some [2, 3])
+
+test> data.List.tail.tests.test2 = check (List.tail [] === None)
+
+data.List.tails : [a] -> [[a]]
+data.List.tails l = l List.+: (match l with
+ [] -> []
+ _ +: xs -> data.List.tails xs)
+
+data.List.tails.doc : Doc
+data.List.tails.doc =
+ {{
+ Returns a {type List} of all final segments of the input. The first element
+ of the result is the input {type List} itself, and the last element is the
+ empty {type List}.
+
+ # Examples
+
+ ```
+ List.tails [1, 2, 3]
+ ```
+ }}
+
+-- builtin data.List.take : Nat -> [a] -> [a]
+
+data.List.take.doc : Doc
+data.List.take.doc =
+ use List take
+ {{
+ Takes the first `n` elements of a {type List}.
+
+ # Examples
+
+ ```
+ take 2 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ take 0 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ take 5 [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ take 6 [1, 2, 3, 4, 5]
+ ```
+ }}
+
+data.List.takeRight : Nat -> [a] -> [a]
+data.List.takeRight n xs =
+ use Nat -
+ List.drop (List.size xs - n) xs
+
+data.List.takeRight.doc : Doc
+data.List.takeRight.doc =
+ {{
+ Returns the last n elements of a list.
+
+ # Example
+
+ ```
+ takeRight 2 [1, 2, 3, 4]
+ ```
+ }}
+
+data.List.takeUntil : (a ->{e} Boolean) -> [a] ->{e} [a]
+data.List.takeUntil f xs =
+ use List :+
+ go acc = cases
+ x +: xs | (>>) f Boolean.not x -> go (acc :+ x) xs
+ x +: _ | f x -> acc :+ x
+ _ -> acc
+ go [] xs
+
+data.List.takeUntil.doc : Doc
+data.List.takeUntil.doc =
+ use List takeUntil
+ {{
+ Takes elements from a list until a predicate is satisfied. The element that
+ satisfies the predicate is included in the result.
+
+ # Examples
+
+ ```
+ takeUntil Nat.isEven [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ takeUntil Nat.isOdd [1, 2, 3, 4, 5]
+ ```
+ }}
+
+data.List.takeWhile : (a ->{e} Boolean) -> [a] ->{e} [a]
+data.List.takeWhile f xs =
+ use List :+
+ go acc = cases
+ x +: xs | f x -> go (acc :+ x) xs
+ _ -> acc
+ go [] xs
+
+data.List.takeWhile.doc : Doc
+data.List.takeWhile.doc =
+ use List takeWhile
+ use Nat <
+ {{
+ `` takeWhile p xs `` returns the longest prefix of `xs` with elements that
+ satisfy `p`.
+
+ # Examples
+
+ ```
+ takeWhile (x -> x < 9) [5, 6, 7, 5, 9, 7, 6]
+ ```
+
+ ```
+ takeWhile Nat.isEven []
+ ```
+ }}
+
+test> data.List.takeWhile.tests.all =
+ check (List.takeWhile (flip Universal.lt 9) [1, 2, 3] === [1, 2, 3])
+
+test> data.List.takeWhile.tests.middle =
+ check (List.takeWhile (flip Universal.lt 3) [1, 2, 3, 4, 5] === [1, 2])
+
+test> data.List.takeWhile.tests.none =
+ check (List.takeWhile (flip Universal.gt 10) [1, 2, 11] === [])
+
+test> data.List.tests.sequenceOptional.empty =
+ test.verify do ensureEqual (List.sequenceOptional []) None
+
+test> data.List.tests.sequenceOptional.non_empty =
+ test.verify do
+ ensureEqual (List.sequenceOptional [Some 1, Some 2]) (Some [1, 2])
+
+test> data.List.tests.sequenceOptional.non_empty_2 =
+ test.verify do
+ ensureEqual (List.sequenceOptional [Some 1, None, Some 2]) None
+
+test> data.List.tests.traverseOptional.empty =
+ test.verify do
+ ensureEqual
+ (List.traverseOptional
+ (n -> (if Nat.mod n 2 Nat.== 0 then Some (n Nat.+ 1) else None)) [])
+ None
+
+test> data.List.tests.traverseOptional.non_empty =
+ test.verify do
+ ensureEqual
+ (List.traverseOptional
+ (n -> (if Nat.mod n 2 Nat.== 0 then Some (n Nat.+ 1) else None)) [1, 2])
+ None
+
+test> data.List.tests.traverseOptional.non_empty_2 =
+ test.verify do
+ ensureEqual
+ (List.traverseOptional
+ (n -> (if Nat.mod n 2 Nat.== 0 then Some (n Nat.+ 1) else None))
+ [2, 4, 6])
+ (Some [3, 5, 7])
+
+data.List.toMap.doc : Doc
+data.List.toMap.doc =
+ use List toMap
+ use Map get
+ {{
+ Converts a {type List} of key-value pairs to a {type Map}. If the {type List}
+ contains duplicate keys, the value from the last pair is used.
+
+ # Examples
+
+ ```
+ get 1 (toMap [(1, "one"), (2, "two"), (3, "three")])
+ ```
+
+ ```
+ get 1 (toMap [(1, "one"), (2, "two"), (1, "uno")])
+ ```
+ }}
+
+data.List.toSet.doc : Doc
+data.List.toSet.doc =
+ {{
+ Converts a {type List} to a {type Set}.
+
+ # Example
+
+ ```
+ Set.toText (List.toSet [?🍎, ?🍌, ?🍊, ?🍌, ?🍎])
+ ```
+ }}
+
+data.List.toText : (a ->{g} Text) -> [a] ->{g} Text
+data.List.toText show =
+ use Text ++
+ foldDelimited (++) show "[" ", " "]"
+
+data.List.toText.doc : Doc
+data.List.toText.doc =
+ use Nat <=
+ use Text ++
+ {{
+ `` List.toText show items `` renders the `items` list as text, using `show`
+ to render each individual element.
+
+ # Examples
+
+ ```
+ List.toText Nat.toText [1, 2, 3]
+ ```
+
+ ```
+ List.toText Nat.toText []
+ ```
+
+ ```
+ List.toText
+ (s -> (if Text.size s <= 3 then s else Text.take 3 s ++ "..."))
+ ["abcd", "efg", "hijk", "lmnop", "qrs", "tuv", "wxyz"]
+ ```
+ }}
+
+test> data.List.toText.tests = test.verify do
+ use Text ++
+ showWithPlus n = "+" ++ Nat.toText n
+ ensureEqual "[]" (List.toText showWithPlus [])
+ ensureEqual "[+1, +2, +3]" (List.toText showWithPlus [1, 2, 3])
+
+data.List.transpose : [[a]] -> [[a]]
+data.List.transpose = cases
+ [] -> []
+ [] +: xss -> data.List.transpose xss
+ [xs] -> List.map List.singleton xs
+ x +: xs +: xss ->
+ (hds, tls) = List.unzip (List.filterMap List.uncons xss)
+ use List +:
+ combine y h ys t = y +: h +: data.List.transpose (ys +: t)
+ combine x hds xs tls
+
+data.List.transpose.doc : Doc
+data.List.transpose.doc =
+ use List map
+ {{
+ `` transpose rows `` swaps the rows and columns of a list of lists. The
+ result is a list of lists, where the first list contains the first elements
+ of each of the input lists, the second list contains the second elements, and
+ so on.
+
+ If any of the input lists are empty, they are ignored.
+
+ # Examples
+
+ ```
+ transpose [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+ ```
+
+ If the list is rectangular (all its rows are the same length), then we can
+ think of this as a rotation of the list of lists by 90 degrees clockwise
+ followed by a horizontal flip, or equivalently a rotation by 90 degrees
+ counter-clockwise followed by a vertical flip.
+
+ ```
+ map
+ fromCharList
+ (transpose
+ (map
+ toCharList
+ [ "⬜🟥🟥⬜⬜⬜🟥🟥⬜"
+ , "🟥🟥🟥🟥⬜🟥🟥🟥🟥"
+ , "🟥🟥🟥🟥🟥🟥🤖🟥🟥"
+ , "🟥🟥🟥🟥🟥🟥🟥🟥🟥"
+ , "⬜🟥🟥🟥🟥🟥🟥🟥⬜"
+ , "⬜⬜🟥🟥🟥🟥🟥⬜⬜"
+ , "⬜⬜⬜🟥🟥🟥⬜⬜⬜"
+ , "⬜⬜⬜⬜🟥⬜⬜⬜⬜"
+ ]))
+ ```
+
+ However, this intution does not quite work if the lists are not all the
+ same length:
+
+ ```
+ map
+ fromCharList
+ (transpose
+ (map
+ toCharList
+ [ "⬜🟥🟥⬜⬜⬜🟥🟥"
+ , "🟥🟥🟥🟥⬜🟥🟥🟥🟥"
+ , "🟥🟥🟥🟥🟥🟥🤖🟥🟥"
+ , "🟥🟥🟥🟥🟥🟥🟥🟥🟥"
+ , "⬜🟥🟥🟥🟥🟥🟥🟥"
+ , "⬜⬜🟥🟥🟥🟥🟥"
+ , "⬜⬜⬜🟥🟥🟥"
+ , "⬜⬜⬜⬜🟥"
+ , ""
+ ]))
+ ```
+ }}
+
+test> data.List.transpose.tests.identityProp = test.verify do
+ use Random listOf
+ _ = "For a square matrix, `transpose . transpose = id`"
+ _ = Each.repeat 1000
+ zeroToTen = do Random.natIn 0 10
+ xss = listOf (do listOf zeroToTen do 4) zeroToTen
+ ensureEqual xss (transpose (transpose xss))
+
+test> data.List.transpose.tests.joinProp = test.verify do
+ use List join sort
+ use Random listOf
+ _ = "The join of the result is a permutation of the join of the input."
+ _ = Each.repeat 1000
+ zeroToTen = do Random.natIn 0 10
+ xss = listOf (do listOf zeroToTen zeroToTen) zeroToTen
+ transposed = transpose xss
+ ensureEqual (sort (join transposed)) (sort (join xss))
+
+test> data.List.transpose.tests.lengthProp =
+ test.verify do
+ use List map size
+ use Random listOf
+ _ =
+ "The length of each row of the result has the same length as the corresponding column of the input."
+ _ = Each.repeat 1000
+ zeroToTen = do Random.natIn 0 10
+ xss = listOf (do listOf zeroToTen zeroToTen) zeroToTen
+ transposed = transpose xss
+ ensureEqual
+ (map size transposed)
+ (map
+ (n -> size (List.filterMap (xs -> List.at n xs) xss))
+ (List.range 0 (size transposed)))
+
+data.List.traverseOptional : (a ->{g} Optional b) -> [a] ->{g} Optional [b]
+data.List.traverseOptional f = cases
+ [] -> None
+ [a] -> Optional.map List.singleton (f a)
+ a +: as1 -> Optional.map2 (List.+:) (f a) (data.List.traverseOptional f as1)
+
+data.List.traverseOptional.doc : Doc
+data.List.traverseOptional.doc =
+ use List traverseOptional
+ use Nat + isEven
+ {{
+ {traverseOptional} applies a function to each element of a {type List}. If
+ one of the results is {None} then the whole result is {None}. Otherwise all
+ the results are collected into ``Some results``.
+
+ # Examples
+
+ ```
+ traverseOptional (n -> (if isEven n then Some (n + 1) else None)) [1, 2, 3]
+ ```
+
+ ```
+ traverseOptional (n -> (if isEven n then Some (n + 1) else None)) [2, 4, 6]
+ ```
+ }}
+
+data.List.uncollate : [a] -> ([a], [a])
+data.List.uncollate =
+ use List :+
+ go acc1 acc2 = cases
+ [] -> (acc1, acc2)
+ [x] -> (acc1 :+ x, acc2)
+ [x, y] ++ xs -> go (acc1 :+ x) (acc2 :+ y) xs
+ go [] []
+
+data.List.uncollate.doc : Doc
+data.List.uncollate.doc =
+ use List interleave
+ {{
+ Splits a list into two lists, alternating elements from the original list. If
+ the original list has an odd number of elements, the first list will have one
+ more element than the second.
+
+ # Examples
+
+ ```
+ uncollate [1, 2, 3, 4, 5]
+ ```
+
+ ```
+ uncollate [1, 2, 3, 4]
+ ```
+
+ # Relationships and properties
+
+ * {interleave} is the inverse of this function. However, this function is
+ not the exact inverse of {interleave}:
+
+ ```
+ uncurry interleave (uncollate [1, 2, 3, 4, 5])
+ ```
+
+ ```
+ uncollate (interleave [1, 2] [3, 5, 7, 9])
+ ```
+ }}
+
+data.List.uncons : [a] -> Optional (a, [a])
+data.List.uncons = cases
+ x +: xs -> Some (x, xs)
+ [] -> None
+
+data.List.uncons.doc : Doc
+data.List.uncons.doc =
+ use List uncons
+ {{
+ Returns the first element of a list paired with the rest of the list, or
+ {None} if the list is empty.
+
+ # Examples
+
+ ```
+ uncons [1, 2, 3]
+ ```
+
+ ```
+ uncons []
+ ```
+ }}
+
+data.List.uncons.tests.applyOrEmpty :
+ (i1 ->{g1} i ->{g} [elem]) -> Optional (i1, i) ->{g1, g} [elem]
+data.List.uncons.tests.applyOrEmpty f = cases
+ Some (x, y) -> f x y
+ None -> []
+
+test> data.List.uncons.tests.isHeadAndTailMerged =
+ runs 100 do
+ xs = gen.listOf natInOrder ()
+ expect
+ ((xs |> List.uncons) === ((List.head xs, List.tail xs) |> tests.merge))
+
+test> data.List.uncons.tests.isInverseOfCons = runs 100 do
+ use List +:
+ xs = gen.listOf natInOrder ()
+ expect (xs === (xs |> List.uncons |> applyOrEmpty (+:)))
+
+test> data.List.uncons.tests.isReversedUnsnocOnReversedList = runs 100 do
+ use List reverse
+ xs = gen.listOf natInOrder ()
+ revOptPair = Optional.map cases (xs, x) -> (x, reverse xs)
+ expect ((xs |> List.uncons) === (xs |> reverse |> List.unsnoc |> revOptPair))
+
+data.List.uncons.tests.merge : (Optional a1, Optional a) -> Optional (a1, a)
+data.List.uncons.tests.merge = cases
+ (Some x, Some y) -> Some (x, y)
+ (None, None) -> None
+ _ -> bug ""
+
+data.List.unfold : s -> (s ->{𝕖} Optional (a, s)) ->{𝕖} [a]
+data.List.unfold s0 f =
+ use List :+
+ go f s acc = match f s with
+ None -> acc
+ Some (a, s) -> go f s (acc :+ a)
+ go f s0 []
+
+data.List.unfold.doc : Doc
+data.List.unfold.doc =
+ use List unfold
+ use Nat + <
+ {{
+ Creates a list by iterating a state transition function starting from a seed
+ value.
+
+ The function is applied to the initial state, and the result is either {None}
+ to indicate that the list is finished, or {Some} containing the next element
+ and the next state.
+
+ # Examples
+
+ All even natural numbers less than 10:
+
+ ```
+ unfold 0 (x -> (if x < 10 then Some (x, x + 2) else None))
+ ```
+
+ The Fibonacci sequence:
+
+ ```
+ unfold (0, 1) cases
+ (x, y) -> if x < 100 then Some (x, (y, x + y)) else None
+ ```
+
+ Uses {unfold} to {List.zip} two lists together:
+
+ ```
+ unfold (toCharList "abc", [1, 2, 3]) cases
+ (c +: cs, n +: ns) -> Some ((c, n), (cs, ns))
+ _ -> None
+ ```
+ }}
+
+data.List.unsafeAt : Nat -> [a] -> a
+data.List.unsafeAt n as = match List.at n as with
+ Some a -> a
+ None -> bug ("List index out of bounds: " Text.++ Nat.toText n)
+
+data.List.unsafeAt.doc : Doc
+data.List.unsafeAt.doc =
+ use List unsafeAt
+ {{
+ `` unsafeAt n `` gets the element at the position `n` in the list (using
+ [zero-based indexing](https://en.wikipedia.org/wiki/Zero-based_numbering)),
+ or crashes the program if the list has fewer than `n+1` elements.
+
+ # Examples
+
+ ```
+ unsafeAt 0 [10, 20, 30]
+ ```
+
+ ```
+ unsafeAt 2 [10, 20, 30]
+ ```
+
+ ```
+ unsafeAt 3 [10, 20, 30]
+ ```
+ }}
+
+data.List.unsnoc : [a] -> Optional ([a], a)
+data.List.unsnoc = cases
+ xs :+ x -> Some (xs, x)
+ [] -> None
+
+data.List.unsnoc.doc : Doc
+data.List.unsnoc.doc =
+ use List unsnoc
+ {{
+ Returns the list without its last element paired with the last element, or
+ {None} if the list is empty.
+
+ # Examples
+
+ ```
+ unsnoc [1, 2, 3]
+ ```
+
+ ```
+ unsnoc []
+ ```
+ }}
+
+test> data.List.unsnoc.tests.isInitAndLastMerged =
+ runs 100 do
+ xs = gen.listOf natInOrder ()
+ expect
+ ((xs |> List.unsnoc) === ((List.init xs, List.last xs) |> tests.merge))
+
+test> data.List.unsnoc.tests.isInverseOfSnoc = runs 100 do
+ use List :+
+ xs = gen.listOf natInOrder ()
+ expect (xs === (xs |> List.unsnoc |> applyOrEmpty (:+)))
+
+data.List.unzip : [(a, b)] -> ([a], [b])
+data.List.unzip = cases
+ [] -> ([], [])
+ (a, b) +: xs ->
+ (as, bs) = data.List.unzip xs
+ (a List.+: as, b List.+: bs)
+
+data.List.unzip.doc : Doc
+data.List.unzip.doc =
+ use List unzip
+ {{
+ `` unzip pairs `` splits a list of pairs into two lists.
+
+ # Examples
+
+ ```
+ unzip [(1, 2), (3, 4), (5, 6)]
+ ```
+
+ ```
+ unzip (Bag.occurrenceList (Bag.fromText "🍓🍉🍇🍋🍊🍓🍉🍇🍋🍈🍓🍉🍋🍊"))
+ ```
+ }}
+
+data.List.updateAt : (a ->{g1} a) -> Nat -> [a] ->{g1} [a]
+data.List.updateAt f n as = match List.splitAt n as with
+ (bs, c +: cs) -> bs List.++ (f c List.+: cs)
+ _ -> as
+
+data.List.updateAt.doc : Doc
+data.List.updateAt.doc =
+ use Nat * +
+ {{
+ `` updateAt f n xs `` modifies the `n`th element of `xs` by applying the
+ function `f` to it. If there is no `n`th element, this does nothing.
+
+ # Example
+
+ Adds `1` to the `0`th element of the list:
+
+ ```
+ updateAt (x -> x + 1) 0 [1, 2, 3]
+ ```
+
+ Does nothing, as there is no `3`rd element of the list:
+
+ ```
+ updateAt (x -> x * 2) 3 [1, 2, 3]
+ ```
+ }}
+
+test> data.List.updateAt.tests.updates = runs 1000 do
+ use List at
+ use Nat + ==
+ xs = gen.listOf natInOrder ()
+ n = natInOrder()
+ f x = x + 1
+ updated = updateAt f n xs
+ oa = Optional.map f (at n xs)
+ ob = at n updated
+ expect match (oa, ob) with
+ (None, None) -> true
+ (Some a, Some b) -> a == b
+ _ -> false
+
+data.List.zip : [a] -> [b] -> [(a, b)]
+data.List.zip =
+ use List :+
+ go acc as bs = match (as, bs) with
+ (a +: as, b +: bs) -> go (acc :+ (a, b)) as bs
+ _ -> acc
+ go []
+
+data.List.zip.doc : Doc
+data.List.zip.doc =
+ use List zip
+ {{
+ Take corresponding elements from two {type List}s and combine them into a
+ {type List} of pairs.
+
+ If the two lists are of different lengths, the result will be the same length
+ as the shorter list.
+
+ # Examples
+
+ ```
+ zip [1, 2, 3] [?a, ?b, ?c]
+ ```
+
+ ```
+ zip [1, 2, 3] [?a, ?b]
+ ```
+
+ ```
+ zip [1, 2] [?a, ?b, ?c]
+ ```
+
+ ```
+ zip [1, 2] []
+ ```
+
+ ```
+ zip [] [?a, ?b, ?c]
+ ```
+
+ ```
+ zip [] []
+ ```
+ }}
+
+test> data.List.zip.tests.length = runs 1000 do
+ use List size
+ use gen boolean listOf
+ l1 = listOf boolean ()
+ l2 = listOf boolean ()
+ expect (size (List.zip l1 l2) === Nat.min (size l1) (size l2))
+
+test> data.List.zip.tests.productLaw =
+ runs 1000 do
+ use List at! size
+ use gen boolean listOf
+ l1 = listOf boolean ()
+ l2 = listOf boolean ()
+ z = List.zip l1 l2
+ r = List.range 0 (Nat.min (size l1) (size l2))
+ expect
+ (List.all (i -> List.at i z === (toOptional! do (at! i l1, at! i l2))) r)
+
+data.List.zipWith : (a ->{𝕖} b ->{𝕖} c) -> [a] -> [b] ->{𝕖} [c]
+data.List.zipWith f a b =
+ use List :+
+ go acc as bs = match (as, bs) with
+ (ah +: at, bh +: bt) -> go (acc :+ f ah bh) at bt
+ _ -> acc
+ go [] a b
+
+data.List.zipWith.doc : Doc
+data.List.zipWith.doc =
+ use List zipWith
+ use Nat +
+ use Tuple pair
+ {{
+ Apply a function to corresponding elements of two lists, producing a list of
+ the results. The output list is the same length as the shorter of the two
+ input lists. Each element of the output list is the result of applying the
+ function to the elements of the input lists at the same position. If one of
+ the input lists is shorter than the other, the extra elements of the longer
+ list are ignored.
+
+ # Examples
+
+ ```
+ xs = [1, 2, 3, 4, 5]
+ ys = [7, 8, 9, 10, 11]
+ zipWith (+) xs ys
+ ```
+
+ The length of the output list is the length of the shorter input list:
+
+ ```
+ xs = [1, 2, 3]
+ ys = [7, 8, 9, 10, 11]
+ zipWith pair xs ys
+ ```
+
+ ```
+ xs = [1, 2, 3, 4, 5]
+ ys = [7, 8, 9]
+ zipWith pair xs ys
+ ```
+
+ We can find all adjacent pairs in a list by zipping it with its own tail:
+
+ ```
+ xs = 1 +| [2, 3, 4, 5]
+ zipWith pair (List.Nonempty.toList xs) (Nonempty.tail xs)
+ ```
+ }}
+
+test> data.List.zipWith.tests.edge1 =
+ use Nat +
+ check
+ let
+ actual = List.zipWith (+) [1, 5, 10] []
+ expected = []
+ if Boolean.not (actual === expected) then
+ bug ("Not equal!", actual, expected)
+ else true
+
+test> data.List.zipWith.tests.edge2 =
+ use Nat +
+ check
+ let
+ actual = List.zipWith (+) [] []
+ expected = []
+ if Boolean.not (actual === expected) then
+ bug ("Not equal!", actual, expected)
+ else true
+
+test> data.List.zipWith.tests.edge3 = runs 100 do
+ use Nat +
+ xs = gen.listOf natInOrder ()
+ expect (List.zipWith (+) xs [] === [])
+
+test> data.List.zipWith.tests.edge4 = runs 100 do
+ use Nat +
+ xs = gen.listOf natInOrder ()
+ expect (List.zipWith (+) [] xs === [])
+
+test> data.List.zipWith.tests.ex1 =
+ use Nat +
+ check
+ let
+ actual = List.zipWith (+) [1, 5, 10] [1, 2, 3]
+ expected = [2, 7, 13]
+ if Boolean.not (actual === expected) then
+ bug ("Not equal!", actual, expected)
+ else true
+
+test> data.List.zipWith.tests.ex2 =
+ use Nat +
+ check
+ let
+ xs = [1, 2, 3, 4, 5]
+ ys = [7, 8, 9, 10, 11]
+ actual = List.zipWith (+) xs ys
+ expected = [8, 10, 12, 14, 16]
+ assert (actual === expected) ("Not equal", actual, expected) true
+
+test> data.List.zipWith.tests.zipWithRange = runs 100 do
+ xs = gen.listOf natInOrder ()
+ usingZipWith = List.zipWith (x y -> (x, y)) (List.range 0 (List.size xs)) xs
+ usingMapIndexed = mapIndexed (x y -> (x, y)) xs
+ expect (usingZipWith === usingMapIndexed)
+
+(data.Map.==) : Map k v -> Map k v -> Boolean
+x data.Map.== y =
+ use List sort
+ use Map toList
+ sort (toList x) === sort (toList y)
+
+data.Map.adjust : (v ->{e} v) -> k -> Map k v ->{e} Map k v
+data.Map.adjust f = Map.adjustWithKey (const f)
+
+data.Map.adjust.doc : Doc
+data.Map.adjust.doc =
+ use List reverse
+ use Map adjust toList
+ {{
+ `` adjust f k m `` updates the value under the key `k` in the {type Map} `m`
+ with the result of applying the function `f` to the existing value under `k`.
+ Does nothing if the key `k` is not in the {type Map}.
+
+ # Examples
+
+ ```
+ a = Map.fromList [(3, [4, 5]), (5, [1, 2])]
+ toList (adjust reverse 5 a)
+ ```
+
+ ```
+ toList (adjust reverse 7 Map.empty)
+ ```
+ }}
+
+test> data.Map.adjust.tests.adjusts = runs 100 do
+ use List reverse
+ use Map get
+ m1 = tests.mapOf natInOrder (gen.listOf natInOrder) ()
+ k = natInOrder()
+ m2 = Map.adjust reverse k m1
+ v1 = get k m1
+ v2 = get k m2
+ expect (assertEquals v2 (Optional.map reverse v1))
+
+test> data.Map.adjust.tests.alterMap = runs 100 do
+ use List reverse
+ m = tests.mapOf natInOrder (gen.listOf natInOrder) ()
+ k = natInOrder()
+ expect (Map.adjust reverse k m === Map.alter (Optional.map reverse) k m)
+
+data.Map.adjustWithKey : (k ->{e} v ->{e} v) -> k -> Map k v ->{e} Map k v
+data.Map.adjustWithKey f k = cases
+ internal.Tip -> internal.Tip
+ internal.Bin sx kx x l r ->
+ match Universal.ordering k kx with
+ Less -> internal.Bin sx kx x (data.Map.adjustWithKey f k l) r
+ Greater -> internal.Bin sx kx x l (data.Map.adjustWithKey f k r)
+ Equal -> internal.Bin sx kx (f kx x) l r
+
+data.Map.adjustWithKey.doc : Doc
+data.Map.adjustWithKey.doc =
+ use List +:
+ use Map adjustWithKey toList
+ {{
+ `` adjustWithKey f k m `` updates the value under the key `k` in the
+ {type Map} `m` with the result of applying the function `f` to both `k` and
+ the existing value under `k`. Does nothing if the key `k` is not in the
+ {type Map}.
+
+ # Examples
+
+ ```
+ a = Map.fromList [(5, [1, 2]), (3, [4, 5])]
+ toList (adjustWithKey (+:) 5 a)
+ ```
+
+ ```
+ toList (adjustWithKey (+:) 5 Map.empty)
+ ```
+ }}
+
+test> data.Map.adjustWithKey.tests.adjusts = runs 100 do
+ use List :+ reverse
+ use Map get
+ m1 = tests.mapOf natInOrder (gen.listOf natInOrder) ()
+ k = natInOrder()
+ m2 = Map.adjustWithKey (k v -> reverse v :+ k) k m1
+ v1 = get k m1
+ v2 = get k m2
+ expect (assertEquals v2 (Optional.map (v -> reverse v :+ k) v1))
+
+data.Map.align : Map k a -> Map k b -> Map k (OneOrBoth a b)
+data.Map.align = Map.alignWith id
+
+data.Map.align.doc : Doc
+data.Map.align.doc =
+ use Map fromList
+ {{
+ Aligns two maps into a map of {type OneOrBoth} values.
+
+ The result will have the same keys as the union of the keys of the two input
+ maps, and each value will be a {type OneOrBoth} containing the corresponding
+ values from the two input maps. If a key is present in only one of the input
+ maps, the result will contain {This} or {That} values accordingly. If a key
+ is present in both input maps, the result will contain a {Both} value.
+
+ # Example
+
+ ```
+ Map.toList
+ (Map.align
+ (fromList [(1, "hello"), (2, "world")]) (fromList [(2, 42), (3, 43)]))
+ ```
+
+ # See also
+
+ * {Map.alignWith} - a variant where you can specify a function to apply to
+ the values.
+ }}
+
+data.Map.alignWith :
+ (OneOrBoth a b ->{g} c) -> Map k a -> Map k b ->{g} Map k c
+data.Map.alignWith f m1 m2 = Map.alignWithKey (_ x -> f x) m1 m2
+
+data.Map.alignWith.doc : Doc
+data.Map.alignWith.doc =
+ use Map fromList
+ use Text ++
+ {{
+ Aligns two maps into a map of values using a function.
+
+ The result will have the same keys as the union of the keys of the two input
+ maps, and each value will be the result of applying the given function to the
+ corresponding values from the two input maps – {This} and {That} for keys
+ that are present in only one of the input maps, and {Both} for keys that are
+ present in both input maps.
+
+ # Example
+
+ ```
+ f = cases
+ This a -> "only in the first map: " ++ a
+ That b -> "only in the second map: " ++ b
+ Both a b -> "in both maps: " ++ a ++ " and " ++ b
+ Map.values
+ (Map.alignWith
+ f
+ (fromList [(1, "circuit"), (2, "quasar")])
+ (fromList [(2, "voyage"), (3, "harmony")]))
+ ```
+
+ # See also
+
+ * {Map.align} - a variant that returns a map of {type OneOrBoth} values.
+ * {Map.alignWithKey} - a variant where the function also receives the key.
+ * {Map.mergeWith} - a variant that allows the function to remove keys from
+ the result map.
+ }}
+
+data.Map.alignWithKey :
+ (k ->{e} OneOrBoth a b ->{f} c) -> Map k a -> Map k b ->{e, f} Map k c
+data.Map.alignWithKey f m1 m2 =
+ match (m1, m2) with
+ (_, internal.Tip) -> Map.mapWithKey (k a -> f k (This a)) m1
+ (internal.Tip, _) -> Map.mapWithKey (k b -> f k (That b)) m2
+ (internal.Bin _ k1 x1 l1 r1, internal.Bin _ k2 x2 l2 r2) ->
+ (l2, mb, r2) = splitLookup k1 m2
+ use Map.internal link
+ use data.Map alignWithKey
+ l1l2 = alignWithKey f l1 l2
+ r1r2 = alignWithKey f r1 r2
+ match mb with
+ None -> link k1 (f k1 (This x1)) l1l2 r1r2
+ Some x2 -> link k1 (f k1 (Both x1 x2)) l1l2 r1r2
+
+data.Map.alignWithKey.doc : Doc
+data.Map.alignWithKey.doc =
+ use Map fromList
+ use Nat toText
+ use Text ++
+ {{
+ Aligns two maps into a map of values using a function.
+
+ The result will have the same keys as the union of the keys of the two input
+ maps, and each value will be the result of applying the given function to the
+ corresponding key-value pairs from the two input maps. The function receives
+ {This} and {That} for values under keys that are present in only one of the
+ input maps, and {Both} for keys that are present in both input maps.
+
+ # Example
+
+ ```
+ f k = cases
+ This a -> "only in the first map: " ++ toText k ++ " -> " ++ a
+ That b -> "only in the second map: " ++ toText k ++ " -> " ++ b
+ Both a b -> "in both maps: " ++ toText k ++ " -> " ++ a ++ " and " ++ b
+ Map.values
+ (Map.alignWithKey
+ f
+ (fromList [(1, "circuit"), (2, "quasar")])
+ (fromList [(2, "voyage"), (3, "harmony")]))
+ ```
+
+ # See also
+
+ * {Map.align} - a variant that returns a map of {type OneOrBoth} values.
+ * {Map.alignWith} - a variant where the function doesn't take the key.
+ }}
+
+test> data.Map.alignWithKey.tests.effects = test.verify do
+ use Map == singleton
+ m = singleton "foo" ()
+ go k v =
+ emit (k, v)
+ v
+ let
+ (seen, result) = toListWithResult do Map.alignWithKey go m m
+ labeled "result" do ensuring do result == singleton "foo" (Both () ())
+ labeled "seen" do ensuring do seen === [("foo", Both () ())]
+
+data.Map.alter : (Optional v ->{e} Optional v) -> k -> Map k v ->{e} Map k v
+data.Map.alter f k = cases
+ internal.Tip ->
+ match f None with
+ None -> internal.Tip
+ Some x -> Map.singleton k x
+ internal.Bin sx kx x l r ->
+ match Universal.ordering k kx with
+ Less -> balance kx x (data.Map.alter f k l) r
+ Greater -> balance kx x l (data.Map.alter f k r)
+ Equal ->
+ match f (Some x) with
+ Some x' -> internal.Bin sx kx x' l r
+ None -> glue l r
+
+data.Map.alter.doc : Doc
+data.Map.alter.doc =
+ use Map fromList toList
+ {{
+ The expression `` Map.alter f k m `` changes the value under the key `k` in
+ the {type Map} `m`, or the absence thereof. {Map.alter} can be used to
+ insert, delete, or update a value in a {type Map}.
+
+ The functions {Map.alter} and {Map.get} stand in a naturality relationship,
+ as follows:
+
+ `` get k (alter f k m) === f (get k m) ``
+
+ # Examples
+
+ If the key is absent in the {type Map} and the function returns {None},
+ then {Map.alter} leaves the {type Map} alone:
+
+ ```
+ m = fromList [(5, "a"), (3, "b")]
+ toList (Map.alter (const None) 7 m)
+ ```
+
+ If the function returns {None} and the key is present in the {type Map},
+ then {Map.alter} deletes the key:
+
+ ```
+ m = fromList [(5, "a"), (3, "b")]
+ toList (Map.alter (const None) 5 m)
+ ```
+
+ If the function returns {Some} and the key is present in the {type Map},
+ then {Map.alter} replaces the value under the key:
+
+ ```
+ m = fromList [(5, ?a), (3, ?b)]
+ toList (Map.alter (Optional.map ascii.toUpper) 5 m)
+ ```
+
+ If the function returns {Some} and the key is not in the {type Map}, then
+ {Map.alter} adds the key:
+
+ ```
+ m = fromList [(5, "a"), (3, "b")]
+ toList (Map.alter (const (Some "c")) 7 m)
+ ```
+ }}
+
+test> data.Map.alter.tests.functor = runs 100 do
+ m = tests.mapOf natInOrder natInOrder ()
+ k = natInOrder()
+ expect (Map.alter id k m === m)
+
+test> data.Map.alter.tests.homomorphism = runs 100 do
+ use Map alter
+ use gen boolean
+ m = tests.mapOf natInOrder boolean ()
+ k = natInOrder()
+ f = someOrNone yesNo() boolean() ()
+ g = someOrNone yesNo() boolean() ()
+ expect (alter (f << g) k m === alter f k (alter g k m))
+
+test> data.Map.alter.tests.naturality = runs 100 do
+ use Map get
+ use gen boolean
+ m = tests.mapOf natInOrder boolean ()
+ k = natInOrder()
+ f = someOrNone yesNo() boolean() ()
+ x = get k (Map.alter f k m)
+ y = f (get k m)
+ expect (x === y)
+
+data.Map.breakOffMax : Map k v -> Optional ((k, v), Map k v)
+data.Map.breakOffMax = cases
+ internal.Bin _ k x l internal.Tip -> Some ((k, x), l)
+ internal.Bin _ k x l r ->
+ f = cases (km, r') -> (km, Map.internal.balanceL k x l r')
+ Optional.map f (data.Map.breakOffMax r)
+ internal.Tip -> None
+
+data.Map.breakOffMax.doc : Doc
+data.Map.breakOffMax.doc =
+ {{
+ Finds the maximal key in the {type Map} and removes it. Returns the key, the
+ value under the key, and the remainder of the {type Map} with that key
+ removed. If the {type Map} is empty, returns {None}.
+
+ # Example
+
+ ```
+ Optional.map
+ (Tuple.mapRight Map.toList)
+ (Map.breakOffMax (Map.fromList [(5, "a"), (3, "b")]))
+ ```
+ }}
+
+test> data.Map.breakOffMax.tests.isMax =
+ runs 100 do
+ x = sortBy at1 (gen.listOf (pairOf natInOrder Text.ascii) ())
+ kv = List.last x
+ m = Map.fromList x
+ expect
+ (assertEquals
+ (Map.breakOffMax m)
+ (Optional.map (kv -> (kv, Map.delete (at1 kv) m)) kv))
+
+data.Map.breakOffMax! : Map k v ->{Abort} ((k, v), Map k v)
+data.Map.breakOffMax! = cases
+ internal.Bin _ k x l internal.Tip -> ((k, x), l)
+ internal.Bin _ k x l r ->
+ f = cases (km, r') -> (km, Map.internal.balanceL k x l r')
+ f (data.Map.breakOffMax! r)
+ internal.Tip -> abort
+
+data.Map.breakOffMax!.doc : Doc
+data.Map.breakOffMax!.doc =
+ {{
+ Finds the maximal key in the {type Map} and removes it. Returns the key, the
+ value under the key, and the remainder of the {type Map} with that key
+ removed. If the {type Map} is empty, calls {abort}.
+
+ # Example
+
+ ```
+ toOptional! do
+ Tuple.mapRight
+ Map.toList (breakOffMax! (Map.fromList [(5, "a"), (3, "b")]))
+ ```
+ }}
+
+data.Map.breakOffMin : Map k v -> Optional ((k, v), Map k v)
+data.Map.breakOffMin = cases
+ internal.Bin _ k x internal.Tip r -> Some ((k, x), r)
+ internal.Bin _ k x l r ->
+ f = cases (km, l') -> (km, Map.internal.balanceR k x l' r)
+ Optional.map f (data.Map.breakOffMin l)
+ internal.Tip -> None
+
+data.Map.breakOffMin.doc : Doc
+data.Map.breakOffMin.doc =
+ {{
+ Finds the minimal key in the {type Map} and removes it. Returns the key, the
+ value under the key, and the remainder of the {type Map} with that key
+ removed. If the {type Map} is empty, returns {None}.
+
+ # Example
+
+ ```
+ Optional.map
+ (Tuple.mapRight Map.toList)
+ (Map.breakOffMin (Map.fromList [(5, "a"), (3, "b")]))
+ ```
+ }}
+
+test> data.Map.breakOffMin.tests.isMin =
+ runs 100 do
+ x =
+ distinctBy
+ at1 (sortBy at1 (gen.listOf (pairOf natInOrder Text.ascii) ()))
+ kv = List.head x
+ m = Map.fromList x
+ expect
+ (assertEquals
+ (Map.breakOffMin m)
+ (Optional.map (kv -> (kv, Map.delete (at1 kv) m)) kv))
+
+data.Map.breakOffMin! : Map k v ->{Abort} ((k, v), Map k v)
+data.Map.breakOffMin! = cases
+ internal.Bin _ k x internal.Tip r -> ((k, x), r)
+ internal.Bin _ k x l r ->
+ f = cases (km, l') -> (km, Map.internal.balanceR k x l' r)
+ f (data.Map.breakOffMin! l)
+ internal.Tip -> abort
+
+data.Map.breakOffMin!.doc : Doc
+data.Map.breakOffMin!.doc =
+ {{
+ Finds the minimal key in the {type Map} and removes it. Returns the key, the
+ value under the key, and the remainder of the {type Map} with that key
+ removed. If the {type Map} is empty, calls {abort}.
+
+ # Example
+
+ ```
+ Abort.toOptional do
+ Tuple.mapRight
+ Map.toList (breakOffMin! (Map.fromList [(5, "a"), (3, "b")]))
+ ```
+ }}
+
+data.Map.contains : k -> Map k v -> Boolean
+data.Map.contains k m = match Map.get k m with
+ None -> false
+ Some _ -> true
+
+data.Map.contains.doc : Doc
+data.Map.contains.doc =
+ {{
+ The expression `` Map.contains k m `` returns `` true `` if the key `k`
+ exists in the {type Map} `m`, and `` false `` otherwise.
+ }}
+
+test> data.Map.contains.tests.delete = runs 100 do
+ m = tests.mapOf natInOrder natInOrder ()
+ k = natInOrder()
+ expect (Boolean.not (Map.contains k (Map.delete k m)))
+
+test> data.Map.contains.tests.put = runs 100 do
+ m = tests.mapOf natInOrder natInOrder ()
+ k = natInOrder()
+ v = natInOrder()
+ expect (Map.contains k (Map.insert k v m))
+
+data.Map.delete : k -> Map k v -> Map k v
+data.Map.delete k = cases
+ internal.Tip -> internal.Tip
+ internal.Bin _ kx x l r ->
+ match Universal.ordering k kx with
+ Less -> Map.internal.balanceR kx x (data.Map.delete k l) r
+ Greater -> Map.internal.balanceL kx x l (data.Map.delete k r)
+ Equal -> glue l r
+
+data.Map.delete.doc : Doc
+data.Map.delete.doc =
+ {{
+ Deletes a key and its value from the {type Map}. Does nothing if the key is
+ not in the {type Map}.
+ }}
+
+test> data.Map.delete.tests.deletes =
+ runs 100 do
+ k = natInOrder()
+ expect
+ (Boolean.not
+ (Map.contains k (Map.delete k (tests.mapOf natInOrder natInOrder ()))))
+
+data.Map.difference : Map k a -> Map k b -> Map k a
+data.Map.difference = cases
+ internal.Tip, _ -> internal.Tip
+ t1, internal.Tip -> t1
+ t1, internal.Bin _ k _ l2 r2 ->
+ (l1, r1) = Map.split k t1
+ use Map size
+ use Nat +
+ use data.Map difference
+ l1l2 = difference l1 l2
+ r1r2 = difference r1 r2
+ if size l1l2 + size r1r2 === size t1 then t1 else link2 l1l2 r1r2
+
+data.Map.difference.doc : Doc
+data.Map.difference.doc =
+ use Map difference fromList
+ {{
+ The expression `` difference m1 m2 `` constructs a new {type Map} with all
+ elements `(k,v)` from `m1` except those where `k` exist as a key in `m2`.
+
+ # Example
+
+ ```
+ m1 = fromList [(1, ?a), (2, ?b)]
+ m2 = fromList [(1, ?a), (3, ?c)]
+ Map.toList (difference m1 m2)
+ ```
+ }}
+
+test> data.Map.difference.tests.prop = runs 100 do
+ use Map contains keys
+ use tests mapOf
+ m1 = mapOf natInOrder natInOrder ()
+ m2 = mapOf natInOrder natInOrder ()
+ m3 = Map.difference m1 m2
+ k1 = keys m1
+ k2 = keys m2
+ k3 = keys m3
+ expect (List.all (flip contains m1) k3 && List.none (flip contains m3) k2)
+
+data.Map.doc : Doc
+data.Map.doc =
+ use Map contains delete fromList get insert size toList
+ use Nat +
+ {{
+ {type Map} is a sorted finite map, supporting efficient lookup and deletion
+ of entries by key. Each key is associated with a single unique value.
+
+ The {insert} and {get} and related functions all use {Universal.ordering} for
+ ordering the keys.
+
+ ```
+ fromList [("apple", 1), ("pear", 2)] |> get "pear"
+ ```
+
+ # Common functions
+
+ @signatures{fromList, get, insert, delete, contains, toList}
+
+ # More examples
+
+ ```
+ toList (insert "apple" 1 Map.empty)
+ ```
+
+ ```
+ Map.union (fromList [("mango", 2)]) (fromList [("orange", 3)]) |> toList
+ ```
+
+ ```
+ delete "blueberry" (fromList [("apple", 1), ("blueberry", 2)]) |> toList
+ ```
+
+ ```
+ contains "mango" (fromList [("apple", 1), ("blueberry", 2)])
+ ```
+
+ ```
+ size (fromList [("apple", 1), ("blueberry", 2)])
+ ```
+
+ ```
+ Map.unionWith (+) (fromList [("a", 1)]) (fromList [("a", 1), ("b", 2)])
+ |> toList
+ ```
+
+ There are a lot more functions defined. Browse the namespace under
+ {type Map} to see more.
+
+ # Implementation notes
+
+ The type is implemented as a balanced binary tree of logarithmic depth, so
+ most operations (such as {get} or {insert}) that touch an individual key
+ take logarithmic time.
+
+ ```
+ fromList [("a", "v1"), ("b", "v2"), ("c", "v3"), ("d", "v4"), ("e", "v5")]
+ ```
+
+ The size of each subtree is cached at non-leaf nodes, and the entry at each
+ node has a key which is bigger than all entries in the left subtree, and
+ smaller than all entries in the right subtree. Operations like {insert} and
+ {delete} maintain a balanced tree so its depth is logarithmic in the
+ {size}.
+ }}
+
+data.Map.empty : Map k v
+data.Map.empty = internal.Tip
+
+data.Map.empty.doc : Doc
+data.Map.empty.doc = {{ The empty {type Map}. }}
+
+data.Map.equals.doc : Doc
+data.Map.equals.doc =
+ {{
+ Checks if two {type Map}s are equal. They're equal precisely when they have
+ all the same keys, and each key has the same value in one {type Map} as it
+ does in the other.
+ }}
+
+test> data.Map.equals.tests.reflexive = runs 100 do
+ m = tests.mapOf Text.ascii natInOrder ()
+ expect (Map.equals m m)
+
+test> data.Map.equals.tests.symmetric = runs 100 do
+ use Map equals
+ use gen boolean
+ use tests mapOf
+ a = mapOf boolean boolean ()
+ b = mapOf boolean boolean ()
+ expect (equals a b === equals b a)
+
+test> data.Map.equals.tests.transitive = runs 100 do
+ use Map equals
+ use gen boolean
+ use tests mapOf
+ a = mapOf boolean boolean ()
+ b = mapOf boolean boolean ()
+ c = mapOf boolean boolean ()
+ expect (implies (equals a b && equals b c) (equals a c))
+
+data.Map.filter : (v ->{g} Boolean) -> Map k v ->{g} Map k v
+data.Map.filter p = Map.filterWithKey (_ v -> p v)
+
+data.Map.filter.doc : Doc
+data.Map.filter.doc =
+ {{
+ Filters a {type Map} by retaining only entries where the value satisfies the
+ given predicate.
+
+ # Example
+
+ This filters a map to retain only entries where the value is even:
+
+ ```
+ Map.toList (Map.filter Nat.isEven (Map.fromList [(1, 2), (2, 3), (3, 4)]))
+ ```
+
+ # See also
+
+ * {Map.filterWithKey} - Filters a map by the keys and values.
+ * {{ docLink (docEmbedTermLink do Map.filterKeys) }} - Filters a map by the
+ keys.
+ * {Map.mapOptional} - Maps over a map, possibly removing entries.
+ }}
+
+data.Map.filterAlignWithKey :
+ (k ->{e} OneOrBoth a b ->{f} Optional c)
+ -> Map k a
+ -> Map k b
+ ->{e, f} Map k c
+data.Map.filterAlignWithKey f m1 m2 =
+ match (m1, m2) with
+ (_, internal.Tip) -> Map.mapOptionalWithKey (k a -> f k (This a)) m1
+ (internal.Tip, _) -> Map.mapOptionalWithKey (k b -> f k (That b)) m2
+ (internal.Bin _ k1 x1 l1 r1, internal.Bin _ k2 x2 l2 r2) ->
+ (l2, mb, r2) = splitLookup k1 m2
+ use Map.internal link
+ use data.Map filterAlignWithKey
+ l1l2 = filterAlignWithKey f l1 l2
+ r1r2 = filterAlignWithKey f r1 r2
+ match mb with
+ None ->
+ match f k1 (This x1) with
+ Some v -> link k1 v l1l2 r1r2
+ None -> link2 l1l2 r1r2
+ Some x2 ->
+ match f k1 (Both x1 x2) with
+ Some v -> link k1 v l1l2 r1r2
+ None -> link2 l1l2 r1r2
+
+data.Map.filterAlignWithKey.doc : Doc
+data.Map.filterAlignWithKey.doc =
+ use Map filterAlignWithKey fromList toList
+ use Nat + ==
+ {{
+ Aligns two maps by their keys and applies a function to the aligned pairs.
+ The function receives the key and a {type OneOrBoth} value, which is either
+ {This} if the key is only in the first map, {That} if the key is only in the
+ second map, or {Both} if the key is in both maps. The function can return a
+ value for the aligned pair, which will be the value under that key in the
+ result, or {None} to omit that key from the result.
+
+ # Examples
+
+ This aligns two maps and sum the values under each key:
+
+ ```
+ toList
+ (filterAlignWithKey
+ (k ob -> (match ob with
+ This x -> Some x
+ That y -> Some y
+ Both x y -> Some (x + y)))
+ (fromList [(1, 2), (2, 3)])
+ (fromList [(2, 4), (3, 5)]))
+ ```
+
+ This aligns two maps and filters out keys where the values are equal,
+ taking the minimum of the two values otherwise:
+
+ ```
+ toList
+ (filterAlignWithKey
+ (k ob -> (match ob with
+ Both x y
+ | x == y -> None
+ | otherwise -> Some (Nat.min x y)
+ This x -> Some x
+ That y -> Some y))
+ (fromList [(1, 2), (2, 3)])
+ (fromList [(2, 3), (3, 5)]))
+ ```
+ }}
+
+test> data.Map.filterAlignWithKey.tests.difference =
+ test.verify do
+ use List zip
+ use Map fromList toList
+ _ = Each.repeat 100
+ n = do Random.natIn 0 20
+ list = do Random.listOf n n
+ m1 = fromList (zip list() list())
+ m2 = fromList (zip list() list())
+ f _ = cases
+ This x -> Some x
+ That _ -> None
+ Both _ _ -> None
+ ensureEqual
+ (toList (Map.filterAlignWithKey f m1 m2)) (toList (Map.difference m1 m2))
+
+test> data.Map.filterAlignWithKey.tests.effects = test.verify do
+ use Map == singleton
+ m = singleton "foo" ()
+ go k v =
+ emit (k, v)
+ Some v
+ let
+ (seen, result) = toListWithResult do Map.filterAlignWithKey go m m
+ labeled "result" do ensuring do result == singleton "foo" (Both () ())
+ labeled "seen" do ensuring do seen === [("foo", Both () ())]
+
+test> data.Map.filterAlignWithKey.tests.empty = test.verify do
+ use List zip
+ use Map fromList
+ _ = Each.repeat 100
+ n = do Random.natIn 0 20
+ list = do Random.listOf n n
+ m1 = fromList (zip list() list())
+ m2 = fromList (zip list() list())
+ f _ = cases
+ This _ -> None
+ That _ -> None
+ Both _ _ -> None
+ ensureEqual (Map.toList (Map.filterAlignWithKey f m1 m2)) []
+
+test> data.Map.filterAlignWithKey.tests.intersection =
+ test.verify do
+ use List zip
+ use Map fromList toList
+ use Nat +
+ _ = Each.repeat 100
+ n = do Random.natIn 0 20
+ list = do Random.listOf n n
+ m1 = fromList (zip list() list())
+ m2 = fromList (zip list() list())
+ f _ = cases
+ This _ -> None
+ That _ -> None
+ Both x y -> Some (x + y)
+ ensureEqual
+ (toList (Map.filterAlignWithKey f m1 m2))
+ (toList (Map.intersectWith (+) m1 m2))
+
+test> data.Map.filterAlignWithKey.tests.leftIdentity = test.verify do
+ use List zip
+ use Map fromList toList
+ _ = Each.repeat 100
+ n = do Random.natIn 0 20
+ list = do Random.listOf n n
+ m1 = fromList (zip list() list())
+ m2 = fromList (zip list() list())
+ f _ = cases
+ This x -> Some x
+ That y -> None
+ Both x _ -> Some x
+ ensureEqual (toList (Map.filterAlignWithKey f m1 m2)) (toList m1)
+
+test> data.Map.filterAlignWithKey.tests.rightIdentity = test.verify do
+ use List zip
+ use Map fromList toList
+ _ = Each.repeat 100
+ n = do Random.natIn 0 20
+ list = do Random.listOf n n
+ m1 = fromList (zip list() list())
+ m2 = fromList (zip list() list())
+ f _ = cases
+ This x -> None
+ That y -> Some y
+ Both _ y -> Some y
+ ensureEqual (toList (Map.filterAlignWithKey f m1 m2)) (toList m2)
+
+test> data.Map.filterAlignWithKey.tests.union =
+ test.verify do
+ use List zip
+ use Map fromList toList
+ use Nat +
+ _ = Each.repeat 100
+ n = do Random.natIn 0 20
+ list = do Random.listOf n n
+ m1 = fromList (zip list() list())
+ m2 = fromList (zip list() list())
+ f _ = cases
+ This x -> Some x
+ That y -> Some y
+ Both x y -> Some (x + y)
+ ensureEqual
+ (toList (Map.filterAlignWithKey f m1 m2))
+ (toList (Map.unionWith (+) m1 m2))
+
+data.Map.filterKeys : (k ->{g} Boolean) -> Map k v ->{g} Map k v
+data.Map.filterKeys p = Map.filterWithKey (k _ -> p k)
+
+data.Map.filterKeys.doc : Doc
+data.Map.filterKeys.doc =
+ {{
+ Filters a {type Map} by retaining only entries where the key satisfies the
+ given predicate.
+
+ # Example
+
+ This filters a map to retain only entries where the key is even:
+
+ ```
+ Map.toList
+ (Map.filterKeys Nat.isEven (Map.fromList [(1, 2), (2, 3), (3, 4)]))
+ ```
+
+ # See also
+
+ * {Map.filterWithKey} - Filters a map by the keys and values.
+ * {{ docLink (docEmbedTermLink do Map.filter) }} - Filters a map by the
+ values.
+ * {Map.mapOptional} - Maps over a map, possibly removing entries.
+ }}
+
+data.Map.filterWithKey : (k ->{e} a ->{f} Boolean) -> Map k a ->{e, f} Map k a
+data.Map.filterWithKey f = cases
+ internal.Bin n k x l r ->
+ use data.Map filterWithKey
+ l' = filterWithKey f l
+ r' = filterWithKey f r
+ match f k x with
+ false -> glue l' r'
+ true -> Map.internal.link k x l' r'
+ internal.Tip -> internal.Tip
+
+data.Map.filterWithKey.doc : Doc
+data.Map.filterWithKey.doc =
+ use Map filterKeys
+ use Nat ==
+ {{
+ Filters a {type Map} by retaining only entries that satisfy the given
+ predicate.
+
+ # Example
+
+ This filters a map to retain only entries where the key and value are
+ equal:
+
+ ```
+ Map.toList (Map.filterWithKey (==) (Map.fromList [(1, 1), (2, 3), (3, 3)]))
+ ```
+
+ # See also
+
+ * {filterKeys} - Filters a map by the values.
+ * {filterKeys} - Filters a map by the keys.
+ * {Map.mapOptional} - Maps over a map, possibly removing entries.
+ }}
+
+data.Map.foldLeft : (a ->{e} b ->{e} a) -> a -> Map k b ->{e} a
+data.Map.foldLeft f z = cases
+ internal.Tip -> z
+ m@(internal.Bin _ _ x l r) ->
+ data.Map.foldLeft f (f (data.Map.foldLeft f z l) x) r
+
+data.Map.foldLeft.doc : Doc
+data.Map.foldLeft.doc =
+ use Map foldLeft
+ use Text ++
+ {{
+ The expression `` foldLeft f z m `` folds the values in the {type Map} `m`
+ using the left-associative binary operator `f`, starting with `z`. The values
+ in the {type Map} are passed to `f` in key-ascending order.
+
+ # Example
+
+ ```
+ m = Map.fromList [(1, "a"), (2, "b"), (3, "c")]
+ f acc v = acc ++ v
+ foldLeft f "x" m
+ ```
+ }}
+
+test> data.Map.foldLeft.tests.onElements = runs 100 do
+ use Text ++
+ m = tests.mapOf natInOrder Text.ascii ()
+ expect (Map.foldLeft (++) "" m === List.foldLeft (++) "" (Map.values m))
+
+data.Map.foldLeftWithKey : (a ->{e} k ->{e} b ->{e} a) -> a -> Map k b ->{e} a
+data.Map.foldLeftWithKey f z m =
+ go z' = cases
+ internal.Tip -> z'
+ internal.Bin _ kx x l r -> go (f (go z' l) kx x) r
+ go z m
+
+data.Map.foldLeftWithKey.doc : Doc
+data.Map.foldLeftWithKey.doc =
+ use Map foldLeftWithKey
+ use Text ++
+ {{
+ The expression `` foldLeftWithKey f z m `` folds the keys and values in the
+ {type Map} `m` using the left-associative operator `f` starting with `z`. The
+ values in the {type Map} are passed to the function `f` in key-ascending
+ order.
+
+ # Example
+
+ ```
+ m = Map.fromList [(1, "a"), (2, "b"), (3, "c")]
+ f acc k v = acc ++ Text.repeat k v
+ foldLeftWithKey f "x" m
+ ```
+ }}
+
+test> data.Map.foldLeftWithKey.tests.onAssoc =
+ runs 100 do
+ use Text ++ ascii
+ m = tests.mapOf ascii ascii ()
+ f acc k v = acc ++ k ++ v
+ expect
+ (Map.foldLeftWithKey f "" m
+ === List.foldLeft (uncurry << f) "" (Map.toList m))
+
+data.Map.foldMap :
+ (a ->{e} a ->{e} a) -> (k ->{e} v ->{e} a) -> Map k v ->{e} Optional a
+data.Map.foldMap f g = cases
+ internal.Tip -> None
+ internal.Bin sz kx x l r ->
+ Some (data.Map.Nonempty.foldMap f g (Map.Nonempty.Bin sz kx x l r))
+
+data.Map.foldMap.doc : Doc
+data.Map.foldMap.doc =
+ use Nat +
+ {{
+ Folds a {type Map} using a function to apply to each key-value pair and a
+ function to combine the results of each application. Returns {None} if the
+ map is empty.
+
+ # Example
+
+ ```
+ Map.foldMap (a b -> a + b) (k v -> k + v) (Map.fromList [(1, 2), (2, 3)])
+ ```
+ }}
+
+data.Map.foldRight : (a ->{e} b ->{e} b) -> b -> Map k a ->{e} b
+data.Map.foldRight f z = cases
+ internal.Tip -> z
+ m@(internal.Bin _ _ x l r) ->
+ data.Map.foldRight f (f x (data.Map.foldRight f z r)) l
+
+data.Map.foldRight.doc : Doc
+data.Map.foldRight.doc =
+ use Map foldRight
+ use Text ++
+ {{
+ The expression `` foldRight f z m `` folds the values in the {type Map} `m`
+ using the right-associative binary operator `f` starting with `z`. The values
+ in the {type Map} are passed to `f` in key-ascending order.
+
+ ```
+ m = Map.fromList [(1, "a"), (2, "b"), (3, "c")]
+ foldRight (++) "x" m
+ ```
+ }}
+
+test> data.Map.foldRight.tests.onElements = runs 100 do
+ use Text ++ ascii
+ m = tests.mapOf ascii ascii ()
+ f v acc = v ++ acc
+ expect (Map.foldRight f "" m === List.foldRight f "" (Map.values m))
+
+data.Map.foldRightWithKey : (k ->{e} a ->{e} b ->{e} b) -> b -> Map k a ->{e} b
+data.Map.foldRightWithKey f z m =
+ go z' = cases
+ internal.Tip -> z'
+ internal.Bin _ kx x l r -> go (f kx x (go z' r)) l
+ go z m
+
+data.Map.foldRightWithKey.doc : Doc
+data.Map.foldRightWithKey.doc =
+ use Map foldRightWithKey
+ use Text ++
+ {{
+ The expression `` foldRightWithKey f z m `` folds the keys and values in the
+ {type Map} `m` using the right-associative operator `f` starting with `z`.
+ The values in the {type Map} are passed to the function `f` in key-ascending
+ order.
+
+ ```
+ m = Map.fromList [(1, "a"), (2, "b"), (3, "c")]
+ f k v acc = Text.repeat k v ++ acc
+ foldRightWithKey f "x" m
+ ```
+ }}
+
+test> data.Map.foldRightWithKey.tests.onElements =
+ runs 100 do
+ use Text ++ ascii
+ m = tests.mapOf ascii ascii ()
+ f k v acc = k ++ v ++ acc
+ expect
+ (Map.foldRightWithKey f "" m
+ === List.foldRight (uncurry f) "" (Map.toList m))
+
+data.Map.foreach : Map k v -> (k ->{e} v ->{e} ()) ->{e} ()
+data.Map.foreach = cases
+ internal.Tip, _ -> ()
+ internal.Bin sx kx x l r, f ->
+ use data.Map foreach
+ f kx x
+ foreach l f
+ foreach r f
+
+data.Map.foreach.doc : Doc
+data.Map.foreach.doc =
+ use Text ++
+ {{
+ Applies an effectful function to each key-value pair in the map.
+
+ # Example
+
+ @typecheck ```
+ Map.foreach
+ (Map.fromList
+ [ ("👋", "wave")
+ , ("👍", "thumbsup")
+ , ("👎", "thumbsdown")
+ , ("👌", "ok_hand")
+ , ("👊", "punch")
+ , ("👏", "clap")
+ ])
+ cases k, v -> printLine (k ++ " " ++ v)
+ ```
+ }}
+
+data.Map.fromList : [(k, v)] -> Map k v
+data.Map.fromList = cases
+ [] -> internal.Tip
+ [(kx, x)] -> Map.singleton kx x
+ (kx0, x0) +: xs0 ->
+ use Int shiftRight
+ use Map singleton
+ use Map.internal link putMax
+ notOrdered kx = cases
+ [] -> false
+ (ky, _) +: _ -> Universal.gteq kx ky
+ fromList' t0 xs = List.foldLeft (cases t, (k, x) -> Map.insert k x t) t0 xs
+ create s = cases
+ [] -> (internal.Tip, [], [])
+ xs@(xp +: xss) ->
+ if s === +1 then
+ (kx, x) = xp
+ if notOrdered kx xss then (singleton kx x, [], xss)
+ else (singleton kx x, xss, [])
+ else
+ match create (shiftRight s 1) xs with
+ res@(_, [], _) -> res
+ (l, [(ky, y)], zs) -> (putMax ky y l, [], zs)
+ (l, ys@((ky, y) +: yss), _) ->
+ if notOrdered ky yss then (l, [], ys)
+ else
+ (r, zs, ws) = create (shiftRight s 1) yss
+ (link ky y l r, zs, ws)
+ go = cases
+ _, t, [] -> t
+ _, t, [(kx, x)] -> putMax kx x t
+ s, l, xs@((kx, x) +: xss) ->
+ if notOrdered kx xss then fromList' l xs
+ else
+ match create s xss with
+ (r, ys, []) -> go (Int.shiftLeft s 1) (link kx x l r) ys
+ (r, _, ys) -> fromList' (link kx x l r) ys
+ if notOrdered kx0 xs0 then fromList' (singleton kx0 x0) xs0
+ else go +1 (singleton kx0 x0) xs0
+
+data.Map.fromList.doc : Doc
+data.Map.fromList.doc =
+ {{
+ Constructs a {type Map} from a {type List} of key/value pairs. If the
+ {type List} contains more than one value for the same key, the last value for
+ the key is used. See {Map.fromListWith} and {Map.fromListWithKey} if you want
+ to combine values under duplicate keys in some other way.
+ }}
+
+test> data.Map.fromList.tests.roundtrip =
+ runs 100 do
+ kvs = gen.listOf (pairOf natInOrder natInOrder) ()
+ trip = Map.toList (Map.fromList kvs)
+ sorted = sortBy at1 kvs
+ bins = groupSublistsBy (cases (k, _), (k', _) -> k === k') sorted
+ expect
+ (assertEquals
+ (List.flatMap
+ (Optional.toList << List.last) (List.map List.Nonempty.toList bins))
+ trip)
+
+data.Map.fromListWith : (v ->{e} v ->{e} v) -> [(k, v)] ->{e} Map k v
+data.Map.fromListWith f = Map.fromListWithKey (const f)
+
+data.Map.fromListWith.doc : Doc
+data.Map.fromListWith.doc =
+ {{
+ Constructs a {type Map} from a {type List} of key/value pairs with a
+ combining function. If the {type List} contains more than one value for the
+ same key, the values are combined using the function.
+
+ See also {Map.fromList} and {Map.fromListWithKey}.
+ }}
+
+test> data.Map.fromListWith.tests.roundtrip = runs 100 do
+ use Nat +
+ kvs = gen.listOf (pairOf natInOrder natInOrder) ()
+ trip = Map.toList (Map.fromListWith (+) kvs)
+ sorted = sortBy at1 kvs
+ bins = groupSublistsBy (cases (k, _), (k', _) -> k === k') sorted
+ addUp = List.map (reduceRight cases (k, v), (k', v') -> (k, v + v')) bins
+ expect (assertEquals addUp trip)
+
+data.Map.fromListWithKey :
+ (k ->{e} v ->{e} v ->{e} v) -> [(k, v)] ->{e} Map k v
+data.Map.fromListWithKey f xs =
+ List.foldLeft (cases t, (k, x) -> Map.putWithKey f k x t) Map.empty xs
+
+data.Map.fromListWithKey.doc : Doc
+data.Map.fromListWithKey.doc =
+ {{
+ Constructs a {type Map} from a {type List} of key/value pairs with a
+ combining function. If the {type List} contains more than one value for the
+ same key, the values are combined using the function, which can take the key
+ into account.
+
+ See also {Map.fromList} and {Map.fromListWith}.
+ }}
+
+test> data.Map.fromListWithKey.tests.roundtrip = runs 100 do
+ use Nat +
+ kvs = gen.listOf (pairOf natInOrder natInOrder) ()
+ trip = Map.toList (Map.fromListWithKey (k v v' -> k + v + v') kvs)
+ sorted = sortBy at1 kvs
+ bins = groupSublistsBy (cases (k, _), (k', _) -> k === k') sorted
+ addUp = List.map (reduceRight cases (k, v), (_, v') -> (k, k + v + v')) bins
+ expect (assertEquals addUp trip)
+
+data.Map.get : k -> Map k v -> Optional v
+data.Map.get k = cases
+ internal.Tip -> None
+ internal.Bin _ kx x l r ->
+ match Universal.ordering k kx with
+ Less -> data.Map.get k l
+ Greater -> data.Map.get k r
+ Equal -> Some x
+
+data.Map.get.doc : Doc
+data.Map.get.doc =
+ {{
+ The expression `` Map.get k m `` returns the value under the key `k` in the
+ {type Map} `m`, or {None} if the key `k` is not present in the {type Map}.
+ }}
+
+test> data.Map.get.tests.spec = runs 100 do
+ use Map get
+ ks = Set.toList (setOf natInOrder ())
+ k = natInOrder()
+ kvs = List.zip ks ks
+ m = Map.fromList kvs
+ allPresent = List.all (cases (k', v) -> get k' m === Some v) kvs
+ noJunk = get k m === None || List.any (cases (k', _) -> k' === k) kvs
+ expect (allPresent && noJunk)
+
+data.Map.getMax : Map k v -> Optional (k, v)
+data.Map.getMax =
+ use internal Bin Tip
+ go k x = cases
+ Tip -> (k, x)
+ Bin _ k' v _ r -> go k' v r
+ cases
+ Tip -> None
+ Bin _ k x _ r -> Some (go k x r)
+
+data.Map.getMax.doc : Doc
+data.Map.getMax.doc =
+ {{
+ Finds the maximal key of the {type Map} and the value under that key. Returns
+ {None} if the {type Map} is empty.
+ }}
+
+test> data.Map.getMax.tests.isMax = runs 100 do
+ x = sortBy at1 (gen.listOf (pairOf natInOrder Text.ascii) ())
+ kv = List.last x
+ m = Map.fromList x
+ expect (assertEquals (Map.getMax m) kv)
+
+data.Map.getMin : Map k v -> Optional (k, v)
+data.Map.getMin =
+ use internal Bin Tip
+ go k x = cases
+ Tip -> (k, x)
+ Bin _ k v l _ -> go k v l
+ cases
+ Tip -> None
+ Bin _ k x l _ -> Some (go k x l)
+
+data.Map.getMin.doc : Doc
+data.Map.getMin.doc =
+ {{
+ Finds the minimal key of the {type Map} and the value under that key. Returns
+ {None} if the {type Map} is empty.
+ }}
+
+test> data.Map.getMin.tests.isMin = runs 100 do
+ x = sortBy at1 (gen.listOf (pairOf natInOrder Text.ascii) ())
+ kv = List.head x
+ m = Map.fromList (List.reverse x)
+ expect (assertEquals (Map.getMin m) kv)
+
+data.Map.getOrAbort : k -> Map k v ->{Abort} v
+data.Map.getOrAbort k = cases
+ internal.Tip -> abort
+ internal.Bin _ kx x l r ->
+ match Universal.ordering k kx with
+ Less -> data.Map.getOrAbort k l
+ Greater -> data.Map.getOrAbort k r
+ Equal -> x
+
+data.Map.getOrAbort.doc : Doc
+data.Map.getOrAbort.doc =
+ {{
+ `Map.getOrAbort k m` returns the value under the key `k` in the map `m`, or
+ calls {abort} if `k` is not in the map.
+ }}
+
+data.Map.getOrElse : v -> k -> Map k v -> v
+data.Map.getOrElse def k m = match Map.get k m with
+ None -> def
+ Some x -> x
+
+data.Map.getOrElse.doc : Doc
+data.Map.getOrElse.doc =
+ use Map fromList getOrElse
+ {{
+ The expression `` getOrElse v k m `` returns the value under the key `k` in
+ the {type Map} `m` if the key `k` is present, or the default value `v` if `k`
+ is not present in the {type Map} `m`.
+
+ ```
+ m = fromList [(1, "one"), (3, "three")]
+ getOrElse "oops" 2 m
+ ```
+
+ ```
+ m = fromList [(1, "one"), (3, "three")]
+ getOrElse "oops" 1 m
+ ```
+ }}
+
+test> data.Map.getOrElse.tests.spec = runs 100 do
+ use Text ascii
+ m = tests.mapOf natInOrder ascii ()
+ k = natInOrder()
+ v = ascii()
+ expect (Optional.getOrElse v (Map.get k m) === Map.getOrElse v k m)
+
+data.Map.getOrThrow : e -> k -> Map k v ->{Throw e} v
+data.Map.getOrThrow e k m = match Map.get k m with
+ None -> throw e
+ Some v -> v
+
+data.Map.getOrThrow.doc : Doc
+data.Map.getOrThrow.doc =
+ {{
+ `` Map.getOrThrow e k m `` returns the value of the key `k` in the {type Map}
+ `m`, or throws `e` with {type Throw} if the key is not present.
+ }}
+
+data.Map.insert : k -> v -> Map k v -> Map k v
+data.Map.insert kx x = cases
+ internal.Tip -> Map.singleton kx x
+ internal.Bin sz ky y l r ->
+ match Universal.ordering kx ky with
+ Less -> Map.internal.balanceL ky y (data.Map.insert kx x l) r
+ Greater -> Map.internal.balanceR ky y l (data.Map.insert kx x r)
+ Equal -> internal.Bin sz kx x l r
+
+data.Map.insert.doc : Doc
+data.Map.insert.doc =
+ use Map empty get insert
+ {{
+ Inserts a key-value pair into a {type Map}. If the key is already present,
+ the value is replaced.
+
+ # Examples
+
+ ```
+ get 1 (insert 1 "one" empty)
+ ```
+
+ ```
+ get 1 (insert 1 "uno" (insert 1 "one" empty))
+ ```
+ }}
+
+data.Map.insertNonempty : k -> v -> Map k v -> Map.Nonempty k v
+data.Map.insertNonempty kx x = cases
+ internal.Tip -> Map.Nonempty.singleton kx x
+ internal.Bin sz ky y l r ->
+ match Universal.ordering kx ky with
+ Less -> Nonempty.internal.balanceL ky y (Map.insert kx x l) r
+ Greater -> Nonempty.internal.balanceR ky y l (Map.insert kx x r)
+ Equal -> Map.Nonempty.Bin sz kx x l r
+
+data.Map.insertNonempty.doc : Doc
+data.Map.insertNonempty.doc =
+ use Map fromList insertNonempty
+ use Map.Nonempty toList
+ {{
+ Inserts a key-value pair into a {type Map}, returning a {type Map.Nonempty}.
+ If the key is already present in the map, the value is replaced.
+
+ # Example
+
+ ```
+ toList (insertNonempty 2 4 (fromList [(1, 2), (2, 3)]))
+ ```
+
+ ```
+ toList (insertNonempty 3 5 (fromList [(1, 2), (2, 3)]))
+ ```
+ }}
+
+data.Map.internal.balance : k -> v -> Map k v -> Map k v -> Map k v
+data.Map.internal.balance k x l r =
+ use Map size
+ use Map.internal bin
+ use Nat * +
+ use Universal gt lt
+ use internal Bin
+ rotateL : a -> b -> Map a b -> Map a b -> Map a b
+ rotateL k x l = cases
+ r@(Bin _ _ _ ly ry) | lt (size ly) (ratio * size ry) -> singleL k x l r
+ r -> doubleL k x l r
+ rotateR : a -> b -> Map a b -> Map a b -> Map a b
+ rotateR k x = cases
+ l@(Bin _ _ _ ly ry) | lt (size ry) (ratio * size ly) -> singleR k x l
+ l -> doubleR k x l
+ singleL : a -> b -> Map a b -> Map a b -> Map a b
+ singleL k1 x1 t1 = cases
+ Bin _ k2 x2 t2 t3 -> bin k2 x2 (bin k1 x1 t1 t2) t3
+ _ -> bug "singleL: Tip"
+ singleR : a -> b -> Map a b -> Map a b -> Map a b
+ singleR k1 x1 = cases
+ Bin _ k2 x2 t1 t2, t3 -> bin k2 x2 t1 (bin k1 x1 t2 t3)
+ _, _ -> bug "singleR: Tip"
+ doubleL : a -> b -> Map a b -> Map a b -> Map a b
+ doubleL k1 x1 t1 = cases
+ Bin _ k2 x2 (Bin _ k3 x3 t2 t3) t4 ->
+ bin k3 x3 (bin k1 x1 t1 t2) (bin k2 x2 t3 t4)
+ _ -> bug "doubleL: Tip"
+ doubleR : a -> b -> Map a b -> Map a b -> Map a b
+ doubleR k1 x1 = cases
+ Bin _ k2 x2 t1 (Bin _ k3 x3 t2 t3), t4 ->
+ bin k3 x3 (bin k2 x2 t1 t2) (bin k1 x1 t3 t4)
+ _, _ -> bug "doubleR: Tip"
+ sizeL = size l
+ sizeR = size r
+ sizeX = sizeL + sizeR + 1
+ if Universal.lteq (sizeL + sizeR) 1 then Bin sizeX k x l r
+ else
+ if gt sizeR (delta * sizeL) then rotateL k x l r
+ else
+ if gt sizeL (delta * sizeR) then rotateR k x l r else Bin sizeX k x l r
+
+data.Map.internal.balanceL : k -> v -> Map k v -> Map k v -> Map k v
+data.Map.internal.balanceL k x l = cases
+ internal.Tip ->
+ match l with
+ internal.Tip -> Map.singleton k x
+ internal.Bin _ _ _ internal.Tip internal.Tip ->
+ internal.Bin 2 k x l internal.Tip
+ internal.Bin _ lk lx internal.Tip (internal.Bin _ lrk lrx _ _) ->
+ internal.Bin 3 lrk lrx (Map.singleton lk lx) (Map.singleton k x)
+ internal.Bin _ lk lx ll@(internal.Bin _ _ _ _ _) internal.Tip ->
+ internal.Bin 3 lk lx ll (Map.singleton k x)
+ internal.Bin
+ ls
+ lk
+ lx
+ ll@(internal.Bin lls _ _ _ _)
+ lr@(internal.Bin lrs lrk lrx lrl lrr) ->
+ if Universal.lt lrs (ratio Nat.* lls) then
+ internal.Bin
+ (1 Nat.+ ls)
+ lk
+ lx
+ ll
+ (internal.Bin (1 Nat.+ lrs) k x lr internal.Tip)
+ else
+ internal.Bin
+ (1 Nat.+ ls)
+ lrk
+ lrx
+ (internal.Bin (1 Nat.+ lls Nat.+ Map.size lrl) lk lx ll lrl)
+ (internal.Bin (1 Nat.+ Map.size lrr) k x lrr internal.Tip)
+ r@(internal.Bin rs _ _ _ _) ->
+ match l with
+ internal.Tip -> internal.Bin (1 Nat.+ rs) k x internal.Tip r
+ internal.Bin ls lk lx ll lr ->
+ if Universal.gt ls (delta Nat.* rs) then
+ match (ll, lr) with
+ (internal.Bin lls _ _ _ _, internal.Bin lrs lrk lrx lrl lrr) ->
+ if Universal.lt lrs (ratio Nat.* lls) then
+ internal.Bin
+ (1 Nat.+ ls Nat.+ rs)
+ lk
+ lx
+ ll
+ (internal.Bin (1 Nat.+ rs Nat.+ lrs) k x lr r)
+ else
+ internal.Bin
+ (1 Nat.+ ls Nat.+ rs)
+ lrk
+ lrx
+ (internal.Bin (1 Nat.+ lls Nat.+ Map.size lrl) lk lx ll lrl)
+ (internal.Bin (1 Nat.+ rs Nat.+ Map.size lrr) k x lrr r)
+ (_, _) -> bug "failure in balanceL"
+ else internal.Bin (1 Nat.+ ls Nat.+ rs) k x l r
+
+data.Map.internal.balanceR : k -> v -> Map k v -> Map k v -> Map k v
+data.Map.internal.balanceR k x l r =
+ match l with
+ internal.Tip ->
+ match r with
+ internal.Tip -> internal.Bin 1 k x internal.Tip internal.Tip
+ internal.Bin _ _ _ internal.Tip internal.Tip ->
+ internal.Bin 2 k x internal.Tip r
+ internal.Bin _ rk rx internal.Tip rr@(internal.Bin _ _ _ _ _) ->
+ internal.Bin
+ 3 rk rx (internal.Bin 1 k x internal.Tip internal.Tip) rr
+ internal.Bin _ rk rx (internal.Bin _ rlk rlx _ _) internal.Tip ->
+ internal.Bin
+ 3
+ rlk
+ rlx
+ (internal.Bin 1 k x internal.Tip internal.Tip)
+ (internal.Bin 1 rk rx internal.Tip internal.Tip)
+ internal.Bin
+ rs
+ rk
+ rx
+ rl@(internal.Bin rls rlk rlx rll rlr)
+ rr@(internal.Bin rrs _ _ _ _)
+ | Universal.lt rls (ratio Nat.* rrs) ->
+ internal.Bin
+ (1 Nat.+ rs)
+ rk
+ rx
+ (internal.Bin (1 Nat.+ rls) k x internal.Tip rl)
+ rr
+ | otherwise ->
+ internal.Bin
+ (1 Nat.+ rs)
+ rlk
+ rlx
+ (internal.Bin (1 Nat.+ Map.size rll) k x internal.Tip rll)
+ (internal.Bin (1 Nat.+ rrs Nat.+ Map.size rlr) rk rx rlr rr)
+ internal.Bin ls _ _ _ _ ->
+ match r with
+ internal.Tip -> internal.Bin (1 Nat.+ ls) k x l internal.Tip
+ internal.Bin rs rk rx rl rr
+ | Universal.gt rs (delta Nat.* ls) ->
+ match (rl, rr) with
+ (internal.Bin rls rlk rlx rll rlr, internal.Bin rrs _ _ _ _)
+ | Universal.lt rls (ratio Nat.* rrs) ->
+ internal.Bin
+ (1 Nat.+ ls Nat.+ rs)
+ rk
+ rx
+ (internal.Bin (1 Nat.+ ls Nat.+ rls) k x l rl)
+ rr
+ | otherwise ->
+ internal.Bin
+ (1 Nat.+ ls Nat.+ rs)
+ rlk
+ rlx
+ (internal.Bin (1 Nat.+ ls Nat.+ Map.size rll) k x l rll)
+ (internal.Bin (1 Nat.+ rrs Nat.+ Map.size rlr) rk rx rlr rr)
+ (_, _) -> bug "Failure in balanceR"
+ | otherwise ->
+ internal.Bin (1 Nat.+ ls Nat.+ rs) k x l r
+
+data.Map.internal.bin : k -> v -> Map k v -> Map k v -> Map k v
+data.Map.internal.bin k x l r =
+ use Map size
+ use Nat +
+ internal.Bin (size l + size r + 1) k x l r
+
+data.Map.internal.delta : Nat
+data.Map.internal.delta = 3
+
+data.Map.internal.glue : Map k v -> Map k v -> Map k v
+data.Map.internal.glue = cases
+ internal.Tip -> id
+ l@(internal.Bin sl kl xl ll lr) ->
+ cases
+ internal.Tip -> l
+ r@(internal.Bin sr kr xr rl rr)
+ | Universal.gt sl sr ->
+ (MaxView km m l') = maxViewSure kl xl ll lr
+ Map.internal.balanceR km m l' r
+ | otherwise ->
+ (MinView km m r') = minViewSure kr xr rl rr
+ Map.internal.balanceL km m l r'
+
+data.Map.internal.link : k -> v -> Map k v -> Map k v -> Map k v
+data.Map.internal.link kx x = cases
+ internal.Tip, r -> Map.internal.putMin kx x r
+ l, internal.Tip -> Map.internal.putMax kx x l
+ l@(internal.Bin sizeL ky y ly ry), r@(internal.Bin sizeR kz z lz rz) ->
+ if Universal.lt (delta Nat.* sizeL) sizeR then
+ Map.internal.balanceL kz z (data.Map.internal.link kx x l lz) rz
+ else
+ if Universal.lt (delta Nat.* sizeR) sizeL then
+ Map.internal.balanceR ky y ly (data.Map.internal.link kx x ry r)
+ else Map.internal.bin kx x l r
+
+data.Map.internal.link2 : Map k v -> Map k v -> Map k v
+data.Map.internal.link2 l r =
+ match (l, r) with
+ (internal.Tip, r) -> r
+ (l, internal.Tip) -> l
+ (internal.Bin sizeL kx x lx rx, internal.Bin sizeR ky y ly ry) ->
+ if Universal.lt (delta Nat.* sizeL) sizeR then
+ Map.internal.balanceL ky y (data.Map.internal.link2 l ly) ry
+ else
+ if Universal.lt (delta Nat.* sizeR) sizeL then
+ Map.internal.balanceR kx x lx (data.Map.internal.link2 rx r)
+ else glue l r
+
+data.Map.internal.maxViewSure : k -> v -> Map k v -> Map k v -> MaxView k v
+data.Map.internal.maxViewSure k x = cases
+ l, internal.Tip -> MaxView k x l
+ l, internal.Bin _ kr xr rl rr ->
+ (MaxView km xm r') = data.Map.internal.maxViewSure kr xr rl rr
+ MaxView km xm (Map.internal.balanceL k x l r')
+
+data.Map.internal.minViewSure : k -> v -> Map k v -> Map k v -> MinView k v
+data.Map.internal.minViewSure k x = cases
+ internal.Tip -> MinView k x
+ internal.Bin _ kl xl ll lr ->
+ r -> let
+ (MinView km xm l') = data.Map.internal.minViewSure kl xl ll lr
+ MinView km xm (Map.internal.balanceR k x l' r)
+
+data.Map.internal.putMax : k -> v -> Map k v -> Map k v
+data.Map.internal.putMax kx x = cases
+ internal.Tip -> Map.singleton kx x
+ internal.Bin _ ky y l r ->
+ Map.internal.balanceR ky y l (data.Map.internal.putMax kx x r)
+
+data.Map.internal.putMin : k -> v -> Map k v -> Map k v
+data.Map.internal.putMin kx x = cases
+ internal.Tip -> Map.singleton kx x
+ internal.Bin _ ky y l r ->
+ Map.internal.balanceL ky y (data.Map.internal.putMin kx x l) r
+
+data.Map.internal.putWithKeyR :
+ (k ->{e} v ->{e} v ->{e} v) -> k -> v -> Map k v ->{e} Map k v
+data.Map.internal.putWithKeyR f kx x = cases
+ internal.Tip -> Map.singleton kx x
+ internal.Bin sy ky y l r ->
+ match Universal.ordering kx ky with
+ Less ->
+ Map.internal.balanceL ky y (data.Map.internal.putWithKeyR f kx x l) r
+ Greater ->
+ Map.internal.balanceR ky y l (data.Map.internal.putWithKeyR f kx x r)
+ Equal -> internal.Bin sy ky (f ky y x) l r
+
+data.Map.internal.putWithR :
+ (v ->{e} v ->{e} v) -> k -> v -> Map k v ->{e} Map k v
+data.Map.internal.putWithR f kx x = cases
+ internal.Tip -> Map.singleton kx x
+ internal.Bin sy ky y l r ->
+ match Universal.ordering kx ky with
+ Less ->
+ Map.internal.balanceL ky y (data.Map.internal.putWithR f kx x l) r
+ Greater ->
+ Map.internal.balanceR ky y l (data.Map.internal.putWithR f kx x r)
+ Equal -> bug "putWithR: key already present"
+
+data.Map.internal.ratio : Nat
+data.Map.internal.ratio = 2
+
+data.Map.internal.splitLookup : k -> Map k v -> (Map k v, Optional v, Map k v)
+data.Map.internal.splitLookup k = cases
+ internal.Tip -> (internal.Tip, None, internal.Tip)
+ internal.Bin _ kx x l r ->
+ match Universal.ordering k kx with
+ Less ->
+ (lt, z, gt) = data.Map.internal.splitLookup k l
+ (lt, z, Map.internal.link kx x gt r)
+ Greater ->
+ (lt, z, gt) = data.Map.internal.splitLookup k r
+ (Map.internal.link kx x l lt, z, gt)
+ Equal -> (l, Some x, r)
+
+data.Map.intersect : Map k a -> Map k b -> Map k a
+data.Map.intersect = Map.intersectWith const
+
+data.Map.intersect.doc : Doc
+data.Map.intersect.doc =
+ use Map fromList
+ {{
+ The intersection of two {type Map}s. Returns data in the first {type Map} for
+ keys existing in both {type Map}s.
+
+ ```
+ x = fromList [(5, "a"), (3, "b")]
+ y = fromList [(5, "A"), (7, "C")]
+ Map.toList (Map.intersect x y)
+ ```
+ }}
+
+test> data.Map.intersect.tests.associative =
+ runs 100 do
+ laws.associative (tests.mapOf natInOrder Text.ascii) Map.intersect
+
+test> data.Map.intersect.tests.idempotent = runs 100 do
+ use Map == intersect toList
+ x = tests.mapOf Text.ascii natInOrder ()
+ b = intersect x x == x
+ if b then expect b else bug (toList (intersect x x), toList x)
+
+test> data.Map.intersect.tests.zero = runs 100 do
+ use Map empty intersect
+ m = tests.mapOf natInOrder Text.ascii ()
+ expect (intersect empty m === empty && intersect m empty === empty)
+
+data.Map.intersectWith :
+ (a ->{e} b ->{e} c) -> Map k a -> Map k b ->{e} Map k c
+data.Map.intersectWith f = Map.intersectWithKey (const f)
+
+data.Map.intersectWith.doc : Doc
+data.Map.intersectWith.doc =
+ use Map fromList intersectWith
+ use Nat +
+ {{
+ Intersection with a combining function. The expression ``
+ intersectWith f m1 m2 `` combines data in the {type Map} `m1` and the
+ {type Map} `m2` with the function `f`, for keys existing in both maps. It
+ discards other data and keys.
+
+ ```
+ x = fromList [("a", 5), ("b", 3)]
+ y = fromList [("a", 6), ("c", 7)]
+ Map.toList (intersectWith (+) x y)
+ ```
+ }}
+
+test> data.Map.intersectWith.tests.commutative =
+ runs 200 do
+ laws.commutative
+ (tests.mapOf Text.ascii natInOrder) (Map.intersectWith (Nat.+))
+
+test> data.Map.intersectWith.tests.idempotent = runs 100 do
+ use Map ==
+ use Nat * +
+ x = tests.mapOf Text.ascii natInOrder ()
+ expect (Map.intersectWith (+) x x == Map.map (v -> v * 2) x)
+
+test> data.Map.intersectWith.tests.zero = runs 100 do
+ use Map == empty
+ use Nat +
+ x = tests.mapOf Text.ascii natInOrder ()
+ expect (Map.intersectWith (+) x empty == empty)
+
+data.Map.intersectWithKey :
+ (k ->{e} a ->{e} b ->{e} c) -> Map k a -> Map k b ->{e} Map k c
+data.Map.intersectWithKey f = cases
+ internal.Tip, _ -> internal.Tip
+ _, internal.Tip -> internal.Tip
+ internal.Bin _ k x1 l1 r1, t2 ->
+ (l2, mb, r2) = splitLookup k t2
+ use data.Map intersectWithKey
+ l1l2 = intersectWithKey f l1 l2
+ r1r2 = intersectWithKey f r1 r2
+ match mb with
+ Some x2 -> Map.internal.link k (f k x1 x2) l1l2 r1r2
+ None -> link2 l1l2 r1r2
+
+data.Map.intersectWithKey.doc : Doc
+data.Map.intersectWithKey.doc =
+ use Map fromList intersectWithKey
+ use Nat +
+ {{
+ Intersection with a combining function. The expression ``
+ intersectWithKey f m1 m2 `` combines data and keys in the {type Map} `m1` and
+ the {type Map} `m2` using the function `f`, for keys existing in both maps.
+ It discards other data and keys.
+
+ # Example
+
+ ```
+ x = fromList [(1, 5), (2, 4)]
+ y = fromList [(1, 6), (3, 7)]
+ Map.toList (intersectWithKey (k v1 v2 -> k + v1 + v2) x y)
+ ```
+ }}
+
+test> data.Map.intersectWithKey.tests.commutative =
+ runs 200 do
+ laws.commutative
+ (tests.mapOf natInOrder natInOrder)
+ (Map.intersectWithKey (k a b -> k Nat.+ a Nat.+ b))
+
+test> data.Map.intersectWithKey.tests.idempotent =
+ runs 100 do
+ use Map ==
+ use Nat +
+ x = tests.mapOf natInOrder natInOrder ()
+ expect
+ (Map.intersectWithKey (k a b -> k + a + b) x x
+ == Map.mapWithKey (k v -> k + v + v) x)
+
+test> data.Map.intersectWithKey.tests.zero = runs 100 do
+ use Map == empty
+ use Nat +
+ x = tests.mapOf natInOrder natInOrder ()
+ expect (Map.intersectWithKey (k a b -> k + a + b) x empty == empty)
+
+data.Map.isEmpty : Map k v -> Boolean
+data.Map.isEmpty = cases
+ internal.Tip -> true
+ _ -> false
+
+data.Map.isEmpty.doc : Doc
+data.Map.isEmpty.doc =
+ {{
+ Returns `` true `` if the {type Map} has no elements, otherwise ``false``.
+
+ See {Map.empty}.
+ }}
+
+test> data.Map.isEmpty.tests.spec = runs 100 do
+ use Map ==
+ use gen boolean
+ m = tests.mapOf boolean boolean ()
+ expect (m == Map.empty === Map.isEmpty m)
+
+data.Map.keys : Map k a -> [k]
+data.Map.keys =
+ use List +:
+ Map.foldRightWithKey (k _ ks -> k +: ks) []
+
+data.Map.keys.doc : Doc
+data.Map.keys.doc =
+ {{ Returns all the keys in the {type Map}, as a {type List}. }}
+
+test> data.Map.keys.tests.spec =
+ runs 100 do
+ kvs = gen.listOf (pairOf natInOrder gen.boolean) ()
+ m = Map.fromList kvs
+ ks = Map.keys m
+ kk = natInOrder()
+ allPresent = List.all (k -> List.any (cases (k', _) -> k === k') kvs) ks
+ noJunk =
+ implies
+ (Boolean.not (List.contains kk ks))
+ (List.none (cases (k, _) -> k === kk) kvs)
+ expect (allPresent && noJunk)
+
+data.Map.lookup.doc : Doc
+data.Map.lookup.doc =
+ use Map fromList lookup
+ {{
+ Looks up a key in a {type Map} and returns the corresponding value, if any.
+
+ # Examples
+
+ ```
+ lookup 1 (fromList [(1, "one"), (2, "two"), (3, "three")])
+ ```
+
+ ```
+ lookup 1 (fromList [(2, "two"), (3, "three")])
+ ```
+ }}
+
+data.Map.map : (a ->{e} b) -> Map k a ->{e} Map k b
+data.Map.map f = Map.mapWithKey (const f)
+
+data.Map.map.doc : Doc
+data.Map.map.doc =
+ use Map map
+ {{
+ The expression `` map f m `` applies the function `f` to all values in the
+ {type Map} `m` returning a new {type Map} with the results. If the input
+ {type Map} contains a value `v` under a key `k`, then the output {type Map}
+ will contain `f v` under the key `k`.
+
+ # Example
+
+ ```
+ m = Map.fromList [(1, "abc"), (2, "xyz")]
+ Map.toList (map Text.reverse m)
+ ```
+ }}
+
+test> data.Map.map.tests.functor = runs 100 do
+ use Map ==
+ m = tests.mapOf natInOrder natInOrder ()
+ expect (Map.map id m == m)
+
+data.Map.mapKeys : (k1 ->{g} k2) -> Map k1 v ->{g} Map k2 v
+data.Map.mapKeys f =
+ use List +:
+ Map.fromList << Map.foldRightWithKey (k x xs -> (f k, x) +: xs) []
+
+data.Map.mapKeys.doc : Doc
+data.Map.mapKeys.doc =
+ use Map fromList mapKeys toList
+ {{
+ The expression `` mapKeys f m `` creates a new {type Map} by applying `f` to
+ each key of the {type Map} `m`. The size of the result may be smaller if `f`
+ maps two or more distinct keys to the same new key. In this case the value at
+ the greatest (by {Universal.ordering}) of the original keys is retained.
+
+ # Examples
+
+ ```
+ m = fromList [("abc", 1), ("xyz", 2)]
+ toList (mapKeys Text.reverse m)
+ ```
+
+ ```
+ m = fromList [("abc", 1), ("xyz", 2)]
+ toList (mapKeys (k -> 7) m)
+ ```
+ }}
+
+test> data.Map.mapKeys.tests.functorish = runs 100 do
+ use Map ==
+ m = tests.mapOf natInOrder natInOrder ()
+ expect (Map.mapKeys id m == m)
+
+test> data.Map.mapKeys.tests.retainGreatest = runs 100 do
+ use Map ==
+ m = tests.mapOf natInOrder natInOrder ()
+ kvs = sortBy at1 (Map.toList m)
+ mapped = Map.mapKeys (const 1) m
+ expect match List.last kvs with
+ Some (k, v) -> mapped == Map.singleton 1 v
+ None -> Map.isEmpty mapped
+
+data.Map.mapKeysWith :
+ (v ->{g} v ->{g} v) -> (k1 ->{g} k2) -> Map k1 v ->{g} Map k2 v
+data.Map.mapKeysWith c f =
+ use List +:
+ Map.fromListWith c << Map.foldRightWithKey (k x xs -> (f k, x) +: xs) []
+
+data.Map.mapKeysWith.doc : Doc
+data.Map.mapKeysWith.doc =
+ use Map fromList mapKeysWith toList
+ use Nat +
+ {{
+ The expression `` mapKeysWith c f m `` creates a new {type Map} by applying
+ `f` to each key of the {type Map} `m`. The size of the result may be smaller
+ if `f` maps two or more distinct keys to the same new key. In this case the
+ associated values will be combined using the function `c`. The value under
+ the greater (by universal equality) of the original keys is passed as the
+ first argument to `c`.
+
+ # Examples
+
+ ```
+ m = fromList [("abc", 1), ("xyz", 2)]
+ toList (mapKeysWith (+) Text.reverse m)
+ ```
+
+ ```
+ m = fromList [("abc", 1), ("xyz", 2)]
+ toList (mapKeysWith (+) (const 7) m)
+ ```
+ }}
+
+test> data.Map.mapKeysWith.tests.functorish = runs 100 do
+ use Map ==
+ use Nat +
+ m = tests.mapOf natInOrder natInOrder ()
+ expect (Map.mapKeysWith (+) id m == m)
+
+test> data.Map.mapKeysWith.tests.greatestFirst =
+ runs 100 do
+ use Map ==
+ use Text ++
+ m = tests.mapOf natInOrder Text.ascii ()
+ kvs = List.reverse (sortBy at1 (Map.toList m))
+ mapped = Map.mapKeysWith (++) (const 1) m
+ expect match List.last kvs with
+ Some (k, v) ->
+ mapped == Map.singleton 1 (List.foldLeft (++) "" (List.map at2 kvs))
+ None -> Map.isEmpty mapped
+
+data.Map.mapOptional : (a ->{f} Optional b) -> Map k a ->{f} Map k b
+data.Map.mapOptional f = Map.mapOptionalWithKey (_ -> f)
+
+data.Map.mapOptional.doc : Doc
+data.Map.mapOptional.doc =
+ use Nat >
+ {{
+ Maps a function over the values of a map, possibly removing some keys.
+
+ The result will have a subset of the keys from the input map, and each value
+ will be the result of applying the given function to the corresponding value
+ from the input map. The function should return {None} to indicate that the
+ key should be removed from the result map, and {Some} to indicate that the
+ key should be present in the result map with the given value.
+
+ # Example
+
+ ```
+ f a = if Text.size a > 5 then Some a else None
+ Map.toList
+ (Map.mapOptional
+ f (Map.fromList [(1, "Circuit"), (2, "Quasar"), (3, "Voyage")]))
+ ```
+
+ # See also
+
+ * {Map.mapOptionalWithKey} - a variant that also receives the key.
+ }}
+
+data.Map.mapOptionalWithKey :
+ (k ->{e} a ->{f} Optional b) -> Map k a ->{e, f} Map k b
+data.Map.mapOptionalWithKey f = cases
+ internal.Bin n k x l r ->
+ use data.Map mapOptionalWithKey
+ l' = mapOptionalWithKey f l
+ r' = mapOptionalWithKey f r
+ match f k x with
+ None -> glue l' r'
+ Some y -> Map.internal.link k y l' r'
+ internal.Tip -> internal.Tip
+
+data.Map.mapOptionalWithKey.doc : Doc
+data.Map.mapOptionalWithKey.doc =
+ {{
+ Maps a function over the values of a map, possibly removing some keys.
+
+ The result will have a subset of the keys from the input map, and each value
+ will be the result of applying the given function to the corresponding
+ key-value pair. The function should return {None} to indicate that the key
+ should be removed from the result map, and {Some} to indicate that the key
+ should be present in the result map with the given value.
+
+ # Example
+
+ ```
+ f k a = if Nat.isEven k then Some a else None
+ Map.toList
+ (Map.mapOptionalWithKey
+ f (Map.fromList [(1, "Circuit"), (2, "Quasar"), (3, "Voyage")]))
+ ```
+
+ # See also
+
+ * {Map.mapOptional} - a variant that does not receive the key.
+ }}
+
+data.Map.mapWithKey : (k ->{e} a ->{e} b) -> Map k a ->{e} Map k b
+data.Map.mapWithKey f = cases
+ internal.Tip -> internal.Tip
+ internal.Bin sx kx x l r ->
+ internal.Bin
+ sx kx (f kx x) (data.Map.mapWithKey f l) (data.Map.mapWithKey f r)
+
+data.Map.mapWithKey.doc : Doc
+data.Map.mapWithKey.doc =
+ use Map mapWithKey
+ use Nat +
+ {{
+ The expression `` mapWithKey f m `` applies the function `f` to all values
+ and keys in the {type Map} `m` returning a new @Map with the results as the
+ values. If the input {type Map} contains a value `v` under a key `k`, then
+ the output {type Map} will contain the value `f k v` under the key `k`.
+
+ # Example
+
+ ```
+ m = Map.fromList [(1, 9), (2, 3)]
+ Map.toList (mapWithKey (+) m)
+ ```
+ }}
+
+test> data.Map.mapWithKey.tests.functorish = runs 100 do
+ use Map ==
+ m = tests.mapOf natInOrder natInOrder ()
+ expect (Map.mapWithKey (flip const) m == m)
+
+test> data.Map.mapWithKey.tests.worksLikeList =
+ runs 100 do
+ use Nat +
+ kvs =
+ distinctBy
+ at1 (sortBy at1 (gen.listOf (pairOf natInOrder natInOrder) ()))
+ expect
+ (assertEquals
+ (List.map (uncurry (+)) kvs)
+ (Map.values (Map.mapWithKey (+) (Map.fromList kvs))))
+
+data.Map.mergeWith :
+ (OneOrBoth a b ->{g} Optional c) -> Map k a -> Map k b ->{g} Map k c
+data.Map.mergeWith f m1 m2 = Map.mergeWithKey (_ x -> f x) m1 m2
+
+data.Map.mergeWith.doc : Doc
+data.Map.mergeWith.doc =
+ use Map fromList mergeWith toList
+ use Text ++
+ {{
+ Merges two maps into a map of values using a function.
+
+ The result will have a subset of the union of keys from the two input maps,
+ and each value will be the result of applying the given function to the
+ corresponding values. The function receives {This} for values under keys that
+ are present in only the first input map, {That} for values under keys that
+ are present in only the second input map, and {Both} for keys that are
+ present in both input maps. The function should return {None} to indicate
+ that the key should be removed from the result map, and {Some} to indicate
+ that the key should be present in the result map with the given value.
+
+ # Example
+
+ We can use this function to find the symmetric difference of two maps:
+
+ ```
+ f = cases
+ This a -> Some a
+ That b -> Some b
+ Both _ _ -> None
+ toList
+ (mergeWith
+ f
+ (fromList [(1, "Circuit"), (2, "Quasar")])
+ (fromList [(2, "Voyage"), (3, "Harmony")]))
+ ```
+
+ Or we can use it to find the intersection of two maps:
+
+ ```
+ f = cases
+ This a -> None
+ That b -> None
+ Both a b -> Some a
+ toList
+ (mergeWith
+ f
+ (fromList [(1, "Circuit"), (2, "Quasar")])
+ (fromList [(2, "Voyage"), (3, "Harmony")]))
+ ```
+
+ Or we can use it to find the union of two maps:
+
+ ```
+ f = cases
+ This a -> Some a
+ That b -> Some b
+ Both a b -> Some (a ++ b)
+ toList
+ (mergeWith
+ f
+ (fromList [(1, "Circuit"), (2, "Quasar")])
+ (fromList [(2, "Voyage"), (3, "Harmony")]))
+ ```
+
+ Or something else entirely!
+
+ # See also
+
+ * {Map.mergeWithKey} - a variant where the function also receives the key.
+ * {Map.alignWith} - a variant that does not remove keys from the result
+ map.
+ }}
+
+data.Map.mergeWithKey :
+ (k ->{e} OneOrBoth a b ->{f} Optional c)
+ -> Map k a
+ -> Map k b
+ ->{e, f} Map k c
+data.Map.mergeWithKey f m1 m2 =
+ match (m1, m2) with
+ (_, internal.Tip) -> Map.mapOptionalWithKey (k a -> f k (This a)) m1
+ (internal.Tip, _) -> Map.mapOptionalWithKey (k b -> f k (That b)) m2
+ (internal.Bin _ k1 x1 l1 r1, internal.Bin _ k2 x2 l2 r2) ->
+ (l2, mb, r2) = splitLookup k1 m2
+ use Map.internal link
+ use data.Map mergeWithKey
+ l1l2 = mergeWithKey f l1 l2
+ r1r2 = mergeWithKey f r1 r2
+ match mb with
+ None ->
+ match f k1 (This x1) with
+ None -> glue l1l2 r1r2
+ Some x -> link k1 x l1l2 r1r2
+ Some x2 ->
+ match f k1 (Both x1 x2) with
+ None -> glue l1l2 r1r2
+ Some x -> link k1 x l1l2 r1r2
+
+data.Map.mergeWithKey.doc : Doc
+data.Map.mergeWithKey.doc =
+ use Map fromList mergeWithKey toList
+ use Nat isEven
+ use Text ++
+ {{
+ Merges two maps into a map of values using a function.
+
+ The result will have a subset of the union of the keys of the two input maps,
+ and each value will be the result of applying the given function to the
+ corresponding key-value pairs from the two input maps. The function receives
+ {This} for values under keys that are present in only the first input map,
+ {That} for values under keys that are present in only the second input map,
+ and {Both} for keys that are present in both input maps. The function should
+ return {None} to indicate that the key should be removed from the result map,
+ and {Some} to indicate that the key should be present in the result map with
+ the given value.
+
+ # Example
+
+ We can use this function to find the symmetric difference of two maps:
+
+ ```
+ f k = cases
+ This a -> Some a
+ That b -> Some b
+ Both _ _ -> None
+ toList
+ (mergeWithKey
+ f
+ (fromList [(1, "Circuit"), (2, "Quasar")])
+ (fromList [(2, "Voyage"), (3, "Harmony")]))
+ ```
+
+ Or we can use it to find the intersection of two maps:
+
+ ```
+ f k = cases
+ This a -> None
+ That b -> None
+ Both a b -> Some a
+ toList
+ (mergeWithKey
+ f
+ (fromList [(1, "Circuit"), (2, "Quasar")])
+ (fromList [(2, "Voyage"), (3, "Harmony")]))
+ ```
+
+ Or we can use it to find the union of two maps:
+
+ ```
+ f k = cases
+ This a -> Some a
+ That b -> Some b
+ Both a b -> Some (a ++ b)
+ toList
+ (mergeWithKey
+ f
+ (fromList [(1, "Circuit"), (2, "Quasar")])
+ (fromList [(2, "Voyage"), (3, "Harmony")]))
+ ```
+
+ Or something more complex, like this function that selects odd keys from
+ the first map and even keys from the second map:
+
+ ```
+ f k = cases
+ This a -> if isEven k then Some a else None
+ That b -> if Nat.isOdd k then Some b else None
+ Both a b -> if isEven k then Some a else Some b
+ toList
+ (mergeWithKey
+ f
+ (fromList [(1, "Circuit"), (2, "Quasar")])
+ (fromList [(2, "Voyage"), (3, "Harmony")]))
+ ```
+ }}
+
+(data.Map.Nonempty.==) : Map.Nonempty k v -> Map.Nonempty k v -> Boolean
+x data.Map.Nonempty.== y =
+ use Map.Nonempty toList
+ toList x === toList y
+
+data.Map.Nonempty.==.doc : Doc
+data.Map.Nonempty.==.doc =
+ use Map.Nonempty ==
+ {{
+ Checks if two nonempty maps are equal. They're equal exactly when they have
+ the same keys and values.
+
+ # Example
+
+ ```
+ toNonemptyMap ((1, "a") +| [(2, "b")])
+ == toNonemptyMap ((1, "a") +| [(2, "b")])
+ ```
+ }}
+
+data.Map.Nonempty.adjust :
+ (v ->{e} v) -> k -> Map.Nonempty k v ->{e} Map.Nonempty k v
+data.Map.Nonempty.adjust f = Map.Nonempty.adjustWithKey (const f)
+
+data.Map.Nonempty.adjust.doc : Doc
+data.Map.Nonempty.adjust.doc =
+ {{
+ Adjusts the value at a key in a nonempty map using the provided function. If
+ the key is not present, the map is unchanged.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList
+ (Map.Nonempty.adjust Nat.increment 1 (toNonemptyMap ((1, 2) +| [(2, 3)])))
+ ```
+ }}
+
+test> data.Map.Nonempty.adjust.tests.adjusts =
+ runs 1000 do
+ use Boolean not
+ use Map.Nonempty get
+ use Text ascii
+ use gen boolean
+ m = nonemptyMapOf ascii boolean ()
+ k = ascii()
+ v = boolean()
+ expect
+ (implies
+ (get k m === Some v)
+ (get k (Map.Nonempty.adjust not k m) === Some (not v)))
+
+data.Map.Nonempty.adjustWithKey :
+ (k ->{e} v ->{e} v) -> k -> Map.Nonempty k v ->{e} Map.Nonempty k v
+data.Map.Nonempty.adjustWithKey f k = cases
+ Map.Nonempty.Bin sx kx x l r ->
+ match Universal.ordering k kx with
+ Less -> Map.Nonempty.Bin sx kx x (Map.adjustWithKey f k l) r
+ Greater -> Map.Nonempty.Bin sx kx x l (Map.adjustWithKey f k r)
+ Equal -> Map.Nonempty.Bin sx kx (f kx x) l r
+
+data.Map.Nonempty.adjustWithKey.doc : Doc
+data.Map.Nonempty.adjustWithKey.doc =
+ use Nat +
+ {{
+ Adjusts the value at a key in a nonempty map using the provided function
+ which also receives the key. If the key is not present, the map is unchanged.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList
+ (Map.Nonempty.adjustWithKey
+ (k v -> k + v) 1 (toNonemptyMap ((1, 2) +| [(2, 3)])))
+ ```
+ }}
+
+test> data.Map.Nonempty.adjustWithKey.tests.adjusts =
+ runs 1000 do
+ use Map.Nonempty get
+ use Nat isOdd
+ use Text ascii size
+ use gen boolean
+ m = nonemptyMapOf ascii boolean ()
+ k = ascii()
+ v = boolean()
+ f = logic()
+ expect
+ (implies
+ (get k m === Some v)
+ (get k (Map.Nonempty.adjustWithKey (k v -> f (isOdd (size k)) v) k m)
+ === Some (f (isOdd (size k)) v)))
+
+data.Map.Nonempty.align :
+ Map.Nonempty k a -> Map.Nonempty k b -> Map.Nonempty k (OneOrBoth a b)
+data.Map.Nonempty.align = Map.Nonempty.alignWith id
+
+data.Map.Nonempty.align.doc : Doc
+data.Map.Nonempty.align.doc =
+ {{
+ Aligns two nonempty maps into a nonempty map of {type OneOrBoth} values.
+
+ The result will have the same keys as the union of the keys of the two input
+ maps, and each value will be a {type OneOrBoth} containing the corresponding
+ values from the two input maps. If a key is present in only one of the input
+ maps, the result will contain {This} or {That} values accordingly. If a key
+ is present in both input maps, the result will contain a {Both} value.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList
+ (Map.Nonempty.align
+ (toNonemptyMap ((1, "hello") +| [(2, "world")]))
+ (toNonemptyMap ((2, 42) +| [(3, 43)])))
+ ```
+
+ # See also
+
+ * {Map.Nonempty.alignWith} - a variant where you can specify a function to
+ apply to the values.
+ }}
+
+data.Map.Nonempty.alignWith :
+ (OneOrBoth a b ->{g} c)
+ -> Map.Nonempty k a
+ -> Map.Nonempty k b
+ ->{g} Map.Nonempty k c
+data.Map.Nonempty.alignWith f m1 m2 =
+ Map.Nonempty.alignWithKey (_ x -> f x) m1 m2
+
+data.Map.Nonempty.alignWith.doc : Doc
+data.Map.Nonempty.alignWith.doc =
+ use Text ++
+ {{
+ Aligns two nonempty maps into a nonempty map of values using a function.
+
+ The result will have the same keys as the union of the keys of the two input
+ maps, and each value will be the result of applying the given function to the
+ corresponding values from the two input maps – {This} and {That} for keys
+ that are present in only one of the input maps, and {Both} for keys that are
+ present in both input maps.
+
+ # Example
+
+ ```
+ f = cases
+ This a -> "only in the first map: " ++ a
+ That b -> "only in the second map: " ++ b
+ Both a b -> "in both maps: " ++ a ++ " and " ++ b
+ Map.Nonempty.values
+ (Map.Nonempty.alignWith
+ f
+ (toNonemptyMap ((1, "circuit") +| [(2, "quasar")]))
+ (toNonemptyMap ((2, "voyage") +| [(3, "harmony")])))
+ ```
+
+ # See also
+
+ * {Map.Nonempty.align} - a variant that returns a nonempty map of
+ {type OneOrBoth} values.
+ * {Map.Nonempty.alignWithKey} - a variant where the function also receives
+ the key.
+ * {Nonempty.mergeWith} - a variant that allows the function to remove keys
+ from the result map.
+ }}
+
+data.Map.Nonempty.alignWithKey :
+ (k ->{e} OneOrBoth a b ->{f} c)
+ -> Map.Nonempty k a
+ -> Map.Nonempty k b
+ ->{e, f} Map.Nonempty k c
+data.Map.Nonempty.alignWithKey f m1 m2 =
+ use Map alignWithKey
+ use Map.Nonempty Bin
+ use Nonempty.internal link
+ (Bin sz1 k1 x1 l1 r1, Bin sz2 k2 x2 l2 r2) = (m1, m2)
+ (l2, mb, r2) = splitLookup k1 (Map.Nonempty.toMap m2)
+ l1l2 = alignWithKey f l1 l2
+ r1r2 = alignWithKey f r1 r2
+ match mb with
+ None -> link k1 (f k1 (This x1)) l1l2 r1r2
+ Some x2 -> link k1 (f k1 (Both x1 x2)) l1l2 r1r2
+
+data.Map.Nonempty.alignWithKey.doc : Doc
+data.Map.Nonempty.alignWithKey.doc =
+ use Nat toText
+ use Text ++
+ {{
+ Aligns two nonempty maps into a nonempty map of values using a function.
+
+ The result will have the same keys as the union of the keys of the two input
+ maps, and each value will be the result of applying the given function to the
+ corresponding key-value pairs from the two input maps. The function receives
+ {This} and {That} for values under keys that are present in only one of the
+ input maps, and {Both} for keys that are present in both input maps.
+
+ # Example
+
+ ```
+ f k = cases
+ This a -> "only in the first map: " ++ toText k ++ " -> " ++ a
+ That b -> "only in the second map: " ++ toText k ++ " -> " ++ b
+ Both a b -> "in both maps: " ++ toText k ++ " -> " ++ a ++ " and " ++ b
+ Map.Nonempty.values
+ (Map.Nonempty.alignWithKey
+ f
+ (toNonemptyMap ((1, "circuit") +| [(2, "quasar")]))
+ (toNonemptyMap ((2, "voyage") +| [(3, "harmony")])))
+ ```
+
+ # See also
+
+ * {Map.Nonempty.align} - a variant that returns a nonempty map of
+ {type OneOrBoth} values.
+ * {Map.Nonempty.alignWith} - a variant where the function doesn't take the
+ key.
+ }}
+
+data.Map.Nonempty.alter :
+ (Optional v ->{e} Optional v) -> k -> Map.Nonempty k v ->{e} Map k v
+data.Map.Nonempty.alter f k = Map.alter f k << Map.Nonempty.toMap
+
+data.Map.Nonempty.alter.doc : Doc
+data.Map.Nonempty.alter.doc =
+ {{
+ Alters the value at a key in a nonempty map using the provided function. The
+ function receives the current value at the key, if present, and returns the
+ new value to be stored at the key. If the function returns {None}, the key is
+ deleted from the map. If the key is not present, the map is unchanged.
+
+ # Example
+
+ ```
+ Map.toList
+ (Map.Nonempty.alter
+ (const (Some "a")) 1 (toNonemptyMap ((1, "b") +| [(2, "c")])))
+ ```
+ }}
+
+data.Map.Nonempty.breakOffMax : Map.Nonempty k v -> ((k, v), Map k v)
+data.Map.Nonempty.breakOffMax = cases
+ Map.Nonempty.Bin _ k x l internal.Tip -> ((k, x), l)
+ Map.Nonempty.Bin _ k x l r ->
+ match Map.breakOffMax r with
+ Some ((k', v'), m) -> ((k', v'), Map.insert k x m)
+ None -> ((k, x), glue l r)
+
+data.Map.Nonempty.breakOffMax.doc : Doc
+data.Map.Nonempty.breakOffMax.doc =
+ {{
+ Breaks off the maximum key and value from a nonempty map, returning the pair
+ and the (possibly empty) {type Map} without the pair.
+
+ # Example
+
+ ```
+ Tuple.second
+ Map.toList
+ (Map.Nonempty.breakOffMax (toNonemptyMap ((1, "a") +| [(2, "b")])))
+ ```
+ }}
+
+data.Map.Nonempty.breakOffMin : Map.Nonempty k v -> ((k, v), Map k v)
+data.Map.Nonempty.breakOffMin = cases
+ Map.Nonempty.Bin _ k x internal.Tip r -> ((k, x), r)
+ Map.Nonempty.Bin _ k x l r ->
+ match Map.breakOffMin l with
+ Some ((k', v'), m) -> ((k', v'), Map.insert k x m)
+ None -> ((k, x), glue l r)
+
+data.Map.Nonempty.breakOffMin.doc : Doc
+data.Map.Nonempty.breakOffMin.doc =
+ {{
+ Breaks off the minimum key and value from a nonempty map, returning the pair
+ and the (possibly empty) {type Map} without the pair.
+
+ # Example
+
+ ```
+ Tuple.second
+ Map.toList
+ (Map.Nonempty.breakOffMin (toNonemptyMap ((1, "a") +| [(2, "b")])))
+ ```
+ }}
+
+data.Map.Nonempty.contains : k -> Map.Nonempty k v -> Boolean
+data.Map.Nonempty.contains k m = match Map.Nonempty.get k m with
+ None -> false
+ Some _ -> true
+
+data.Map.Nonempty.contains.doc : Doc
+data.Map.Nonempty.contains.doc =
+ {{
+ Checks if a key is present in a nonempty map.
+
+ # Example
+
+ ```
+ Map.Nonempty.contains 1 (toNonemptyMap ((1, "a") +| [(2, "b")]))
+ ```
+ }}
+
+data.Map.Nonempty.delete : k -> Map.Nonempty k v -> Map k v
+data.Map.Nonempty.delete k = Map.delete k << Map.Nonempty.toMap
+
+data.Map.Nonempty.delete.doc : Doc
+data.Map.Nonempty.delete.doc =
+ {{
+ Deletes a key from a nonempty map, returning the (possibly empty) map without
+ the key.
+
+ # Example
+
+ ```
+ Map.toList (Map.Nonempty.delete 1 (toNonemptyMap ((1, "a") +| [(2, "b")])))
+ ```
+ }}
+
+data.Map.Nonempty.doc : Doc
+data.Map.Nonempty.doc =
+ use Map Nonempty.toList
+ use Map.Nonempty contains delete get insert size
+ use Nat +
+ {{
+ {type Map.Nonempty} is a nonempty version of {type Map}. It is a sorted
+ finite map from keys to values, with at least one key/value pair.
+
+ The type is parameterized by the key type `k` and the value type `v`.
+
+ The {insert} and {get} and related functions all use {Universal.ordering} for
+ ordering the keys.
+
+ ```
+ toNonemptyMap (("apple", 1) +| [("pear", 2)]) |> get "pear"
+ ```
+
+ # Common functions
+
+ @signatures{toNonemptyMap, get, insert, delete, contains, Nonempty.toList}
+
+ # More examples
+
+ ```
+ Nonempty.toList (Map.Nonempty.singleton "apple" 1)
+ ```
+
+ ```
+ Map.Nonempty.union
+ (toNonemptyMap (List.Nonempty.singleton ("mango", 2)))
+ (toNonemptyMap (List.Nonempty.singleton ("orange", 3)))
+ |> Nonempty.toList
+ ```
+
+ ```
+ delete "blueberry" (toNonemptyMap (("apple", 1) +| [("blueberry", 2)]))
+ |> Map.toList
+ ```
+
+ ```
+ contains "mango" (toNonemptyMap (("apple", 1) +| [("blueberry", 2)]))
+ ```
+
+ ```
+ size (toNonemptyMap (("apple", 1) +| [("blueberry", 2)]))
+ ```
+
+ ```
+ Map.Nonempty.unionWith
+ (+)
+ (toNonemptyMap (List.Nonempty.singleton ("a", 1)))
+ (toNonemptyMap (("a", 1) +| [("b", 2)]))
+ |> Nonempty.toList
+ ```
+
+ There are a lot more functions defined. Browse the namespace under
+ {type Map.Nonempty} to see more.
+
+ # Implementation notes
+
+ The type is implemented as a balanced binary tree of logarithmic depth, so
+ most operations (such as {get} or {insert}) that touch an individual key
+ take logarithmic time.
+
+ ```
+ toNonemptyMap
+ (("a", "v1") +| [("b", "v2"), ("c", "v3"), ("d", "v4"), ("e", "v5")])
+ ```
+
+ The size of each subtree is cached at non-leaf nodes, and the entry at each
+ node has a key which is bigger than all entries in the left subtree, and
+ smaller than all entries in the right subtree. Operations like {insert} and
+ {delete} maintain a balanced tree so its depth is logarithmic in the
+ {size}.
+ }}
+
+test> data.Map.Nonempty.equals.tests.reflexive = runs 1000 do
+ m = nonemptyMapOf Text.ascii gen.boolean ()
+ expect (Map.Nonempty.equals m m)
+
+test> data.Map.Nonempty.equals.tests.symmetrical = runs 1000 do
+ use Map.Nonempty equals
+ use Text ascii
+ use gen boolean
+ m = nonemptyMapOf ascii boolean ()
+ n = nonemptyMapOf ascii boolean ()
+ expect (iff (equals m n) (equals n m))
+
+test> data.Map.Nonempty.equals.tests.transitive = runs 1000 do
+ use Map.Nonempty equals
+ use Text ascii
+ use gen boolean
+ m = nonemptyMapOf ascii boolean ()
+ n = nonemptyMapOf ascii boolean ()
+ o = nonemptyMapOf ascii boolean ()
+ p = implies (equals m n && equals n o) (equals m o)
+ if p then expect p else bug (m, n, o)
+
+data.Map.Nonempty.filter : (v ->{g} Boolean) -> Map.Nonempty k v ->{g} Map k v
+data.Map.Nonempty.filter p = Map.Nonempty.toMap >> Map.filter p
+
+data.Map.Nonempty.filter.doc : Doc
+data.Map.Nonempty.filter.doc =
+ {{
+ Filters a {type Map.Nonempty} by retaining only entries where the value
+ satisfies the given predicate.
+
+ # Example
+
+ This filters a map to retain only entries where the value is even:
+
+ ```
+ Map.toList
+ (Map.Nonempty.filter
+ Nat.isEven (toNonemptyMap ((1, 2) +| [(2, 3), (3, 4)])))
+ ```
+
+ # See also
+
+ * {Map.Nonempty.filterWithKey} - Filters a map by the keys and values.
+ * {{ docLink (docEmbedTermLink do Nonempty.filterKeys) }} - Filters a map
+ by the keys.
+ }}
+
+data.Map.Nonempty.filterAlignWithKey :
+ (k ->{e} OneOrBoth a b ->{f} Optional c)
+ -> Map.Nonempty k a
+ -> Map.Nonempty k b
+ ->{e, f} Map k c
+data.Map.Nonempty.filterAlignWithKey f = cases
+ m1@(Map.Nonempty.Bin sz1 k1 x1 l1 r1), m2@(Map.Nonempty.Bin sz2 k2 x2 l2 r2) ->
+ (l2, mb, r2) = splitLookup k1 (Map.Nonempty.toMap m2)
+ use Map filterAlignWithKey
+ use Map.internal link
+ l1l2 = filterAlignWithKey f l1 l2
+ r1r2 = filterAlignWithKey f r1 r2
+ match mb with
+ None ->
+ match f k1 (This x1) with
+ Some v -> link k1 v l1l2 r1r2
+ None -> link2 l1l2 r1r2
+ Some x2 ->
+ match f k1 (Both x1 x2) with
+ Some v -> link k1 v l1l2 r1r2
+ None -> link2 l1l2 r1r2
+
+data.Map.Nonempty.filterAlignWithKey.doc : Doc
+data.Map.Nonempty.filterAlignWithKey.doc =
+ use Map toList
+ use Nat + ==
+ {{
+ Aligns two nonempty maps by their keys and applies a function to the aligned
+ pairs. The function receives the key and a {type OneOrBoth} value, which is
+ either {This} if the key is only in the first map, {That} if the key is only
+ in the second map, or {Both} if the key is in both maps. The function can
+ return a value for the aligned pair, which will be the value under that key
+ in the result, or {None} to omit that key from the result.
+
+ # Examples
+
+ This aligns two nonempty maps and sum the values under each key:
+
+ ```
+ toList
+ (Nonempty.filterAlignWithKey
+ (k ob -> (match ob with
+ This x -> Some x
+ That y -> Some y
+ Both x y -> Some (x + y)))
+ (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ (toNonemptyMap ((2, 4) +| [(3, 5)])))
+ ```
+
+ This aligns two nonempty maps and filters out keys where the values are
+ equal, taking the minimum of the two values otherwise:
+
+ ```
+ toList
+ (Nonempty.filterAlignWithKey
+ (k ob -> (match ob with
+ Both x y
+ | x == y -> None
+ | otherwise -> Some (Nat.min x y)
+ This x -> Some x
+ That y -> Some y))
+ (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ (toNonemptyMap ((2, 4) +| [(3, 5)])))
+ ```
+
+ # See also
+
+ * {Map.filterAlignWithKey} - The same operation on (possibly empty) maps.
+ * {Map.Nonempty.alignWithKey} - A similar operation that doesn't filter out
+ keys.
+ }}
+
+data.Map.Nonempty.filterKeys :
+ (k ->{g} Boolean) -> Map.Nonempty k v ->{g} Map k v
+data.Map.Nonempty.filterKeys p = Map.Nonempty.toMap >> Map.filterKeys p
+
+data.Map.Nonempty.filterKeys.doc : Doc
+data.Map.Nonempty.filterKeys.doc =
+ {{
+ Filters a {type Map.Nonempty} by retaining only entries where the key
+ satisfies the given predicate.
+
+ # Example
+
+ This filters a map to retain only entries where the key is even:
+
+ ```
+ Map.toList
+ (Nonempty.filterKeys
+ Nat.isEven (toNonemptyMap ((1, 2) +| [(2, 3), (3, 4)])))
+ ```
+
+ # See also
+
+ * {Map.Nonempty.filterWithKey} - Filters a map by the keys and values.
+ * {{ docLink (docEmbedTermLink do Map.Nonempty.filter) }} - Filters a map
+ by the values.
+ }}
+
+data.Map.Nonempty.filterWithKey :
+ (k ->{e} a ->{f} Boolean) -> Map.Nonempty k a ->{e, f} Map k a
+data.Map.Nonempty.filterWithKey p = Map.Nonempty.toMap >> Map.filterWithKey p
+
+data.Map.Nonempty.filterWithKey.doc : Doc
+data.Map.Nonempty.filterWithKey.doc =
+ use Nat ==
+ use Nonempty filterKeys
+ {{
+ Filters a {type Map.Nonempty} by retaining only entries that satisfy the
+ given predicate.
+
+ # Example
+
+ This filters a map to retain only entries where the key and value are
+ equal:
+
+ ```
+ Map.toList
+ (Map.Nonempty.filterWithKey
+ (==) (toNonemptyMap ((1, 1) +| [(2, 3), (3, 3)])))
+ ```
+
+ # See also
+
+ * {filterKeys} - Filters a map by the values.
+ * {filterKeys} - Filters a map by the keys.
+ }}
+
+data.Map.Nonempty.foldLeft :
+ (a ->{e} b ->{e} a) -> a -> Map.Nonempty k b ->{e} a
+data.Map.Nonempty.foldLeft f z = cases
+ Map.Nonempty.Bin _ _ x l r -> Map.foldLeft f (f (Map.foldLeft f z l) x) r
+
+data.Map.Nonempty.foldLeft.doc : Doc
+data.Map.Nonempty.foldLeft.doc =
+ use Nat +
+ {{
+ Folds a nonempty map from left (smallest key) to right (largest key) using a
+ function to combine the values under the keys.
+
+ # Example
+
+ ```
+ Map.Nonempty.foldLeft (a b -> a + b) 0 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.foldLeftWithKey :
+ (a ->{e} k ->{e} b ->{e} a) -> a -> Map.Nonempty k b ->{e} a
+data.Map.Nonempty.foldLeftWithKey f z = cases
+ Map.Nonempty.Bin _ k x l r ->
+ Map.foldLeftWithKey f (f (Map.foldLeftWithKey f z l) k x) r
+
+data.Map.Nonempty.foldLeftWithKey.doc : Doc
+data.Map.Nonempty.foldLeftWithKey.doc =
+ use Nat +
+ {{
+ Folds a nonempty map from left (smallest key) to right (largest key) using a
+ function to combine the keys and values.
+
+ # Example
+
+ ```
+ Nonempty.foldLeftWithKey
+ (a k v -> a + k + v) 0 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.foldMap :
+ (a ->{e} a ->{e} a) -> (k ->{e} v ->{e} a) -> Map.Nonempty k v ->{e} a
+data.Map.Nonempty.foldMap f g = cases
+ Map.Nonempty.Bin _ kx x l r ->
+ match (data.Map.foldMap f g l, data.Map.foldMap f g r) with
+ (None, None) -> g kx x
+ (Some l, None) -> f l (g kx x)
+ (None, Some r) -> f (g kx x) r
+ (Some l, Some r) -> f (f l (g kx x)) r
+
+data.Map.Nonempty.foldMap.doc : Doc
+data.Map.Nonempty.foldMap.doc =
+ use Nat +
+ {{
+ Folds a nonempty map using a function to apply to each key-value pair and a
+ function to combine the results of each application.
+
+ # Example
+
+ ```
+ Map.Nonempty.foldMap
+ (a b -> a + b) (k v -> k + v) (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.foldRight :
+ (a ->{e} b ->{e} b) -> b -> Map.Nonempty k a ->{e} b
+data.Map.Nonempty.foldRight f z = cases
+ m@(Map.Nonempty.Bin _ _ x l r) ->
+ Map.foldRight f (f x (Map.foldRight f z r)) l
+
+data.Map.Nonempty.foldRight.doc : Doc
+data.Map.Nonempty.foldRight.doc =
+ use Nat +
+ {{
+ Folds a nonempty map from right (largest key) to left (smallest key) using a
+ function to combine the values under the keys.
+
+ # Example
+
+ ```
+ Map.Nonempty.foldRight
+ (a b -> a + b) 0 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.foldRightWithKey :
+ (k ->{e} a ->{e} b ->{e} b) -> b -> Map.Nonempty k a ->{e} b
+data.Map.Nonempty.foldRightWithKey f z = cases
+ Map.Nonempty.Bin _ k x l r ->
+ Map.foldRightWithKey f (f k x (Map.foldRightWithKey f z r)) l
+
+data.Map.Nonempty.foldRightWithKey.doc : Doc
+data.Map.Nonempty.foldRightWithKey.doc =
+ use Nat +
+ {{
+ Folds a nonempty map from right (largest key) to left (smallest key) using a
+ function to combine the keys and values.
+
+ # Example
+
+ ```
+ Nonempty.foldRightWithKey
+ (k v b -> k + v + b) 0 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.foreach : Map.Nonempty k v -> (k ->{e} v ->{e} ()) ->{e} ()
+data.Map.Nonempty.foreach = cases
+ Map.Nonempty.Bin sx kx x l r, f ->
+ use Map foreach
+ f kx x
+ foreach l f
+ foreach r f
+
+data.Map.Nonempty.foreach.doc : Doc
+data.Map.Nonempty.foreach.doc =
+ use Nat +
+ use Store get
+ {{
+ Applies a function to each key and value in a nonempty map and discards the
+ results.
+
+ # Example
+
+ ```
+ withInitialValue 0 do
+ Nonempty.foreach
+ (toNonemptyMap ((1, 2) +| [(2, 3)])) (k v -> let
+ x = get
+ Store.put (x + k + v))
+ get
+ ```
+ }}
+
+data.Map.Nonempty.fromList : List.Nonempty (k, v) -> Map.Nonempty k v
+data.Map.Nonempty.fromList =
+ List.Nonempty.foldMap Map.Nonempty.union (uncurry Map.Nonempty.singleton)
+
+data.Map.Nonempty.fromList.doc : Doc
+data.Map.Nonempty.fromList.doc =
+ {{
+ Creates a nonempty map from a nonempty list of key-value pairs.
+
+ # Example
+
+ ```
+ Map.Nonempty.fromList
+ (("a", "v1") +| [("b", "v2"), ("c", "v3"), ("d", "v4"), ("e", "v5")])
+ ```
+ }}
+
+data.Map.Nonempty.fromListWith :
+ (v ->{e} v ->{e} v) -> List.Nonempty (k, v) ->{e} Map.Nonempty k v
+data.Map.Nonempty.fromListWith f = Map.Nonempty.fromListWithKey (const f)
+
+data.Map.Nonempty.fromListWith.doc : Doc
+data.Map.Nonempty.fromListWith.doc =
+ use Text ++
+ {{
+ Creates a nonempty map from a nonempty list of key-value pairs, combining
+ values with the given function.
+
+ # Example
+
+ ```
+ Map.Nonempty.fromListWith
+ (++)
+ (("a", "v1") +| [("a", "v2"), ("a", "v3"), ("a", "v4"), ("a", "v5")])
+ ```
+ }}
+
+data.Map.Nonempty.fromListWithKey :
+ (k ->{e} v ->{e} v ->{e} v) -> List.Nonempty (k, v) ->{e} Map.Nonempty k v
+data.Map.Nonempty.fromListWithKey f = cases
+ Nonempty.Nonempty (kx, x) xs ->
+ m = Map.Nonempty.singleton kx x
+ List.foldRight (cases (k, x), t -> Nonempty.putWithKey f k x t) m xs
+
+data.Map.Nonempty.fromListWithKey.doc : Doc
+data.Map.Nonempty.fromListWithKey.doc =
+ use Text ++
+ {{
+ Creates a nonempty map from a nonempty list of key-value pairs, combining
+ values and keys with the given function.
+
+ # Example
+
+ ```
+ Map.Nonempty.fromListWithKey
+ (k v1 v2 -> k ++ v1 ++ v2)
+ (("a", "v1") +| [("a", "v2"), ("a", "v3"), ("a", "v4"), ("a", "v5")])
+ ```
+ }}
+
+data.Map.Nonempty.get : k -> Map.Nonempty k v -> Optional v
+data.Map.Nonempty.get k = cases
+ Map.Nonempty.Bin _ kx x l r ->
+ match Universal.ordering k kx with
+ Less -> Map.get k l
+ Greater -> Map.get k r
+ Equal -> Some x
+
+data.Map.Nonempty.get.doc : Doc
+data.Map.Nonempty.get.doc =
+ {{
+ Gets the value associated with a key in a nonempty map.
+
+ # Example
+
+ ```
+ Map.Nonempty.get 2 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.getMax : Map.Nonempty k v -> Optional (k, v)
+data.Map.Nonempty.getMax =
+ go k x = cases
+ internal.Tip -> (k, x)
+ internal.Bin _ k' v _ r -> go k' v r
+ cases Map.Nonempty.Bin _ k x _ r -> Some (go k x r)
+
+data.Map.Nonempty.getMax.doc : Doc
+data.Map.Nonempty.getMax.doc =
+ {{
+ Gets the largest key and its associated value in a nonempty map.
+
+ # Example
+
+ ```
+ Map.Nonempty.getMax (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.getMin : Map.Nonempty k v -> Optional (k, v)
+data.Map.Nonempty.getMin =
+ go k x = cases
+ internal.Tip -> (k, x)
+ internal.Bin _ k v l _ -> go k v l
+ cases Map.Nonempty.Bin _ k x l _ -> Some (go k x l)
+
+data.Map.Nonempty.getMin.doc : Doc
+data.Map.Nonempty.getMin.doc =
+ {{
+ Gets the smallest key and its associated value in a nonempty map.
+
+ # Example
+
+ ```
+ Map.Nonempty.getMin (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.getOrAbort : k -> Map.Nonempty k v ->{Abort} v
+data.Map.Nonempty.getOrAbort k = cases
+ Map.Nonempty.Bin _ kx x l r ->
+ match Universal.ordering k kx with
+ Less -> Map.getOrAbort k l
+ Greater -> Map.getOrAbort k r
+ Equal -> x
+
+data.Map.Nonempty.getOrAbort.doc : Doc
+data.Map.Nonempty.getOrAbort.doc =
+ use Map.Nonempty getOrAbort
+ {{
+ Gets the value associated with a key in a nonempty map, or calls {abort} if
+ the key is not present.
+
+ # Example
+
+ ```
+ toOptional! do getOrAbort 2 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+
+ ```
+ toOptional! do getOrAbort 3 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.getOrElse : v -> k -> Map.Nonempty k v -> v
+data.Map.Nonempty.getOrElse def k m = match Map.Nonempty.get k m with
+ None -> def
+ Some x -> x
+
+data.Map.Nonempty.getOrElse.doc : Doc
+data.Map.Nonempty.getOrElse.doc =
+ use Map.Nonempty getOrElse
+ {{
+ Gets the value associated with a key in a nonempty map, or returns the given
+ default value if the key is not present.
+
+ # Example
+
+ ```
+ getOrElse 0 2 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+
+ ```
+ getOrElse 0 3 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.getOrThrow : e -> k -> Map.Nonempty k v ->{Throw e} v
+data.Map.Nonempty.getOrThrow e k m = match Map.Nonempty.get k m with
+ None -> throw e
+ Some v -> v
+
+data.Map.Nonempty.getOrThrow.doc : Doc
+data.Map.Nonempty.getOrThrow.doc =
+ {{
+ Gets the value associated with the given key, or calls {throw} with the given
+ error value if the key is not present.
+
+ # Example
+
+ ```
+ toEither do
+ Nonempty.getOrThrow "not found" 3 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.insert : k -> v -> Map.Nonempty k v -> Map.Nonempty k v
+data.Map.Nonempty.insert kx x = cases
+ Map.Nonempty.Bin sz ky y l r ->
+ match Universal.ordering kx ky with
+ Less -> Nonempty.internal.balanceL ky y (Map.insert kx x l) r
+ Greater -> Nonempty.internal.balanceR ky y l (Map.insert kx x r)
+ Equal -> Map.Nonempty.Bin sz kx x l r
+
+data.Map.Nonempty.insert.doc : Doc
+data.Map.Nonempty.insert.doc =
+ use Map.Nonempty insert toList
+ {{
+ Inserts a new key and value into the map. If the key is already present in
+ the map, the old value is replaced with the new value.
+
+ # Example
+
+ ```
+ toList (insert 3 4 (toNonemptyMap ((1, 2) +| [(2, 3)])))
+ ```
+
+ ```
+ toList (insert 2 4 (toNonemptyMap ((1, 2) +| [(2, 3)])))
+ ```
+ }}
+
+data.Map.Nonempty.internal.balanceL :
+ k -> v -> Map k v -> Map k v -> Map.Nonempty k v
+data.Map.Nonempty.internal.balanceL k x l = cases
+ internal.Tip ->
+ match l with
+ internal.Tip -> Map.Nonempty.singleton k x
+ internal.Bin _ _ _ internal.Tip internal.Tip ->
+ Map.Nonempty.Bin 2 k x l internal.Tip
+ internal.Bin _ lk lx internal.Tip (internal.Bin _ lrk lrx _ _) ->
+ Map.Nonempty.Bin 3 lrk lrx (Map.singleton lk lx) (Map.singleton k x)
+ internal.Bin _ lk lx ll@(internal.Bin _ _ _ _ _) internal.Tip ->
+ Map.Nonempty.Bin 3 lk lx ll (Map.singleton k x)
+ internal.Bin
+ ls
+ lk
+ lx
+ ll@(internal.Bin lls _ _ _ _)
+ lr@(internal.Bin lrs lrk lrx lrl lrr) ->
+ if Universal.lt lrs (ratio Nat.* lls) then
+ Map.Nonempty.Bin
+ (1 Nat.+ ls)
+ lk
+ lx
+ ll
+ (internal.Bin (1 Nat.+ lrs) k x lr internal.Tip)
+ else
+ Map.Nonempty.Bin
+ (1 Nat.+ ls)
+ lrk
+ lrx
+ (internal.Bin (1 Nat.+ lls Nat.+ Map.size lrl) lk lx ll lrl)
+ (internal.Bin (1 Nat.+ Map.size lrr) k x lrr internal.Tip)
+ r@(internal.Bin rs _ _ _ _) ->
+ match l with
+ internal.Tip -> Map.Nonempty.Bin (1 Nat.+ rs) k x internal.Tip r
+ internal.Bin ls lk lx ll lr ->
+ if Universal.gt ls (delta Nat.* rs) then
+ match (ll, lr) with
+ (internal.Bin lls _ _ _ _, internal.Bin lrs lrk lrx lrl lrr) ->
+ if Universal.lt lrs (ratio Nat.* lls) then
+ Map.Nonempty.Bin
+ (1 Nat.+ ls Nat.+ rs)
+ lk
+ lx
+ ll
+ (internal.Bin (1 Nat.+ rs Nat.+ lrs) k x lr r)
+ else
+ Map.Nonempty.Bin
+ (1 Nat.+ ls Nat.+ rs)
+ lrk
+ lrx
+ (internal.Bin (1 Nat.+ lls Nat.+ Map.size lrl) lk lx ll lrl)
+ (internal.Bin (1 Nat.+ rs Nat.+ Map.size lrr) k x lrr r)
+ (_, _) -> bug "failure in Nonempty.balanceL"
+ else Map.Nonempty.Bin (1 Nat.+ ls Nat.+ rs) k x l r
+
+data.Map.Nonempty.internal.balanceR :
+ k -> v -> Map k v -> Map k v -> Map.Nonempty k v
+data.Map.Nonempty.internal.balanceR k x l r =
+ match l with
+ internal.Tip ->
+ match r with
+ internal.Tip -> Map.Nonempty.Bin 1 k x internal.Tip internal.Tip
+ internal.Bin _ _ _ internal.Tip internal.Tip ->
+ Map.Nonempty.Bin 2 k x internal.Tip r
+ internal.Bin _ rk rx internal.Tip rr@(internal.Bin _ _ _ _ _) ->
+ Map.Nonempty.Bin
+ 3 rk rx (internal.Bin 1 k x internal.Tip internal.Tip) rr
+ internal.Bin _ rk rx (internal.Bin _ rlk rlx _ _) internal.Tip ->
+ Map.Nonempty.Bin
+ 3
+ rlk
+ rlx
+ (internal.Bin 1 k x internal.Tip internal.Tip)
+ (internal.Bin 1 rk rx internal.Tip internal.Tip)
+ internal.Bin
+ rs
+ rk
+ rx
+ rl@(internal.Bin rls rlk rlx rll rlr)
+ rr@(internal.Bin rrs _ _ _ _)
+ | Universal.lt rls (ratio Nat.* rrs) ->
+ Map.Nonempty.Bin
+ (1 Nat.+ rs)
+ rk
+ rx
+ (internal.Bin (1 Nat.+ rls) k x internal.Tip rl)
+ rr
+ | otherwise ->
+ Map.Nonempty.Bin
+ (1 Nat.+ rs)
+ rlk
+ rlx
+ (internal.Bin (1 Nat.+ Map.size rll) k x internal.Tip rll)
+ (internal.Bin (1 Nat.+ rrs Nat.+ Map.size rlr) rk rx rlr rr)
+ internal.Bin ls _ _ _ _ ->
+ match r with
+ internal.Tip -> Map.Nonempty.Bin (1 Nat.+ ls) k x l internal.Tip
+ internal.Bin rs rk rx rl rr
+ | Universal.gt rs (delta Nat.* ls) ->
+ match (rl, rr) with
+ (internal.Bin rls rlk rlx rll rlr, internal.Bin rrs _ _ _ _)
+ | Universal.lt rls (ratio Nat.* rrs) ->
+ Map.Nonempty.Bin
+ (1 Nat.+ ls Nat.+ rs)
+ rk
+ rx
+ (internal.Bin (1 Nat.+ ls Nat.+ rls) k x l rl)
+ rr
+ | otherwise ->
+ Map.Nonempty.Bin
+ (1 Nat.+ ls Nat.+ rs)
+ rlk
+ rlx
+ (internal.Bin (1 Nat.+ ls Nat.+ Map.size rll) k x l rll)
+ (internal.Bin (1 Nat.+ rrs Nat.+ Map.size rlr) rk rx rlr rr)
+ (_, _) -> bug "Failure in balanceR"
+ | otherwise ->
+ Map.Nonempty.Bin (1 Nat.+ ls Nat.+ rs) k x l r
+
+data.Map.Nonempty.internal.link :
+ k -> v -> Map k v -> Map k v -> Map.Nonempty k v
+data.Map.Nonempty.internal.link kx x = cases
+ internal.Tip, r -> Nonempty.internal.putMin kx x r
+ l, internal.Tip -> Nonempty.internal.putMax kx x l
+ l@(internal.Bin sizeL ky y ly ry), r@(internal.Bin sizeR kz z lz rz) ->
+ if Universal.lt (delta Nat.* sizeL) sizeR then
+ Nonempty.internal.balanceL kz z (Map.internal.link kx x l lz) rz
+ else
+ if Universal.lt (delta Nat.* sizeR) sizeL then
+ Nonempty.internal.balanceR ky y ly (Map.internal.link kx x ry r)
+ else Map.Nonempty.Bin (Map.size l Nat.+ Map.size r Nat.+ 1) kx x l r
+
+data.Map.Nonempty.internal.putMax : k -> v -> Map k v -> Map.Nonempty k v
+data.Map.Nonempty.internal.putMax kx x = cases
+ internal.Tip -> Map.Nonempty.singleton kx x
+ internal.Bin _ ky y l r ->
+ Nonempty.internal.balanceR ky y l (Map.internal.putMax kx x r)
+
+data.Map.Nonempty.internal.putMin : k -> v -> Map k v -> Map.Nonempty k v
+data.Map.Nonempty.internal.putMin kx x = cases
+ internal.Tip -> Map.Nonempty.singleton kx x
+ internal.Bin _ ky y l r ->
+ Nonempty.internal.balanceL ky y (Map.internal.putMin kx x l) r
+
+data.Map.Nonempty.internal.putWithKeyR :
+ (k ->{e} v ->{e} v ->{e} v)
+ -> k
+ -> v
+ -> Map.Nonempty k v
+ ->{e} Map.Nonempty k v
+data.Map.Nonempty.internal.putWithKeyR f kx x = cases
+ Map.Nonempty.Bin sy ky y l r ->
+ match Universal.ordering kx ky with
+ Less ->
+ Nonempty.internal.balanceL ky y (Map.internal.putWithKeyR f kx x l) r
+ Greater ->
+ Nonempty.internal.balanceR ky y l (Map.internal.putWithKeyR f kx x r)
+ Equal -> Map.Nonempty.Bin sy ky (f ky y x) l r
+
+data.Map.Nonempty.intersect : Map.Nonempty k a -> Map.Nonempty k b -> Map k a
+data.Map.Nonempty.intersect = Map.Nonempty.intersectWith const
+
+data.Map.Nonempty.intersect.doc : Doc
+data.Map.Nonempty.intersect.doc =
+ {{
+ Returns a map containing the keys that are present in both maps. The values
+ in the returned map are the values from the first map.
+
+ # Example
+
+ ```
+ Map.toList
+ (Map.Nonempty.intersect
+ (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ (toNonemptyMap ((2, 4) +| [(3, 5)])))
+ ```
+ }}
+
+data.Map.Nonempty.intersectWith :
+ (a ->{e} b ->{e} c) -> Map.Nonempty k a -> Map.Nonempty k b ->{e} Map k c
+data.Map.Nonempty.intersectWith f = Map.Nonempty.intersectWithKey (const f)
+
+data.Map.Nonempty.intersectWith.doc : Doc
+data.Map.Nonempty.intersectWith.doc =
+ use Nat +
+ {{
+ Returns a map containing the keys that are present in both maps. The values
+ in the returned map are the results of applying the given function to the
+ values from the two maps.
+
+ # Example
+
+ ```
+ Map.toList
+ (Map.Nonempty.intersectWith
+ (+)
+ (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ (toNonemptyMap ((2, 4) +| [(3, 5)])))
+ ```
+ }}
+
+data.Map.Nonempty.intersectWithKey :
+ (k ->{e} a ->{e} b ->{e} c)
+ -> Map.Nonempty k a
+ -> Map.Nonempty k b
+ ->{e} Map k c
+data.Map.Nonempty.intersectWithKey f = cases
+ Map.Nonempty.Bin _ k x1 l1 r1, t2 ->
+ (l2, mb, r2) = splitLookup k (Map.Nonempty.toMap t2)
+ use Map intersectWithKey
+ l1l2 = intersectWithKey f l1 l2
+ r1r2 = intersectWithKey f r1 r2
+ match mb with
+ Some x2 -> Map.internal.link k (f k x1 x2) l1l2 r1r2
+ None -> link2 l1l2 r1r2
+
+data.Map.Nonempty.intersectWithKey.doc : Doc
+data.Map.Nonempty.intersectWithKey.doc =
+ use Nat +
+ {{
+ Returns a map containing the keys that are present in both maps. The values
+ in the returned map are the results of applying the given function to the
+ values from the two maps. The function is passed the key as the first
+ argument.
+
+ # Example
+
+ ```
+ Map.toList
+ (Map.Nonempty.intersectWithKey
+ (k v1 v2 -> k + v1 + v2)
+ (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ (toNonemptyMap ((2, 4) +| [(3, 5)])))
+ ```
+ }}
+
+data.Map.Nonempty.keys : Map.Nonempty k a -> List.Nonempty k
+data.Map.Nonempty.keys = cases
+ Map.Nonempty.Bin _ k _ l r -> prependList (Map.keys l) (k +| Map.keys r)
+
+data.Map.Nonempty.keys.doc : Doc
+data.Map.Nonempty.keys.doc =
+ {{
+ Returns the keys in the map as a non-empty list.
+
+ # Example
+
+ ```
+ Map.Nonempty.keys (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.map : (a ->{e} b) -> Map.Nonempty k a ->{e} Map.Nonempty k b
+data.Map.Nonempty.map f = Map.Nonempty.mapWithKey (const f)
+
+data.Map.Nonempty.map.doc : Doc
+data.Map.Nonempty.map.doc =
+ {{
+ Applies a function to every value in the map.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList
+ (Map.Nonempty.map Nat.increment (toNonemptyMap ((1, 2) +| [(2, 3)])))
+ ```
+ }}
+
+data.Map.Nonempty.mapKeys :
+ (k1 ->{g} k2) -> Map.Nonempty k1 v ->{g} Map.Nonempty k2 v
+data.Map.Nonempty.mapKeys f = cases
+ Map.Nonempty.Bin sz k v l r ->
+ Map.insertNonempty (f k) v (Map.union (Map.mapKeys f l) (Map.mapKeys f r))
+
+data.Map.Nonempty.mapKeys.doc : Doc
+data.Map.Nonempty.mapKeys.doc =
+ {{
+ Applies a function to every key in the map.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList
+ (Nonempty.mapKeys Nat.increment (toNonemptyMap ((1, 2) +| [(2, 3)])))
+ ```
+ }}
+
+data.Map.Nonempty.mapKeysWith :
+ (v ->{g} v ->{g} v)
+ -> (k1 ->{g} k2)
+ -> Map.Nonempty k1 v
+ ->{g} Map.Nonempty k2 v
+data.Map.Nonempty.mapKeysWith c f = cases
+ Map.Nonempty.Bin sz k v l r ->
+ Map.Nonempty.fromListWith
+ c
+ ((f k, v)
+ +| Map.toList
+ (Map.union (Map.mapKeysWith c f l) (Map.mapKeysWith c f r)))
+
+data.Map.Nonempty.mapKeysWith.doc : Doc
+data.Map.Nonempty.mapKeysWith.doc =
+ use Map.Nonempty toList
+ use Nat + /
+ use Nonempty mapKeysWith
+ {{
+ Applies a function to every key in the map, combining values with the given
+ function if the keys map to the same value.
+
+ # Example
+
+ ```
+ toList (mapKeysWith (+) Nat.increment (toNonemptyMap ((1, 2) +| [(2, 3)])))
+ ```
+
+ ```
+ toList (mapKeysWith (+) (x -> x / 2) (toNonemptyMap ((4, 2) +| [(5, 3)])))
+ ```
+ }}
+
+data.Map.Nonempty.mapWithKey :
+ (k ->{e} a ->{e} b) -> Map.Nonempty k a ->{e} Map.Nonempty k b
+data.Map.Nonempty.mapWithKey f = cases
+ Map.Nonempty.Bin sx kx x l r ->
+ Map.Nonempty.Bin sx kx (f kx x) (Map.mapWithKey f l) (Map.mapWithKey f r)
+
+data.Map.Nonempty.mapWithKey.doc : Doc
+data.Map.Nonempty.mapWithKey.doc =
+ use Nat +
+ {{
+ Applies a function to every key-value pair in the map, replacing the values
+ with the results of the function. Leaves the keys unchanged.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList
+ (Map.Nonempty.mapWithKey
+ (k v -> (k, v + 1)) (toNonemptyMap ((1, 2) +| [(2, 3)])))
+ ```
+ }}
+
+data.Map.Nonempty.mergeWith :
+ (OneOrBoth a b ->{g} Optional c)
+ -> Map.Nonempty k a
+ -> Map.Nonempty k b
+ ->{g} Map k c
+data.Map.Nonempty.mergeWith f m1 m2 = Nonempty.mergeWithKey (_ x -> f x) m1 m2
+
+data.Map.Nonempty.mergeWith.doc : Doc
+data.Map.Nonempty.mergeWith.doc =
+ use Map toList
+ use Nonempty mergeWith
+ use Text ++
+ {{
+ Merges two nonempty maps into a (possibly empty) {type Map} of values using a
+ function.
+
+ The result will have a subset of the union of the keys from the two input
+ maps, and each value will be the result of applying the given function to the
+ corresponding key-value pairs. The function receives {This} for values under
+ keys that are present in only the first input map, {That} for values under
+ keys that are present in only the second input map, and {Both} for keys that
+ are present in both input maps. The function should return {None} to indicate
+ that the key should be removed from the result map, and {Some} to indicate
+ that the key should be present in the result map with the given value.
+
+ # Example
+
+ We can use this function to find the symmetric difference of two nonempty
+ maps:
+
+ ```
+ f = cases
+ This a -> Some a
+ That b -> Some b
+ Both _ _ -> None
+ toList
+ (mergeWith
+ f
+ (toNonemptyMap ((1, "Circuit") +| [(2, "Quasar")]))
+ (toNonemptyMap ((2, "Voyage") +| [(3, "Harmony")])))
+ ```
+
+ Or we can use it to find the intersection of two nonempty maps:
+
+ ```
+ f = cases
+ This a -> None
+ That b -> None
+ Both a b -> Some a
+ toList
+ (mergeWith
+ f
+ (toNonemptyMap ((1, "Circuit") +| [(2, "Quasar")]))
+ (toNonemptyMap ((2, "Voyage") +| [(3, "Harmony")])))
+ ```
+
+ Or we can use it to find the union of two nonempty maps:
+
+ ```
+ f = cases
+ This a -> Some a
+ That b -> Some b
+ Both a b -> Some (a ++ b)
+ toList
+ (mergeWith
+ f
+ (toNonemptyMap ((1, "Circuit") +| [(2, "Quasar")]))
+ (toNonemptyMap ((2, "Voyage") +| [(3, "Harmony")])))
+ ```
+
+ Or something else entirely!
+
+ # See also
+
+ * {Nonempty.mergeWithKey} - a variant where the function also receives the
+ key.
+ * {Map.Nonempty.alignWith} - a variant that does not remove keys from the
+ result map.
+ }}
+
+data.Map.Nonempty.mergeWithKey :
+ (k ->{e} OneOrBoth a b ->{f} Optional c)
+ -> Map.Nonempty k a
+ -> Map.Nonempty k b
+ ->{e, f} Map k c
+data.Map.Nonempty.mergeWithKey f m1 m2 =
+ use Map mergeWithKey
+ use Map.Nonempty Bin
+ use Map.internal link
+ (Bin sz1 k1 x1 l1 r1, Bin sz2 k2 x2 l2 r2) = (m1, m2)
+ (l2, mb, r2) = splitLookup k1 (Map.Nonempty.toMap m2)
+ l1l2 = mergeWithKey f l1 l2
+ r1r2 = mergeWithKey f r1 r2
+ match mb with
+ None ->
+ match f k1 (This x1) with
+ None -> glue l1l2 r1r2
+ Some x -> link k1 x l1l2 r1r2
+ Some x2 ->
+ match f k1 (Both x1 x2) with
+ None -> glue l1l2 r1r2
+ Some x -> link k1 x l1l2 r1r2
+
+data.Map.Nonempty.mergeWithKey.doc : Doc
+data.Map.Nonempty.mergeWithKey.doc =
+ use Map toList
+ use Nat isEven
+ use Nonempty mergeWithKey
+ use Text ++
+ {{
+ Merges two nonempty maps into a (possibly empty) {type Map} of values using a
+ function.
+
+ The result will have a subset of the union of the keys from the two input
+ maps, and each value will be the result of applying the given function to the
+ corresponding key-value pairs. The function receives {This} for values under
+ keys that are present in only the first input map, {That} for values under
+ keys that are present in only the second input map, and {Both} for keys that
+ are present in both input maps. The function should return {None} to indicate
+ that the key should be removed from the result map, and {Some} to indicate
+ that the key should be present in the result map with the given value.
+
+ # Example
+
+ We can use this function to find the symmetric difference of two nonempty
+ maps:
+
+ ```
+ f k = cases
+ This a -> Some a
+ That b -> Some b
+ Both _ _ -> None
+ toList
+ (mergeWithKey
+ f
+ (toNonemptyMap ((1, "Circuit") +| [(2, "Quasar")]))
+ (toNonemptyMap ((2, "Voyage") +| [(3, "Harmony")])))
+ ```
+
+ Or we can use it to find the intersection of two nonempty maps:
+
+ ```
+ f k = cases
+ This a -> None
+ That b -> None
+ Both a b -> Some a
+ toList
+ (mergeWithKey
+ f
+ (toNonemptyMap ((1, "Circuit") +| [(2, "Quasar")]))
+ (toNonemptyMap ((2, "Voyage") +| [(3, "Harmony")])))
+ ```
+
+ Or we can use it to find the union of two nonempty maps:
+
+ ```
+ f k = cases
+ This a -> Some a
+ That b -> Some b
+ Both a b -> Some (a ++ b)
+ toList
+ (mergeWithKey
+ f
+ (toNonemptyMap ((1, "Circuit") +| [(2, "Quasar")]))
+ (toNonemptyMap ((2, "Voyage") +| [(3, "Harmony")])))
+ ```
+
+ Or something more complex, like this function that selects odd keys from
+ the first map and even keys from the second map:
+
+ ```
+ f k = cases
+ This a -> if isEven k then Some a else None
+ That b -> if Nat.isOdd k then Some b else None
+ Both a b -> if isEven k then Some a else Some b
+ toList
+ (mergeWithKey
+ f
+ (toNonemptyMap ((1, "Circuit") +| [(2, "Quasar")]))
+ (toNonemptyMap ((2, "Voyage") +| [(3, "Harmony")])))
+ ```
+
+ # See also
+
+ * {Nonempty.mergeWith} - a variant that allows the function to remove keys
+ from the result map.
+ }}
+
+data.Map.Nonempty.nth : Nat -> Map.Nonempty k v -> Optional (k, v)
+data.Map.Nonempty.nth n = cases
+ Map.Nonempty.Bin sz k v l r ->
+ use Map nth
+ use Nat + -
+ sizel = Map.size l
+ match Universal.ordering sizel n with
+ Greater -> nth n l
+ Equal -> Some (k, v)
+ Less -> nth (n - sizel + 1) r
+
+data.Map.Nonempty.nth.doc : Doc
+data.Map.Nonempty.nth.doc =
+ use Map.Nonempty nth
+ {{
+ {{ docExample 2 do i m -> nth i m }} returns the key-value pair in `m` with
+ the `i`-th smallest key, where `i`=0 is the smallest key (according to
+ {Universal.ordering}).
+
+ # Example
+
+ ```
+ nth 0 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+
+ ```
+ nth 1 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+
+ ```
+ nth 2 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+test> data.Map.Nonempty.nth.tests =
+ test.verify do
+ use Random natIn
+ Each.repeat 100
+ s =
+ (natIn 0 20, natIn 0 10)
+ +| (List.replicate (natIn 0 19) do (natIn 0 20, natIn 0 10))
+ |> toNonemptyMap
+ ensure
+ (List.somes
+ (List.map
+ (i -> Map.Nonempty.nth i s) (List.range 0 (Map.Nonempty.size s)))
+ === Map.Nonempty.toList s)
+
+data.Map.Nonempty.putGetWithKey :
+ (k ->{e} v ->{e} v ->{e} v)
+ -> k
+ -> v
+ -> Map.Nonempty k v
+ ->{e} (Optional v, Map.Nonempty k v)
+data.Map.Nonempty.putGetWithKey f kx x = cases
+ Map.Nonempty.Bin sy ky y l r ->
+ match Universal.ordering kx ky with
+ Less ->
+ (found, l') = Map.putGetWithKey f kx x l
+ (found, Nonempty.internal.balanceL ky y l' r)
+ Greater ->
+ (found, r') = Map.putGetWithKey f kx x r
+ (found, Nonempty.internal.balanceR ky y l r')
+ Equal -> (Some y, Map.Nonempty.Bin sy kx (f kx x y) l r)
+
+data.Map.Nonempty.putGetWithKey.doc : Doc
+data.Map.Nonempty.putGetWithKey.doc =
+ use Nat +
+ use Nonempty putGetWithKey
+ {{
+ Inserts a key-value pair into a {type Map.Nonempty}, returning the old value
+ and the new map. If the key is already present in the map, the new and old
+ values are combined with the given function. The function is passed the key,
+ the new value, and the old value, in that order.
+
+ # Example
+
+ ```
+ putGetWithKey
+ (k old new -> k + old + new) 2 4 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+
+ ```
+ putGetWithKey
+ (k old new -> k + old + new) 3 5 (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.putWith :
+ (v ->{e} v ->{e} v) -> k -> v -> Map.Nonempty k v ->{e} Map.Nonempty k v
+data.Map.Nonempty.putWith f = Nonempty.putWithKey (_ x y -> f x y)
+
+data.Map.Nonempty.putWith.doc : Doc
+data.Map.Nonempty.putWith.doc =
+ use Map.Nonempty toList
+ use Nat +
+ use Nonempty putWith
+ {{
+ Inserts a key-value pair into a {type Map.Nonempty}, combining values with
+ the given function if the key is already present in the map.
+
+ # Example
+
+ ```
+ toList (putWith (+) 2 4 (toNonemptyMap ((1, 2) +| [(2, 3)])))
+ ```
+
+ ```
+ toList (putWith (+) 3 5 (toNonemptyMap ((1, 2) +| [(2, 3)])))
+ ```
+ }}
+
+data.Map.Nonempty.putWithKey :
+ (k ->{e} v ->{e} v ->{e} v)
+ -> k
+ -> v
+ -> Map.Nonempty k v
+ ->{e} Map.Nonempty k v
+data.Map.Nonempty.putWithKey f kx x = cases
+ Map.Nonempty.Bin sy ky y l r ->
+ match Universal.ordering kx ky with
+ Less -> Nonempty.internal.balanceL ky y (Map.putWithKey f kx x l) r
+ Greater -> Nonempty.internal.balanceR ky y l (Map.putWithKey f kx x r)
+ Equal -> Map.Nonempty.Bin sy kx (f kx x y) l r
+
+data.Map.Nonempty.putWithKey.doc : Doc
+data.Map.Nonempty.putWithKey.doc =
+ use Map.Nonempty toList
+ use Nat +
+ use Nonempty putWithKey
+ {{
+ Inserts a key-value pair into a {type Map.Nonempty}, combining values with
+ the given function if the key is already present in the map. The function is
+ passed the key, the new value, and the old value, in that order.
+
+ # Example
+
+ ```
+ toList
+ (putWithKey
+ (k v1 v2 -> k + v1 + v2) 2 4 (toNonemptyMap ((1, 2) +| [(2, 3)])))
+ ```
+
+ ```
+ toList
+ (putWithKey
+ (k v1 v2 -> k + v1 + v2) 3 5 (toNonemptyMap ((1, 2) +| [(2, 3)])))
+ ```
+ }}
+
+data.Map.Nonempty.randomChoice : Map.Nonempty k v ->{Random} (k, v)
+data.Map.Nonempty.randomChoice map =
+ randomIndex = Random.natIn 0 (Map.Nonempty.size map)
+ Map.Nonempty.nth randomIndex map
+ |> getOrBug "Map.Nonempty.randomChoice: index out of bounds"
+
+data.Map.Nonempty.randomChoice.doc : Doc
+data.Map.Nonempty.randomChoice.doc =
+ use Map.Nonempty randomChoice
+ use Nonempty Nonempty
+ {{
+ Picks a random key-value pair from the given {type Map.Nonempty}. Assumes
+ that the {type Map.Nonempty} is not empty, so an empty {type Map.Nonempty}
+ will raise an {type Exception}.
+
+ # Examples
+
+ ```
+ lcg 4096 do
+ randomChoice
+ (toNonemptyMap
+ (Nonempty (5, "five") [(4, "four"), (2, "two"), (1, "one")]))
+ ```
+
+ ```
+ lcg 2510 do
+ randomChoice
+ (toNonemptyMap
+ (Nonempty (5, "five") [(4, "four"), (2, "two"), (1, "one")]))
+ ```
+ }}
+
+test> data.Map.Nonempty.randomChoice.test = test.verify do
+ map = toNonemptyMap ((0, 0) +| [(1, 1), (2, 2), (3, 3), (4, 4)])
+ Each.repeat 1000
+ e = Map.Nonempty.randomChoice map
+ ensure (Map.Nonempty.contains (at1 e) map)
+
+data.Map.Nonempty.randomKey : Map.Nonempty k v ->{Random} k
+data.Map.Nonempty.randomKey map = Map.Nonempty.randomChoice map |> at1
+
+data.Map.Nonempty.randomKey.doc : Doc
+data.Map.Nonempty.randomKey.doc =
+ use Map.Nonempty randomKey
+ use Nonempty Nonempty
+ {{
+ Picks a random key from the given {type Map.Nonempty}.
+
+ # Examples
+
+ ```
+ lcg 4096 do
+ randomKey
+ (toNonemptyMap
+ (Nonempty
+ (6, "six") [(5, "five"), (4, "four"), (2, "two"), (1, "one")]))
+ ```
+
+ ```
+ lcg 2510 do
+ randomKey
+ (toNonemptyMap
+ (Nonempty
+ (6, "six") [(5, "five"), (4, "four"), (2, "two"), (1, "one")]))
+ ```
+ }}
+
+data.Map.Nonempty.randomValue : Map.Nonempty k v ->{Random} v
+data.Map.Nonempty.randomValue map = Map.Nonempty.randomChoice map |> at2
+
+data.Map.Nonempty.randomValue.doc : Doc
+data.Map.Nonempty.randomValue.doc =
+ use Map.Nonempty randomValue
+ use Nonempty Nonempty
+ {{
+ Picks a random value from the given {type Map.Nonempty}.
+
+ # Examples
+
+ ```
+ lcg 4096 do
+ randomValue
+ (toNonemptyMap
+ (Nonempty
+ (6, "six") [(5, "five"), (4, "four"), (2, "two"), (1, "one")]))
+ ```
+
+ ```
+ lcg 2510 do
+ randomValue
+ (toNonemptyMap
+ (Nonempty
+ (6, "six") [(5, "five"), (4, "four"), (2, "two"), (1, "one")]))
+ ```
+ }}
+
+data.Map.Nonempty.singleton : k -> v -> Map.Nonempty k v
+data.Map.Nonempty.singleton k x =
+ use internal Tip
+ Map.Nonempty.Bin 1 k x Tip Tip
+
+data.Map.Nonempty.singleton.doc : Doc
+data.Map.Nonempty.singleton.doc =
+ {{
+ Creates a singleton nonempty map containing a single key-value pair.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList (Map.Nonempty.singleton 1 2)
+ ```
+ }}
+
+data.Map.Nonempty.size : Map.Nonempty k v -> Nat
+data.Map.Nonempty.size = cases Map.Nonempty.Bin sz _ _ _ _ -> sz
+
+data.Map.Nonempty.size.doc : Doc
+data.Map.Nonempty.size.doc =
+ {{
+ Returns the number of key-value pairs in the map.
+
+ # Example
+
+ ```
+ Map.Nonempty.size (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.tests.nonemptyMapOf :
+ '{Gen} k -> '{Gen} v -> '{Gen} Map.Nonempty k v
+data.Map.Nonempty.tests.nonemptyMapOf k v =
+ do
+ Gen.sample
+ (dedupe
+ (toWeighted do toNonemptyMap (atLeastOneDistinct (pairOf k v) ())))
+
+data.Map.Nonempty.tests.nonemptyMapOf.doc : Doc
+data.Map.Nonempty.tests.nonemptyMapOf.doc =
+ {{
+ Generates nonempty maps using the given generators for keys and values.
+
+ # Example
+
+ ```
+ List.map
+ Map.Nonempty.toList
+ (deprecated.sample 10 (nonemptyMapOf gen.nat gen.boolean))
+ ```
+ }}
+
+data.Map.Nonempty.toList : Map.Nonempty k v -> [(k, v)]
+data.Map.Nonempty.toList =
+ use List +:
+ Nonempty.foldRightWithKey (k x xs -> (k, x) +: xs) []
+
+data.Map.Nonempty.toList.doc : Doc
+data.Map.Nonempty.toList.doc =
+ {{
+ Converts a {type Map.Nonempty} to a list of key-value pairs.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.toMap : Map.Nonempty k v -> Map k v
+data.Map.Nonempty.toMap = cases
+ Map.Nonempty.Bin _ k v l r -> Map.internal.link k v l r
+
+data.Map.Nonempty.toMap.doc : Doc
+data.Map.Nonempty.toMap.doc =
+ {{
+ Converts a non-empty map to a {type Map}.
+
+ # Example
+
+ ```
+ Map.Nonempty.toMap (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.toNonemptyList : Map.Nonempty k v -> List.Nonempty (k, v)
+data.Map.Nonempty.toNonemptyList = cases
+ Map.Nonempty.Bin _ k v l r ->
+ prependList (Map.toList l) ((k, v) +| Map.toList r)
+
+data.Map.Nonempty.toNonemptyList.doc : Doc
+data.Map.Nonempty.toNonemptyList.doc =
+ {{
+ Converts a {type Map.Nonempty} to a nonempty list of key-value pairs.
+
+ # Example
+
+ ```
+ Nonempty.toNonemptyList (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.Nonempty.union :
+ Map.Nonempty k v -> Map.Nonempty k v -> Map.Nonempty k v
+data.Map.Nonempty.union = Map.Nonempty.unionWith const
+
+data.Map.Nonempty.union.doc : Doc
+data.Map.Nonempty.union.doc =
+ {{
+ Merges two {type Map.Nonempty}s, preferring the values from the first map
+ when there are duplicate keys.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList
+ (Map.Nonempty.union
+ (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ (toNonemptyMap ((2, 4) +| [(3, 5)])))
+ ```
+ }}
+
+data.Map.Nonempty.unions : List.Nonempty (Map.Nonempty k v) -> Map.Nonempty k v
+data.Map.Nonempty.unions maps = reduceRight Map.Nonempty.union maps
+
+data.Map.Nonempty.unions.doc : Doc
+data.Map.Nonempty.unions.doc =
+ {{
+ Merges a nonempty list of {type Map.Nonempty}s, preferring the values from
+ the first map when there are duplicate keys.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList
+ (Map.Nonempty.unions
+ (toNonemptyMap ((1, 2) +| [(2, 3)])
+ +| [toNonemptyMap ((2, 4) +| [(3, 5)])]))
+ ```
+ }}
+
+data.Map.Nonempty.unionWith :
+ (v ->{e} v ->{e} v)
+ -> Map.Nonempty k v
+ -> Map.Nonempty k v
+ ->{e} Map.Nonempty k v
+data.Map.Nonempty.unionWith f = Map.Nonempty.unionWithKey (const f)
+
+data.Map.Nonempty.unionWith.doc : Doc
+data.Map.Nonempty.unionWith.doc =
+ use Nat +
+ {{
+ Merges two {type Map.Nonempty}s, combining values with the given function
+ when there are duplicate keys.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList
+ (Map.Nonempty.unionWith
+ (+)
+ (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ (toNonemptyMap ((2, 4) +| [(3, 5)])))
+ ```
+ }}
+
+data.Map.Nonempty.unionWithKey :
+ (k ->{e} v ->{e} v ->{e} v)
+ -> Map.Nonempty k v
+ -> Map.Nonempty k v
+ ->{e} Map.Nonempty k v
+data.Map.Nonempty.unionWithKey f t1 t2 =
+ match (t1, t2) with
+ (_, Map.Nonempty.Bin _ k x internal.Tip internal.Tip) ->
+ Nonempty.internal.putWithKeyR f k x t1
+ (Map.Nonempty.Bin _ k x internal.Tip internal.Tip, _) ->
+ Nonempty.putWithKey f k x t2
+ (Map.Nonempty.Bin _ k1 x1 l1 r1, _) ->
+ (l2, mb, r2) = splitLookup k1 (Map.Nonempty.toMap t2)
+ use Map unionWithKey
+ use Nonempty.internal link
+ l1l2 = unionWithKey f l1 l2
+ r1r2 = unionWithKey f r1 r2
+ match mb with
+ None -> link k1 x1 l1l2 r1r2
+ Some x2 -> link k1 (f k1 x1 x2) l1l2 r1r2
+
+data.Map.Nonempty.unionWithKey.doc : Doc
+data.Map.Nonempty.unionWithKey.doc =
+ use Nat +
+ {{
+ Merges two {type Map.Nonempty}s, combining values with the given function
+ when there are duplicate keys. The function is passed the key, the value from
+ the first map, and the value from the second map, in that order.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList
+ (Map.Nonempty.unionWithKey
+ (k v1 v2 -> k + v1 + v2)
+ (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ (toNonemptyMap ((2, 4) +| [(3, 5)])))
+ ```
+ }}
+
+data.Map.Nonempty.update :
+ (v ->{e} Optional v) -> k -> Map.Nonempty k v ->{e} Map k v
+data.Map.Nonempty.update f k =
+ Map.updateWithKey (const f) k << Map.Nonempty.toMap
+
+data.Map.Nonempty.update.doc : Doc
+data.Map.Nonempty.update.doc =
+ use Nat + ==
+ {{
+ Updates a value at a key in a {type Map.Nonempty}, using the given function
+ to compute the new value. If the function returns {None}, the key is deleted
+ from the map.
+
+ # Example
+
+ ```
+ Map.toList
+ (Map.Nonempty.update
+ (x -> (if x == 2 then None else Some (x + 1)))
+ 2
+ (toNonemptyMap ((1, 2) +| [(2, 3)])))
+ ```
+ }}
+
+data.Map.Nonempty.upsert :
+ (Optional v ->{e} v) -> k -> Map.Nonempty k v ->{e} Map.Nonempty k v
+data.Map.Nonempty.upsert f k = cases
+ Map.Nonempty.Bin sz kx x l r ->
+ match Universal.ordering k kx with
+ Less -> Nonempty.internal.balanceL kx x (Map.upsert f k l) r
+ Greater -> Nonempty.internal.balanceR kx x l (Map.upsert f k r)
+ Equal -> Map.Nonempty.Bin sz kx (f (Some x)) l r
+
+data.Map.Nonempty.upsert.doc : Doc
+data.Map.Nonempty.upsert.doc =
+ use Map.Nonempty toList
+ use Nat +
+ use Nonempty upsert
+ {{
+ Updates or inserts a value under a key in a {type Map.Nonempty}. The
+ expression `` upsert f k m `` updates the value under `k` in the
+ {type Map.Nonempty} `m`, if present, by passing the existing value to the
+ function `f` (or passing {None} if it's not present). Either way, the value
+ under `k` becomes the return value of `f`.
+
+ # Examples
+
+ In this example, we increment the value under key `5`, since it's present:
+
+ ```
+ m = toNonemptyMap ((5, 10) +| [(3, 21)])
+ f = cases
+ None -> 0
+ Some x -> x + 1
+ toList (upsert f 5 m)
+ ```
+
+ In this example, we insert the value `0` under key `7`, since it's not
+ present:
+
+ ```
+ toNonemptyMap ((5, 10) +| [(3, 21)])
+ |> upsert (Optional.fold (do 0) Nat.increment) 7
+ |> toList
+ ```
+ }}
+
+data.Map.Nonempty.values : Map.Nonempty k v -> List.Nonempty v
+data.Map.Nonempty.values = cases
+ Map.Nonempty.Bin _ _ v l r ->
+ appendList
+ (prependList (Map.values l) (List.Nonempty.singleton v)) (Map.values r)
+
+data.Map.Nonempty.values.doc : Doc
+data.Map.Nonempty.values.doc =
+ {{
+ Converts a {type Map.Nonempty} to a nonempty list of the values in the map.
+
+ # Example
+
+ ```
+ Map.Nonempty.values (toNonemptyMap ((1, 2) +| [(2, 3)]))
+ ```
+ }}
+
+data.Map.nth : Nat -> Map k v -> Optional (k, v)
+data.Map.nth n = cases
+ internal.Tip -> None
+ internal.Bin sz k v l r ->
+ use Nat + -
+ use data.Map nth
+ sizel = Map.size l
+ match Universal.ordering sizel n with
+ Greater -> nth n l
+ Equal -> Some (k, v)
+ Less -> nth (n - sizel + 1) r
+
+data.Map.nth.doc : Doc
+data.Map.nth.doc =
+ use Map nth
+ {{
+ {{ docExample 2 do i m -> nth i m }} returns the key-value pair in `m` with
+ the `i`-th smallest key, where `i`=0 is the smallest key (according to
+ {Universal.ordering}).
+
+ Is the same as {{
+ docExample 2 do i as -> List.at i (sortBy at1 (Map.toList as)) }} but doesn't
+ require instantiating the intermediate {type List}.
+
+ ```
+ s =
+ Map.fromList [(6, "six"), (5, "five"), (4, "four"), (2, "two"), (1, "one")]
+ List.map (i -> nth i s) (List.range 0 (Map.size s))
+ ```
+ }}
+
+test> data.Map.nth.tests =
+ test.verify do
+ use Random natIn
+ Each.repeat 100
+ s =
+ (List.replicate (natIn 0 20) do (natIn 0 20, natIn 0 10)) |> Map.fromList
+ ensure
+ (List.somes (List.map (i -> Map.nth i s) (List.range 0 (Map.size s)))
+ === Map.toList s)
+
+data.Map.put.doc : Doc
+data.Map.put.doc =
+ {{
+ Puts a new key and value in the {type Map}.
+
+ The expression `` Map.put k v m `` puts the key `k` and associated value `v`
+ in the {type Map} `m`. If the key `k` is already present in the {type Map}
+ `m`, the old value is replaced with `v`.
+ }}
+
+data.Map.putGetWithKey :
+ (k ->{e} v ->{e} v ->{e} v) -> k -> v -> Map k v ->{e} (Optional v, Map k v)
+data.Map.putGetWithKey f kx x = cases
+ internal.Tip -> (None, Map.singleton kx x)
+ internal.Bin sy ky y l r ->
+ match Universal.ordering kx ky with
+ Less ->
+ (found, l') = data.Map.putGetWithKey f kx x l
+ (found, Map.internal.balanceL ky y l' r)
+ Greater ->
+ (found, r') = data.Map.putGetWithKey f kx x r
+ (found, Map.internal.balanceR ky y l r')
+ Equal -> (Some y, internal.Bin sy kx (f kx x y) l r)
+
+data.Map.putGetWithKey.doc : Doc
+data.Map.putGetWithKey.doc =
+ {{
+ Puts a new key and value in the {type Map} while also getting the old value
+ under that key, if any.
+
+ The expression `` Map.putGetWithKey f k v m `` is equivalent to
+ ``(Map.get k m, putWithKey f k v m)``. It returns a pair where the first
+ element is the value under the key `k` in the {type Map} `m` (or {None} if
+ `k` is not present) and the second element is the new {type Map} after
+ putting the value `v` under the key `k` using the function `f` to combine `v`
+ with the old value.
+ }}
+
+test> data.Map.putGetWithKey.tests.putsAndGets = runs 100 do
+ use gen boolean
+ m = tests.mapOf boolean boolean ()
+ k = boolean()
+ v = boolean()
+ f = logic()
+ g = logic()
+ p k n o = f k (g n o)
+ expect (Map.putGetWithKey p k v m === (Map.get k m, Map.putWithKey p k v m))
+
+data.Map.putWith : (v ->{e} v ->{e} v) -> k -> v -> Map k v ->{e} Map k v
+data.Map.putWith f = Map.putWithKey (_ x y -> f x y)
+
+data.Map.putWith.doc : Doc
+data.Map.putWith.doc =
+ use Map fromList putWith toList
+ use Text ++
+ {{
+ The expression `` putWith f k v m `` puts a new key `k` and associated value
+ `v` in the {type Map} `m`. If the key `k` is already present in the
+ {type Map}, the new value `v` is combined with the old value using the
+ function `f`. The function `f` will receive the new value `v` as its first
+ argument and the old value under `k` as the second argument.
+
+ # Examples
+
+ ```
+ m = fromList [(5, "a"), (3, "b")]
+ toList (putWith (++) 5 "x" m)
+ ```
+
+ ```
+ m = fromList [(5, "a"), (3, "b")]
+ toList (putWith (++) 7 "x" m)
+ ```
+
+ ```
+ toList (putWith (++) 5 "x" Map.empty)
+ ```
+ }}
+
+test> data.Map.putWith.tests.puts = runs 100 do
+ use Map get
+ use Nat +
+ m = tests.mapOf natInOrder natInOrder ()
+ k = natInOrder()
+ v = natInOrder()
+ added = Map.putWith (+) k v m
+ expect match get k m with
+ None -> get k added === Some v
+ Some x -> get k added === Some (x + v)
+
+data.Map.putWithKey :
+ (k ->{e} v ->{e} v ->{e} v) -> k -> v -> Map k v ->{e} Map k v
+data.Map.putWithKey f kx x = cases
+ internal.Tip -> Map.singleton kx x
+ internal.Bin sy ky y l r ->
+ match Universal.ordering kx ky with
+ Less -> Map.internal.balanceL ky y (data.Map.putWithKey f kx x l) r
+ Greater -> Map.internal.balanceR ky y l (data.Map.putWithKey f kx x r)
+ Equal -> internal.Bin sy kx (f kx x y) l r
+
+data.Map.putWithKey.doc : Doc
+data.Map.putWithKey.doc =
+ use Map putWith toList
+ use Text ++
+ {{
+ Adds a key and value to the {type Map}, combining the key, new value and old
+ value with a function.
+
+ The expression `` Map.putWithKey f k v m `` puts a new key `k` and associated
+ value `v` in the {type Map} `m`. If the key `k` is already present in `m`,
+ the new value `v` is combined with the key and old value using the function
+ `f`. The function `f` will receive the new value `v` as its second argument
+ and the old value under `k` as the third argument.
+
+ # Examples
+
+ ```
+ f new old = new ++ "|" ++ old
+ m = Map.fromList [(5, "a"), (3, "b")]
+ toList (putWith f 5 "x" m)
+ ```
+
+ ```
+ toList (putWith const 7 "x" Map.empty)
+ ```
+ }}
+
+data.Map.randomChoice : Map k v ->{Exception, Random} (k, v)
+data.Map.randomChoice map =
+ randomIndex = Random.natIn 0 (Map.size map)
+ Map.nth randomIndex map
+ |> Optional.toException "Map.randomChoice: empty map" (typeLink Map)
+
+data.Map.randomChoice.doc : Doc
+data.Map.randomChoice.doc =
+ use Map fromList randomChoice
+ {{
+ Picks a random key-value pair from the given {type Map}. Assumes that the
+ {type Map} is not empty, so an empty map will raise an {type Exception}.
+
+ # Examples
+
+ ```
+ catch do
+ lcg 4096 do randomChoice (fromList [(0, ?a), (3, ?b), (5, ?c), (7, ?d)])
+ ```
+
+ ```
+ catch do
+ lcg 2510 do randomChoice (fromList [(0, ?a), (3, ?b), (5, ?c), (7, ?d)])
+ ```
+ }}
+
+test> data.Map.randomChoice.test = test.verify do
+ map = Map.fromList [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
+ Each.repeat 1000
+ e = Map.randomChoice map
+ ensure (Map.contains (at1 e) map)
+
+data.Map.randomKey : Map k v ->{Exception, Random} k
+data.Map.randomKey map = Map.randomChoice map |> at1
+
+data.Map.randomKey.doc : Doc
+data.Map.randomKey.doc =
+ use Map fromList randomKey
+ {{
+ Picks a random key from the given {type Map}. Assumes that the {type Map} is
+ not empty, so an empty map will raise an {type Exception}.
+
+ # Examples
+
+ ```
+ catch do
+ lcg 4096 do randomKey (fromList [(0, ?a), (3, ?b), (5, ?c), (7, ?d)])
+ ```
+
+ ```
+ catch do
+ lcg 2510 do randomKey (fromList [(0, ?a), (3, ?b), (5, ?c), (7, ?d)])
+ ```
+ }}
+
+data.Map.randomValue : Map k v ->{Exception, Random} v
+data.Map.randomValue map = Map.randomChoice map |> at2
+
+data.Map.randomValue.doc : Doc
+data.Map.randomValue.doc =
+ use Map fromList randomValue
+ {{
+ Picks a random value from the given {type Map}. Assumes that the {type Map}
+ is not empty, so an empty map will raise an {type Exception}.
+
+ # Examples
+
+ ```
+ catch do
+ lcg 4096 do randomValue (fromList [(0, ?a), (3, ?b), (5, ?c), (7, ?d)])
+ ```
+
+ ```
+ catch do
+ lcg 2510 do randomValue (fromList [(0, ?a), (3, ?b), (5, ?c), (7, ?d)])
+ ```
+ }}
+
+data.Map.singleton : k -> v -> Map k v
+data.Map.singleton k x =
+ use internal Tip
+ internal.Bin 1 k x Tip Tip
+
+data.Map.singleton.doc : Doc
+data.Map.singleton.doc =
+ {{ Constructs a new {type Map} with a single key and value. }}
+
+test> data.Map.singleton.tests.roundtrip = runs 100 do
+ k = natInOrder()
+ v = natInOrder()
+ expect (Map.get k (Map.singleton k v) === Some v)
+
+data.Map.size : Map k v -> Nat
+data.Map.size = cases
+ internal.Tip -> 0
+ internal.Bin sz _ _ _ _ -> sz
+
+data.Map.size.doc : Doc
+data.Map.size.doc = {{ Gets the number of elements in the {type Map}. }}
+
+test> data.Map.size.tests.numberOfKeys = runs 100 do
+ m = tests.mapOf natInOrder natInOrder ()
+ expect (List.size (Map.keys m) === Map.size m)
+
+data.Map.split : k -> Map k a -> (Map k a, Map k a)
+data.Map.split k = cases
+ internal.Tip -> (internal.Tip, internal.Tip)
+ internal.Bin _ kx x l r ->
+ match Universal.ordering k kx with
+ Less ->
+ (lt, gt) = data.Map.split k l
+ (lt, Map.internal.link kx x gt r)
+ Greater ->
+ (lt, gt) = data.Map.split k r
+ (Map.internal.link kx x l lt, gt)
+ Equal -> (l, r)
+
+data.Map.split.doc : Doc
+data.Map.split.doc =
+ {{
+ Splits the {type Map} in two parts around a given key. The expression ``
+ Map.split k m `` is a pair `` (m1, m2) `` where all keys in `m1` are smaller
+ than `k` and all keys in `m2` are larger than `k`. Any key equal to `k` is
+ discarded.
+ }}
+
+test> data.Map.split.tests.splits = runs 100 do
+ use List ++ all
+ use Map keys
+ m = tests.mapOf natInOrder natInOrder ()
+ k = natInOrder()
+ let
+ (m1, m2) = Map.split k m
+ smaller = all (x -> Universal.lt x k) (keys m1)
+ larger = all (x -> Universal.gt x k) (keys m2)
+ equal = List.none (x -> x === k) (keys m1 ++ keys m2)
+ expect (smaller && larger && equal)
+
+data.Map.takeLargest : Nat -> Map k v -> Map k v
+data.Map.takeLargest n m =
+ use Map size
+ use Nat - == >=
+ if n == 0 then Map.empty
+ else
+ if n >= size m then m
+ else
+ match Map.nth (size m - n - 1) m with
+ None -> m
+ Some (k, _) -> at2 (Map.split k m)
+
+data.Map.takeLargest.doc : Doc
+data.Map.takeLargest.doc =
+ use Map fromList toList
+ {{
+ `` takeSmallest n m `` returns a submap of the largest `n` entries of `m`
+ (ordered by key). Runs in logarithmic time.
+
+ # Examples
+
+ ```
+ fromList [(2, 2), (3, 3), (1, 1), (4, 4)] |> takeLargest 2 |> toList
+ ```
+
+ ```
+ Map.empty |> takeLargest 2 |> toList
+ ```
+
+ ```
+ fromList [(2, 2), (3, 3), (1, 1), (4, 4)] |> takeLargest 100 |> toList
+ ```
+
+ **Also see:** {takeSmallest}
+ }}
+
+test> data.Map.takeLargest.tests =
+ test.verify do
+ use Each repeat
+ use Map size
+ use Nat +
+ use Random natIn
+ repeat 100
+ kvs = Each.toList do
+ repeat (natIn 0 25)
+ (natIn 0 20, natIn 0 20)
+ m = Map.fromList kvs
+ k = Each.range 0 (size m)
+ labeled "takeLargest 0" do ensureEqual (takeLargest 0 m) Map.empty
+ labeled "takeLargest (n = size)" do
+ ensureEqual
+ (Map.toList (takeLargest (size m + each [0, 1, 2]) m)) (Map.toList m)
+ labeled "takeLargest (consistent with toList impl)" do
+ use Map toList
+ mk = toList m
+ ensureEqual (toList (takeLargest k m)) (takeRight k mk)
+
+data.Map.takeSmallest : Nat -> Map k v -> Map k v
+data.Map.takeSmallest n m =
+ use Nat ==
+ if n == 0 then Map.empty
+ else
+ match Map.nth n m with
+ None -> m
+ Some (k, _) -> at1 (Map.split k m)
+
+data.Map.takeSmallest.doc : Doc
+data.Map.takeSmallest.doc =
+ use Map fromList toList
+ {{
+ `` takeSmallest n m `` returns a submap of the smallest `n` entries of `m`
+ (ordered by key). Runs in logarithmic time.
+
+ # Examples
+
+ ```
+ fromList [(2, 2), (3, 3), (1, 1), (4, 4)] |> takeSmallest 2 |> toList
+ ```
+
+ ```
+ Map.empty |> takeSmallest 2 |> toList
+ ```
+
+ ```
+ fromList [(2, 2), (3, 3), (1, 1), (4, 4)] |> takeSmallest 100 |> toList
+ ```
+
+ **Also see:** {takeLargest}
+ }}
+
+test> data.Map.takeSmallest.tests =
+ test.verify do
+ use Each repeat
+ use Map size
+ use Nat +
+ use Random natIn
+ repeat 100
+ kvs = Each.toList do
+ repeat (natIn 0 25)
+ (natIn 0 20, natIn 0 20)
+ m = Map.fromList kvs
+ k = Each.range 0 (size m)
+ labeled "takeSmallest 0" do ensureEqual (takeSmallest 0 m) Map.empty
+ labeled "takeSmallest (n = size)" do
+ ensureEqual
+ (Map.toList (takeSmallest (size m + each [0, 1, 2]) m)) (Map.toList m)
+ labeled "takeSmallest (consistent with toList impl)" do
+ use Map toList
+ mk = toList m
+ ensureEqual (toList (takeSmallest k m)) (List.take k mk)
+
+data.Map.tests.mapOf.doc : Doc
+data.Map.tests.mapOf.doc =
+ {{
+ Constructs a generator for {type Map} with keys of type `k` and values of
+ type `v`, given generators for `k` and `v`.
+ }}
+
+data.Map.toList : Map k v -> [(k, v)]
+data.Map.toList =
+ use List +:
+ Map.foldRightWithKey (k x xs -> (k, x) +: xs) []
+
+data.Map.toList.doc : Doc
+data.Map.toList.doc = {{ Gets the list of key-value pairs in the {type Map}. }}
+
+test> data.Map.toList.tests.roundtrip =
+ runs 100 do
+ kvs =
+ distinctBy
+ at1 (sortBy at1 (gen.listOf (pairOf natInOrder natInOrder) ()))
+ expect (Map.toList (Map.fromList kvs) === kvs)
+
+data.Map.union : Map k v -> Map k v -> Map k v
+data.Map.union = Map.unionWith const
+
+data.Map.union.doc : Doc
+data.Map.union.doc =
+ use Map fromList
+ {{
+ Left-biased union of two {type Map}s. Contains data from both, and uses
+ values from the first if there are duplicate keys.
+
+ # Example
+
+ ```
+ x = fromList [(5, "a"), (3, "b")]
+ y = fromList [(5, "A"), (7, "C")]
+ Map.toList (Map.union x y)
+ ```
+ }}
+
+test> data.Map.union.tests.associative =
+ runs 100 do laws.associative (tests.mapOf natInOrder Text.ascii) Map.union
+
+test> data.Map.union.tests.idempotent = runs 100 do
+ use Map == toList union
+ x = tests.mapOf Text.ascii natInOrder ()
+ b = union x x == x
+ if b then expect b else bug (toList (union x x), toList x)
+
+test> data.Map.union.tests.unit = runs 100 do
+ use Map == empty union
+ x = tests.mapOf Text.ascii natInOrder ()
+ expect (union x empty == x && x == union empty x)
+
+data.Map.unions : [Map k v] -> Map k v
+data.Map.unions maps = List.foldRight Map.union Map.empty maps
+
+data.Map.unions.doc : Doc
+data.Map.unions.doc =
+ use Map singleton toList unions
+ {{
+ Takes a list of maps and unions them together with {Map.union}. If there are
+ duplicate keys, the first value under that key wins.
+
+ # Examples
+
+ ```
+ toList (unions [Map.empty, singleton "a" 1, singleton "b" 2])
+ ```
+
+ ```
+ toList (unions [singleton "a" 1, singleton "a" 2])
+ ```
+ }}
+
+data.Map.unionWith : (v ->{e} v ->{e} v) -> Map k v -> Map k v ->{e} Map k v
+data.Map.unionWith f = Map.unionWithKey (const f)
+
+data.Map.unionWith.doc : Doc
+data.Map.unionWith.doc =
+ use Map fromList
+ use Text ++
+ {{
+ Union of two maps. Contains data from both maps, and uses the given function
+ to combine values if there are duplicate keys.
+
+ # Example
+
+ ```
+ x = fromList [(5, "a"), (3, "b")]
+ y = fromList [(5, "A"), (7, "C")]
+ Map.toList (Map.unionWith (++) x y)
+ ```
+ }}
+
+test> data.Map.unionWith.tests.commutative = runs 100 do
+ use Map == unionWith
+ use Nat +
+ gen = tests.mapOf natInOrder natInOrder
+ x = gen()
+ y = gen()
+ expect (unionWith (+) x y == unionWith (+) y x)
+
+test> data.Map.unionWith.tests.idempotent = runs 100 do
+ use Map ==
+ use Nat * +
+ x = tests.mapOf Text.ascii natInOrder ()
+ expect (Map.unionWith (+) x x == Map.map (v -> v * 2) x)
+
+test> data.Map.unionWith.tests.unit = runs 100 do
+ use Map ==
+ use Nat +
+ x = tests.mapOf Text.ascii natInOrder ()
+ expect (Map.unionWith (+) x Map.empty == x)
+
+data.Map.unionWithKey :
+ (k ->{e} v ->{e} v ->{e} v) -> Map k v -> Map k v ->{e} Map k v
+data.Map.unionWithKey f t1 t2 =
+ match (t1, t2) with
+ (_, internal.Tip) -> t1
+ (_, internal.Bin _ k x internal.Tip internal.Tip) ->
+ Map.internal.putWithKeyR f k x t1
+ (internal.Bin _ k x internal.Tip internal.Tip, _) ->
+ Map.putWithKey f k x t2
+ (internal.Tip, _) -> t2
+ (internal.Bin _ k1 x1 l1 r1, _) ->
+ (l2, mb, r2) = splitLookup k1 t2
+ use Map.internal link
+ use data.Map unionWithKey
+ l1l2 = unionWithKey f l1 l2
+ r1r2 = unionWithKey f r1 r2
+ match mb with
+ None -> link k1 x1 l1l2 r1r2
+ Some x2 -> link k1 (f k1 x1 x2) l1l2 r1r2
+
+data.Map.unionWithKey.doc : Doc
+data.Map.unionWithKey.doc =
+ use Map fromList
+ use Text ++
+ {{
+ Union of two maps. Contains data from both maps, and uses the given function
+ to combine values, and the key, for any duplicate key.
+
+ # Example
+
+ ```
+ x = fromList [(5, "a"), (3, "b")]
+ y = fromList [(5, "A"), (7, "C")]
+ f key left right = Nat.toText key ++ ":" ++ left ++ "|" ++ right
+ Map.toList (Map.unionWithKey f x y)
+ ```
+ }}
+
+test> data.Map.unionWithKey.tests.commutative = runs 100 do
+ use Map == unionWithKey
+ use Nat +
+ gen = tests.mapOf natInOrder natInOrder
+ x = gen()
+ y = gen()
+ f k a b = k + a + b
+ expect (unionWithKey f x y == unionWithKey f y x)
+
+test> data.Map.unionWithKey.tests.idempotent =
+ runs 100 do
+ use Map ==
+ use Nat +
+ x = tests.mapOf natInOrder natInOrder ()
+ expect
+ (Map.unionWithKey (k a b -> k + a + b) x x
+ == Map.mapWithKey (k v -> k + v + v) x)
+
+test> data.Map.unionWithKey.tests.unit = runs 100 do
+ use Map ==
+ use Nat +
+ x = tests.mapOf natInOrder natInOrder ()
+ expect (Map.unionWithKey (k a b -> k + a + b) x Map.empty == x)
+
+data.Map.update : (v ->{e} Optional v) -> k -> Map k v ->{e} Map k v
+data.Map.update f = Map.updateWithKey (const f)
+
+data.Map.update.doc : Doc
+data.Map.update.doc =
+ use Map fromList toList update
+ use Nat + ==
+ {{
+ Updates or deletes a value under a key in a {type Map.} The expression ``
+ update f k m `` updates the value under `k` in the {type Map} `m`, if
+ present, by passing the existing value to the function `f`. If `f` returns
+ ``None``, the key `k` is deleted from the {type Map}. If `f` returns
+ ``Some x``, the value under `k` becomes `x`.
+
+ # Examples
+
+ ```
+ m = fromList [(5, 10), (3, 21)]
+ f x = if Nat.mod x 2 == 0 then Some (x + 1) else None
+ toList (update f 5 m)
+ ```
+
+ ```
+ toList (update (x -> Some x) 7 Map.empty)
+ ```
+
+ ```
+ m = fromList [(5, 10), (3, 21)]
+ toList (update (const None) 3 m)
+ ```
+ }}
+
+test> data.Map.update.tests.updates = runs 100 do
+ use Map get
+ use Nat +
+ m = tests.mapOf natInOrder natInOrder ()
+ k = natInOrder()
+ b = gen.boolean()
+ h v = if b then Some (k + v) else None
+ updated = get k (Map.update h k m)
+ expect match get k m with
+ None -> updated === None
+ Some x -> Boolean.not b && updated === None || updated === h x
+
+data.Map.updateWithKey :
+ (k ->{e} v ->{e} Optional v) -> k -> Map k v ->{e} Map k v
+data.Map.updateWithKey f k = cases
+ internal.Tip -> internal.Tip
+ internal.Bin sx kx x l r ->
+ match Universal.ordering k kx with
+ Less -> Map.internal.balanceR kx x (data.Map.updateWithKey f k l) r
+ Greater -> Map.internal.balanceL kx x l (data.Map.updateWithKey f k r)
+ Equal ->
+ match f kx x with
+ Some x' -> internal.Bin sx kx x' l r
+ None -> glue l r
+
+data.Map.updateWithKey.doc : Doc
+data.Map.updateWithKey.doc =
+ use Map fromList toList updateWithKey
+ use Nat + ==
+ {{
+ Updates or deletes a value under a key in a {type Map}. The expression ``
+ updateWithKey f k m `` updates the value under `k` in the {type Map} `m`, if
+ present, by passing both the key `k` and its value to the function `f`. If
+ `f` returns {None}, the key `k` is deleted from the {type Map}. If `f`
+ returns ``Some x``, the value under `k` becomes `x`.
+
+ # Example
+
+ ```
+ m = fromList [(5, 10), (3, 21)]
+ f k x = if Nat.mod x 2 == 0 then Some (x + k) else None
+ toList (updateWithKey f 5 m)
+ ```
+
+ ```
+ toList (updateWithKey (k v -> Some k) 7 Map.empty)
+ ```
+
+ ```
+ m = fromList [(5, 10), (3, 21)]
+ toList (updateWithKey (k v -> None) 3 m)
+ ```
+ }}
+
+test> data.Map.updateWithKey.tests.updates = runs 100 do
+ use Map get
+ use gen boolean
+ m = tests.mapOf boolean boolean ()
+ k = boolean()
+ f = logic()
+ b = boolean()
+ h k v = if b then Some (f k v) else None
+ updated = get k (Map.updateWithKey h k m)
+ expect match get k m with
+ None -> updated === None
+ Some x -> Boolean.not b && updated === None || updated === h k x
+
+data.Map.upsert : (Optional a ->{g} a) -> k -> Map k a ->{g} Map k a
+data.Map.upsert f = Map.alter (f >> Some)
+
+data.Map.upsert.doc : Doc
+data.Map.upsert.doc =
+ use Map fromList toList upsert
+ use Nat +
+ {{
+ Updates or inserts a value under a key in a {type Map}. The expression ``
+ upsert f k m `` updates the value under `k` in the {type Map} `m`, if
+ present, by passing the existing value to the function `f` (or passing {None}
+ if it's not present). Either way, the value under `k` becomes the return
+ value of `f`.
+
+ # Examples
+
+ In this example, we increment the value under key `5`, since it's present:
+
+ ```
+ m = fromList [(5, 10), (3, 21)]
+ f = cases
+ None -> 0
+ Some x -> x + 1
+ toList (upsert f 5 m)
+ ```
+
+ In this example, we insert the value `0` under key `7`, since it's not
+ present:
+
+ ```
+ fromList [(5, 10), (3, 21)]
+ |> upsert (Optional.fold (do 0) Nat.increment) 7
+ |> toList
+ ```
+ }}
+
+data.Map.values : Map k v -> [v]
+data.Map.values =
+ use List +:
+ Map.foldRightWithKey (_ v vs -> v +: vs) []
+
+data.Map.values.doc : Doc
+data.Map.values.doc =
+ {{ Gets all the values in a {type Map}, as a {type List}. }}
+
+test> data.Map.values.tests.roundtrip =
+ runs 100 do
+ kvs =
+ distinctBy
+ at1 (sortBy at1 (gen.listOf (pairOf natInOrder natInOrder) ()))
+ m = Map.fromList kvs
+ expect (Map.values m === List.map at2 kvs)
+
+data.Multimap.insert : k -> v -> Map k [v] -> Map k [v]
+data.Multimap.insert k v m = match Map.get k m with
+ None -> Map.insert k [v] m
+ Some vs -> Map.insert k (vs List.:+ v) m
+
+data.Multimap.insert.doc : Doc
+data.Multimap.insert.doc =
+ use Map empty toList
+ use Multimap insert
+ {{
+ Inserts a key-value pair into the given {type Map} and returns the resulting
+ {type Map}. If the key already exists in the {type Map}, the value is added
+ to the existing {type List} of values.
+
+ # Examples
+
+ ```
+ toList (insert "a" 1 (insert "a" 2 (insert "b" 3 empty)))
+ ```
+
+ ```
+ toList (insert "a" 1 (insert "b" 2 empty))
+ ```
+ }}
+
+data.Multimap.lookup : k -> Map k [elem] -> [elem]
+data.Multimap.lookup k m = Optional.getOrElse [] (Map.get k m)
+
+data.Multimap.lookup.doc : Doc
+data.Multimap.lookup.doc =
+ use Map empty
+ use Multimap insert lookup
+ {{
+ Looks up the given key in the given {type Map} and returns the corresponding
+ {type List} of values, or the empty {type List} if the key is not present in
+ the {type Map}.
+
+ # Examples
+
+ ```
+ lookup "a" (insert "a" 1 (insert "a" 2 (insert "b" 3 empty)))
+ ```
+
+ ```
+ lookup "a" (insert "b" 2 empty)
+ ```
+ }}
+
+data.NatBag.add : Nat -> NatBag -> NatBag
+data.NatBag.add n b = toNatBag (add.nonempty n b)
+
+data.NatBag.add.doc : Doc
+data.NatBag.add.doc =
+ {{
+ Adds a single {type Nat} to a {type NatBag}. Constructs a new {type NatBag}
+ with the element added. If the element already exists in the {type NatBag},
+ the number of times it appears in the new {type NatBag} will be one more than
+ the number of times it appears in the original {type NatBag}.
+
+ # Example
+
+ ```
+ NatBag.toList (NatBag.add 2 (NatBag.fromList [1, 2, 3]))
+ ```
+
+ # See also
+
+ * {add.nonempty} for a version of this that returns a
+ {type NatBag.Nonempty}
+ * {NatBag.addMany} to add a {type List} of {type Nat}s to a {type NatBag}.
+ * {NatBag.addAll} to add two {type NatBag}s together.
+ }}
+
+data.NatBag.add.nonempty : Nat -> NatBag -> NatBag.Nonempty
+data.NatBag.add.nonempty n = cases
+ NatBag m -> NatBag.Nonempty (NatMap.insertWith (Nat.+) n 1 m)
+
+data.NatBag.add.nonempty.doc : Doc
+data.NatBag.add.nonempty.doc =
+ {{
+ Adds a single {type Nat} to a {type NatBag}. Constructs a new
+ {type NatBag.Nonempty} with the element added. If the element already exists
+ in the {type NatBag}, the number of times it appears in the new
+ {type NatBag.Nonempty} will be one more than the number of times it appears
+ in the original {type NatBag}.
+
+ # Example
+
+ ```
+ NatBag.Nonempty.toList (add.nonempty 2 (NatBag.fromList [1, 2, 3]))
+ ```
+
+ # See also
+
+ * {NatBag.addMany} to add a {type List} of {type Nat}s to a {type NatBag}.
+ * {NatBag.addAll} to add two {type NatBag}s together.
+ }}
+
+test> data.NatBag.add.nonempty.test =
+ test.verify do
+ use List +:
+ use Random natIn
+ _ = Each.range 0 100
+ x = natIn 0 1000
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ ensureEqual
+ (NatBag.toList (NatBag.add x (NatBag.fromList xs))) (List.sort (x +: xs))
+
+data.NatBag.addAll : NatBag -> NatBag -> NatBag
+data.NatBag.addAll = cases
+ NatBag m1, NatBag m2 -> NatBag (NatMap.unionWith (Nat.+) m1 m2)
+
+data.NatBag.addAll.doc : Doc
+data.NatBag.addAll.doc =
+ use NatBag fromList
+ {{
+ Adds two {type NatBag}s together. Constructs a new {type NatBag} with the
+ elements of both {type NatBag}s. The number of times an element appears in
+ the new {type NatBag} is the sum of the number of times it appears in the two
+ {type NatBag}s.
+
+ # Example
+
+ ```
+ NatBag.toList
+ (NatBag.addAll (fromList [1, 2, 2, 3]) (fromList [2, 3, 3, 4]))
+ ```
+
+ # See also
+
+ * {add.nonempty} to add a single {type Nat} to a {type NatBag}.
+ * {NatBag.addMany} to add a {type List} of {type Nat}s to a {type NatBag}.
+ * {NatBag.difference} to subtract one {type NatBag} from another.
+ * {NatBag.intersect} to find the intersection of two {type NatBag}s.
+ * {NatBag.union} to find the union of two {type NatBag}s.
+ }}
+
+test> data.NatBag.addAll.test =
+ test.verify do
+ use List ++
+ use NatBag fromList
+ use Random listOf natIn
+ _ = Each.range 0 100
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ ensureEqual
+ (NatBag.toList (NatBag.addAll (fromList xs) (fromList ys)))
+ (Heap.sort (xs ++ ys))
+
+data.NatBag.addMany : NatBag -> [Nat] -> NatBag
+data.NatBag.addMany = cases
+ NatBag m, nats ->
+ NatBag
+ (List.foldLeft
+ (nm n -> toNatMap (NatMap.insertWith (Nat.+) n 1 nm)) m nats)
+
+data.NatBag.addMany.doc : Doc
+data.NatBag.addMany.doc =
+ {{
+ Adds a {type List} of {type Nat}s to a {type NatBag}. Constructs a new
+ {type NatBag} with the elements added. If an element already exists in the
+ {type NatBag}, the number of times it appears in the new {type NatBag} will
+ be incremented by the number of times it appears in the {type List}.
+
+ # Example
+
+ ```
+ NatBag.toList (NatBag.addMany (NatBag.fromList [1, 2, 3]) [2, 3, 4])
+ ```
+
+ # See also
+
+ * {add.nonempty} to add a single {type Nat} to a {type NatBag}.
+ * {NatBag.addAll} to add two {type NatBag}s together.
+ }}
+
+test> data.NatBag.addMany.test =
+ test.verify do
+ use List ++
+ use Random listOf natIn
+ _ = Each.range 0 100
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ ns = listOf (do natIn 0 1000) do natIn 0 100
+ ensureEqual
+ (NatBag.toList (NatBag.addMany (NatBag.fromList xs) ns))
+ (List.sort (ns ++ xs))
+
+data.NatBag.addN : Nat -> Nat -> NatBag -> NatBag
+data.NatBag.addN = cases
+ n, elem, b ->
+ NatBag.addAll (NatBag.scale n (toNatBag (NatBag.singleton elem))) b
+
+data.NatBag.addN.doc : Doc
+data.NatBag.addN.doc =
+ use NatBag addN fromList
+ {{
+ Adds a given number of occurrences of a {type Nat} to a {type NatBag}.
+
+ # Examples
+
+ ```
+ addN 2 3 (fromList [2, 3, 4])
+ ```
+
+ ```
+ addN 2 3 (fromList [1, 4])
+ ```
+
+ # See also
+
+ * {add.nonempty} to add a single occurrence of a {type Nat} to a
+ {type NatBag}.
+ * {NatBag.addAll} to add all a whole bag to another.
+ }}
+
+test> data.NatBag.addN.test = test.verify do
+ use Nat + ==
+ use NatBag count
+ use Random natIn
+ _ = Each.range 0 100
+ x = natIn 0 1000
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ n = natIn 0 100
+ b = NatBag.fromList xs
+ ensure (count x (NatBag.addN n x b) == count x b + n)
+
+data.NatBag.all : (Nat ->{g} Boolean) ->{g} NatBag ->{g} Boolean
+data.NatBag.all p = NatSet.all p << NatBag.toNatSet
+
+data.NatBag.all.doc : Doc
+data.NatBag.all.doc =
+ use Nat isEven
+ use NatBag all fromList
+ {{
+ Determines whether all {type Nat}s in a {type NatBag} satisfy a predicate.
+
+ # Examples
+
+ ```
+ all isEven (fromList [1, 2, 3, 4])
+ ```
+
+ ```
+ all isEven (fromList [2, 4, 6])
+ ```
+
+ # See also
+
+ * {NatBag.any} to determine whether any {type Nat} in a {type NatBag}
+ satisfies a predicate.
+ }}
+
+test> data.NatBag.all.test : [Result]
+data.NatBag.all.test = test.verify do
+ use Nat isEven
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ b = NatBag.fromList xs
+ ensure (iff (NatBag.all isEven b) (List.all isEven xs))
+
+data.NatBag.any : (Nat ->{g} Boolean) ->{g} NatBag ->{g} Boolean
+data.NatBag.any p = NatSet.any p << NatBag.toNatSet
+
+data.NatBag.any.doc : Doc
+data.NatBag.any.doc =
+ use Nat isEven
+ use NatBag any fromList
+ {{
+ Determines whether any {type Nat} in a {type NatBag} satisfies a predicate.
+
+ # Examples
+
+ ```
+ any isEven (fromList [1, 2, 3, 4])
+ ```
+
+ ```
+ any isEven (fromList [1, 3, 5])
+ ```
+
+ # See also
+
+ * {NatBag.all} to determine whether all {type Nat}s in a {type NatBag}
+ satisfy a predicate.
+ }}
+
+test> data.NatBag.any.test : [Result]
+data.NatBag.any.test = test.verify do
+ use Nat isEven
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ b = NatBag.fromList xs
+ ensure (iff (NatBag.any isEven b) (List.any isEven xs))
+
+data.NatBag.contains : Nat -> NatBag -> Boolean
+data.NatBag.contains n b =
+ use Nat >
+ NatBag.count n b > 0
+
+data.NatBag.contains.doc : Doc
+data.NatBag.contains.doc =
+ use NatBag contains fromList
+ {{
+ Determines whether a {type NatBag} contains a given {type Nat}.
+
+ # Examples
+
+ ```
+ contains 1 (fromList [1, 2, 3, 4])
+ ```
+
+ ```
+ contains 5 (fromList [1, 2, 3, 4])
+ ```
+
+ # See also
+
+ * {NatBag.count} to count the number of occurrences of a given {type Nat}
+ in a {type NatBag}.
+ }}
+
+test> data.NatBag.contains.test : [Result]
+data.NatBag.contains.test = test.verify do
+ use Each range
+ use Random natIn
+ ignore (range 0 100)
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ b = NatBag.fromList xs
+ ignore (range 0 1000)
+ n = natIn 0 1000
+ ensure (iff (NatBag.contains n b) (List.contains n xs))
+
+data.NatBag.convolve :
+ (Nat ->{e} Nat ->{e} Nat) -> NatBag -> NatBag ->{e} NatBag
+data.NatBag.convolve f b1 b2 =
+ g k1 v1 m = NatBag.addAll m (NatBag.scale v1 (NatBag.map (k2 -> f k1 k2) b2))
+ NatMap.foldWithKey g NatBag.empty (NatBag.counts b1)
+
+data.NatBag.convolve.doc : Doc
+data.NatBag.convolve.doc =
+ use Nat * +
+ use NatBag convolve flatMap fromList map toList
+ {{
+ `` convolve f xs ys `` applies the function `f` to every pair of elements `x`
+ and `y`, where `x` is an element of `xs` and `y` is an element of `ys`. This
+ is called the **convolution** of `xs` and `ys` with the function `f`.
+
+ # Examples
+
+ ```
+ toList (convolve (*) (fromList [1, 2, 3, 4]) (fromList [1, 2, 3, 4]))
+ ```
+
+ ```
+ toList (convolve (+) (fromList [1, 2, 3, 4]) (fromList [1, 2, 3, 4]))
+ ```
+
+ # See also
+
+ {flatMap} is a generalization of {convolve}. For example, the following two
+ expressions are equivalent:
+
+ ```
+ toList (convolve (*) (fromList [1, 2, 3, 4]) (fromList [1, 2, 3, 4]))
+ ```
+
+ ```
+ toList
+ (flatMap
+ (x -> map (y -> x * y) (fromList [1, 2, 3, 4])) (fromList [1, 2, 3, 4]))
+ ```
+
+ {map} is a special case of {convolve}:
+
+ ```
+ toList (convolve (x _ -> x + 1) (fromList [1, 2, 3, 4]) (fromList [0]))
+ ```
+
+ ```
+ toList (map (x -> x + 1) (fromList [1, 2, 3, 4]))
+ ```
+ }}
+
+data.NatBag.count : Nat -> NatBag -> Nat
+data.NatBag.count = cases n, NatBag m -> NatMap.getOrElse n 0 m
+
+data.NatBag.count.doc : Doc
+data.NatBag.count.doc =
+ use NatBag count fromList
+ {{
+ Returns the number of occurrences of a {type Nat} in a {type NatBag}.
+
+ # Examples
+
+ ```
+ count 3 (fromList [1, 2, 3, 3, 4])
+ ```
+
+ ```
+ count 3 (fromList [1, 2, 4])
+ ```
+ }}
+
+test> data.NatBag.count.test : [Result]
+data.NatBag.count.test = test.verify do
+ use Nat ==
+ use Random natIn
+ _ = Each.range 0 100
+ x = natIn 0 1000
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ b = NatBag.fromList xs
+ ensure (NatBag.count x b == List.size (List.filter (y -> y == x) xs))
+
+data.NatBag.counts : NatBag -> NatMap Nat
+data.NatBag.counts = cases NatBag m -> m
+
+data.NatBag.counts.doc : Doc
+data.NatBag.counts.doc =
+ use NatBag counts fromList
+ {{
+ Returns a {type NatMap} containing the number of occurrences of each
+ {type Nat} in a {type NatBag}.
+
+ # Examples
+
+ ```
+ counts (fromList [1, 2, 3, 4])
+ ```
+
+ ```
+ counts (fromList [1, 2, 3, 4, 1, 2, 1])
+ ```
+
+ # See also
+
+ * {NatBag.count} to count the number of occurrences of a given {type Nat}
+ in a {type NatBag}.
+ }}
+
+test> data.NatBag.counts.test : [Result]
+data.NatBag.counts.test = test.verify do
+ use Map ==
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ b = NatBag.fromList xs
+ ensure (NatMap.toMap (NatBag.counts b) == Bag.counts (Bag.fromList xs))
+
+data.NatBag.difference : NatBag -> NatBag -> NatBag
+data.NatBag.difference = cases
+ NatBag m1, NatBag m2 ->
+ NatBag
+ (NatMap.differenceWith
+ (x y -> Optional.filter (z -> z Nat.> 0) (Some (x Nat.- y))) m1 m2)
+
+data.NatBag.difference.doc : Doc
+data.NatBag.difference.doc =
+ use NatBag fromList
+ {{
+ Computes the difference between two {type NatBag}s. Constructs a new
+ {type NatBag} with the elements of the first argument, minus the elements of
+ the second argument. If an element appears `n` times in the first and `m`
+ times in the second, the element will appear `n - m` times in the result. If
+ the number of times an element appears in the second argument is greater than
+ the number of times it appears in the first, the element will not appear in
+ the result.
+
+ # Example
+
+ ```
+ NatBag.toList (NatBag.difference (fromList [1, 2, 3]) (fromList [2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatBag.union} to compute the union of two bags.
+ * {NatBag.intersect} to compute the intersection.
+ * {NatBag.remove} to remove a single element from a bag.
+ * {NatBag.removeAll} to remove all occurrences of an element.
+ * {NatBag.removeN} to remove a specific number of occurrences.
+ }}
+
+test> data.NatBag.difference.test =
+ test.verify do
+ use Nat ==
+ use NatBag fromList
+ use Random listOf natIn
+ _ = Each.range 0 100
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ zs = List.foldRight (y xs -> deleteFirst ((==) y) xs) xs ys
+ ensureEqual
+ (NatBag.toList (NatBag.difference (fromList xs) (fromList ys)))
+ (List.sort zs)
+
+data.NatBag.doc : Doc
+data.NatBag.doc =
+ use NatBag filterMap
+ {{
+ A multiset of {type Nat} values. This specialized type is much more efficient
+ than the generic {type Bag} type when the elements are of type {type Nat} or
+ can be encoded as {type Nat} values.
+
+ {{
+ docAside
+ {{
+ The implementation of {type NatBag} is based on a
+ [patricia tree](https://en.wikipedia.org/wiki/Radix_tree) and uses a
+ {type NatMap} for its representation
+ }} }}
+
+ # Constructing bags
+
+ The empty bag:
+
+ @signature{NatBag.empty}
+
+ A bag with a single value in it. Note that this returns
+ {type NatBag.Nonempty} rather than {type NatBag}:
+
+ @signature{NatBag.singleton}
+
+ Construct a bag from a list of values:
+
+ @signature{NatBag.fromList}
+
+ Construct a bag from a list of values and their multiplicities:
+
+ @signature{NatBag.fromOccurrenceList}
+
+ Construct a bag from a {type NatSet}:
+
+ @signature{NatBag.fromNatSet}a
+
+ Construct a {type NatBag} from a {type Bag}:
+
+ @signature{fromBag}
+
+ # Querying bags
+
+ Check if a bag is empty:
+
+ @signature{NatBag.isEmpty}
+
+ Check if a bag contains a value:
+
+ @signature{NatBag.contains}
+
+ Get the number of elements in a bag:
+
+ @signature{NatBag.size}
+
+ Get the number of times a value appears in a bag:
+
+ @signature{NatBag.count}
+
+ Get the counts of all values in a bag, as a {type NatMap}:
+
+ @signature{NatBag.counts}
+
+ Get the counts of all values in a bag, as a {type List}:
+
+ @signature{NatBag.occurrenceList}
+
+ Check if all values satisfy a predicate:
+
+ @signature{NatBag.all}
+
+ Check if any values satisfy a predicate:
+
+ @signature{NatBag.any}
+
+ Get the largest value in a bag:
+
+ @signature{NatBag.getMax}
+
+ Get the smallest value in a bag:
+
+ @signature{NatBag.getMin}
+
+ Partition a bag into two bags based on a predicate:
+
+ @signature{NatBag.partition}
+
+ # Adding elements
+
+ Add a value to a bag:
+
+ @signature{NatBag.add}
+
+ Add a value to a bag, returning a nonempty bag:
+
+ @signature{add.nonempty}
+
+ Add a whole bag to a bag:
+
+ @signature{NatBag.addAll}
+
+ Add a list of values to a bag:
+
+ @signature{NatBag.addMany}
+
+ Add N occurrences of a value to a bag:
+
+ @signature{NatBag.addN}
+
+ # Removing elements
+
+ Remove a value from a bag:
+
+ @signature{NatBag.remove}
+
+ Remove all occurrences of a value from a bag:
+
+ @signature{NatBag.removeAll}
+
+ Remove N occurrences of a value from a bag:
+
+ @signature{NatBag.removeN}
+
+ # Combining bags
+
+ Union of two bags:
+
+ @signature{NatBag.union}
+
+ Intersection of two bags:
+
+ @signature{NatBag.intersect}
+
+ Difference between two bags:
+
+ @signature{NatBag.difference}
+
+ # Transforming bags
+
+ Apply function to every element of a bag:
+
+ @signature{NatBag.map}
+
+ Apply a bag-valued function to every element of a bag, collecting the
+ results into one bag:
+
+ @signature{NatBag.flatMap}
+
+ Apply a partial function to every element of a bag, discarding results that
+ are {None}:
+
+ @signature{filterMap}
+
+ Apply function to every element of a bag, removing elements for which the
+ function returns {None}:
+
+ @signature{filterMap}
+
+ Apply a function to all pairs from two bags:
+
+ @signature{NatBag.convolve}
+
+ Remove elements that don't satisfy a predicate:
+
+ @signature{NatBag.filter}
+
+ Reduce a bag to a single value by reducing with a binary function:
+
+ @signatures{NatBag.foldLeft, NatBag.foldRight}
+
+ Scale the counts of all values in a bag:
+
+ @signature{NatBag.scale}
+
+ # Comparing bags
+
+ Check if all elements in one bag occur in another:
+
+ @signature{NatBag.from}
+
+ Check if two bags are exactly equal:
+
+ @signature{NatBag.equals}
+
+ Check if one bag is a subbag of another:
+
+ @signatures{NatBag.subbag, NatBag.superbag}
+
+ # Conversions to other types
+
+ Convert a bag to a list:
+
+ @signature{NatBag.toList}
+
+ Convert to a {type NatSet}:
+
+ @signature{NatBag.toNatSet}
+
+ Convert to a {type Bag}:
+
+ @signature{NatBag.toBag}
+
+ # See also
+
+ * {NatMap} for a {type NatMap} from {type Nat} to any type.
+ * {NatSet} for a {type NatSet} of {type Nat}s.
+ }}
+
+data.NatBag.empty : NatBag
+data.NatBag.empty = NatBag NatMap.empty
+
+data.NatBag.empty.doc : Doc
+data.NatBag.empty.doc =
+ {{
+ The empty {type NatBag}.
+
+ # Examples
+
+ ```
+ NatBag.toList NatBag.empty
+ ```
+
+ # See also
+
+ * {NatBag.isEmpty} to check if a {type NatBag} is empty.
+ }}
+
+data.NatBag.equals : NatBag -> NatBag -> Boolean
+data.NatBag.equals = cases
+ NatBag m1, NatBag m2 -> NatMap.equalBy (Nat.==) m1 m2
+
+data.NatBag.filter : (Nat ->{e} Boolean) -> NatBag ->{e} NatBag
+data.NatBag.filter p =
+ NatBag << NatMap.filterWithKey (k _ -> p k) << NatBag.counts
+
+data.NatBag.filter.doc : Doc
+data.NatBag.filter.doc =
+ {{
+ Filters a {type NatBag} by removing all {type Nat}s that do not satisfy the
+ predicate.
+
+ # Examples
+
+ ```
+ NatBag.toList
+ (NatBag.filter Nat.isEven (NatBag.fromList [1, 2, 3, 4, 1, 2, 1]))
+ ```
+
+ # See also
+
+ * {NatBag.partition} to partition a {type NatBag} into two {type NatBag}s.
+ * {NatBag.filterMap} to filter a {type NatBag} with a function that returns
+ {type Optional}.
+ }}
+
+data.NatBag.filterMap : (Nat ->{e} Optional Nat) -> NatBag ->{e} NatBag
+data.NatBag.filterMap f b =
+ g k v m = match f k with
+ Some k' -> NatBag.addN v k' m
+ None -> m
+ NatMap.foldWithKey g NatBag.empty (NatBag.counts b)
+
+data.NatBag.filterMap.doc : Doc
+data.NatBag.filterMap.doc =
+ use Nat *
+ {{
+ Replaces each {type Nat} in a {type NatBag} with the result of applying a
+ function to it. If the function returns {None}, the {type Nat} is removed
+ from the {type NatBag}. If the function returns {Some}, the {type Nat} is
+ replaced with the {type Nat} in the {Some}.
+
+ # Examples
+
+ ```
+ NatBag.toList
+ (NatBag.filterMap
+ (x -> (if Nat.isEven x then Some (x * 2) else None))
+ (NatBag.fromList [1, 2, 3, 4, 1, 2, 1]))
+ ```
+
+ # See also
+
+ * {NatBag.partition} to partition a {type NatBag} into two {type NatBag}s
+ based on a predicate.
+ * {NatBag.filter} to filter a {type NatBag} by removing all {type Nat}s
+ that do not satisfy a predicate.
+ }}
+
+data.NatBag.flatMap : (Nat ->{e} NatBag) -> NatBag ->{e} NatBag
+data.NatBag.flatMap f b =
+ g k v m = NatBag.addAll m (NatBag.scale v (f k))
+ NatMap.foldWithKey g NatBag.empty (NatBag.counts b)
+
+data.NatBag.flatMap.doc : Doc
+data.NatBag.flatMap.doc =
+ use NatBag flatMap fromList toList
+ {{
+ Maps each {type Nat} in a {type NatBag} to a {type NatBag} and combines the
+ results.
+
+ # Examples
+
+ ```
+ toList (flatMap (n -> fromList [n, n]) (fromList [1, 2, 3, 4]))
+ ```
+
+ ```
+ toList (flatMap (n -> fromList [n, n]) (fromList [1, 2, 3, 4, 1, 2, 1]))
+ ```
+
+ # See also
+
+ * {NatBag.map} to transform each {type Nat} in a {type NatBag} to a
+ different {type Nat}.
+ * {NatBag.scale} to scale the number of occurrences of each {type Nat} in a
+ {type NatBag}.
+ }}
+
+data.NatBag.foldLeft : (a ->{e} Nat ->{e} a) -> a -> NatBag ->{e} a
+data.NatBag.foldLeft f a b =
+ g k v a = f a k
+ NatMap.foldWithKey g a (NatBag.counts b)
+
+data.NatBag.foldLeft.doc : Doc
+data.NatBag.foldLeft.doc =
+ use Nat +
+ {{
+ Folds a {type NatBag} with a binary function associating to the left.
+
+ # Examples
+
+ ```
+ NatBag.foldLeft (+) 0 (NatBag.fromList [1, 2, 3, 4, 1, 2, 1])
+ ```
+
+ # See also
+
+ * {NatBag.foldRight} to fold a {type NatBag} from right to left.
+ }}
+
+data.NatBag.foldRight : (Nat ->{e} a ->{e} a) -> a -> NatBag ->{e} a
+data.NatBag.foldRight f a b =
+ g k v a = f k a
+ NatMap.foldWithKey g a (NatBag.counts b)
+
+data.NatBag.foldRight.doc : Doc
+data.NatBag.foldRight.doc =
+ use Nat +
+ {{
+ Folds a {type NatBag} with a binary function associating to the right.
+
+ # Examples
+
+ ```
+ NatBag.foldRight (+) 0 (NatBag.fromList [1, 2, 3, 4, 1, 2, 1])
+ ```
+
+ # See also
+
+ * {NatBag.foldLeft} to fold a {type NatBag} from left to right.
+ }}
+
+data.NatBag.from : NatBag -> NatBag -> Boolean
+data.NatBag.from b1 b2 =
+ use NatBag toNatSet
+ NatSet.subset (toNatSet b1) (toNatSet b2)
+
+data.NatBag.from.doc : Doc
+data.NatBag.from.doc =
+ use NatBag fromList
+ {{
+ Checks if all the {type Nat}s in the first {type NatBag} are also present in
+ the second {type NatBag}.
+
+ # Examples
+
+ ```
+ NatBag.from (fromList [1, 2, 3]) (fromList [1, 2, 3, 4, 1, 2, 1])
+ ```
+
+ # See also
+
+ * {NatBag.subbag} to check if all the {type Nat}s in the first bag occur at
+ least as many times in the second bag.
+ * {NatBag.superbag} to check if all the {type Nat}s in the second bag occur
+ at least as many times in the first bag.
+ }}
+
+data.NatBag.fromBag : Bag Nat -> NatBag
+data.NatBag.fromBag = cases MkBag m -> NatBag (NatMap.fromMap m)
+
+data.NatBag.fromBag.doc : Doc
+data.NatBag.fromBag.doc =
+ use Bag fromList
+ use NatBag toList
+ {{
+ Converts a {type Bag} of {type Nat}s to a {type NatBag}.
+
+ # Examples
+
+ ```
+ toList (fromBag (fromList [1, 2, 3, 4]))
+ ```
+
+ ```
+ toList (fromBag (fromList [1, 2, 3, 4, 1, 2, 1]))
+ ```
+
+ # See also
+
+ * {NatBag.toBag} to convert the other way.
+ }}
+
+data.NatBag.fromList : [Nat] -> NatBag
+data.NatBag.fromList = cases
+ [] -> NatBag NatMap.empty
+ xs -> List.foldRight (v m -> toNatBag (add.nonempty v m)) NatBag.empty xs
+
+data.NatBag.fromList.doc : Doc
+data.NatBag.fromList.doc =
+ use NatBag toList
+ {{
+ Converts a {type List} of {type Nat}s into a {type NatBag}. If an element
+ appears multiple times in the {type List}, it will appear multiple times in
+ the {type NatBag}.
+
+ # Example
+
+ ```
+ toList (NatBag.fromList [1, 2, 2, 3])
+ ```
+
+ # See also
+
+ * {toList} to convert a {type NatBag} to a {type List}.
+ * {NatBag.fromNatSet} to convert a {type NatSet} to a {type NatBag}.
+ }}
+
+test> data.NatBag.fromList.test = test.verify do
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ ensureEqual (NatBag.toList (NatBag.fromList xs)) (List.sort xs)
+
+data.NatBag.fromNatSet : NatSet -> NatBag
+data.NatBag.fromNatSet = cases
+ NatSet None -> NatBag NatMap.empty
+ NatSet (Some t) ->
+ NatBag
+ (toNatMap
+ (NatSet.Nonempty.foldMap
+ (NatMap.Nonempty.unionWith (Nat.+)) (flip NatMap.singleton 1) t))
+
+data.NatBag.fromNatSet.doc : Doc
+data.NatBag.fromNatSet.doc =
+ {{
+ Converts a {type NatSet} into a {type NatBag}.
+
+ # Example
+
+ ```
+ NatBag.toList (NatBag.fromNatSet (NatSet.fromList [1, 2, 2, 3]))
+ ```
+
+ # See also
+
+ * {NatBag.fromList} to convert a {type List} to a {type NatBag}.
+ * {NatBag.toNatSet} to convert a {type NatBag} to a {type NatSet}.
+ }}
+
+test> data.NatBag.fromNatSet.test =
+ test.verify do
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 0 100
+ ensureEqual
+ (NatBag.toList (NatBag.fromNatSet (NatSet.fromList xs)))
+ (List.sort (Set.toList (Set.fromList xs)))
+
+data.NatBag.fromOccurrenceList : [(Nat, Nat)] -> NatBag
+data.NatBag.fromOccurrenceList = NatBag << NatMap.fromList
+
+data.NatBag.fromOccurrenceList.doc : Doc
+data.NatBag.fromOccurrenceList.doc =
+ use NatBag toList
+ {{
+ Converts a list of {type Nat}s and their number of occurrences to a
+ {type NatBag}.
+
+ # Examples
+
+ ```
+ toList (NatBag.fromOccurrenceList [(1, 3), (2, 2), (3, 1), (4, 1)])
+ ```
+
+ # See also
+
+ * {toList} to convert a {type NatBag} to a list of {type Nat}s.
+ * {NatBag.occurrenceList} to convert a {type NatBag} to a list of
+ {type Nat}s and their number of occurrences.
+ }}
+
+data.NatBag.getMax : NatBag ->{Abort} Nat
+data.NatBag.getMax b = NatMap.maxKey (NatBag.counts b)
+
+data.NatBag.getMax.doc : Doc
+data.NatBag.getMax.doc =
+ {{
+ Returns the largest {type Nat} in a {type NatBag}, or calls {abort} if the
+ {type NatBag} is empty.
+
+ # Examples
+
+ ```
+ toOptional! do NatBag.getMax (NatBag.fromList [1, 2, 3, 4, 1, 2, 1])
+ ```
+
+ # See also
+
+ * {NatBag.getMin} to return the smallest {type Nat} in a {type NatBag}.
+ }}
+
+data.NatBag.getMin : NatBag ->{Abort} Nat
+data.NatBag.getMin b = NatMap.minKey (NatBag.counts b)
+
+data.NatBag.getMin.doc : Doc
+data.NatBag.getMin.doc =
+ {{
+ Returns the smallest {type Nat} in a {type NatBag}, or calls {abort} if the
+ {type NatBag} is empty.
+
+ # Examples
+
+ ```
+ toOptional! do NatBag.getMin (NatBag.fromList [1, 2, 3, 4, 1, 2, 1])
+ ```
+
+ # See also
+
+ * {NatBag.getMax} to return the largest {type Nat} in a {type NatBag}.
+ }}
+
+data.NatBag.intersect : NatBag -> NatBag -> NatBag
+data.NatBag.intersect = cases
+ NatBag m1, NatBag m2 -> NatBag (NatMap.intersectWith Nat.min m1 m2)
+
+data.NatBag.intersect.doc : Doc
+data.NatBag.intersect.doc =
+ use NatBag fromList
+ {{
+ Computes the intersection between two {type NatBag}s. Constructs a new
+ {type NatBag} with the elements that appear in both arguments. If an element
+ appears `n` times in the first and `m` times in the second, the element will
+ appear `` Nat.min n m `` times in the result.
+
+ # Example
+
+ ```
+ NatBag.toList (NatBag.intersect (fromList [1, 2, 3]) (fromList [2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatBag.union} to compute the union of two {type NatBag}s.
+ * {NatBag.difference} to compute the difference between two {type NatBag}s.
+ * {NatBag.remove} to remove a single {type Nat} from a {type NatBag}.
+ * {NatBag.removeN} to remove multiple occurrences of a {type Nat}.
+ * {NatBag.removeAll} to remove all occurrences of a {type Nat}.
+ }}
+
+test> data.NatBag.intersect.test =
+ verifyAndIgnore do
+ use Nat ==
+ use NatBag fromList
+ use Random listOf natIn
+ _ = Each.range 0 100
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ zs = List.foldRight (y xs -> deleteFirst ((==) y) xs) xs ys
+ ensureEqual
+ (NatBag.toList (NatBag.difference (fromList xs) (fromList ys)))
+ (List.sort zs)
+
+data.NatBag.isEmpty : NatBag -> Boolean
+data.NatBag.isEmpty = cases NatBag m -> NatMap.isEmpty m
+
+data.NatBag.isEmpty.doc : Doc
+data.NatBag.isEmpty.doc =
+ use NatBag empty isEmpty
+ {{
+ Checks if a {type NatBag} is empty.
+
+ # Examples
+
+ ```
+ isEmpty empty
+ ```
+
+ ```
+ isEmpty (NatBag.fromList [1, 2, 3, 4, 1, 2, 1])
+ ```
+
+ # See also
+
+ * {empty} to get the empty {type NatBag}.
+ * {NatBag.count} to get the number of occurrences of a {type Nat} in a
+ {type NatBag}.
+ }}
+
+data.NatBag.map : (Nat ->{e} Nat) -> NatBag ->{e} NatBag
+data.NatBag.map f b =
+ g k v m = NatBag.addN v (f k) m
+ NatMap.foldWithKey g NatBag.empty (NatBag.counts b)
+
+data.NatBag.map.doc : Doc
+data.NatBag.map.doc =
+ use Nat *
+ use NatBag fromList map toList
+ {{
+ Transforms each {type Nat} in a {type NatBag} to a different {type Nat} using
+ a given function.
+
+ # Examples
+
+ ```
+ toList (map (n -> n * 2) (fromList [1, 2, 3, 4]))
+ ```
+
+ ```
+ toList (map (n -> n * 2) (fromList [1, 2, 3, 4, 1, 2, 1]))
+ ```
+
+ # See also
+
+ * {NatBag.flatMap} to map each {type Nat} in a {type NatBag} to a
+ {type NatBag} and combine the results.
+ * {NatBag.scale} to scale the number of occurrences of each {type Nat} in a
+ {type NatBag}.
+ }}
+
+data.NatBag.Nonempty.add : Nat -> NatBag.Nonempty -> NatBag.Nonempty
+data.NatBag.Nonempty.add n = cases
+ NatBag.Nonempty m -> NatBag.Nonempty (Nonempty.insertWith (Nat.+) n 1 m)
+
+data.NatBag.Nonempty.add.doc : Doc
+data.NatBag.Nonempty.add.doc =
+ {{
+ Adds a single {type Nat} to a {type NatBag.Nonempty}. Constructs a new
+ {type NatBag.Nonempty} with the element added. If the element already exists
+ in the {type NatBag.Nonempty}, the number of times it appears in the new
+ {type NatBag.Nonempty} will be one more than the number of times it appears
+ in the original {type NatBag.Nonempty}.
+
+ # Example
+
+ ```
+ NatBag.Nonempty.toList
+ (Nonempty.add 2 (NatBag.Nonempty.fromList (1 +| [2, 3])))
+ ```
+
+ # See also
+
+ * {Nonempty.addMany} to add a {type List} of {type Nat}s to a
+ {type NatBag.Nonempty}.
+ * {Nonempty.addAll} to add two {type NatBag.Nonempty}s together.
+ }}
+
+test> data.NatBag.Nonempty.add.test =
+ test.verify do
+ use List :+
+ use Random natIn
+ _ = Each.range 0 100
+ x = natIn 0 1000
+ x' = natIn 0 1000
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 10
+ id
+ ensureEqual
+ (List.Nonempty.toList
+ (NatBag.Nonempty.toList
+ (Nonempty.add x (NatBag.Nonempty.fromList (x' +| xs)))))
+ (List.sort (xs :+ x' :+ x))
+
+data.NatBag.Nonempty.addAll :
+ NatBag.Nonempty -> NatBag.Nonempty -> NatBag.Nonempty
+data.NatBag.Nonempty.addAll = cases
+ NatBag.Nonempty m1, NatBag.Nonempty m2 ->
+ NatBag.Nonempty (NatMap.Nonempty.unionWith (Nat.+) m1 m2)
+
+data.NatBag.Nonempty.addAll.doc : Doc
+data.NatBag.Nonempty.addAll.doc =
+ use NatBag.Nonempty fromList
+ {{
+ Adds two {type NatBag.Nonempty}s together.
+
+ # Example
+
+ ```
+ NatBag.Nonempty.toList
+ (Nonempty.addAll (fromList (1 +| [2, 2, 3])) (fromList (2 +| [3, 3, 4])))
+ ```
+
+ # See also
+
+ * {Nonempty.add} to add a single {type Nat} to a {type NatBag.Nonempty}.
+ * {Nonempty.addMany} to add a {type List} of {type Nat}s to a
+ {type NatBag.Nonempty}.
+ * {NatBag.Nonempty.difference} to subtract one {type NatBag.Nonempty} from
+ another.
+ * {NatBag.Nonempty.intersect} to find the intersection of two
+ {type NatBag.Nonempty}s.
+ * {NatBag.Nonempty.union} to find the union of two {type NatBag.Nonempty}s.
+ }}
+
+test> data.NatBag.Nonempty.addAll.test =
+ test.verify do
+ use List ++ +:
+ use NatBag.Nonempty fromList
+ use Random listOf nat natIn
+ _ = Each.range 0 100
+ x = nat()
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ y = nat()
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ ensureEqual
+ (List.Nonempty.toList
+ (NatBag.Nonempty.toList
+ (Nonempty.addAll (fromList (x +| xs)) (fromList (y +| ys)))))
+ (List.sort (x +: xs ++ (y +: ys)))
+
+data.NatBag.Nonempty.addMany : NatBag.Nonempty -> [Nat] -> NatBag.Nonempty
+data.NatBag.Nonempty.addMany = cases
+ NatBag.Nonempty m, nats ->
+ NatBag.Nonempty
+ (List.foldLeft (flip (flip (Nonempty.insertWith (Nat.+)) 1)) m nats)
+
+data.NatBag.Nonempty.addMany.doc : Doc
+data.NatBag.Nonempty.addMany.doc =
+ {{
+ Adds a {type List} of {type Nat}s to a {type NatBag.Nonempty}. Constructs a
+ new {type NatBag.Nonempty} with the elements added. If an element already
+ exists in the {type NatBag.Nonempty}, the number of times it appears in the
+ new {type NatBag.Nonempty} will be incremented by the number of times it
+ appears in the {type List}.
+
+ # Example
+
+ ```
+ NatBag.Nonempty.toList
+ (Nonempty.addMany (NatBag.Nonempty.fromList (1 +| [2, 3])) [2, 3, 4])
+ ```
+
+ # See also
+
+ * {Nonempty.add} to add a single {type Nat} to a {type NatBag.Nonempty}.
+ * {Nonempty.addAll} to add two {type NatBag.Nonempty}s together.
+ }}
+
+test> data.NatBag.Nonempty.addMany.test =
+ test.verify do
+ use List ++ :+
+ use Random listOf natIn
+ _ = Each.range 0 100
+ x = Random.nat()
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ ns = listOf (do natIn 0 1000) do natIn 0 100
+ ensureEqual
+ (List.Nonempty.toList
+ (NatBag.Nonempty.toList
+ (Nonempty.addMany (NatBag.Nonempty.fromList (x +| xs)) ns)))
+ (List.sort (ns ++ xs :+ x))
+
+data.NatBag.Nonempty.addN : Nat -> Nat -> NatBag.Nonempty -> NatBag.Nonempty
+data.NatBag.Nonempty.addN = cases
+ 0, _, b -> b
+ n, elem, NatBag.Nonempty m ->
+ use Nat +
+ use NatBag Nonempty
+ m' = NatMap.Nonempty.adjust (v -> v + n) elem m
+ if NatMap.Nonempty.contains elem m' then Nonempty m'
+ else Nonempty (NatMap.Nonempty.insert elem n m')
+
+data.NatBag.Nonempty.addN.doc : Doc
+data.NatBag.Nonempty.addN.doc =
+ use NatBag.Nonempty fromList
+ use Nonempty addN
+ {{
+ Adds a given number of occurrences of a {type Nat} to a
+ {type NatBag.Nonempty}.
+
+ # Examples
+
+ ```
+ addN 2 3 (fromList (2 +| [3, 4]))
+ ```
+
+ ```
+ addN 2 3 (fromList (1 +| [4]))
+ ```
+
+ # See also
+
+ * {Nonempty.add} to add a single occurrence of a {type Nat} to a
+ {type NatBag.Nonempty}.
+ * {Nonempty.addAll} to add a whole bag to another.
+ }}
+
+test> data.NatBag.Nonempty.addN.test = test.verify do
+ use Nat + ==
+ use Nonempty count
+ use Random natIn
+ _ = Each.range 0 100
+ x = natIn 0 1000
+ y = natIn 0 1000
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ n = natIn 0 100
+ b = NatBag.Nonempty.fromList (x +| xs)
+ ensure (count y (Nonempty.addN n y b) == count y b + n)
+
+data.NatBag.Nonempty.count : Nat -> NatBag.Nonempty -> Nat
+data.NatBag.Nonempty.count = cases
+ n, NatBag.Nonempty m -> NatMap.Nonempty.getOrElse n 0 m
+
+data.NatBag.Nonempty.count.doc : Doc
+data.NatBag.Nonempty.count.doc =
+ use NatBag.Nonempty fromList
+ use Nonempty count
+ {{
+ Returns the number of occurrences of a {type Nat} in a
+ {type NatBag.Nonempty}.
+
+ # Examples
+
+ ```
+ count 3 (fromList (1 +| [2, 3, 3, 4]))
+ ```
+
+ ```
+ count 3 (fromList (1 +| [2, 4]))
+ ```
+ }}
+
+test> data.NatBag.Nonempty.count.test =
+ test.verify do
+ use List +:
+ use Nat ==
+ use Random natIn
+ _ = Each.range 0 100
+ x = natIn 0 1000
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ b = NatBag.Nonempty.fromList (x +| xs)
+ ensure
+ (Nonempty.count x b == List.size (List.filter (y -> y == x) (x +: xs)))
+
+data.NatBag.Nonempty.counts : NatBag.Nonempty -> NatMap.Nonempty Nat
+data.NatBag.Nonempty.counts = cases NatBag.Nonempty m -> m
+
+data.NatBag.Nonempty.counts.doc : Doc
+data.NatBag.Nonempty.counts.doc =
+ {{
+ Returns the {type NatMap} of counts in a {type NatBag.Nonempty}.
+
+ # Examples
+
+ ```
+ Nonempty.counts (NatBag.Nonempty.fromList (1 +| [2, 3, 4, 1, 2, 1]))
+ ```
+
+ # See also
+
+ * {NatBag.count} to return the count of a single {type Nat}.
+ * {Nonempty.occurrenceList} to return a list of all {type Nat}s in the bag,
+ together with their counts.
+ }}
+
+data.NatBag.Nonempty.difference : NatBag.Nonempty -> NatBag.Nonempty -> NatBag
+data.NatBag.Nonempty.difference b1 b2 =
+ NatBag.difference (toNatBag b1) (toNatBag b2)
+
+data.NatBag.Nonempty.difference.doc : Doc
+data.NatBag.Nonempty.difference.doc =
+ use NatBag.Nonempty fromList
+ {{
+ Computes the difference between two {type NatBag.Nonempty}s. Constructs a new
+ {type NatBag} with the elements of the first argument, minus the elements of
+ the second argument. If an element appears `n` times in the first and `m`
+ times in the second, the element will appear `n - m` times in the result. If
+ the number of times an element appears in the second argument is greater than
+ the number of times it appears in the first, the element will not appear in
+ the result.
+
+ # Example
+
+ ```
+ NatBag.toList
+ (NatBag.Nonempty.difference
+ (fromList (1 +| [2, 3])) (fromList (2 +| [3, 4])))
+ ```
+
+ # See also
+
+ * {NatBag.Nonempty.union} to compute the union of two bags.
+ * {NatBag.Nonempty.intersect} to compute the intersection.
+ * {Nonempty.remove} to remove a single element from a bag.
+ * {Nonempty.removeAll} to remove all occurrences of an element.
+ * {Nonempty.removeN} to remove a specific number of occurrences.
+ }}
+
+test> data.NatBag.Nonempty.difference.test =
+ test.verify do
+ use List +:
+ use Nat ==
+ use NatBag.Nonempty fromList
+ use Random listOf nat natIn
+ _ = Each.range 0 100
+ x = nat()
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ y = nat()
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ zs = List.foldRight (y xs -> deleteFirst ((==) y) xs) xs ys
+ ensureEqual
+ (NatBag.toList
+ (NatBag.Nonempty.difference (fromList (x +| xs)) (fromList (y +| ys))))
+ (List.sort (x +: zs))
+
+data.NatBag.Nonempty.equals : NatBag.Nonempty -> NatBag.Nonempty -> Boolean
+data.NatBag.Nonempty.equals = cases
+ NatBag.Nonempty m1, NatBag.Nonempty m2 -> Nonempty.equalBy (Nat.==) m1 m2
+
+data.NatBag.Nonempty.equals.doc : Doc
+data.NatBag.Nonempty.equals.doc =
+ use NatBag.Nonempty fromList
+ {{
+ Checks whether two {type NatBag.Nonempty} are equal. Two
+ {type NatBag.Nonempty} are equal if they contain the same elements with the
+ same multiplicities.
+
+ # Example
+
+ ```
+ NatBag.Nonempty.equals (fromList (1 +| [2, 3])) (fromList (1 +| [2, 3]))
+ ```
+ }}
+
+test> data.NatBag.Nonempty.equals.test : [Result]
+data.NatBag.Nonempty.equals.test = test.verify do
+ use NatBag.Nonempty equals fromList
+ use Random listOf natIn
+ _ = Each.range 0 100
+ x = natIn 0 1000
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ y = natIn 0 1000
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ z = natIn 0 1000
+ zs = listOf (do natIn 0 1000) do natIn 0 100
+ b1 = fromList (x +| xs)
+ b2 = fromList (y +| ys)
+ b3 = fromList (z +| zs)
+ ensure (equals b1 b1)
+ ensure (equals b2 b2)
+ ensure (equals b3 b3)
+ ensure (iff (equals b1 b2) (equals b2 b1))
+ ensure (iff (equals b1 b3) (equals b3 b1))
+ ensure (iff (equals b2 b3) (equals b3 b2))
+ ensure (iff (equals b1 b2) (equals b1 b2))
+ ensure (iff (equals b1 b3) (equals b1 b3))
+ ensure (iff (equals b2 b3) (equals b2 b3))
+ ensure (implies (equals b1 b2 && equals b2 b3) (equals b1 b3))
+ ensure (implies (equals b1 b2 && equals b1 b3) (equals b2 b3))
+ ensure (implies (equals b2 b3 && equals b1 b3) (equals b1 b2))
+ ensure (implies (equals b2 b3 && equals b2 b1) (equals b1 b3))
+ ensure (implies (equals b3 b1 && equals b2 b1) (equals b2 b3))
+ ensure (implies (equals b3 b1 && equals b3 b2) (equals b2 b1))
+
+data.NatBag.Nonempty.fromList : List.Nonempty Nat -> NatBag.Nonempty
+data.NatBag.Nonempty.fromList =
+ List.Nonempty.foldMap Nonempty.addAll NatBag.singleton
+
+data.NatBag.Nonempty.fromList.doc : Doc
+data.NatBag.Nonempty.fromList.doc =
+ use NatBag.Nonempty toList
+ {{
+ Converts a {type List.Nonempty} of {type Nat}s into a {type NatBag.Nonempty}.
+ If an element appears multiple times in the {type List.Nonempty}, it will
+ appear multiple times in the {type NatBag.Nonempty}.
+
+ # Example
+
+ ```
+ toList (NatBag.Nonempty.fromList (1 +| [2, 2, 3]))
+ ```
+
+ # See also
+
+ * {toList} to convert a {type NatBag.Nonempty} to a {type List.Nonempty}.
+ * {Nonempty.fromNatSet} to convert a {type NatSet.Nonempty} to a
+ {type NatBag.Nonempty}.
+ }}
+
+test> data.NatBag.Nonempty.fromList.test : [Result]
+data.NatBag.Nonempty.fromList.test =
+ test.verify do
+ use List +:
+ use Random natIn
+ _ = Each.range 0 100
+ x = Random.nat()
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ ensureEqual
+ (List.Nonempty.toList
+ (NatBag.Nonempty.toList (NatBag.Nonempty.fromList (x +| xs))))
+ (Heap.sort (x +: xs))
+
+data.NatBag.Nonempty.fromNatSet : NatSet.Nonempty -> NatBag.Nonempty
+data.NatBag.Nonempty.fromNatSet s =
+ NatSet.Nonempty.foldMap Nonempty.addAll NatBag.singleton s
+
+data.NatBag.Nonempty.fromNatSet.doc : Doc
+data.NatBag.Nonempty.fromNatSet.doc =
+ {{
+ Converts a {type NatSet.Nonempty} into a {type NatBag.Nonempty}. If an
+ element appears multiple times in the {type NatSet.Nonempty}, it will appear
+ multiple times in the {type NatBag.Nonempty}.
+
+ # Example
+
+ ```
+ NatBag.Nonempty.toList
+ (Nonempty.fromNatSet (NatSet.Nonempty.fromList (1 +| [2, 2, 3])))
+ ```
+
+ # See also
+
+ * {NatBag.Nonempty.toNatSet} to convert a {type NatBag.Nonempty} to a
+ {type NatSet.Nonempty}.
+ * {NatBag.Nonempty.fromList} to convert a {type List.Nonempty} to a
+ {type NatBag.Nonempty}.
+ }}
+
+test> data.NatBag.Nonempty.fromNatSet.test : [Result]
+data.NatBag.Nonempty.fromNatSet.test =
+ test.verify do
+ use List +:
+ use Random natIn
+ _ = Each.range 0 100
+ x = Random.nat()
+ xs = Random.listOf (do natIn 0 100) do natIn 0 100
+ ensureEqual
+ (List.Nonempty.toList
+ (NatBag.Nonempty.toList
+ (Nonempty.fromNatSet (NatSet.Nonempty.fromList (x +| xs)))))
+ (distinct (Heap.sort (x +: xs)))
+
+data.NatBag.Nonempty.fromOccurrenceList :
+ List.Nonempty (Nat, Nat) -> NatBag.Nonempty
+data.NatBag.Nonempty.fromOccurrenceList l =
+ NatBag.Nonempty (NatMap.Nonempty.fromList l)
+
+data.NatBag.Nonempty.fromOccurrenceList.doc : Doc
+data.NatBag.Nonempty.fromOccurrenceList.doc =
+ {{
+ Constructs a {type NatBag.Nonempty} from a non-empty list of {type Nat}s and
+ their counts.
+
+ # Examples
+
+ ```
+ NatBag.Nonempty.toList
+ (Nonempty.fromOccurrenceList ((1, 2) +| [(2, 3), (3, 4), (4, 1)]))
+ ```
+
+ # See also
+
+ * {Nonempty.occurrenceList} to return a list of all {type Nat}s in the bag,
+ together with their counts.
+ * {NatBag.Nonempty.fromList} to construct a {type NatBag.Nonempty} from a
+ non-empty list of {type Nat}s.
+ }}
+
+data.NatBag.Nonempty.getMax : NatBag.Nonempty -> Nat
+data.NatBag.Nonempty.getMax b = Nonempty.maxKey (Nonempty.counts b)
+
+data.NatBag.Nonempty.getMax.doc : Doc
+data.NatBag.Nonempty.getMax.doc =
+ {{
+ Returns the largest {type Nat} in a {type NatBag.Nonempty}.
+
+ # Examples
+
+ ```
+ NatBag.Nonempty.getMax (NatBag.Nonempty.fromList (1 +| [2, 3, 4, 1, 2, 1]))
+ ```
+
+ # See also
+
+ * {NatBag.Nonempty.getMin} to return the smallest {type Nat} in a
+ {type NatBag.Nonempty}.
+ }}
+
+data.NatBag.Nonempty.getMin : NatBag.Nonempty -> Nat
+data.NatBag.Nonempty.getMin b = Nonempty.minKey (Nonempty.counts b)
+
+data.NatBag.Nonempty.getMin.doc : Doc
+data.NatBag.Nonempty.getMin.doc =
+ {{
+ Returns the smallest {type Nat} in a {type NatBag.Nonempty}.
+
+ # Examples
+
+ ```
+ NatBag.Nonempty.getMin (NatBag.Nonempty.fromList (1 +| [2, 3, 4, 1, 2, 1]))
+ ```
+
+ # See also
+
+ * {NatBag.Nonempty.getMax} to return the largest {type Nat} in a
+ {type NatBag.Nonempty}.
+ }}
+
+data.NatBag.Nonempty.intersect : NatBag.Nonempty -> NatBag.Nonempty -> NatBag
+data.NatBag.Nonempty.intersect = cases
+ NatBag.Nonempty m1, NatBag.Nonempty m2 ->
+ NatBag (NatMap.Nonempty.intersectWith Nat.min m1 m2)
+
+data.NatBag.Nonempty.intersect.doc : Doc
+data.NatBag.Nonempty.intersect.doc =
+ use NatBag.Nonempty fromList
+ {{
+ Computes the intersection between two {type NatBag.Nonempty}s. Constructs a
+ new {type NatBag} with the elements that appear in both arguments. If an
+ element appears `n` times in the first and `m` times in the second, the
+ element will appear `` Nat.min n m `` times in the result.
+
+ # Example
+
+ ```
+ NatBag.toList
+ (NatBag.Nonempty.intersect
+ (fromList (1 +| [2, 3])) (fromList (2 +| [3, 4])))
+ ```
+
+ # See also
+
+ * {NatBag.Nonempty.union} to compute the union of two
+ {type NatBag.Nonempty}s.
+ * {NatBag.Nonempty.difference} to compute the difference between two
+ {type NatBag.Nonempty}s.
+ * {Nonempty.remove} to remove a single {type Nat} from a
+ {type NatBag.Nonempty}.
+ * {Nonempty.removeN} to remove multiple occurrences of a {type Nat}.
+ * {NatBag.removeAll} to remove all occurrences of a {type Nat}.
+ }}
+
+data.NatBag.Nonempty.nth : Nat -> NatBag.Nonempty -> Optional Nat
+data.NatBag.Nonempty.nth index bag =
+ use Nat + -
+ loop : Nat -> NatMap.Nonempty Nat -> Nat -> (Optional Nat, Nat)
+ loop index map visited = match map with
+ NatMap.Nonempty.Tip key value ->
+ match Universal.ordering (index - visited) value with
+ Less -> (Some key, visited + value)
+ otherwise -> (None, visited + value)
+ NatMap.Nonempty.Bin p m sz l r ->
+ match loop index l visited with
+ (Some k, v) -> (Some k, v)
+ (None, v) -> loop index r v
+ loop index (Nonempty.counts bag) 0 |> at1
+
+data.NatBag.Nonempty.nth.doc : Doc
+data.NatBag.Nonempty.nth.doc =
+ use NatBag.Nonempty nth
+ {{
+ {{ docExample 2 do i b -> nth i b }} returns the `i`-th element in `b`, where
+ `i`=0 is the smallest element (according to {Universal.ordering}).
+
+ Is the same as {{
+ docExample 2 do i as -> Nonempty.at i (NatBag.Nonempty.toList as) }} but
+ doesn't require instantiating the intermediate {type List}.
+
+ ```
+ b = NatBag.Nonempty.fromList (3 +| [1, 2, 3, 4, 5, 1, 1])
+ List.map (i -> nth i b) (List.range 0 (NatBag.Nonempty.size b))
+ ```
+ }}
+
+test> data.NatBag.Nonempty.nth.tests =
+ test.verify do
+ use Random natIn
+ Each.repeat 100
+ s =
+ natIn 0 10 +| (List.replicate (natIn 0 19) do natIn 0 10)
+ |> NatBag.Nonempty.fromList
+ ensure
+ (List.somes
+ (List.map
+ (i -> NatBag.Nonempty.nth i s)
+ (List.range 0 (NatBag.Nonempty.size s)))
+ === (NatBag.Nonempty.toList s |> List.Nonempty.toList))
+
+data.NatBag.Nonempty.occurrenceList :
+ NatBag.Nonempty -> List.Nonempty (Nat, Nat)
+data.NatBag.Nonempty.occurrenceList b =
+ NatMap.Nonempty.toList (Nonempty.counts b)
+
+data.NatBag.Nonempty.occurrenceList.doc : Doc
+data.NatBag.Nonempty.occurrenceList.doc =
+ {{
+ Returns a list of all {type Nat}s in a {type NatBag.Nonempty}, together with
+ their counts.
+
+ # Examples
+
+ ```
+ Nonempty.occurrenceList
+ (NatBag.Nonempty.fromList (1 +| [2, 3, 4, 1, 2, 1]))
+ ```
+
+ # See also
+
+ * {Nonempty.fromOccurrenceList} to construct a {type NatBag.Nonempty} from
+ a non-empty list of {type Nat}s and their counts.
+ * {Nonempty.counts} to return the {type NatMap} of elements and their
+ counts.
+ * {NatBag.Nonempty.toList} to return a list of all {type Nat}s in the bag.
+ }}
+
+data.NatBag.Nonempty.randomChoice : NatBag.Nonempty ->{Random} Nat
+data.NatBag.Nonempty.randomChoice bag =
+ randomIndex = Random.natIn 0 (NatBag.Nonempty.size bag)
+ NatBag.Nonempty.nth randomIndex bag
+ |> getOrBug "NatBag.Nonempty.randomChoice: index out of bounds"
+
+test> data.NatBag.Nonempty.randomChoice.test = test.verify do
+ list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 1, 4, 6, 6]
+ set = Set.fromList list
+ bag = NatBag.Nonempty.fromList (Abort.toBug do List.nonempty list)
+ Each.repeat 1000
+ e = NatBag.Nonempty.randomChoice bag
+ ensure (Set.contains e set)
+
+data.NatBag.Nonempty.remove : Nat -> NatBag.Nonempty -> NatBag
+data.NatBag.Nonempty.remove = Nonempty.removeN 1
+
+data.NatBag.Nonempty.remove.doc : Doc
+data.NatBag.Nonempty.remove.doc =
+ {{
+ Removes a {type Nat} from a {type NatBag.Nonempty}. Constructs a new
+ {type NatBag} with the element removed. If the element appears `n` times in
+ the {type NatBag}, the element will appear `n - 1` times in the result. If
+ the element does not appear in the {type NatBag}, the result will be the same
+ as the original {type NatBag}.
+
+ # Example
+
+ ```
+ NatBag.toList (Nonempty.remove 2 (NatBag.Nonempty.fromList (1 +| [2, 3])))
+ ```
+
+ # See also
+
+ * {Nonempty.removeN} to remove multiple {type Nat}s from a
+ {type NatBag.Nonempty}.
+ * {Nonempty.removeAll} to remove all occurrences of a {type Nat} from a
+ {type NatBag.Nonempty}.
+ * {Nonempty.add} to add a single {type Nat} to a {type NatBag.Nonempty}.
+ * {Nonempty.addN} to add multiple occurrences of a {type Nat} to a
+ {type NatBag.Nonempty}.
+ * {Nonempty.addAll} to add a whole bag to another.
+ }}
+
+test> data.NatBag.Nonempty.remove.test : [Result]
+data.NatBag.Nonempty.remove.test =
+ test.verify do
+ use Random natIn
+ _ = Each.range 0 10
+ x = natIn 0 10
+ xs = Random.listOf (do natIn 0 10) do natIn 0 10
+ ensureEqual
+ (NatBag.toList (Nonempty.remove x (NatBag.Nonempty.fromList (x +| xs))))
+ (Heap.sort xs)
+
+data.NatBag.Nonempty.removeAll : Nat -> NatBag.Nonempty -> NatBag
+data.NatBag.Nonempty.removeAll = cases
+ n, NatBag.Nonempty m -> NatBag (NatMap.Nonempty.alter (const None) n m)
+
+data.NatBag.Nonempty.removeAll.doc : Doc
+data.NatBag.Nonempty.removeAll.doc =
+ use NatBag toList
+ use NatBag.Nonempty fromList
+ use Nonempty removeAll
+ {{
+ Removes all occurrences of a {type Nat} from a {type NatBag.Nonempty}.
+ Returns a (possibly empty) {type NatBag}.
+
+ # Examples
+
+ ```
+ toList (removeAll 3 (fromList (1 +| [2, 3, 3, 4])))
+ ```
+
+ ```
+ toList (removeAll 3 (fromList (1 +| [2, 4])))
+ ```
+
+ # See also
+
+ * {Nonempty.remove} to remove a single occurrence of a {type Nat} from a
+ {type NatBag.Nonempty}.
+ * {Nonempty.removeN} to remove a specific number of occurrences of a
+ {type Nat} from a {type NatBag.Nonempty}.
+ * {NatBag.Nonempty.difference} to subtract one bag from another.
+ }}
+
+test> data.NatBag.Nonempty.removeAll.test : [Result]
+data.NatBag.Nonempty.removeAll.test =
+ test.verify do
+ use Nat !=
+ use Random natIn
+ _ = Each.range 0 100
+ x = natIn 0 1000
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ b = NatBag.Nonempty.fromList (x +| xs)
+ ensure
+ (NatBag.equals
+ (Nonempty.removeAll x b)
+ (NatBag.fromList (List.filter (z -> z != x) xs)))
+
+data.NatBag.Nonempty.removeMax : NatBag.Nonempty -> NatBag
+data.NatBag.Nonempty.removeMax b =
+ max = NatBag.Nonempty.getMax b
+ Nonempty.remove max b
+
+data.NatBag.Nonempty.removeMax.doc : Doc
+data.NatBag.Nonempty.removeMax.doc =
+ {{
+ Removes one occurrence of the largest {type Nat} in a {type NatBag.Nonempty}.
+
+ # Examples
+
+ ```
+ NatBag.toList
+ (removeMax (NatBag.Nonempty.fromList (1 +| [2, 3, 4, 1, 2, 1])))
+ ```
+
+ # See also
+
+ * {removeMin} to remove the smallest {type Nat}.
+ }}
+
+data.NatBag.Nonempty.removeMin : NatBag.Nonempty -> NatBag
+data.NatBag.Nonempty.removeMin b =
+ min = NatBag.Nonempty.getMin b
+ Nonempty.remove min b
+
+data.NatBag.Nonempty.removeMin.doc : Doc
+data.NatBag.Nonempty.removeMin.doc =
+ {{
+ Removes one occurrence of the smallest {type Nat} from a
+ {type NatBag.Nonempty}.
+
+ # Examples
+
+ ```
+ NatBag.toList
+ (removeMin (NatBag.Nonempty.fromList (1 +| [2, 3, 4, 1, 2, 1])))
+ ```
+
+ # See also
+
+ * {removeMax} to remove the largest {type Nat}.
+ }}
+
+data.NatBag.Nonempty.removeN : Nat -> Nat -> NatBag.Nonempty -> NatBag
+data.NatBag.Nonempty.removeN = cases
+ n, elem, NatBag.Nonempty m ->
+ NatBag
+ (NatMap.Nonempty.alter
+ (Optional.filter (z -> z Nat.> 0) << Optional.map (z -> z Nat.- n))
+ elem
+ m)
+
+data.NatBag.Nonempty.removeN.doc : Doc
+data.NatBag.Nonempty.removeN.doc =
+ use NatBag.Nonempty fromList
+ use Nonempty removeN
+ {{
+ Removes a given number of occurrences of a {type Nat} from a
+ {type NatBag.Nonempty}. If the number of occurrences of the {type Nat} in the
+ {type NatBag.Nonempty} is less than the given number, then the {type Nat} is
+ removed entirely.
+
+ # Examples
+
+ ```
+ removeN 2 3 (fromList (2 +| [3, 4]))
+ ```
+
+ ```
+ removeN 2 3 (fromList (1 +| [3, 3, 3, 4]))
+ ```
+
+ # See also
+
+ * {Nonempty.remove} to remove a single occurrence of a {type Nat} from a
+ {type NatBag.Nonempty}.
+ * {Nonempty.removeAll} to remove all occurrences of a {type Nat} from a
+ {type NatBag.Nonempty}.
+ * {NatBag.Nonempty.difference} to subtract a {type NatBag.Nonempty} from
+ another.
+ }}
+
+test> data.NatBag.Nonempty.removeN.test : [Result]
+data.NatBag.Nonempty.removeN.test = test.verify do
+ use Nat - ==
+ use Random natIn
+ _ = Each.range 0 100
+ x = natIn 0 1000
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ n = natIn 0 100
+ b = NatBag.Nonempty.fromList (x +| xs)
+ ensure (NatBag.count x (Nonempty.removeN n x b) == Nonempty.count x b - n)
+
+data.NatBag.Nonempty.size : NatBag.Nonempty -> Nat
+data.NatBag.Nonempty.size b =
+ use Nat +
+ Nonempty.fold (+) 0 (Nonempty.counts b)
+
+data.NatBag.Nonempty.size.doc : Doc
+data.NatBag.Nonempty.size.doc =
+ {{
+ Returns the sum of all occurrences of all {type Nat} in a
+ {type NatBag.Nonempty}.
+
+ # Examples
+
+ ```
+ NatBag.Nonempty.size
+ (NatBag.Nonempty.fromList (Nonempty.Nonempty 0 [1, 2, 3, 4, 1, 2, 1]))
+ ```
+ }}
+
+data.NatBag.Nonempty.subbag : NatBag.Nonempty -> NatBag.Nonempty -> Boolean
+data.NatBag.Nonempty.subbag = flip Nonempty.superbag
+
+data.NatBag.Nonempty.subbag.doc : Doc
+data.NatBag.Nonempty.subbag.doc =
+ use NatBag.Nonempty fromList
+ use Nonempty subbag
+ {{
+ Determines whether a {type NatBag.Nonempty} is a __subbag__ of another.
+
+ A {type NatBag.Nonempty} is a subbag of another if it contains as many or
+ fewer occurrences of each {type Nat} as the other.
+
+ Every {type NatBag.Nonempty} is a subbag of itself.
+
+ # Examples
+
+ ```
+ subbag (fromList (2 +| [3, 4])) (fromList (1 +| [2, 3, 4]))
+ ```
+
+ ```
+ subbag (fromList (2 +| [3, 4])) (fromList (1 +| [2, 3, 3, 4]))
+ ```
+
+ # See also
+
+ * {Nonempty.superbag} to determine whether a {type NatBag.Nonempty} is a
+ superbag of another.
+ * {NatBag.Nonempty.intersect} to compute the largest {type NatBag} that is
+ a subbag of both of two bags.
+ * {NatBag.Nonempty.union} to compute the smallest {type NatBag.Nonempty}
+ that is a superbag of both of two bags.
+ }}
+
+test> data.NatBag.Nonempty.subbag.test : [Result]
+data.NatBag.Nonempty.subbag.test = test.verify do
+ use NatBag.Nonempty fromList
+ use Random listOf natIn
+ _ = Each.range 0 100
+ x = natIn 0 1000
+ y = natIn 0 1000
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ b1 = fromList (x +| xs)
+ b2 = fromList (y +| ys)
+ i = NatBag.Nonempty.intersect b1 b2
+ u = NatBag.Nonempty.union b1 b2
+ ensure (Nonempty.subbag b1 b1)
+ ensure (Nonempty.subbag b2 b2)
+ ensure (NatBag.subbag i (toNatBag b1))
+ ensure (NatBag.subbag i (toNatBag b2))
+ ensure (Nonempty.subbag b1 u)
+ ensure (Nonempty.subbag b2 u)
+
+data.NatBag.Nonempty.superbag : NatBag.Nonempty -> NatBag.Nonempty -> Boolean
+data.NatBag.Nonempty.superbag = cases
+ NatBag.Nonempty m1, NatBag.Nonempty m2 ->
+ Nonempty.isSubmapOfBy (Nat.<=) m2 m1
+
+data.NatBag.Nonempty.superbag.doc : Doc
+data.NatBag.Nonempty.superbag.doc =
+ use NatBag.Nonempty fromList
+ {{
+ Checks whether the first {type NatBag.Nonempty} is a __superbag__ of the
+ second. A {type NatBag.Nonempty} is a superbag of another if it contains all
+ the elements of the other, and possibly more.
+
+ Returns `` true `` if the first {type NatBag.Nonempty} contains at least as
+ many occurrences of each element as the second.
+
+ Every {type NatBag.Nonempty} is a superbag of itself.
+
+ # Example
+
+ ```
+ Nonempty.superbag (fromList (1 +| [2, 3])) (fromList (2 +| [3, 4]))
+ ```
+
+ # See also
+
+ * {Nonempty.subbag} to check whether a {type NatBag.Nonempty} is a subbag
+ of another.
+ * {NatBag.Nonempty.union} to compute the largest bag that is a superbag of
+ both arguments.
+ * {NatBag.Nonempty.intersect} to compute the smallest bag that is a subbag
+ of both arguments.
+ }}
+
+test> data.NatBag.Nonempty.superbag.test : [Result]
+data.NatBag.Nonempty.superbag.test = test.verify do
+ use NatBag.Nonempty fromList union
+ use Nonempty superbag
+ use Random listOf nat natIn
+ _ = Each.range 0 100
+ x = nat()
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ y = nat()
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ b1 = fromList (x +| xs)
+ b2 = fromList (y +| ys)
+ ensure (superbag b1 b1)
+ ensure (superbag b2 b2)
+ ensure (superbag (union b1 b2) b1)
+ ensure (superbag (union b1 b2) b2)
+
+data.NatBag.Nonempty.toBag : NatBag.Nonempty -> Bag Nat
+data.NatBag.Nonempty.toBag = cases
+ NatBag.Nonempty m -> MkBag (Map.Nonempty.toMap (NatMap.Nonempty.toMap m))
+
+data.NatBag.Nonempty.toBag.doc : Doc
+data.NatBag.Nonempty.toBag.doc =
+ use Bag toList
+ use NatBag.Nonempty fromList
+ use Nonempty toBag
+ {{
+ Converts a {type NatBag.Nonempty} to a {type Bag} of {type Nat}s.
+
+ # Examples
+
+ ```
+ toList (toBag (fromList (1 +| [2, 3, 3, 4])))
+ ```
+
+ ```
+ toList (toBag (fromList (1 +| [2, 4])))
+ ```
+ }}
+
+test> data.NatBag.Nonempty.toBag.test : [Result]
+data.NatBag.Nonempty.toBag.test = test.verify do
+ use Bag ==
+ use List +:
+ use Random natIn
+ _ = Each.range 0 100
+ x = natIn 0 1000
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ b = NatBag.Nonempty.fromList (x +| xs)
+ ensure (Nonempty.toBag b == Bag.fromList (x +: xs))
+
+data.NatBag.Nonempty.toList : NatBag.Nonempty -> List.Nonempty Nat
+data.NatBag.Nonempty.toList = cases
+ NatBag.Nonempty m ->
+ foldMapWithKey (Nonempty.++) (cases k, v -> k +| List.fill (v Nat.- 1) k) m
+
+data.NatBag.Nonempty.toList.doc : Doc
+data.NatBag.Nonempty.toList.doc =
+ use NatBag.Nonempty fromList
+ {{
+ Converts a {type NatBag.Nonempty} into a {type List.Nonempty} of {type Nat}s.
+ If an element appears multiple times in the {type NatBag.Nonempty}, it will
+ appear multiple times in the {type List.Nonempty}.
+
+ # Example
+
+ ```
+ NatBag.Nonempty.toList (fromList (1 +| [2, 2, 3]))
+ ```
+
+ # See also
+
+ * {fromList} to convert a {type List.Nonempty} to a {type NatBag.Nonempty}.
+ * {NatBag.Nonempty.toNatSet} to convert a {type NatBag.Nonempty} to a
+ {type NatSet.Nonempty}.
+ }}
+
+test> data.NatBag.Nonempty.toList.test : [Result]
+data.NatBag.Nonempty.toList.test =
+ verifyAndIgnore do
+ use List +:
+ use Random natIn
+ _ = Each.range 0 100
+ x = Random.nat()
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ ensureEqual
+ (List.Nonempty.toList
+ (NatBag.Nonempty.toList (NatBag.Nonempty.fromList (x +| xs))))
+ (Heap.sort (x +: xs))
+
+data.NatBag.Nonempty.toNatBag : NatBag.Nonempty -> NatBag
+data.NatBag.Nonempty.toNatBag = cases
+ NatBag.Nonempty m -> NatBag (NatMap (Some m))
+
+data.NatBag.Nonempty.toNatBag.doc : Doc
+data.NatBag.Nonempty.toNatBag.doc =
+ {{
+ Converts a {type NatBag.Nonempty} to a {type NatBag}.
+
+ # Example
+
+ ```
+ NatBag.toList (toNatBag (NatBag.Nonempty.fromList (2 +| [3, 4])))
+ ```
+ }}
+
+data.NatBag.Nonempty.toNatSet : NatBag.Nonempty -> NatSet.Nonempty
+data.NatBag.Nonempty.toNatSet = cases NatBag.Nonempty m -> Nonempty.keySet m
+
+data.NatBag.Nonempty.toNatSet.doc : Doc
+data.NatBag.Nonempty.toNatSet.doc =
+ {{
+ Converts a {type NatBag.Nonempty} into a {type NatSet.Nonempty}. If an
+ element appears multiple times in the {type NatBag.Nonempty}, it will appear
+ only once in the {type NatSet.Nonempty}.
+
+ # Example
+
+ ```
+ Nonempty.toListAscending
+ (NatBag.Nonempty.toNatSet (NatBag.Nonempty.fromList (1 +| [2, 2, 3])))
+ ```
+
+ # See also
+
+ * {Nonempty.fromNatSet} to convert a {type NatSet.Nonempty} to a
+ {type NatBag.Nonempty}.
+ * {NatBag.Nonempty.toList} to convert a {type NatBag.Nonempty} to a
+ {type List.Nonempty}.
+ }}
+
+test> data.NatBag.Nonempty.toNatSet.test : [Result]
+data.NatBag.Nonempty.toNatSet.test =
+ test.verify do
+ use List +:
+ use Random natIn
+ _ = Each.range 0 100
+ x = Random.nat()
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ ensure
+ (List.Nonempty.toList
+ (Nonempty.toListAscending
+ (NatBag.Nonempty.toNatSet (NatBag.Nonempty.fromList (x +| xs))))
+ === distinct (Heap.sort (x +: xs)))
+
+data.NatBag.Nonempty.union :
+ NatBag.Nonempty -> NatBag.Nonempty -> NatBag.Nonempty
+data.NatBag.Nonempty.union = cases
+ NatBag.Nonempty m1, NatBag.Nonempty m2 ->
+ NatBag.Nonempty (NatMap.Nonempty.unionWith Nat.max m1 m2)
+
+data.NatBag.Nonempty.union.doc : Doc
+data.NatBag.Nonempty.union.doc =
+ use NatBag.Nonempty fromList
+ {{
+ Computes the union of two {type NatBag.Nonempty}s. This is the smallest
+ {type NatBag.Nonempty} that is a {Nonempty.superbag} of both arguments.
+ Returns a new {type NatBag.Nonempty} with the elements that appear in either
+ argument, such that if an element appears `n` times in the first and `m`
+ times in the second, the element will appear `` Nat.max n m `` times in the
+ result.
+
+ # Example
+
+ ```
+ NatBag.Nonempty.toList
+ (NatBag.Nonempty.union (fromList (1 +| [2, 3])) (fromList (2 +| [3, 4])))
+ ```
+
+ # See also
+
+ * {NatBag.Nonempty.intersect} to compute the intersection between two
+ {type NatBag.Nonempty}s.
+ * {NatBag.Nonempty.difference} to compute the difference between two
+ {type NatBag.Nonempty}s.
+ * {Nonempty.add} to add a single {type Nat} to a {type NatBag.Nonempty}.
+ * {Nonempty.addN} to add multiple occurrences of a {type Nat} to a
+ {type NatBag.Nonempty}.
+ * {Nonempty.addAll} to add a whole bag to another.
+ }}
+
+test> data.NatBag.Nonempty.union.test : [Result]
+data.NatBag.Nonempty.union.test = test.verify do
+ use NatBag.Nonempty fromList union
+ use Nonempty superbag
+ use Random listOf nat natIn
+ _ = Each.range 0 100
+ x = nat()
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ y = nat()
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ b1 = fromList (x +| xs)
+ b2 = fromList (y +| ys)
+ ensure (superbag (union b1 b2) b1)
+ ensure (superbag (union b1 b2) b2)
+
+data.NatBag.nth : Nat -> NatBag -> Optional Nat
+data.NatBag.nth index bag = match NatBag.counts bag with
+ NatMap None -> None
+ NatMap (Some nonemptyMap) ->
+ use Nat + -
+ loop : Nat -> NatMap.Nonempty Nat -> Nat -> (Optional Nat, Nat)
+ loop index map visited = match map with
+ NatMap.Nonempty.Tip key value ->
+ match Universal.ordering (index - visited) value with
+ Less -> (Some key, visited + value)
+ otherwise -> (None, visited + value)
+ NatMap.Nonempty.Bin p m sz l r ->
+ match loop index l visited with
+ (Some k, v) -> (Some k, v)
+ (None, v) -> loop index r v
+ loop index nonemptyMap 0 |> at1
+
+data.NatBag.nth.doc : Doc
+data.NatBag.nth.doc =
+ use NatBag nth
+ {{
+ {{ docExample 2 do i b -> nth i b }} returns the `i`-th element in `b`, where
+ `i`=0 is the smallest element (according to {Universal.ordering}).
+
+ Is the same as {{ docExample 2 do i as -> List.at i (NatBag.toList as) }} but
+ doesn't require instantiating the intermediate {type List}.
+
+ ```
+ b = NatBag.fromList [3, 1, 2, 3, 4, 5, 1, 1]
+ List.map (i -> nth i b) (List.range 0 (NatBag.size b))
+ ```
+ }}
+
+test> data.NatBag.nth.tests =
+ test.verify do
+ use Random natIn
+ Each.repeat 100
+ s = (List.replicate (natIn 0 20) do natIn 0 10) |> NatBag.fromList
+ ensure
+ (List.somes
+ (List.map (i -> NatBag.nth i s) (List.range 0 (NatBag.size s)))
+ === NatBag.toList s)
+
+data.NatBag.occurrenceList : NatBag -> [(Nat, Nat)]
+data.NatBag.occurrenceList = cases NatBag m -> NatMap.toList m
+
+data.NatBag.occurrenceList.doc : Doc
+data.NatBag.occurrenceList.doc =
+ {{
+ Converts a {type NatBag} to a list of {type Nat}s and their number of
+ occurrences.
+
+ # Examples
+
+ ```
+ NatBag.occurrenceList (NatBag.fromList [1, 2, 3, 4, 1, 2, 1])
+ ```
+
+ # See also
+
+ * {NatBag.toList} to convert a {type NatBag} to a list of {type Nat}s.
+ * {NatBag.fromOccurrenceList} to convert a list of {type Nat}s and their
+ number of occurrences to a {type NatBag}.
+ }}
+
+data.NatBag.partition : (Nat ->{e} Boolean) -> NatBag ->{e} (NatBag, NatBag)
+data.NatBag.partition p b =
+ use NatBag addN empty
+ g k v = cases (t, f) -> if p k then (addN v k t, f) else (t, addN v k f)
+ NatMap.foldWithKey g (empty, empty) (NatBag.counts b)
+
+data.NatBag.partition.doc : Doc
+data.NatBag.partition.doc =
+ use NatBag toList
+ {{
+ Partitions a {type NatBag} into two {type NatBag}s, one containing all
+ {type Nat}s that satisfy the predicate and the other containing all
+ {type Nat}s that do not satisfy the predicate.
+
+ # Examples
+
+ ```
+ (evens, odds) =
+ NatBag.partition Nat.isEven (NatBag.fromList [1, 2, 3, 4, 1, 2, 1])
+ (toList evens, toList odds)
+ ```
+
+ # See also
+
+ * {NatBag.filter} to filter a {type NatBag} by removing all {type Nat}s
+ that do not satisfy the predicate.
+ }}
+
+data.NatBag.randomChoice : NatBag ->{Random} Nat
+data.NatBag.randomChoice bag =
+ randomIndex = Random.natIn 0 (NatBag.size bag)
+ match NatBag.nth randomIndex bag with
+ Some v -> v
+ None -> bug "NatBag.randomChoice: index out of bounds"
+
+data.NatBag.randomChoice.doc : Doc
+data.NatBag.randomChoice.doc =
+ use NatBag fromList randomChoice
+ {{
+ Returns a random {type Nat} from the given {type NatBag}. Assumes that the
+ {type NatBag} is not empty, so an empty {type NatBag} will cause a runtime
+ exception.
+
+ # Examples
+
+ ```
+ lcg 4096 do randomChoice (fromList [0, 3, 5, 7])
+ ```
+
+ ```
+ lcg 2510 do randomChoice (fromList [0, 3, 5, 7])
+ ```
+ }}
+
+test> data.NatBag.randomChoice.test = test.verify do
+ bag = NatBag.fromList [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 1, 4, 6, 6]
+ Each.repeat 1000
+ e = NatBag.randomChoice bag
+ ensure (NatBag.contains e bag)
+
+data.NatBag.remove : Nat -> NatBag -> NatBag
+data.NatBag.remove = NatBag.removeN 1
+
+data.NatBag.remove.doc : Doc
+data.NatBag.remove.doc =
+ {{
+ Removes a {type Nat} from a {type NatBag}. Constructs a new {type NatBag}
+ with the element removed. If the element appears `n` times in the
+ {type NatBag}, the element will appear `n - 1` times in the result. If the
+ element does not appear in the {type NatBag}, the result will be the same as
+ the original {type NatBag}.
+
+ # Example
+
+ ```
+ NatBag.toList (NatBag.remove 2 (NatBag.fromList [1, 2, 3]))
+ ```
+
+ # See also
+
+ * {NatBag.removeN} to remove multiple {type Nat}s from a {type NatBag}.
+ * {NatBag.removeAll} to remove all occurrences of a {type Nat} from a
+ {type NatBag}.
+ * {add.nonempty} to add a single {type Nat} to a {type NatBag}.
+ * {NatBag.addN} to add multiple occurrences of a {type Nat} to a
+ {type NatBag}.
+ * {NatBag.addAll} to add a whole bag to another.
+ }}
+
+test> data.NatBag.remove.test =
+ test.verify do
+ use Nat ==
+ use Random natIn
+ _ = Each.range 0 100
+ x = natIn 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 0 100
+ ensureEqual
+ (NatBag.toList (NatBag.remove x (NatBag.fromList xs)))
+ (List.sort (deleteFirst (y -> y == x) xs))
+
+data.NatBag.removeAll : Nat -> NatBag -> NatBag
+data.NatBag.removeAll = cases
+ n, NatBag m -> NatBag (NatMap.alter (const None) n m)
+
+data.NatBag.removeAll.doc : Doc
+data.NatBag.removeAll.doc =
+ use NatBag fromList removeAll toList
+ {{
+ Removes all occurrences of a {type Nat} from a {type NatBag}.
+
+ # Examples
+
+ ```
+ toList (removeAll 3 (fromList [1, 2, 3, 3, 4]))
+ ```
+
+ ```
+ toList (removeAll 3 (fromList [1, 2, 4]))
+ ```
+
+ # See also
+
+ * {NatBag.remove} to remove a single occurrence of a {type Nat} from a
+ {type NatBag}.
+ * {NatBag.removeN} to remove a specific number of occurrences of a
+ {type Nat} from a {type NatBag}.
+ }}
+
+test> data.NatBag.removeAll.test : [Result]
+data.NatBag.removeAll.test =
+ test.verify do
+ use Nat !=
+ use NatBag fromList
+ use Random natIn
+ _ = Each.range 0 100
+ x = natIn 0 1000
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ b = fromList xs
+ ensure
+ (NatBag.equals
+ (NatBag.removeAll x b) (fromList (List.filter (z -> z != x) xs)))
+
+data.NatBag.removeN : Nat -> Nat -> NatBag -> NatBag
+data.NatBag.removeN = cases
+ n, elem, NatBag m ->
+ NatBag
+ (NatMap.alter
+ (Optional.filter (z -> z Nat.> 0) << Optional.map (z -> z Nat.- n))
+ elem
+ m)
+
+data.NatBag.removeN.doc : Doc
+data.NatBag.removeN.doc =
+ use NatBag fromList removeN
+ {{
+ Removes a given number of occurrences of a {type Nat} from a {type NatBag}.
+ If the number of occurrences of the {type Nat} in the {type NatBag} is less
+ than the given number, then the {type Nat} is removed entirely.
+
+ # Examples
+
+ ```
+ removeN 2 3 (fromList [2, 3, 4])
+ ```
+
+ ```
+ removeN 2 3 (fromList [1, 3, 3, 3, 4])
+ ```
+
+ # See also
+
+ * {NatBag.remove} to remove a single occurrence of a {type Nat} from a
+ {type NatBag}.
+ * {NatBag.removeAll} to remove all occurrences of a {type Nat} from a
+ {type NatBag}.
+ * {NatBag.difference} to subtract a {type NatBag} from another.
+ }}
+
+test> data.NatBag.removeN.test : [Result]
+data.NatBag.removeN.test = test.verify do
+ use List +:
+ use Nat - ==
+ use NatBag count
+ use Random natIn
+ _ = Each.range 0 100
+ x = natIn 0 1000
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ n = natIn 0 100
+ b = NatBag.fromList (x +: xs)
+ ensure (count x (NatBag.removeN n x b) == count x b - n)
+
+data.NatBag.scale : Nat -> NatBag -> NatBag
+data.NatBag.scale n b =
+ use Nat *
+ NatBag (NatMap.map (x -> x * n) (NatBag.counts b))
+
+data.NatBag.scale.doc : Doc
+data.NatBag.scale.doc =
+ use NatBag fromList scale toList
+ {{
+ Multiplies the number of occurrences of each {type Nat} in a {type NatBag} by
+ a given {type Nat}.
+
+ # Examples
+
+ ```
+ toList (scale 2 (fromList [1, 2, 3, 4]))
+ ```
+
+ ```
+ toList (scale 2 (fromList [1, 2, 3, 4, 1, 2, 1]))
+ ```
+
+ # See also
+
+ * {NatBag.map} to transform each {type Nat} in a {type NatBag} to a
+ different {type Nat}.
+ * {NatBag.flatMap} to map each {type Nat} in a {type NatBag} to a
+ {type NatBag} and combine the results.
+ }}
+
+data.NatBag.singleton : Nat -> NatBag.Nonempty
+data.NatBag.singleton n = NatBag.Nonempty (NatMap.singleton n 1)
+
+data.NatBag.singleton.doc : Doc
+data.NatBag.singleton.doc =
+ {{
+ Constructs a {type NatBag.Nonempty} containing a single {type Nat}.
+
+ # Example
+
+ ```
+ NatBag.Nonempty.toList (NatBag.singleton 1)
+ ```
+
+ # See also
+
+ * {NatBag.fromList} to construct a {type NatBag} from a {type List} of
+ {type Nat}s.
+ * {NatBag.Nonempty.fromList} to construct a {type NatBag.Nonempty} from a
+ {type List.Nonempty} of {type Nat}s.
+ }}
+
+test> data.NatBag.singleton.test =
+ test.verify do
+ _ = Each.range 0 100
+ x = Random.nat()
+ ensureEqual
+ (List.Nonempty.toList (NatBag.Nonempty.toList (NatBag.singleton x))) [x]
+
+data.NatBag.size : NatBag -> Nat
+data.NatBag.size b =
+ use Nat +
+ NatMap.fold (+) 0 (NatBag.counts b)
+
+data.NatBag.size.doc : Doc
+data.NatBag.size.doc =
+ {{
+ Returns the sum of all occurrences of all {type Nat}s in a {type NatBag}.
+
+ # Examples
+
+ ```
+ NatBag.size (NatBag.fromList [1, 2, 3, 4, 1, 2, 1])
+ ```
+
+ # See also
+
+ * {NatBag.isEmpty} to check if a {type NatBag} is empty.
+ * {NatBag.count} to count the occurrences of a given {type Nat} in a
+ {type NatBag}.
+ }}
+
+data.NatBag.subbag : NatBag -> NatBag -> Boolean
+data.NatBag.subbag = flip NatBag.superbag
+
+data.NatBag.subbag.doc : Doc
+data.NatBag.subbag.doc =
+ use NatBag fromList subbag
+ {{
+ Determines whether a {type NatBag} is a __subbag__ of another.
+
+ A {type NatBag} is a subbag of another if it contains as many or fewer
+ occurrences of each {type Nat} as the other.
+
+ Every {type NatBag} is a subbag of itself.
+
+ # Examples
+
+ ```
+ subbag (fromList [2, 3, 4]) (fromList [1, 2, 3, 4])
+ ```
+
+ ```
+ subbag (fromList [2, 3, 4]) (fromList [1, 2, 3, 3, 4])
+ ```
+
+ # See also
+
+ * {NatBag.superbag} to determine whether a {type NatBag} is a superbag of
+ another.
+ * {NatBag.intersect} to compute the largest {type NatBag} that is a subbag
+ of both of two bags.
+ * {NatBag.union} to compute the smallest {type NatBag} that is a superbag
+ of both of two bags.
+ }}
+
+test> data.NatBag.subbag.test : [Result]
+data.NatBag.subbag.test = test.verify do
+ use NatBag fromList subbag
+ use Random listOf natIn
+ _ = Each.range 0 100
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ b1 = fromList xs
+ b2 = fromList ys
+ i = NatBag.intersect b1 b2
+ u = NatBag.union b1 b2
+ ensure (subbag b1 b1)
+ ensure (subbag b2 b2)
+ ensure (subbag i b1)
+ ensure (subbag i b2)
+ ensure (subbag b1 u)
+ ensure (subbag b2 u)
+
+data.NatBag.superbag : NatBag -> NatBag -> Boolean
+data.NatBag.superbag = cases
+ NatBag m1, NatBag m2 -> NatMap.isSubmapOfBy (Nat.<=) m2 m1
+
+data.NatBag.superbag.doc : Doc
+data.NatBag.superbag.doc =
+ use NatBag fromList
+ {{
+ Checks whether the first {type NatBag} is a __superbag__ of the second. A
+ {type NatBag} is a superbag of another if it contains all the elements of the
+ other, and possibly more.
+
+ Returns `` true `` if the first {type NatBag} contains at least as many
+ occurrences of each element as the second.
+
+ Every {type NatBag} is a superbag of itself.
+
+ # Example
+
+ ```
+ NatBag.superbag (fromList [1, 2, 3]) (fromList [2, 3, 4])
+ ```
+ }}
+
+test> data.NatBag.superbag.test = test.verify do
+ use NatBag fromList superbag
+ use Random listOf natIn
+ _ = Each.range 0 100
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ b1 = fromList xs
+ b2 = fromList ys
+ i = NatBag.intersect b1 b2
+ u = NatBag.union b1 b2
+ ensure (superbag b1 b1)
+ ensure (superbag b2 b2)
+ ensure (superbag b1 i)
+ ensure (superbag b2 i)
+ ensure (superbag u b1)
+ ensure (superbag u b2)
+
+data.NatBag.toBag : NatBag -> Bag Nat
+data.NatBag.toBag = cases NatBag m -> MkBag (NatMap.toMap m)
+
+data.NatBag.toBag.doc : Doc
+data.NatBag.toBag.doc =
+ use Bag toList
+ use NatBag fromList toBag
+ {{
+ Converts a {type NatBag} to a {type Bag} of {type Nat}s.
+
+ # Examples
+
+ ```
+ toList (toBag (fromList [1, 2, 3, 3, 4]))
+ ```
+
+ ```
+ toList (toBag (fromList [1, 2, 4]))
+ ```
+ }}
+
+test> data.NatBag.toBag.test : [Result]
+data.NatBag.toBag.test = test.verify do
+ use Bag ==
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ b = NatBag.fromList xs
+ ensure (NatBag.toBag b == Bag.fromList xs)
+
+data.NatBag.toList : NatBag -> [Nat]
+data.NatBag.toList = cases
+ NatBag m -> NatMap.foldWithKey (k v acc -> List.fill v k List.++ acc) [] m
+
+data.NatBag.toList.doc : Doc
+data.NatBag.toList.doc =
+ use NatBag fromList
+ {{
+ Converts a {type NatBag} into a {type List} of {type Nat}s. If an element
+ appears multiple times in the {type NatBag}, it will appear multiple times in
+ the {type List}.
+
+ # Example
+
+ ```
+ NatBag.toList (fromList [1, 2, 2, 3])
+ ```
+
+ # See also
+
+ * {fromList} to convert a {type List} to a {type NatBag}.
+ * {NatBag.toNatSet} to convert a {type NatBag} to a {type NatSet}.
+ }}
+
+test> data.NatBag.toList.test = verifyAndIgnore do
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ ensureEqual (NatBag.toList (NatBag.fromList xs)) (List.sort xs)
+
+data.NatBag.toNatSet : NatBag -> NatSet
+data.NatBag.toNatSet = cases NatBag m -> NatMap.keySet m
+
+data.NatBag.toNatSet.doc : Doc
+data.NatBag.toNatSet.doc =
+ {{
+ Converts a {type NatBag} into a {type NatSet}. If an element appears multiple
+ times in the {type NatBag}, it will appear only once in the {type NatSet}.
+
+ # Example
+
+ ```
+ NatSet.toList (NatBag.toNatSet (NatBag.fromList [1, 2, 2, 3]))
+ ```
+
+ # See also
+
+ * {NatBag.fromNatSet} to convert a {type NatSet} to a {type NatBag}.
+ * {NatBag.toList} to convert a {type NatBag} to a {type List}.
+ }}
+
+test> data.NatBag.toNatSet.test =
+ test.verify do
+ use NatSet ==
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ ensure
+ (NatBag.toNatSet (NatBag.fromList xs) == NatSet.fromList (List.sort xs))
+
+data.NatBag.union : NatBag -> NatBag -> NatBag
+data.NatBag.union = cases
+ NatBag m1, NatBag m2 -> NatBag (NatMap.unionWith Nat.max m1 m2)
+
+data.NatBag.union.doc : Doc
+data.NatBag.union.doc =
+ use NatBag fromList
+ {{
+ Computes the union of two {type NatBag}s. This is the smallest {type NatBag}
+ that is a {NatBag.superbag} of both arguments. Returns a new {type NatBag}
+ with the elements that appear in either argument, such that if an element
+ appears `n` times in the first and `m` times in the second, the element will
+ appear `` Nat.max n m `` times in the result.
+
+ # Example
+
+ ```
+ NatBag.toList (NatBag.union (fromList [1, 2, 3]) (fromList [2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatBag.intersect} to compute the intersection between two
+ {type NatBag}s.
+ * {NatBag.difference} to compute the difference between two {type NatBag}s.
+ * {add.nonempty} to add a single {type Nat} to a {type NatBag}.
+ * {NatBag.addN} to add multiple occurrences of a {type Nat} to a
+ {type NatBag}.
+ * {NatBag.addAll} to add a whole bag to another.
+ }}
+
+test> data.NatBag.union.test : [Result]
+data.NatBag.union.test = test.verify do
+ use NatBag fromList superbag union
+ use Random listOf natIn
+ _ = Each.range 0 100
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ b1 = fromList xs
+ b2 = fromList ys
+ ensure (superbag (union b1 b2) b1)
+ ensure (superbag (union b1 b2) b2)
+
+data.NatMap.adjust : (a ->{g} a) -> Nat -> NatMap a ->{g} NatMap a
+data.NatMap.adjust f k t = NatMap.adjustWithKey (do x -> f x) k t
+
+data.NatMap.adjust.doc : Doc
+data.NatMap.adjust.doc =
+ {{
+ Modifies the value at a key in a {type NatMap}, using a function. If the key
+ is not present, the map is returned unchanged.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.adjust
+ Text.toUppercase 1 (NatMap.fromList [(1, "foo"), (2, "bar")]))
+ ```
+
+ # See also
+
+ * {NatMap.adjustWithKey} for a version of this that also receives the key.
+ * {NatMap.update} for a version that can delete the key.
+ * {NatMap.updateWithKey} for a version that can delete the key and also
+ receives the key.
+ * {NatMap.alter} for a version that can also insert the key if not present.
+ * {NatMap.map} for a version that can modify all values at once.
+ * {NatMap.mapWithKey} for a version that can modify all values at once and
+ also receives the key.
+ }}
+
+test> data.NatMap.adjust.test = test.verify do
+ use NatMap get
+ use Random listOf nat
+ _ = Each.range 0 100
+ len = Random.natIn 0 100
+ keys = listOf nat do len
+ xs = listOf (do (oneOfNonempty (nat() +| keys), Random.boolean())) do len
+ k = oneOfNonempty (nat() +| keys)
+ fs = (do id) +| [do Boolean.not, do const true, do const false]
+ f = List.Nonempty.foldMap (x y -> do Random.either x y) id fs ()
+ m = NatMap.fromList xs
+ ensureEqual (get k (NatMap.adjust f k m)) (Optional.map f (get k m))
+
+data.NatMap.adjustWithKey :
+ (Nat ->{g} a ->{g} a) -> Nat -> NatMap a ->{g} NatMap a
+data.NatMap.adjustWithKey f k t =
+ NatMap.updateWithKey (k' x -> Some (f k' x)) k t
+
+data.NatMap.adjustWithKey.doc : Doc
+data.NatMap.adjustWithKey.doc =
+ use Nat +
+ {{
+ Modifies the value at a key in a {type NatMap}, using a function. If the key
+ is not present, the map is returned unchanged. The function receives the key
+ as its first argument and the value as its second.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.adjustWithKey
+ (k x -> k + x) 1 (NatMap.fromList [(1, 10), (2, 20)]))
+ ```
+
+ # See also
+
+ * {NatMap.adjust} for a version of this that does not receive the key.
+ * {NatMap.updateWithKey} for a version that can delete the key.
+ * {NatMap.alterWithKey} for a version that can also insert the key if not
+ present.
+ * {NatMap.mapWithKey} for a version that can modify all values at once.
+ }}
+
+test> data.NatMap.adjustWithKey.test =
+ test.verify do
+ use Boolean !=
+ use NatMap get
+ use Random listOf nat
+ _ = Each.range 0 100
+ len = Random.natIn 0 100
+ keys = listOf nat do len
+ xs = listOf (do (oneOfNonempty (nat() +| keys), Random.boolean())) do len
+ k = oneOfNonempty (nat() +| keys)
+ f k v = Nat.isEven k != v
+ m = NatMap.fromList xs
+ ensureEqual
+ (get k (NatMap.adjustWithKey f k m)) (Optional.map (f k) (get k m))
+
+data.NatMap.align : NatMap a -> NatMap b -> NatMap (OneOrBoth a b)
+data.NatMap.align = NatMap.alignWith id
+
+data.NatMap.align.doc : Doc
+data.NatMap.align.doc =
+ use NatMap fromList
+ {{
+ Aligns two maps into a map of {type OneOrBoth} values.
+
+ The result will have the same keys as the union of the keys of the two input
+ maps, and each value will be a {type OneOrBoth} containing the corresponding
+ values from the two input maps. If a key is present in only one of the input
+ maps, the result will contain {This} or {That} values accordingly. If a key
+ is present in both input maps, the result will contain a {Both} value.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.align
+ (fromList [(1, "hello"), (2, "world")]) (fromList [(2, 42), (3, 43)]))
+ ```
+
+ # See also
+
+ * {NatMap.alignWith} - a variant where you can specify a function to apply
+ to the values.
+ }}
+
+data.NatMap.alignWith :
+ (OneOrBoth a b ->{g} c) -> NatMap a -> NatMap b ->{g} NatMap c
+data.NatMap.alignWith f m1 m2 = NatMap.alignWithKey (_ x -> f x) m1 m2
+
+data.NatMap.alignWith.doc : Doc
+data.NatMap.alignWith.doc =
+ use NatMap fromList
+ use Text ++
+ {{
+ Aligns two maps into a map of values using a function.
+
+ The result will have the same keys as the union of the keys of the two input
+ maps, and each value will be the result of applying the given function to the
+ corresponding values from the two input maps – {This} for keys that only
+ appear in the first map, {That} for keys that only appear in the second map,
+ and {Both} for keys that appear in both maps.
+
+ # Example
+
+ ```
+ f = cases
+ This a -> "only in the first map: " ++ a
+ That b -> "only in the second map: " ++ b
+ Both a b -> "in both maps: " ++ a ++ " and " ++ b
+ NatMap.values
+ (NatMap.alignWith
+ f
+ (fromList [(1, "circuit"), (2, "quasar")])
+ (fromList [(2, "voyage"), (3, "harmony")]))
+ ```
+
+ # See also
+
+ * {NatMap.align} - a variant that returns a map of {type OneOrBoth} values.
+ * {NatMap.alignWithKey} - a variant where the function also receives the
+ key.
+ }}
+
+data.NatMap.alignWithKey :
+ (Nat ->{e} OneOrBoth a b ->{f} c) -> NatMap a -> NatMap b ->{e, f} NatMap c
+data.NatMap.alignWithKey f = cases
+ NatMap None, t2 -> NatMap.mapWithKey (k b -> f k (That b)) t2
+ t1, NatMap None -> NatMap.mapWithKey (k a -> f k (This a)) t1
+ NatMap (Some t1), NatMap (Some t2) ->
+ NatMap (Some (NatMap.Nonempty.alignWithKey f t1 t2))
+
+data.NatMap.alignWithKey.doc : Doc
+data.NatMap.alignWithKey.doc =
+ use Nat toText
+ use NatMap fromList
+ use Text ++
+ {{
+ Aligns two maps into a map of values using a function.
+
+ The result will have the same keys as the union of the keys of the two input
+ maps, and each value will be the result of applying the given function to the
+ corresponding key-value pairs from the two input maps. The function receives
+ {This} for values under keys that are present in only the first input map,
+ {That} for values under keys that are present in only the second input map,
+ and {Both} for keys that are present in both input maps.
+
+ # Example
+
+ ```
+ f k = cases
+ This a -> "only in the first map: " ++ toText k ++ " -> " ++ a
+ That b -> "only in the second map: " ++ toText k ++ " -> " ++ b
+ Both a b -> "in both maps: " ++ toText k ++ " -> " ++ a ++ " and " ++ b
+ NatMap.values
+ (NatMap.alignWithKey
+ f
+ (fromList [(1, "circuit"), (2, "quasar")])
+ (fromList [(2, "voyage"), (3, "harmony")]))
+ ```
+
+ # See also
+
+ * {NatMap.align} - a variant that returns a map of {type OneOrBoth} values.
+ * {NatMap.alignWith} - a variant where the function doesn't take the key.
+ }}
+
+data.NatMap.alter :
+ (Optional a ->{g} Optional a) -> Nat -> NatMap a ->{g} NatMap a
+data.NatMap.alter f k t = NatMap.alterWithKey (do x -> f x) k t
+
+data.NatMap.alter.doc : Doc
+data.NatMap.alter.doc =
+ use NatMap alter fromList toList
+ use Text ++
+ {{
+ An expression `` alter f k t `` alters the value under the key `k` in the
+ {type NatMap} `t`, or the absence thereof, using the function `f`.
+
+ # Examples
+
+ If the function returns {None}, the key is deleted:
+
+ ```
+ toList (alter (x -> None) 1 (fromList [(1, "foo"), (2, "bar")]))
+ ```
+
+ If the function returns ``Some x``, the value under the key is updated:
+
+ ```
+ toList
+ (alter
+ (x -> Some (Optional.getOrElse "" x ++ "baz"))
+ 1
+ (fromList [(1, "foo"), (2, "bar")]))
+ ```
+
+ If the key is not present, the function receives {None}, and can insert the
+ key by returning {Some} of the new value:
+
+ ```
+ toList (alter (x -> Some "baz") 3 (fromList [(1, "foo"), (2, "bar")]))
+ ```
+
+ # See also
+
+ * {NatMap.alterWithKey} for a version of this that also receives the key.
+ * {NatMap.update} for a version that does nothing if the key is not
+ present.
+ }}
+
+test> data.NatMap.alter.test =
+ test.verify do
+ use NatMap get
+ use Random boolean listOf nat
+ _ = Each.range 0 100
+ len = Random.natIn 0 10
+ keys = listOf nat do len
+ xs = listOf (do (oneOfNonempty (nat() +| keys), boolean())) do len
+ b = boolean()
+ k = oneOfNonempty (nat() +| keys)
+ f =
+ oneOfNonempty
+ (Abort.toBug do
+ List.nonempty (deprecated.sample maxNat do someOrNone yesNo() b ()))
+ m = NatMap.fromList xs
+ ensureEqual (get k (NatMap.alter f k m)) (f (get k m))
+
+data.NatMap.alterWithKey :
+ (Nat ->{g} Optional a ->{g} Optional a) -> Nat -> NatMap a ->{g} NatMap a
+data.NatMap.alterWithKey f k = cases
+ NatMap None ->
+ match f k None with
+ None -> NatMap.empty
+ Some v -> toNatMap (NatMap.singleton k v)
+ NatMap (Some t) -> Nonempty.alterWithKey f k t
+
+data.NatMap.alterWithKey.doc : Doc
+data.NatMap.alterWithKey.doc =
+ use Nat * +
+ use NatMap alterWithKey fromList toList
+ {{
+ An expression `` alterWithKey f k t `` alters the value under the key `k` in
+ the {type NatMap} `t`, or the absence thereof, using the function `f`.
+
+ The function receives the key as its first argument and the value as its
+ second (or {None} if the key is not present).
+
+ # Examples
+
+ If the function returns {None}, the key is deleted:
+
+ ```
+ toList (alterWithKey (k x -> None) 1 (fromList [(1, "foo"), (2, "bar")]))
+ ```
+
+ If the function returns ``Some x``, the value under the key is updated to
+ `x`:
+
+ ```
+ toList
+ (alterWithKey
+ (k x -> Optional.map (v -> v + k) x) 1 (fromList [(1, 10), (2, 20)]))
+ ```
+
+ If the key is not present, the function receives {None} for the value, and
+ can insert the key by returning {Some} of the new value.
+
+ ```
+ toList
+ (alterWithKey
+ (k x -> Optional.orElse x (Some (k * 10)))
+ 3
+ (fromList [(1, 10), (2, 20)]))
+ ```
+
+ # See also
+
+ * {NatMap.alter} for a version of this that does not receive the key.
+ * {NatMap.updateWithKey} for a version that does nothing if the key is not
+ present.
+ }}
+
+test> data.NatMap.alterWithKey.test =
+ test.verify do
+ use NatMap get
+ use Random listOf nat natIn
+ _ = Each.range 0 100
+ len = natIn 0 4
+ keys = listOf (do natIn 0 100) do len
+ xs = listOf (do (oneOfNonempty (nat() +| keys), Random.boolean())) do len
+ k = oneOfNonempty (nat() +| keys)
+ b = Nat.isEven k
+ f =
+ const
+ (oneOfNonempty
+ (Abort.toBug do
+ List.nonempty (deprecated.sample maxNat do someOrNone yesNo() b ())))
+ m = NatMap.fromList xs
+ ensureEqual (get k (NatMap.alterWithKey f k m)) (f k (get k m))
+
+data.NatMap.breakOffMax : NatMap a ->{Abort} ((Nat, a), NatMap a)
+data.NatMap.breakOffMax = cases
+ NatMap None -> abort
+ NatMap (Some t) -> NatMap.Nonempty.breakOffMax t
+
+data.NatMap.breakOffMax.doc : Doc
+data.NatMap.breakOffMax.doc =
+ {{
+ Returns the maximum key and value in the {type NatMap}, and the map without
+ that key.
+
+ If the map is empty, this calls {abort}.
+
+ # Example
+
+ ```
+ toOptional! do
+ Tuple.second
+ NatMap.toList
+ (NatMap.breakOffMax (NatMap.fromList [(1, "foo"), (2, "bar")]))
+ ```
+
+ # See also
+
+ * {NatMap.breakOffMin} for the opposite operation, returning the minimum
+ key and value.
+ * {NatMap.deleteMax} for a version that only returns the updated map.
+ * {NatMap.getMax} for a version that does not remove the key.
+ }}
+
+test> data.NatMap.breakOffMax.test =
+ test.verify do
+ use Nat >=
+ use Random listOf nat
+ _ = Each.range 0 100
+ len = Random.natIn 0 100
+ keys = listOf nat do len
+ xs = listOf (do (oneOfNonempty (nat() +| keys), Random.boolean())) do len
+ m = NatMap.fromList xs
+ f = cases
+ Some (k, v), (k', v') -> if k >= k' then Some (k, v) else Some (k', v')
+ None, (k', v') -> Some (k', v')
+ maxEntry = List.foldLeft f None (NatMap.toList m)
+ ensureEqual
+ (toOptional! do NatMap.breakOffMax m)
+ (maxEntry |> (Optional.map cases (k, v) -> ((k, v), NatMap.delete k m)))
+
+data.NatMap.breakOffMin : NatMap a ->{Abort} ((Nat, a), NatMap a)
+data.NatMap.breakOffMin = cases
+ NatMap None -> abort
+ NatMap (Some t) -> NatMap.Nonempty.breakOffMin t
+
+data.NatMap.breakOffMin.doc : Doc
+data.NatMap.breakOffMin.doc =
+ {{
+ Returns the minimum key and value in the {type NatMap}, and the map without
+ that key.
+
+ If the map is empty, this calls {abort}.
+
+ # Example
+
+ ```
+ toOptional! do
+ Tuple.second
+ NatMap.toList
+ (NatMap.breakOffMin (NatMap.fromList [(1, "foo"), (2, "bar")]))
+ ```
+
+ # See also
+
+ * {NatMap.breakOffMax} for the opposite operation, returning the maximum
+ key and value.
+ * {NatMap.deleteMin} for a version that only returns the updated map.
+ * {NatMap.getMin} for a version that does not remove the key.
+ }}
+
+test> data.NatMap.breakOffMin.test =
+ test.verify do
+ use Nat <=
+ use Random listOf nat
+ _ = Each.range 0 100
+ len = Random.natIn 0 100
+ keys = listOf nat do len
+ xs = listOf (do (oneOfNonempty (nat() +| keys), Random.boolean())) do len
+ m = NatMap.fromList xs
+ f = cases
+ Some (k, v), (k', v') -> if k <= k' then Some (k, v) else Some (k', v')
+ None, (k', v') -> Some (k', v')
+ minEntry = List.foldLeft f None (NatMap.toList m)
+ ensureEqual
+ (toOptional! do NatMap.breakOffMin m)
+ (minEntry |> (Optional.map cases (k, v) -> ((k, v), NatMap.delete k m)))
+
+data.NatMap.compareBy :
+ (a ->{g} a ->{g} Ordering) -> NatMap a -> NatMap a ->{g} Ordering
+data.NatMap.compareBy f = cases
+ NatMap None, NatMap None -> Equal
+ NatMap None, _ -> Less
+ _, NatMap None -> Greater
+ NatMap (Some t1), NatMap (Some t2) -> Nonempty.compareBy f t1 t2
+
+data.NatMap.compareBy.doc : Doc
+data.NatMap.compareBy.doc =
+ use NatMap compareBy fromList
+ use Universal ordering
+ {{
+ Compares two {type NatMap}s using the given comparison function.
+
+ Defines a [total order](https://en.wikipedia.org/wiki/Total_order) on
+ {type NatMap}s.
+
+ The entries are compared in ascending order of key, and the given comparison
+ function is applied to the values of corresponding entries.
+
+ Returns:
+
+ * `` Less `` for any of the following:
+ * The first map is a proper prefix of the second map
+ * The first entry that differs between the maps has a lower key in the
+ first map.
+ * The first entry that differs between the maps has a lower value in the
+ first map, according to the given comparison function.
+ * `` Equal `` when the maps are equal.
+ * `` Greater `` for any of the following:
+ * The second map is a proper prefix of the first map
+ * The first entry that differs between the maps has a lower key in the
+ second map.
+ * The first entry that differs between the maps has a lower value in the
+ second map, according to the given comparison function.
+
+ # Examples
+
+ The maps are equal:
+
+ ```
+ compareBy
+ ordering
+ (fromList [(1, "foo"), (2, "bar")])
+ (fromList [(1, "foo"), (2, "bar")])
+ ```
+
+ The first map is a prefix of the second map:
+
+ ```
+ compareBy
+ ordering
+ (fromList [(1, "foo"), (2, "bar")])
+ (fromList [(1, "foo"), (2, "bar"), (3, "baz")])
+ ```
+
+ The second map is a prefix of the first map:
+
+ ```
+ compareBy
+ ordering (fromList [(1, "foo"), (2, "bar")]) (fromList [(1, "foo")])
+ ```
+
+ The first value that differs is larger in the second map:
+
+ ```
+ compareBy
+ ordering
+ (fromList [(1, "foo"), (2, "bar")])
+ (fromList [(1, "foo"), (2, "baz")])
+ ```
+
+ The first key that differs is larger in the second map:
+
+ ```
+ compareBy
+ ordering
+ (fromList [(1, "foo"), (2, "bar")])
+ (fromList [(1, "foo"), (3, "bar")])
+ ```
+
+ The first key that differs is larger in the first map:
+
+ ```
+ compareBy
+ ordering
+ (fromList [(1, "foo"), (3, "bar")])
+ (fromList [(1, "foo"), (2, "bar")])
+ ```
+
+ The first value that differs is larger in the first map:
+
+ ```
+ compareBy
+ ordering
+ (fromList [(1, "foo"), (2, "baz")])
+ (fromList [(1, "foo"), (2, "bar")])
+ ```
+
+ # See also
+
+ * {NatMap.equalBy} to compare for equality only.
+ * {NatMap.submapCompareBy} to check if one map is a submap of another,
+ using a comparison function.
+ }}
+
+test> data.NatMap.compareBy.test =
+ test.verify do
+ use NatMap fromList size toList
+ use Ordering andThen
+ use Random boolean listOf nat natIn
+ use Universal ordering
+ _ = Each.range 0 101
+ len = natIn 0 4
+ keys = listOf (do natIn 0 100) do len
+ xs = listOf (do (oneOfNonempty (nat() +| keys), boolean())) do len
+ ys = listOf (do (oneOfNonempty (nat() +| keys), boolean())) do len
+ m1 = fromList xs
+ m2 = fromList ys
+ listComp =
+ andThen
+ (List.foldLeft
+ andThen Equal (List.zipWith ordering (toList m1) (toList m2)))
+ (ordering (size m1) (size m2))
+ ensureEqual (NatMap.compareBy ordering m1 m2) listComp
+
+data.NatMap.contains : Nat -> NatMap a -> Boolean
+data.NatMap.contains k m = match NatMap.get k m with
+ None -> false
+ Some _ -> true
+
+data.NatMap.contains.doc : Doc
+data.NatMap.contains.doc =
+ {{
+ Checks if the given key is present in the {type NatMap}.
+
+ # Example
+
+ ```
+ NatMap.contains 1 (NatMap.fromList [(1, "foo"), (2, "bar")])
+ ```
+
+ # See also
+
+ * {NatMap.get} to get the value if it is present.
+ * {NatMap.isSubmapOf} to check if one map contains all the keys of another.
+ }}
+
+data.NatMap.delete : Nat -> NatMap a -> NatMap a
+data.NatMap.delete k = cases
+ NatMap None -> NatMap.empty
+ NatMap (Some t) -> NatMap.Nonempty.delete k t
+
+data.NatMap.delete.doc : Doc
+data.NatMap.delete.doc =
+ {{
+ Deletes the given key from the {type NatMap}.
+
+ # Example
+
+ ```
+ NatMap.toList (NatMap.delete 1 (NatMap.fromList [(1, "foo"), (2, "bar")]))
+ ```
+
+ # See also
+
+ * {NatMap.deleteMax} to delete the largest key.
+ * {NatMap.deleteMin} to delete the smallest key.
+ * {NatMap.difference} to delete all keys in one map from another.
+ }}
+
+data.NatMap.deleteMax : NatMap a ->{Abort} NatMap a
+data.NatMap.deleteMax t =
+ (_, t') = NatMap.breakOffMax t
+ t'
+
+data.NatMap.deleteMax.doc : Doc
+data.NatMap.deleteMax.doc =
+ {{
+ Deletes the largest key from the {type NatMap}.
+
+ If the map is empty, this returns an empty map.
+
+ # Example
+
+ ```
+ toOptional! do
+ NatMap.toList
+ (NatMap.deleteMax (NatMap.fromList [(1, "foo"), (2, "bar")]))
+ ```
+
+ # See also
+
+ * {NatMap.delete} to delete a specific key.
+ * {NatMap.deleteMin} to delete the smallest key.
+ * {NatMap.difference} to delete all keys in one map from another.
+ }}
+
+data.NatMap.deleteMin : NatMap a ->{Abort} NatMap a
+data.NatMap.deleteMin t =
+ (_, t') = NatMap.breakOffMin t
+ t'
+
+data.NatMap.deleteMin.doc : Doc
+data.NatMap.deleteMin.doc =
+ {{
+ Deletes the smallest key from the {type NatMap}.
+
+ If the map is empty, this calls {abort}.
+
+ # Example
+
+ ```
+ toOptional! do
+ NatMap.toList
+ (NatMap.deleteMin (NatMap.fromList [(1, "foo"), (2, "bar")]))
+ ```
+
+ # See also
+
+ * {NatMap.delete} to delete a specific key.
+ * {NatMap.deleteMax} to delete the largest key.
+ * {NatMap.difference} to delete all keys in one map from another.
+ }}
+
+data.NatMap.difference : NatMap a -> NatMap b -> NatMap a
+data.NatMap.difference = NatMap.differenceWith do do None
+
+data.NatMap.difference.doc : Doc
+data.NatMap.difference.doc =
+ use NatMap fromList
+ {{
+ Deletes all keys in the second {type NatMap} from the first.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.difference
+ (fromList [(1, "foo"), (2, "bar")]) (fromList [(1, "foo")]))
+ ```
+
+ # See also
+
+ * {NatMap.differenceWith} to more finely control which keys are deleted, or
+ replace specific values instead of deleting them.
+ * {NatMap.delete} to delete a specific key.
+ * {NatMap.deleteMax} to delete the largest key.
+ * {NatMap.deleteMin} to delete the smallest key.
+ * {NatMap.intersect} to delete all keys not in both maps.
+ }}
+
+data.NatMap.differenceWith :
+ (a ->{g} b ->{g} Optional a) -> NatMap a -> NatMap b ->{g} NatMap a
+data.NatMap.differenceWith f = NatMap.differenceWithKey do x y -> f x y
+
+data.NatMap.differenceWith.doc : Doc
+data.NatMap.differenceWith.doc =
+ use NatMap fromList
+ use Text ==
+ {{
+ Deletes keys in the second {type NatMap} from the first, or replaces their
+ values with the result of the given function.
+
+ # Example
+
+ Delete all keys in the second map from the first unless the values under
+ those keys are equal in both maps:
+
+ ```
+ NatMap.toList
+ (NatMap.differenceWith
+ (x y -> (if x == y then Some x else None))
+ (fromList [(1, "foo"), (2, "bar")])
+ (fromList [(1, "foo"), (2, "baz")]))
+ ```
+
+ # See also
+
+ * {NatMap.differenceWithKey} for a version that also receives the key.
+ * {NatMap.difference} to delete all keys in one map from another.
+ * {NatMap.intersectWith} to delete all keys not in both maps, using a
+ function to combine the remaining values.
+ }}
+
+data.NatMap.differenceWithKey :
+ (Nat ->{g} a ->{g} b ->{g} Optional a) -> NatMap a -> NatMap b ->{g} NatMap a
+data.NatMap.differenceWithKey f = cases
+ NatMap None, _ -> NatMap.empty
+ x, NatMap None -> x
+ NatMap (Some t1), NatMap (Some t2) -> Nonempty.differenceWithKey f t1 t2
+
+data.NatMap.differenceWithKey.doc : Doc
+data.NatMap.differenceWithKey.doc =
+ use Nat ==
+ use NatMap fromList
+ {{
+ Deletes keys in the second {type NatMap} from the first, or replaces their
+ values with the result of the given function.
+
+ # Example
+
+ Delete all keys in the second map from the first unless the values under
+ those keys are both equal to the key:
+
+ ```
+ NatMap.toList
+ (NatMap.differenceWithKey
+ (k x y -> (if x == y && x == k then Some x else None))
+ (fromList [(1, 1), (2, 2)])
+ (fromList [(1, 1), (2, 3)]))
+ ```
+
+ # See also
+
+ * {NatMap.differenceWith} for a version that does not receive the key.
+ * {NatMap.difference} to delete all keys in one map from another.
+ * {NatMap.intersectWithKey} to delete all keys not in both maps, using a
+ function to combine the remaining values.
+ }}
+
+data.NatMap.doc : Doc
+data.NatMap.doc =
+ {{
+ An efficient implementation of maps from keys of type {type Nat} to values of
+ some type. This type is parameterised by the type of the values. This
+ specialized map is much more efficient than the general map type, {type Map},
+ when the keys are of type {type Nat} or can be encoded as {type Nat}s.
+
+ {{
+ docAside
+ {{
+ The implementation of {type NatMap} uses a patricia tree, as described in
+ ["Fast Mergeable Integer Maps" by Chris Okasaki and Andy Gill](https://www.cs.tufts.edu/~nr/cs257/archive/chris-okasaki/IntMap98.ps).
+ The code is largely transliterated to Unison from the Haskell
+ implementation by Daan Leijen and Andriy Palamarchuk, which is in turn
+ based on the paper.
+ }} }}
+
+ # Constructing maps
+
+ The empty map:
+
+ @signature{NatMap.empty}
+
+ A map with a single key/value pair:
+
+ @signature{NatMap.singleton}
+
+ Construct a map from a list of key/value pairs:
+
+ @signature{NatMap.fromList}
+
+ Construct a map from a list of key/value pairs, using a combining function
+ to resolve conflicts:
+
+ @signature{NatMap.fromListWith}
+
+ Construct a map from a list of key/value pairs, using a combining function
+ to resolve conflicts, and passing the key to the combining function:
+
+ @signature{NatMap.fromListWithKey}
+
+ # Querying
+
+ Look up the value at a key:
+
+ @signature{NatMap.get}
+
+ Look up the value at a key, or return a default value if the key is not
+ present:
+
+ @signature{NatMap.getOrElse}
+
+ Check if a key is in the map:
+
+ @signature{NatMap.contains}
+
+ Get the size of the map:
+
+ @signature{NatMap.size}
+
+ Check if the map is empty:
+
+ @signature{NatMap.isEmpty}
+
+ # Inserting
+
+ Insert a key/value pair into the map:
+
+ @signature{NatMap.insert}
+
+ Insert a key/value pair, returning a nonempty map:
+
+ @signature{NatMap.insert.nonempty}
+
+ Insert a key/value pair into the map, using a combining function to resolve
+ conflicts:
+
+ @signature{NatMap.insertWith}
+
+ Insert a key/value pair into the map, using a combining function to resolve
+ conflicts, and passing the key to the combining function:
+
+ @signature{NatMap.insertWithKey}
+
+ Insert a key/value pair into the map, using a combining function to resolve
+ conflicts, and passing the key to the combining function, and returning the
+ old value if it was present:
+
+ @signature{NatMap.insertGetWithKey}
+
+ # Deleting and updating values
+
+ Delete a key and its value from the map:
+
+ @signature{NatMap.delete}
+
+ Update the value at a key with a function, or remove the key if the
+ function returns {None}:
+
+ @signature{NatMap.update}
+
+ Update the value at a key with a function, or remove the key if the
+ function returns {None}, passing the key to the function:
+
+ @signature{NatMap.updateWithKey}
+
+ Update the value at a key with a function, or remove the key if the
+ function returns {None}, passing the key to the function, and returning the
+ old value if it was present:
+
+ @signature{NatMap.updateGetWithKey}
+
+ Update, insert, or delete the value at a key with a function, depending on
+ whether the key is present in the map:
+
+ @signature{NatMap.alter}
+
+ Update, insert, or delete the value at a key with a function, passing the
+ key to the function.
+
+ @signature{NatMap.alterWithKey}
+
+ # Combining maps
+
+ ## Unions
+
+ Add the entries from one map to another, preferring the entries from the
+ first map if there are conflicts:
+
+ @signature{NatMap.union}
+
+ Merge two maps, using a combining function to resolve conflicts:
+
+ @signature{NatMap.unionWith}
+
+ Merge two maps, using a combining function to resolve conflicts, and
+ passing the key to the combining function:
+
+ @signature{NatMap.unionWithKey}
+
+ Combine the entries from a list of maps, preferring the entries from the
+ first map if there are conflicts:
+
+ @signature{NatMap.unions}
+
+ Merge list of maps, using a combining function to resolve conflicts:
+
+ @signature{NatMap.unionsWith}
+
+ ## Difference
+
+ Remove the entries from one map that are present in another map:
+
+ @signature{NatMap.difference}
+
+ Remove the entries from one map that are present in another map, using a
+ combining function to resolve conflicts:
+
+ @signature{NatMap.differenceWith}
+
+ Remove the entries from one map that are present in another map, using a
+ combining function to resolve conflicts, and passing the key to the
+ combining function:
+
+ @signature{NatMap.differenceWithKey}
+
+ ## Intersection
+
+ Keep only the entries from one map that are present in another map:
+
+ @signature{NatMap.intersect}
+
+ Keep only entries present in both of two maps, using a combining
+ function to resolve conflicts:
+
+ @signature{NatMap.intersectWith}
+
+ Keep only entries present in both of two maps, using a combining
+ function to resolve conflicts, and passing the key to the combining
+ function:
+
+ @signature{NatMap.intersectWithKey}
+
+ # Transforming maps
+
+ Apply a function to every value in a map:
+
+ @signature{NatMap.map}
+
+ Apply a function to every value in a map, passing the key to the function:
+
+ @signature{NatMap.mapWithKey}
+
+ Apply a partial function to every value in a map, removing entries for
+ which the function returns {None}:
+
+ @signature{NatMap.mapOptional}
+
+ # Summarizing maps
+
+ Fold a function over the entries in a map, from lowest key to highest key:
+
+ @signature{NatMap.fold}
+
+ Fold a function over the entries in a map, from lowest key to highest key,
+ passing the key to the function:
+
+ @signature{NatMap.foldWithKey}
+
+ # Filtering and partitioning
+
+ Filter a map, keeping only entries for which the predicate returns
+ ``true``:
+
+ @signature{NatMap.filter}
+
+ Filter a map, keeping only entries for which the predicate returns
+ ``true``, passing the key to the predicate:
+
+ @signature{NatMap.filterWithKey}
+
+ Partition a map into two maps, one containing entries for which the
+ predicate returns ``true``, and one containing entries for which the
+ predicate returns ``false``:
+
+ @signature{NatMap.partition}
+
+ Partition a map into two maps, one containing entries for which the
+ predicate returns ``true``, and one containing entries for which the
+ predicate returns ``false``, passing the key to the predicate:
+
+ @signature{NatMap.partitionWithKey}
+
+ Partition a map into two maps, one containing entries for which the
+ function returns {Left} and one containing entries for which the function
+ returns {Right}:
+
+ @signature{NatMap.mapEither}
+
+ Partition a map into two maps, one containing entries for which the
+ function returns {Left} and one containing entries for which the function
+ returns {Right}, passing the key to the function:
+
+ @signature{NatMap.mapEitherWithKey}
+
+ Split a map into two maps at a given key:
+
+ @signature{NatMap.split}
+
+ # Comparing maps
+
+ Check if two maps are equal according to a given comparison on the values:
+
+ @signature{NatMap.equalBy}
+
+ Get a partial order for two maps given a total order on the values:
+
+ @signature{NatMap.compareBy}
+
+ Check if all entries in one map are present in another map, using a given
+ comparison function to compare the values:
+
+ @signature{NatMap.isSubmapOfBy}
+
+ Check if all entries in one map are present in another map, using {===} to
+ compare the values:
+
+ @signature{NatMap.isSubmapOf}
+
+ Check that one map is a proper submap of another map, using a given
+ comparison function to compare the values:
+
+ @signature{NatMap.isProperSubmapOfBy}
+
+ Check that one map is a proper submap of another map, using {===} to
+ compare the values:
+
+ @signature{NatMap.isProperSubmapOf}
+
+ # Operations on the minimum or maximum key
+
+ Get the minimum key in a map:
+
+ @signature{NatMap.getMin}
+
+ Get the maximum key in a map:
+
+ @signature{NatMap.getMax}
+
+ Break a map into its minimum key and value, and the rest of the map:
+
+ @signature{NatMap.breakOffMin}
+
+ Break a map into its maximum key and value, and the rest of the map:
+
+ @signature{NatMap.breakOffMax}
+
+ Break a map into the value at the minimum key, and the rest of the map:
+
+ @signature{NatMap.minView}
+
+ Break a map into the value at the maximum key, and the rest of the map:
+
+ @signature{NatMap.maxView}
+
+ Remove the minimum key from a map:
+
+ @signature{NatMap.deleteMin}
+
+ Remove the maximum key from a map:
+
+ @signature{NatMap.deleteMax}
+
+ Update the value at the minimum key with a function:
+
+ @signature{NatMap.updateMin}
+
+ Update the value at the maximum key with a function:
+
+ @signature{NatMap.updateMax}
+
+ Update the value at the minimum key with a function, passing the key to the
+ function:
+
+ @signature{NatMap.updateMinWithKey}
+
+ Update the value at the maximum key with a function, passing the key to the
+ function:
+
+ @signature{NatMap.updateMaxWithKey}
+
+ # Convertings maps to other types
+
+ Convert a map to a list of key/value pairs:
+
+ @signature{NatMap.toList}
+
+ Get a list of the keys in a map:
+
+ @signature{NatMap.keys}
+
+ Get a list of the values in a map:
+
+ @signature{NatMap.values}
+ }}
+
+data.NatMap.empty : NatMap a
+data.NatMap.empty = NatMap None
+
+data.NatMap.empty.doc : Doc
+data.NatMap.empty.doc = {{ The empty {type NatMap}. }}
+
+data.NatMap.equalBy :
+ (a ->{g} a ->{g} Boolean) -> NatMap a -> NatMap a ->{g} Boolean
+data.NatMap.equalBy f = cases
+ NatMap (Some t1), NatMap (Some t2) -> Nonempty.equalBy f t1 t2
+ NatMap None, NatMap None -> true
+ NatMap _, _ -> false
+
+data.NatMap.equalBy.doc : Doc
+data.NatMap.equalBy.doc =
+ use Nat ==
+ use NatMap equalBy fromList
+ {{
+ Checks if two {type NatMap}s are equal by comparing their values using the
+ given function.
+
+ # Examples
+
+ ```
+ equalBy
+ (x y -> x == y) (fromList [(1, 1), (2, 2)]) (fromList [(1, 1), (2, 2)])
+ ```
+
+ ```
+ equalBy
+ (x y -> x == y) (fromList [(1, 1), (2, 2)]) (fromList [(1, 1), (2, 3)])
+ ```
+
+ # See also
+
+ * {NatMap.compareBy} to order two {type NatMap}s using a custom comparison
+ function.
+ * {NatMap.difference} to see where two {type NatMap}s differ.
+ }}
+
+data.NatMap.filter : (a ->{g} Boolean) -> NatMap a ->{g} NatMap a
+data.NatMap.filter f = NatMap.filterWithKey do x -> f x
+
+data.NatMap.filter.doc : Doc
+data.NatMap.filter.doc =
+ {{
+ Filters a {type NatMap} by retaining only values that satisfy the given
+ predicate.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.filter
+ Nat.isEven (NatMap.fromList [(1, 11), (2, 22), (3, 33), (4, 44)]))
+ ```
+
+ # See also
+
+ * {NatMap.filterWithKey} for a version where the predicate also receives
+ the key.
+ * {NatMap.partition} to split a {type NatMap} into two {type NatMap}s based
+ on a predicate.
+ * {NatMap.intersect} to retain only keys that occur in another map.
+ }}
+
+data.NatMap.filterWithKey :
+ (Nat ->{g} a ->{g} Boolean) -> NatMap a ->{g} NatMap a
+data.NatMap.filterWithKey f = cases
+ NatMap None -> NatMap.empty
+ NatMap (Some t) -> NatMap.Nonempty.filterWithKey f t
+
+data.NatMap.filterWithKey.doc : Doc
+data.NatMap.filterWithKey.doc =
+ use Nat ==
+ {{
+ Filters a {type NatMap} by retaining only entries that satisfy the given
+ predicate.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.filterWithKey
+ (k x -> k == x) (NatMap.fromList [(1, 1), (2, 22), (3, 3), (4, 44)]))
+ ```
+
+ # See also
+
+ * {NatMap.filter} for a version that does not receive the key.
+ * {NatMap.partitionWithKey} to split a {type NatMap} into two
+ {type NatMap}s based on a predicate.
+ * {NatMap.intersectWithKey} to retain only keys that occur in another map.
+ }}
+
+data.NatMap.fold : (a ->{g} b ->{g} b) -> b -> NatMap a ->{g} b
+data.NatMap.fold f = NatMap.foldWithKey do x y -> f x y
+
+data.NatMap.fold.doc : Doc
+data.NatMap.fold.doc =
+ use Nat +
+ {{
+ Summarizes the values in a {type NatMap} using the given function. The
+ function is applied to each value in the {type NatMap} and the function
+ combines the result with the summary so far.
+
+ # Example
+
+ ```
+ NatMap.fold (+) 0 (NatMap.fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.foldWithKey} for a version that also receives the key.
+ }}
+
+data.NatMap.foldWithKey :
+ (Nat ->{g} a ->{g} b ->{g} b) -> b -> NatMap a ->{g} b
+data.NatMap.foldWithKey f z = cases
+ NatMap (Some t) -> Nonempty.foldWithKey f z t
+ NatMap None -> z
+
+data.NatMap.foldWithKey.doc : Doc
+data.NatMap.foldWithKey.doc =
+ use Nat +
+ {{
+ Summarizes the entries in a {type NatMap} using the given function. The
+ function is applied to each entry in the {type NatMap} and the function
+ combines the result with the summary so far.
+
+ # Example
+
+ ```
+ NatMap.foldWithKey
+ (k x y -> k + x + y) 0 (NatMap.fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.fold} for a version that does not receive the key.
+ }}
+
+data.NatMap.fromList : [(Nat, a)] -> NatMap a
+data.NatMap.fromList =
+ List.foldLeft
+ (cases m, (k, v) -> toNatMap (NatMap.insert.nonempty k v m)) NatMap.empty
+
+data.NatMap.fromList.doc : Doc
+data.NatMap.fromList.doc =
+ use NatMap fromList toList
+ {{
+ Creates a {type NatMap} from a list of key-value pairs.
+
+ If the list contains duplicate keys, the last value for a given key is
+ retained.
+
+ # Examples
+
+ ```
+ toList (fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ ```
+ toList (fromList [(1, 10), (2, 20), (1, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.fromListWith} to specify how to combine values for duplicate
+ keys.
+ * {NatMap.fromListWithKey} to specify how to combine values for duplicate
+ keys, taking the key into account.
+ * {toList} to convert a {type NatMap} to a list of key-value pairs.
+ }}
+
+data.NatMap.fromListWith : (a ->{g} a ->{g} a) -> [(Nat, a)] ->{g} NatMap a
+data.NatMap.fromListWith f =
+ List.foldLeft
+ (cases m, (k, v) -> toNatMap (NatMap.insertWith f k v m)) NatMap.empty
+
+data.NatMap.fromListWith.doc : Doc
+data.NatMap.fromListWith.doc =
+ use Nat +
+ use NatMap toList
+ {{
+ Creates a {type NatMap} from a list of key-value pairs, combining duplicate
+ keys using the given function.
+
+ # Example
+
+ ```
+ toList (NatMap.fromListWith (+) [(1, 10), (2, 20), (1, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.fromList} for a version that resolves duplicate keys by retaining
+ the last value for that key.
+ * {NatMap.fromListWithKey} for a version that also takes the key into
+ account when combining duplicates.
+ * {toList} to convert a {type NatMap} to a list of key-value pairs.
+ }}
+
+data.NatMap.fromListWithKey :
+ (Nat ->{g} a ->{g} a ->{g} a) -> [(Nat, a)] ->{g} NatMap a
+data.NatMap.fromListWithKey f =
+ List.foldLeft
+ (cases m, (k, v) -> toNatMap (NatMap.insertWithKey f k v m)) NatMap.empty
+
+data.NatMap.fromListWithKey.doc : Doc
+data.NatMap.fromListWithKey.doc =
+ use Nat +
+ use NatMap toList
+ {{
+ Creates a {type NatMap} from a list of key-value pairs, combining duplicate
+ keys using the given function.
+
+ # Example
+
+ ```
+ toList
+ (NatMap.fromListWithKey (k x y -> k + x + y) [(1, 10), (2, 20), (1, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.fromList} for a version that resolves duplicate keys by retaining
+ the last value for that key.
+ * {NatMap.fromListWith} for a version that does not take the key into
+ account when combining duplicates.
+ * {toList} to convert a {type NatMap} to a list of key-value pairs.
+ }}
+
+data.NatMap.fromMap : Map Nat a -> NatMap a
+data.NatMap.fromMap =
+ Map.foldLeftWithKey
+ (m k v -> toNatMap (NatMap.insert.nonempty k v m)) NatMap.empty
+
+data.NatMap.fromMap.doc : Doc
+data.NatMap.fromMap.doc =
+ use Map fromList
+ use NatMap fromMap toList
+ {{
+ Converts a {type Map} of {type Nat}s to a {type NatMap}.
+
+ # Examples
+
+ ```
+ toList
+ (fromMap (fromList [(1, "one"), (2, "two"), (3, "three"), (4, "four")]))
+ ```
+
+ ```
+ toList
+ (fromMap
+ (fromList
+ [ (1, "one")
+ , (2, "two")
+ , (3, "three")
+ , (4, "four")
+ , (1, "one")
+ , (2, "two")
+ , (1, "one")
+ ]))
+ ```
+
+ # See also
+
+ * {NatMap.toMap} to convert the other way.
+ }}
+
+data.NatMap.get : Nat -> NatMap a -> Optional a
+data.NatMap.get k = cases
+ NatMap (Some t) -> NatMap.Nonempty.get k t
+ NatMap None -> None
+
+data.NatMap.get.doc : Doc
+data.NatMap.get.doc =
+ {{
+ Retrieves the value associated with the given key, if it exists.
+
+ # Example
+
+ ```
+ NatMap.get 2 (NatMap.fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.contains} to check if a key is present in a {type NatMap}.
+ * {NatMap.delete} to delete a key from a {type NatMap}.
+ * {NatMap.insert.nonempty} to insert a key-value pair into a {type NatMap}.
+ }}
+
+data.NatMap.getAbove : Nat -> NatMap v -> Optional (Nat, v)
+data.NatMap.getAbove k = cases
+ NatMap o -> Optional.flatMap (NatMap.Nonempty.getAbove k) o
+
+data.NatMap.getAbove.doc : Doc
+data.NatMap.getAbove.doc =
+ use NatMap fromList
+ {{
+ Returns the key-value pair in a {type NatMap} where the key is the smallest
+ one that is strictly larger than a given key.
+
+ # Examples
+
+ ```
+ NatMap.getAbove 2 (fromList [(1, ?a), (2, ?b), (3, ?c)])
+ ```
+
+ ```
+ NatMap.getAbove 4 (fromList [(1, ?a), (2, ?b), (3, ?c)])
+ ```
+
+ # See also
+
+ * {NatMap.getAtLeast} to get the key-value pair where the key is the
+ smallest one that is larger than __or equal__ to a given key.
+ * {NatMap.getBelow} to get the key-value pair where the key is the largest
+ one that is strictly smaller than a given key.
+ * {NatMap.getAtMost} to get the key-value pair where the key is the largest
+ one that is smaller than or equal to a given key.
+ * {NatMap.Nonempty.getAbove} for the version of this that operates on a
+ nonempty {type NatMap}.
+ }}
+
+data.NatMap.getAtLeast : Nat -> NatMap v -> Optional (Nat, v)
+data.NatMap.getAtLeast k = cases
+ NatMap o -> Optional.flatMap (NatMap.Nonempty.getAtLeast k) o
+
+data.NatMap.getAtLeast.doc : Doc
+data.NatMap.getAtLeast.doc =
+ use NatMap fromList
+ {{
+ Returns the key-value pair in a {type NatMap} where the key is the smallest
+ one that is larger than __or equal__ to a given key.
+
+ # Examples
+
+ ```
+ NatMap.getAtLeast 2 (fromList [(1, ?a), (2, ?b), (3, ?c)])
+ ```
+
+ ```
+ NatMap.getAtLeast 4 (fromList [(1, ?a), (2, ?b), (3, ?c)])
+ ```
+
+ # See also
+
+ * {NatMap.getAbove} to get the key-value pair where the key is the smallest
+ one that is strictly larger than a given key.
+ * {NatMap.getBelow} to get the key-value pair where the key is the largest
+ one that is strictly smaller than a given key.
+ * {NatMap.getAtMost} to get the key-value pair where the key is the largest
+ one that is smaller than or equal to a given key.
+ * {NatMap.Nonempty.getAtLeast} for the version of this that operates on a
+ nonempty {type NatMap}.
+ }}
+
+data.NatMap.getAtMost : Nat -> NatMap v -> Optional (Nat, v)
+data.NatMap.getAtMost k = cases
+ NatMap o -> Optional.flatMap (NatMap.Nonempty.getAtMost k) o
+
+data.NatMap.getAtMost.doc : Doc
+data.NatMap.getAtMost.doc =
+ use NatMap fromList
+ {{
+ Returns the key-value pair in a {type NatMap} where the key is the largest
+ one that is smaller than or equal to a given key.
+
+ # Examples
+
+ ```
+ NatMap.getAtMost 2 (fromList [(1, ?a), (2, ?b), (3, ?c)])
+ ```
+
+ ```
+ NatMap.getAtMost 4 (fromList [(1, ?a), (2, ?b), (3, ?c)])
+ ```
+
+ # See also
+
+ * {NatMap.getBelow} to get the key-value pair where the key is the largest
+ one that is strictly smaller than a given key.
+ * {NatMap.getAbove} to get the key-value pair where the key is the smallest
+ one that is strictly larger than a given key.
+ * {NatMap.getAtLeast} to get the key-value pair where the key is the
+ smallest one that is larger than or equal to a given key.
+ * {NatMap.Nonempty.getAtMost} for the version of this that operates on a
+ nonempty {type NatMap}.
+ }}
+
+data.NatMap.getBelow : Nat -> NatMap v -> Optional (Nat, v)
+data.NatMap.getBelow k = cases
+ NatMap o -> Optional.flatMap (NatMap.Nonempty.getBelow k) o
+
+data.NatMap.getBelow.doc : Doc
+data.NatMap.getBelow.doc =
+ use NatMap fromList
+ {{
+ Returns the key-value pair in a {type NatMap} where the key is the largest
+ one that is strictly smaller than a given key.
+
+ # Examples
+
+ ```
+ NatMap.getBelow 2 (fromList [(1, ?a), (2, ?b), (3, ?c)])
+ ```
+
+ ```
+ NatMap.getBelow 4 (fromList [(1, ?a), (2, ?b), (3, ?c)])
+ ```
+
+ # See also
+
+ * {NatMap.getAtMost} to get the key-value pair where the key is the largest
+ one that is smaller than or equal to a given key.
+ * {NatMap.getAbove} to get the key-value pair where the key is the smallest
+ one that is strictly larger than a given key.
+ * {NatMap.getAtLeast} to get the key-value pair where the key is the
+ smallest one that is larger than or equal to a given key.
+ * {NatMap.Nonempty.getBelow} for the version of this that operates on a
+ nonempty {type NatMap}.
+ }}
+
+data.NatMap.getMax : NatMap a ->{Abort} a
+data.NatMap.getMax t =
+ (v, _) = NatMap.maxView t
+ v
+
+data.NatMap.getMax.doc : Doc
+data.NatMap.getMax.doc =
+ {{
+ Retrieves the value associated with the largest key in the {type NatMap}.
+
+ # Example
+
+ ```
+ toOptional! do NatMap.getMax (NatMap.fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.maxView} for a version of this that also returns the map with the
+ largest key removed.
+ * {NatMap.getMin} to retrieve the value associated with the smallest key.
+ * {NatMap.deleteMax} to remove the largest key from a {type NatMap}.
+ }}
+
+data.NatMap.getMin : NatMap a ->{Abort} a
+data.NatMap.getMin t =
+ (v, _) = NatMap.minView t
+ v
+
+data.NatMap.getMin.doc : Doc
+data.NatMap.getMin.doc =
+ {{
+ Retrieves the value associated with the smallest key in the {type NatMap}.
+
+ # Example
+
+ ```
+ toOptional! do NatMap.getMin (NatMap.fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.minView} for a version of this that also returns the map with the
+ smallest key removed.
+ * {NatMap.getMax} to retrieve the value associated with the largest key.
+ * {NatMap.deleteMin} to remove the smallest key.
+ }}
+
+data.NatMap.getOrAbort : Nat -> NatMap a ->{Abort} a
+data.NatMap.getOrAbort k m = match NatMap.get k m with
+ None -> abort
+ Some v -> v
+
+data.NatMap.getOrAbort.doc : Doc
+data.NatMap.getOrAbort.doc =
+ use NatMap fromList getOrAbort
+ {{
+ Retrieves the value associated with the given key, or calls {abort} if the
+ key is not present.
+
+ # Examples
+
+ ```
+ toOptional! do getOrAbort 2 (fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ ```
+ toOptional! do getOrAbort 4 (fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.get} for a version of this that returns {type Optional} instead
+ of aborting.
+ * {NatMap.getOrElse} to return a default value if the key is not present.
+ }}
+
+data.NatMap.getOrElse : Nat -> a -> NatMap a -> a
+data.NatMap.getOrElse k def m = match NatMap.get k m with
+ None -> def
+ Some v -> v
+
+data.NatMap.getOrElse.doc : Doc
+data.NatMap.getOrElse.doc =
+ use NatMap fromList getOrElse
+ {{
+ Retrieves the value associated with the given key, or returns the given
+ default value if the key is not present.
+
+ # Examples
+
+ ```
+ getOrElse 2 0 (fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ ```
+ getOrElse 4 0 (fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.get} for a version of this that returns {type Optional} instead
+ of a default value.
+ * {NatMap.getOrAbort} to {abort} if the key is not present.
+ }}
+
+data.NatMap.insert : Nat -> a -> NatMap a -> NatMap a
+data.NatMap.insert k v m = toNatMap (NatMap.insert.nonempty k v m)
+
+data.NatMap.insert.doc : Doc
+data.NatMap.insert.doc =
+ use NatMap fromList insert toList
+ {{
+ Inserts a key-value pair into a {type NatMap}, replacing the existing value
+ if the key is already present.
+
+ # Examples
+
+ ```
+ toList (insert 2 20 (fromList [(1, 10), (2, 30), (3, 30)]))
+ ```
+
+ ```
+ toList (insert 4 40 (fromList [(1, 10), (2, 30), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.insert.nonempty} for a version of this that returns a
+ {type NatMap.Nonempty}.
+ * {NatMap.insertWith} for a version of this that allows you to specify a
+ function to combine the new and existing values.
+ * {NatMap.insertWithKey} for a version that also provides the key to the
+ combining function.
+ * {NatMap.insertGetWithKey} for a version that also returns the old value
+ if the key was already present.
+ * {NatMap.delete} to delete a key.
+ * {NatMap.get} to retrieve the value associated with a key.
+ }}
+
+data.NatMap.insert.nonempty : Nat -> a -> NatMap a -> NatMap.Nonempty a
+data.NatMap.insert.nonempty k v = cases
+ NatMap (Some t) -> NatMap.Nonempty.insert k v t
+ NatMap None -> NatMap.singleton k v
+
+data.NatMap.insert.nonempty.doc : Doc
+data.NatMap.insert.nonempty.doc =
+ use NatMap fromList
+ use NatMap.Nonempty toList
+ use NatMap.insert nonempty
+ {{
+ Inserts a key-value pair into a {type NatMap}, replacing the existing value
+ if the key is already present. Returns a {type NatMap.Nonempty}.
+
+ # Examples
+
+ ```
+ toList (nonempty 2 20 (fromList [(1, 10), (2, 30), (3, 30)]))
+ ```
+
+ ```
+ toList (nonempty 4 40 (fromList [(1, 10), (2, 30), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.insertWith} for a version of this that allows you to specify a
+ function to combine the new and existing values.
+ * {NatMap.insertWithKey} for a version that also provides the key to the
+ combining function.
+ * {NatMap.insertGetWithKey} for a version that also returns the old value
+ if the key was already present.
+ * {NatMap.delete} to delete a key.
+ * {NatMap.get} to retrieve the value associated with a key.
+ }}
+
+data.NatMap.insertGetWithKey :
+ (Nat ->{g} a ->{g} a ->{g} a)
+ -> Nat
+ -> a
+ -> NatMap a
+ ->{g} (Optional a, NatMap.Nonempty a)
+data.NatMap.insertGetWithKey f k v = cases
+ NatMap None -> (None, NatMap.singleton k v)
+ NatMap (Some t) -> Nonempty.insertGetWithKey f k v t
+
+data.NatMap.insertGetWithKey.doc : Doc
+data.NatMap.insertGetWithKey.doc =
+ use Nat isEven
+ use NatMap fromList insertGetWithKey
+ use NatMap.Nonempty toList
+ {{
+ Inserts a key-value pair into a {type NatMap}, returning the old value if the
+ key was already present. Returns a {type NatMap.Nonempty}.
+
+ This takes a combining function that is passed the key, the new value, and
+ the old value if the key was already present. The combining function should
+ return the new value to be stored in the map.
+
+ # Examples
+
+ ```
+ (oldKey, newMap) =
+ insertGetWithKey
+ (key old new -> (if isEven key then old else new))
+ 2
+ 20
+ (fromList [(1, 10), (2, 30), (3, 30)])
+ (oldKey, toList newMap)
+ ```
+
+ ```
+ (oldKey, newMap) =
+ insertGetWithKey
+ (key old new -> (if isEven key then old else new))
+ 4
+ 40
+ (fromList [(1, 10), (2, 30), (3, 30)])
+ (oldKey, toList newMap)
+ ```
+
+ # See also
+
+ * {NatMap.insertWithKey} for a version of this that does not return the old
+ value.
+ }}
+
+data.NatMap.insertWith :
+ (a ->{g} a ->{g} a) -> Nat -> a -> NatMap a ->{g} NatMap.Nonempty a
+data.NatMap.insertWith f k v t =
+ NatMap.insertWithKey (do x' y' -> f x' y') k v t
+
+data.NatMap.insertWith.doc : Doc
+data.NatMap.insertWith.doc =
+ use Nat +
+ use NatMap fromList insertWith
+ use NatMap.Nonempty toList
+ {{
+ Inserts a key-value pair into a {type NatMap}, combining the new and existing
+ values if the key is already present.
+
+ Returns a {type NatMap.Nonempty}.
+
+ Takes a combining function that is passed the new and existing values. The
+ combining function should return the new value to be stored in the map.
+
+ # Examples
+
+ ```
+ toList (insertWith (+) 2 20 (fromList [(1, 10), (2, 30), (3, 30)]))
+ ```
+
+ ```
+ toList (insertWith (+) 4 40 (fromList [(1, 10), (2, 30), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.insertWithKey} for a version of this that also provides the key
+ to the combining function.
+ * {NatMap.insert.nonempty} for a version that just replaces the existing
+ value.
+ * {NatMap.insertGetWithKey} for a version that also returns the old value
+ if the key was already present.
+ }}
+
+data.NatMap.insertWithKey :
+ (Nat ->{g} a ->{g} a ->{g} a) -> Nat -> a -> NatMap a ->{g} NatMap.Nonempty a
+data.NatMap.insertWithKey f k v = cases
+ NatMap None -> NatMap.singleton k v
+ NatMap (Some t) -> Nonempty.insertWithKey f k v t
+
+data.NatMap.insertWithKey.doc : Doc
+data.NatMap.insertWithKey.doc =
+ use Nat + - isEven
+ use NatMap fromList insertWithKey
+ use NatMap.Nonempty toList
+ {{
+ Inserts a key-value pair into a {type NatMap}, combining the new and existing
+ values if the key is already present.
+
+ Returns a {type NatMap.Nonempty}.
+
+ Takes a combining function that is passed the key as well as the new and
+ existing values. The combining function should return the new value to be
+ stored in the map.
+
+ # Examples
+
+ ```
+ toList
+ (insertWithKey
+ (key x y -> (if isEven key then x + y else x - y))
+ 2
+ 20
+ (fromList [(1, 10), (2, 30), (3, 30)]))
+ ```
+
+ ```
+ toList
+ (insertWithKey
+ (key x y -> (if isEven key then x + y else x - y))
+ 4
+ 40
+ (fromList [(1, 10), (2, 30), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.insertWith} for a version of this that does not provide the key
+ to the combining function.
+ * {NatMap.insert.nonempty} for a version that just replaces the existing
+ value.
+ * {NatMap.insertGetWithKey} for a version that also returns the old value
+ if the key was already present.
+ }}
+
+data.NatMap.internal.bim :
+ Nat -> Nat -> NatMap.Nonempty a -> NatMap.Nonempty a -> NatMap.Nonempty a
+data.NatMap.internal.bim p m l r =
+ use Nat +
+ use NatMap.Nonempty size
+ sz = size l + size r
+ NatMap.Nonempty.Bin p m sz l r
+
+data.NatMap.internal.bin : Nat -> Nat -> NatMap a -> NatMap a -> NatMap a
+data.NatMap.internal.bin = cases
+ p, m, l, NatMap None -> l
+ p, m, NatMap None, r -> r
+ p, m, NatMap (Some l), NatMap (Some r) ->
+ use Nat +
+ use NatMap.Nonempty size
+ sz = size l + size r
+ NatMap (Some (NatMap.Nonempty.Bin p m sz l r))
+
+data.NatMap.internal.bin.doc : Doc
+data.NatMap.internal.bin.doc =
+ {{
+ Internal function used to construct a {type NatMap} node. Ensures that the
+ resulting tree has no empty branches.
+ }}
+
+data.NatMap.internal.branchMask : Nat -> Nat -> Nat
+data.NatMap.internal.branchMask p1 p2 = highBitMask (Nat.xor p1 p2)
+
+data.NatMap.internal.branchMask.doc : Doc
+data.NatMap.internal.branchMask.doc =
+ {{
+ Internal function used to construct a {type NatMap} node. Computes the mask
+ of the most significant bit that differs between the two prefixes.
+ }}
+
+data.NatMap.internal.highBitMask : Nat -> Nat
+data.NatMap.internal.highBitMask i =
+ use Nat -
+ setBit (63 - Nat.leadingZeros i) 0
+
+data.NatMap.internal.highBitMask.doc : Doc
+data.NatMap.internal.highBitMask.doc =
+ {{
+ Internal function used to construct a {type NatMap} node. Computes the mask
+ of the most significant bit of the given integer.
+ }}
+
+data.NatMap.internal.join :
+ Nat -> NatMap.Nonempty a -> Nat -> NatMap.Nonempty a -> NatMap.Nonempty a
+data.NatMap.internal.join p1 t1 p2 t2 =
+ use Nat ==
+ use NatMap.internal bim
+ m = branchMask p1 p2
+ p = mask p1 m
+ if Nat.and p1 m == 0 then bim p m t1 t2 else bim p m t2 t1
+
+data.NatMap.internal.join.doc : Doc
+data.NatMap.internal.join.doc =
+ {{
+ Internal function used to construct a {type NatMap} node. Joins two trees and
+ computes the shared prefix and critical bit of the resulting node.
+ }}
+
+data.NatMap.internal.mask : Nat -> Nat -> Nat
+data.NatMap.internal.mask i m =
+ use Nat -
+ Nat.and i (Nat.xor (Nat.complement (m - 1)) m)
+
+data.NatMap.internal.mask.doc : Doc
+data.NatMap.internal.mask.doc =
+ {{
+ Internal function used to construct a {type NatMap} node. Computes the prefix
+ of the given integer masked by the given mask.
+ }}
+
+data.NatMap.internal.nomatch : Nat -> Nat -> Nat -> Boolean
+data.NatMap.internal.nomatch k p m =
+ use Nat !=
+ mask k m != p
+
+data.NatMap.internal.nomatch.doc : Doc
+data.NatMap.internal.nomatch.doc =
+ {{
+ Internal function used to find the correct branch of a {type NatMap} node.
+ Checks whether the given key matches the given prefix and mask.
+ }}
+
+data.NatMap.internal.shorter : Nat -> Nat -> Boolean
+data.NatMap.internal.shorter =
+ use Nat >
+ (>)
+
+data.NatMap.internal.shorter.doc : Doc
+data.NatMap.internal.shorter.doc =
+ {{
+ Internal function used to find the correct branch of a {type NatMap} node.
+ Checks whether one mask masks a shorter prefix than the other.
+ }}
+
+data.NatMap.internal.zero : Nat -> Nat -> Boolean
+data.NatMap.internal.zero k m =
+ use Nat ==
+ Nat.and k m == 0
+
+data.NatMap.internal.zero.doc : Doc
+data.NatMap.internal.zero.doc =
+ {{ Internal function to check if the bit at position `m` in `k` is ``0``. }}
+
+data.NatMap.intersect : NatMap a -> NatMap b -> NatMap a
+data.NatMap.intersect = NatMap.intersectWith const
+
+data.NatMap.intersect.doc : Doc
+data.NatMap.intersect.doc =
+ use NatMap fromList
+ {{
+ Returns a {type NatMap} containing only the keys that are present in both of
+ the given {type NatMap}s.
+
+ The values of the resulting {type NatMap} are the values of the first
+ {type NatMap}.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.intersect
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(2, 20), (3, 30), (4, 40)]))
+ ```
+
+ # See also
+
+ * {NatMap.intersectWith} for a version that also combines the values of the
+ two {type NatMap}s.
+ * {NatMap.intersectWithKey} for a version that combines the values and
+ additionally provides the key to the combining function.
+ }}
+
+data.NatMap.intersectWith :
+ (a ->{g} b ->{g} a) -> NatMap a -> NatMap b ->{g} NatMap a
+data.NatMap.intersectWith f = NatMap.intersectWithKey do x y -> f x y
+
+data.NatMap.intersectWith.doc : Doc
+data.NatMap.intersectWith.doc =
+ use Nat +
+ use NatMap fromList
+ {{
+ Returns a {type NatMap} containing only the keys that are present in both of
+ the given {type NatMap}s. The values of the resulting {type NatMap} are
+ computed by combining the values of the two {type NatMap}s using the given
+ combining function.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.intersectWith
+ (+)
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(2, 20), (3, 30), (4, 40)]))
+ ```
+
+ # See also
+
+ * {NatMap.intersectWithKey} for a version that additionally provides
+ * {NatMap.intersect} for a version that throws away the values of the
+ second {type NatMap}. the key to the combining function.
+ }}
+
+data.NatMap.intersectWithKey :
+ (Nat ->{g} a ->{g} b ->{g} a) -> NatMap a -> NatMap b ->{g} NatMap a
+data.NatMap.intersectWithKey f = cases
+ NatMap None, _ -> NatMap.empty
+ NatMap _, NatMap None -> NatMap.empty
+ NatMap (Some t1), NatMap (Some t2) ->
+ NatMap.Nonempty.intersectWithKey f t1 t2
+
+data.NatMap.intersectWithKey.doc : Doc
+data.NatMap.intersectWithKey.doc =
+ use Nat +
+ use NatMap fromList
+ {{
+ Returns a {type NatMap} containing only the keys that are present in both of
+ the given {type NatMap}s. The values of the resulting {type NatMap} are
+ computed by combining the values of the two {type NatMap}s using the given
+ combining function. The combining function can take the key into account.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.intersectWithKey
+ (k x y -> k + x + y)
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(2, 20), (3, 30), (4, 40)]))
+ ```
+
+ # See also
+
+ * {NatMap.intersectWith} for a version that does not provide the key to the
+ combining function.
+ * {NatMap.intersect} for a version that throws away the values of the
+ second {type NatMap}.
+ }}
+
+data.NatMap.isEmpty : NatMap a -> Boolean
+data.NatMap.isEmpty = cases
+ NatMap None -> true
+ _ -> false
+
+data.NatMap.isEmpty.doc : Doc
+data.NatMap.isEmpty.doc =
+ use NatMap fromList isEmpty
+ {{
+ Checks whether the given {type NatMap} is empty.
+
+ # Examples
+
+ ```
+ isEmpty (fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ ```
+ isEmpty (fromList [])
+ ```
+
+ # See also
+
+ * {NatMap.size} to get the number of elements in a {type NatMap}.
+ }}
+
+data.NatMap.isProperSubmapOf : NatMap a -> NatMap a -> Boolean
+data.NatMap.isProperSubmapOf = NatMap.isProperSubmapOfBy (===)
+
+data.NatMap.isProperSubmapOf.doc : Doc
+data.NatMap.isProperSubmapOf.doc =
+ use NatMap fromList isProperSubmapOf
+ {{
+ Checks whether the first {type NatMap} is a proper submap of the second
+ {type NatMap}.
+
+ A {type NatMap} is a proper submap of another {type NatMap} if the second
+ {type NatMap} contains all the keys of the first {type NatMap} and at least
+ one more key, and the values under the shared keys are equal according to the
+ {===} function.
+
+ # Examples
+
+ ```
+ isProperSubmapOf
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(1, 10), (2, 20), (3, 30), (4, 40)])
+ ```
+
+ ```
+ isProperSubmapOf
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ ```
+ isProperSubmapOf
+ (fromList [(1, 10), (2, 20), (3, 30)]) (fromList [(1, 10), (2, 20)])
+ ```
+
+ # See also
+
+ * {NatMap.isSubmapOf} for a version that returns `` true `` also if the two
+ {type NatMap}s are identical.
+ * {NatMap.isProperSubmapOfBy} for a version that allows you to compare the
+ values under the keys with a custom function.
+ }}
+
+data.NatMap.isProperSubmapOfBy :
+ (a ->{g} a ->{g} Boolean) -> NatMap a -> NatMap a ->{g} Boolean
+data.NatMap.isProperSubmapOfBy f t1 t2 =
+ NatMap.submapCompareBy f t1 t2 === Some Less
+
+data.NatMap.isProperSubmapOfBy.doc : Doc
+data.NatMap.isProperSubmapOfBy.doc =
+ use Nat ==
+ use NatMap fromList isProperSubmapOfBy
+ {{
+ Checks whether the first {type NatMap} is a proper submap of the second
+ {type NatMap}, and compares the values under the keys with a custom function.
+
+ Returns `` true `` if the second {type NatMap} contains all the keys of the
+ first {type NatMap} and at least one more key, and the values under the
+ common keys are equal according to the given function.
+
+ # Examples
+
+ ```
+ isProperSubmapOfBy
+ (x y -> x == y)
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(1, 10), (2, 20), (3, 30), (4, 40)])
+ ```
+
+ ```
+ isProperSubmapOfBy
+ (x y -> x == y)
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ ```
+ isProperSubmapOfBy
+ (x y -> x == y)
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(1, 10), (2, 20)])
+ ```
+
+ # See also
+
+ * {NatMap.isSubmapOfBy} for a version that returns `` true `` also if the
+ two {type NatMap}s have exactly the same keys.
+ * {NatMap.isProperSubmapOf} for a version that compares the values under
+ the keys using the {===} function.
+ }}
+
+data.NatMap.isSubmapOf : NatMap a -> NatMap a -> Boolean
+data.NatMap.isSubmapOf = NatMap.isSubmapOfBy (===)
+
+data.NatMap.isSubmapOf.doc : Doc
+data.NatMap.isSubmapOf.doc =
+ use NatMap fromList isSubmapOf
+ {{
+ Checks whether the first {type NatMap} is a submap of the second
+ {type NatMap}.
+
+ A {type NatMap} is a submap of another {type NatMap} if the second
+ {type NatMap} contains all the keys of the first {type NatMap}, and the
+ values under the shared keys in one map are equal to the values under the
+ shared keys in the other map according to the {===} function.
+
+ # Examples
+
+ ```
+ isSubmapOf
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(1, 10), (2, 20), (3, 30), (4, 40)])
+ ```
+
+ ```
+ isSubmapOf
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ ```
+ isSubmapOf
+ (fromList [(1, 10), (2, 20), (3, 30)]) (fromList [(1, 10), (2, 20)])
+ ```
+
+ # See also
+
+ * {NatMap.isProperSubmapOf} for a version that returns `` true `` only if
+ the second {type NatMap} contains at least one more key than the first.
+ * {NatMap.isSubmapOfBy} for a version that allows you to compare the values
+ under the keys with a custom function.
+ }}
+
+data.NatMap.isSubmapOfBy :
+ (a ->{g} a ->{g} Boolean) -> NatMap a -> NatMap a ->{g} Boolean
+data.NatMap.isSubmapOfBy f t1 t2 = match NatMap.submapCompareBy f t1 t2 with
+ Some Less -> true
+ Some Equal -> true
+ _ -> false
+
+data.NatMap.isSubmapOfBy.doc : Doc
+data.NatMap.isSubmapOfBy.doc =
+ use Nat ==
+ use NatMap fromList isSubmapOfBy
+ {{
+ Checks whether the first {type NatMap} is a submap of the second
+ {type NatMap}, and compares the values under the keys with a custom function.
+
+ Returns `` true `` if the second {type NatMap} contains all the keys of the
+ first {type NatMap}, and the values under the common keys are equal according
+ to the given function.
+
+ # Examples
+
+ ```
+ isSubmapOfBy
+ (x y -> x == y)
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(1, 10), (2, 20), (3, 30), (4, 40)])
+ ```
+
+ ```
+ isSubmapOfBy
+ (x y -> x == y)
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ ```
+ isSubmapOfBy
+ (x y -> x == y)
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(1, 10), (2, 20)])
+ ```
+
+ # See also
+
+ * {NatMap.isProperSubmapOfBy} for a version that returns `` true `` only if
+ the second {type NatMap} contains at least one more key than the first.
+ * {NatMap.isSubmapOf} for a version that compares the values under the keys
+ using the {===} function.
+ }}
+
+data.NatMap.keys : NatMap a -> [Nat]
+data.NatMap.keys =
+ use List +:
+ NatMap.foldWithKey (k _ ks -> k +: ks) []
+
+data.NatMap.keys.doc : Doc
+data.NatMap.keys.doc =
+ {{
+ Returns the keys of a {type NatMap} as a list.
+
+ # Example
+
+ ```
+ NatMap.keys (NatMap.fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.values} returns the values rather than the keys.
+ * {NatMap.toList} returns both the keys and values.
+ }}
+
+data.NatMap.keySet : NatMap a -> NatSet
+data.NatMap.keySet = cases
+ NatMap None -> NatSet.empty
+ NatMap (Some t) -> NatSet (Some (Nonempty.keySet t))
+
+data.NatMap.keySet.doc : Doc
+data.NatMap.keySet.doc =
+ use NatMap toList
+ {{
+ Returns the {type NatSet} of keys in a {type NatMap}.
+
+ # Example
+
+ ```
+ toList (NatMap.fromList [(1, "a"), (2, "b"), (3, "c")])
+ ```
+
+ # See also
+
+ * {NatMap.keys} to get the {type List} of keys in a {type NatMap}.
+ * {NatMap.values} to get the {type List} of values in a {type NatMap}.
+ * {toList} to convert a {type NatMap} to a {type List} of key-value pairs.
+ }}
+
+test> data.NatMap.keySet.test : [Result]
+data.NatMap.keySet.test =
+ test.verify do
+ use NatSet ==
+ use Random nat
+ _ = Each.range 0 100
+ xs = Random.listOf (do Tuple.pair nat() nat()) do Random.natIn 0 100
+ ensure
+ (NatMap.keySet (NatMap.fromList xs) == NatSet.fromList (List.map at1 xs))
+
+data.NatMap.map : (a ->{g} b) -> NatMap a ->{g} NatMap b
+data.NatMap.map f = NatMap.mapWithKey do x -> f x
+
+data.NatMap.map.doc : Doc
+data.NatMap.map.doc =
+ use Nat +
+ {{
+ Applies a function to every value in a {type NatMap}.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.map (x -> x + 1) (NatMap.fromList [(1, 10), (2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.mapWithKey} applies a function to both the keys and values.
+ * {NatMap.mapOptional} applies a function to the values, removing the ones
+ that return {None}.
+ * {NatMap.mapEither} applies an {type Either}-valued function to the
+ values, partitioning the {type NatMap} into two {type NatMap}s.
+ * {NatMap.adjust} applies a function to the value under a specific key.
+ }}
+
+data.NatMap.mapEither :
+ (a ->{g} Either b c) -> NatMap a ->{g} (NatMap b, NatMap c)
+data.NatMap.mapEither f = NatMap.mapEitherWithKey do x -> f x
+
+data.NatMap.mapEither.doc : Doc
+data.NatMap.mapEither.doc =
+ {{
+ Applies an {type Either}-valued function to every value in a {type NatMap},
+ partitioning the {type NatMap} into two {type NatMap}s.
+
+ # Example
+
+ ```
+ Tuple.bimap
+ NatMap.toList
+ (NatMap.mapEither
+ (x -> (if Nat.isEven x then Left x else Right x))
+ (NatMap.fromList [(1, 11), (2, 22), (3, 33)]))
+ ```
+
+ # See also
+
+ * {NatMap.mapEitherWithKey} for a version of this that applies the function
+ to both the keys and values.
+ * {NatMap.mapOptional} applies a function to the values, removing the ones
+ that return {None}.
+ * {NatMap.map} just applies a function to all the values.
+ * {NatMap.partition} applies a predicate to the values, partitioning the
+ {type NatMap} into two {type NatMap}s.
+ }}
+
+data.NatMap.mapEitherWithKey :
+ (Nat ->{g} a ->{g} Either b c) -> NatMap a ->{g} (NatMap b, NatMap c)
+data.NatMap.mapEitherWithKey f = cases
+ NatMap (Some t) -> Nonempty.mapEitherWithKey f t
+ NatMap None -> (NatMap.empty, NatMap.empty)
+
+data.NatMap.mapEitherWithKey.doc : Doc
+data.NatMap.mapEitherWithKey.doc =
+ {{
+ Applies an {type Either}-valued function to every value in a {type NatMap},
+ partitioning the {type NatMap} into two {type NatMap}s.
+
+ # Example
+
+ ```
+ Tuple.bimap
+ NatMap.toList
+ (NatMap.mapEitherWithKey
+ (k x -> (if Nat.isEven k then Left x else Right x))
+ (NatMap.fromList [(1, 10), (2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.mapEither} for a version of this that applies the function only
+ to the values.
+ * {NatMap.mapOptionalWithKey} applies a function to the entries, removing
+ the ones that return {None}.
+ * {NatMap.mapWithKey} just applies a function to all the entries.
+ * {NatMap.partitionWithKey} applies a predicate to the entries,
+ partitioning the {type NatMap} into two {type NatMap}s.
+ }}
+
+data.NatMap.mapOptional : (a ->{g} Optional b) -> NatMap a ->{g} NatMap b
+data.NatMap.mapOptional f = NatMap.mapOptionalWithKey do x -> f x
+
+data.NatMap.mapOptional.doc : Doc
+data.NatMap.mapOptional.doc =
+ {{
+ Applies a function to every value in a {type NatMap}, removing the ones that
+ return {None}.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.mapOptional
+ (x -> (if Nat.isEven x then Some x else None))
+ (NatMap.fromList [(1, 11), (2, 22), (3, 33)]))
+ ```
+
+ # See also
+
+ * {NatMap.mapOptionalWithKey} for a version of this that applies the
+ function to both the keys and values.
+ * {NatMap.mapEither} applies an {type Either}-valued function to the
+ values, partitioning the {type NatMap} into two {type NatMap}s.
+ * {NatMap.map} just applies a function to all the values.
+ * {NatMap.filter} applies a predicate to the values, removing the ones that
+ return {false}.
+ }}
+
+data.NatMap.mapOptionalWithKey :
+ (Nat ->{g} a ->{g} Optional b) -> NatMap a ->{g} NatMap b
+data.NatMap.mapOptionalWithKey f = cases
+ NatMap (Some t) -> Nonempty.mapOptionalWithKey f t
+ NatMap None -> NatMap.empty
+
+data.NatMap.mapOptionalWithKey.doc : Doc
+data.NatMap.mapOptionalWithKey.doc =
+ {{
+ Applies a function to every key-value pair in a {type NatMap}, removing the
+ ones that return {None}.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.mapOptionalWithKey
+ (k x -> (if Nat.isEven k then Some x else None))
+ (NatMap.fromList [(1, 10), (2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.mapOptional} for a version of this that applies the function only
+ to the values.
+ * {NatMap.mapEitherWithKey} applies an {type Either}-valued function to the
+ entries, partitioning the {type NatMap} into two {type NatMap}s.
+ * {NatMap.mapWithKey} just applies a function to all the entries.
+ * {NatMap.filterWithKey} applies a predicate to the entries, removing the
+ ones that return {false}.
+ }}
+
+data.NatMap.mapWithKey : (Nat ->{g} a ->{g} b) -> NatMap a ->{g} NatMap b
+data.NatMap.mapWithKey f = cases
+ NatMap (Some t) -> toNatMap (NatMap.Nonempty.mapWithKey f t)
+ NatMap None -> NatMap.empty
+
+data.NatMap.mapWithKey.doc : Doc
+data.NatMap.mapWithKey.doc =
+ use Nat +
+ {{
+ Applies a function to every key-value pair in a {type NatMap}.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.mapWithKey (+) (NatMap.fromList [(1, 10), (2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.map} for a version of this that applies the function only to the
+ values.
+ * {NatMap.mapOptionalWithKey} applies a function to the entries, removing
+ the ones that return {None}.
+ * {NatMap.mapEitherWithKey} applies an {type Either}-valued function to the
+ entries, partitioning the {type NatMap} into two {type NatMap}s.
+ }}
+
+data.NatMap.maxKey : NatMap a ->{Abort} Nat
+data.NatMap.maxKey = cases
+ NatMap (Some t) -> Nonempty.maxKey t
+ NatMap None -> abort
+
+data.NatMap.maxKey.doc : Doc
+data.NatMap.maxKey.doc =
+ {{
+ Returns the largest key in the map. Calls {abort} if the map is empty.
+
+ # Example
+
+ ```
+ toOptional! do
+ NatMap.fromList [(1, "a"), (2, "b"), (3, "c")] |> NatMap.maxKey
+ ```
+ }}
+
+data.NatMap.maxView : NatMap a ->{Abort} (a, NatMap a)
+data.NatMap.maxView t =
+ ((_, v), t') = NatMap.breakOffMax t
+ (v, t')
+
+data.NatMap.maxView.doc : Doc
+data.NatMap.maxView.doc =
+ {{
+ Returns the value of the largest key in the {type NatMap} and the
+ {type NatMap} with that key removed.
+
+ # Example
+
+ ```
+ toOptional! do NatMap.maxView (NatMap.fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.breakOffMax} for a version of this that returns the key as well
+ as the value.
+ * {NatMap.minView} for the value under the smallest key.
+ * {NatMap.breakOffMin} for the key and value under the smallest key.
+ }}
+
+data.NatMap.minKey : NatMap a ->{Abort} Nat
+data.NatMap.minKey = cases
+ NatMap (Some t) -> Nonempty.minKey t
+ NatMap None -> abort
+
+data.NatMap.minView : NatMap a ->{Abort} (a, NatMap a)
+data.NatMap.minView t =
+ ((_, v), t') = NatMap.breakOffMin t
+ (v, t')
+
+data.NatMap.minView.doc : Doc
+data.NatMap.minView.doc =
+ {{
+ Returns the value of the smallest key in the {type NatMap} and the
+ {type NatMap} with that key removed.
+
+ # Example
+
+ ```
+ toOptional! do NatMap.minView (NatMap.fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.breakOffMin} for a version of this that returns the key as well
+ as the value.
+ * {NatMap.maxView} for the value under the largest key.
+ * {NatMap.breakOffMax} for the key and value under the largest key.
+ }}
+
+data.NatMap.Nonempty.adjust :
+ (a ->{g} a) -> Nat -> NatMap.Nonempty a ->{g} NatMap.Nonempty a
+data.NatMap.Nonempty.adjust f k t =
+ NatMap.Nonempty.adjustWithKey (do x -> f x) k t
+
+data.NatMap.Nonempty.adjust.doc : Doc
+data.NatMap.Nonempty.adjust.doc =
+ {{
+ Modifies the value at a key in a {type NatMap.Nonempty}, using a function. If
+ the key is not present, the map is returned unchanged.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.toList
+ (NatMap.Nonempty.adjust
+ Text.toUppercase
+ 1
+ (NatMap.Nonempty.fromList ((1, "foo") +| [(2, "bar")])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.adjustWithKey} for a version of this that also receives
+ the key.
+ * {NatMap.Nonempty.update} for a version that can delete the key.
+ * {Nonempty.updateWithKey} can delete the key and also take the key into
+ account.
+ * {NatMap.Nonempty.alter} can also insert the key if not present.
+ * {NatMap.Nonempty.map} can modify all values at once.
+ * {NatMap.Nonempty.mapWithKey} can modify all values at once and also
+ receives the key.
+ }}
+
+data.NatMap.Nonempty.adjustWithKey :
+ (Nat ->{g} a ->{g} a) -> Nat -> NatMap.Nonempty a ->{g} NatMap.Nonempty a
+data.NatMap.Nonempty.adjustWithKey f k = cases
+ t@(NatMap.Nonempty.Bin prefix mask _ l r)
+ | nomatch k prefix mask -> t
+ | Nat.and k mask Nat.== 0 ->
+ NatMap.internal.bim
+ prefix mask (data.NatMap.Nonempty.adjustWithKey f k l) r
+ | otherwise ->
+ NatMap.internal.bim
+ prefix mask l (data.NatMap.Nonempty.adjustWithKey f k r)
+ t@(NatMap.Nonempty.Tip k' v)
+ | k Nat.== k' -> NatMap.Nonempty.Tip k (f k v)
+ | otherwise -> t
+
+data.NatMap.Nonempty.adjustWithKey.doc : Doc
+data.NatMap.Nonempty.adjustWithKey.doc =
+ use Nat +
+ {{
+ Modifies the value at a key in a {type NatMap.Nonempty}, using a function. If
+ the key is not present, the map is returned unchanged. The function receives
+ the key as its first argument and the value as its second.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.toList
+ (NatMap.Nonempty.adjustWithKey
+ (+) 1 (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.adjust} for a version of this that does not receive the
+ key.
+ * {Nonempty.updateWithKey} for a version that can delete the key.
+ * {Nonempty.alterWithKey} can also insert the key if not present.
+ * {NatMap.Nonempty.mapWithKey} can modify all values at once.
+ }}
+
+data.NatMap.Nonempty.align :
+ NatMap.Nonempty a -> NatMap.Nonempty b -> NatMap.Nonempty (OneOrBoth a b)
+data.NatMap.Nonempty.align = NatMap.Nonempty.alignWith id
+
+data.NatMap.Nonempty.align.doc : Doc
+data.NatMap.Nonempty.align.doc =
+ use NatMap.Nonempty fromList
+ {{
+ Aligns two nonempty maps into a nonempty map of {type OneOrBoth} values.
+
+ The result will have the same keys as the union of the keys of the two input
+ maps, and each value will be a {type OneOrBoth} containing the corresponding
+ values from the two input maps. If a key is present in only one of the input
+ maps, the result will contain {This} or {That} values accordingly. If a key
+ is present in both input maps, the result will contain a {Both} value.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.toList
+ (NatMap.Nonempty.align
+ (fromList ((1, "hello") +| [(2, "world")]))
+ (fromList ((2, 42) +| [(3, 43)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.alignWith} - a variant where you can specify a function
+ to apply to the values.
+ }}
+
+data.NatMap.Nonempty.alignWith :
+ (OneOrBoth a b ->{g} c)
+ -> NatMap.Nonempty a
+ -> NatMap.Nonempty b
+ ->{g} NatMap.Nonempty c
+data.NatMap.Nonempty.alignWith f m1 m2 =
+ NatMap.Nonempty.alignWithKey (_ x -> f x) m1 m2
+
+data.NatMap.Nonempty.alignWith.doc : Doc
+data.NatMap.Nonempty.alignWith.doc =
+ use NatMap.Nonempty fromList
+ use Text ++
+ {{
+ Aligns two nonempty maps into a nonempty map of values using a function.
+
+ The result will have the same keys as the union of the keys of the two input
+ maps, and each value will be the result of applying the given function to the
+ corresponding values from the two input maps – {This} for keys that only
+ appear in the first map, {That} for keys that only appear in the second map,
+ and {Both} for keys that appear in both maps.
+
+ # Example
+
+ ```
+ f = cases
+ This a -> "only in the first map: " ++ a
+ That b -> "only in the second map: " ++ b
+ Both a b -> "in both maps: " ++ a ++ " and " ++ b
+ NatMap.Nonempty.values
+ (NatMap.Nonempty.alignWith
+ f
+ (fromList ((1, "circuit") +| [(2, "quasar")]))
+ (fromList ((2, "voyage") +| [(3, "harmony")])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.align} - a variant that returns a nonempty map of
+ {type OneOrBoth} values.
+ * {NatMap.Nonempty.alignWithKey} - a variant where the function also
+ receives the key.
+ }}
+
+data.NatMap.Nonempty.alignWithKey :
+ (Nat ->{e} OneOrBoth a b ->{f} c)
+ -> NatMap.Nonempty a
+ -> NatMap.Nonempty b
+ ->{e, f} NatMap.Nonempty c
+data.NatMap.Nonempty.alignWithKey f = cases
+ t1@(NatMap.Nonempty.Bin p1 m1 sz1 l1 r1),
+ t2@(NatMap.Nonempty.Bin p2 m2 sz2 l2 r2)
+ | shorter m1 m2 ->
+ if nomatch p2 p1 m1 then
+ internal.join
+ p1
+ (NatMap.Nonempty.mapWithKey (k a -> f k (This a)) t1)
+ p2
+ (NatMap.Nonempty.mapWithKey (k b -> f k (That b)) t2)
+ else
+ if Nat.and p2 m1 Nat.== 0 then
+ NatMap.internal.bim
+ p1
+ m1
+ (data.NatMap.Nonempty.alignWithKey f l1 t2)
+ (NatMap.Nonempty.mapWithKey (k a -> f k (This a)) r1)
+ else
+ NatMap.internal.bim
+ p1
+ m1
+ (NatMap.Nonempty.mapWithKey (k a -> f k (This a)) l1)
+ (data.NatMap.Nonempty.alignWithKey f r1 t2)
+ | shorter m2 m1 ->
+ if nomatch p1 p2 m2 then
+ internal.join
+ p1
+ (NatMap.Nonempty.mapWithKey (k a -> f k (This a)) t1)
+ p2
+ (NatMap.Nonempty.mapWithKey (k b -> f k (That b)) t2)
+ else
+ if Nat.and p1 m2 Nat.== 0 then
+ NatMap.internal.bim
+ p2
+ m2
+ (data.NatMap.Nonempty.alignWithKey f t1 l2)
+ (NatMap.Nonempty.mapWithKey (k b -> f k (That b)) r2)
+ else
+ NatMap.internal.bim
+ p2
+ m2
+ (NatMap.Nonempty.mapWithKey (k b -> f k (That b)) l2)
+ (data.NatMap.Nonempty.alignWithKey f t1 r2)
+ | p1 Nat.== p2 ->
+ NatMap.internal.bim
+ p1
+ m1
+ (data.NatMap.Nonempty.alignWithKey f l1 l2)
+ (data.NatMap.Nonempty.alignWithKey f r1 r2)
+ | otherwise ->
+ internal.join
+ p1
+ (NatMap.Nonempty.mapWithKey (k a -> f k (This a)) t1)
+ p2
+ (NatMap.Nonempty.mapWithKey (k b -> f k (That b)) t2)
+ NatMap.Nonempty.Tip k v, t ->
+ t' = NatMap.Nonempty.mapWithKey (k' y -> f k' (That y)) t
+ match NatMap.Nonempty.get k t with
+ None -> NatMap.Nonempty.insert k (f k (This v)) t'
+ Some x -> NatMap.Nonempty.adjust (_ -> f k (Both v x)) k t'
+ t, NatMap.Nonempty.Tip k v ->
+ t' = NatMap.Nonempty.mapWithKey (k' x -> f k' (This x)) t
+ match NatMap.Nonempty.get k t with
+ None -> NatMap.Nonempty.insert k (f k (That v)) t'
+ Some x -> NatMap.Nonempty.adjust (_ -> f k (Both x v)) k t'
+
+data.NatMap.Nonempty.alignWithKey.doc : Doc
+data.NatMap.Nonempty.alignWithKey.doc =
+ use Nat toText
+ use NatMap.Nonempty fromList
+ use Text ++
+ {{
+ Aligns two nonempty maps into a nonempty map of values using a function.
+
+ The result will have the same keys as the union of the keys of the two input
+ maps, and each value will be the result of applying the given function to the
+ corresponding key-value pairs from the two input maps. The function receives
+ {This} for values under keys that are present in only the first input map,
+ {That} for values under keys that are present in only the second input map,
+ and {Both} for keys that are present in both input maps.
+
+ # Example
+
+ ```
+ f k = cases
+ This a -> "only in the first map: " ++ toText k ++ " -> " ++ a
+ That b -> "only in the second map: " ++ toText k ++ " -> " ++ b
+ Both a b -> "in both maps: " ++ toText k ++ " -> " ++ a ++ " and " ++ b
+ NatMap.Nonempty.values
+ (NatMap.Nonempty.alignWithKey
+ f
+ (fromList ((1, "circuit") +| [(2, "quasar")]))
+ (fromList ((2, "voyage") +| [(3, "harmony")])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.align} - a variant that returns a nonempty map of
+ {type OneOrBoth} values.
+ * {NatMap.Nonempty.alignWith} - a variant where the function doesn't take
+ the key.
+ }}
+
+data.NatMap.Nonempty.alter :
+ (Optional a ->{g} Optional a) -> Nat -> NatMap.Nonempty a ->{g} NatMap a
+data.NatMap.Nonempty.alter f k t = Nonempty.alterWithKey (do x -> f x) k t
+
+data.NatMap.Nonempty.alter.doc : Doc
+data.NatMap.Nonempty.alter.doc =
+ use NatMap toList
+ use NatMap.Nonempty alter fromList
+ use Text ++
+ {{
+ An expression `` alter f k t `` alters the value under the key `k` in the
+ {type NatMap.Nonempty} `t`, or the absence thereof, using the function `f`.
+ Returns a (possibly empty) {type NatMap} with the new value.
+
+ # Examples
+
+ If the function returns {None}, the key is deleted:
+
+ ```
+ toList (alter (do None) 1 (fromList ((1, "foo") +| [(2, "bar")])))
+ ```
+
+ If the function returns ``Some x``, the value under the key is updated:
+
+ ```
+ toList
+ (alter
+ (x -> Some (Optional.getOrElse "" x ++ "baz"))
+ 1
+ (fromList ((1, "foo") +| [(2, "bar")])))
+ ```
+
+ If the key is not present, the function receives {None}, and can insert the
+ key by returning {Some} of the new value:
+
+ ```
+ toList (alter (do Some "baz") 3 (fromList ((1, "foo") +| [(2, "bar")])))
+ ```
+
+ # See also
+
+ * {Nonempty.alterWithKey} for a version of this that also receives the key.
+ * {NatMap.Nonempty.update} for a version that does nothing if the key is
+ not present.
+ }}
+
+data.NatMap.Nonempty.alterWithKey :
+ (Nat ->{g} Optional a ->{g} Optional a)
+ -> Nat
+ -> NatMap.Nonempty a
+ ->{g} NatMap a
+data.NatMap.Nonempty.alterWithKey f k = cases
+ t@(NatMap.Nonempty.Bin prefix mask _ l r)
+ | nomatch k prefix mask ->
+ match f k None with
+ None -> toNatMap t
+ Some v -> toNatMap (internal.join k (NatMap.Nonempty.Tip k v) prefix t)
+ | Nat.and k mask Nat.== 0 ->
+ NatMap.internal.bin
+ prefix mask (data.NatMap.Nonempty.alterWithKey f k l) (toNatMap r)
+ | otherwise ->
+ NatMap.internal.bin
+ prefix mask (toNatMap l) (data.NatMap.Nonempty.alterWithKey f k r)
+ t@(NatMap.Nonempty.Tip k' v)
+ | k Nat.== k' ->
+ match f k (Some v) with
+ None -> NatMap.empty
+ Some v' -> toNatMap (NatMap.Nonempty.Tip k v')
+ | otherwise ->
+ match f k None with
+ None -> toNatMap t
+ Some v -> toNatMap (internal.join k (NatMap.Nonempty.Tip k v) k' t)
+
+data.NatMap.Nonempty.alterWithKey.doc : Doc
+data.NatMap.Nonempty.alterWithKey.doc =
+ use Nat * +
+ use NatMap toList
+ use NatMap.Nonempty fromList
+ use Nonempty alterWithKey
+ {{
+ An expression `` alterWithKey f k t `` alters the value under the key `k` in
+ the {type NatMap.Nonempty} `t`, or the absence thereof, using the function
+ `f`.
+
+ The function receives the key as its first argument and the value as its
+ second (or {None} if the key is not present).
+
+ # Examples
+
+ If the function returns {None}, the key is deleted:
+
+ ```
+ toList
+ (alterWithKey (do do None) 1 (fromList ((1, "foo") +| [(2, "bar")])))
+ ```
+
+ If the function returns ``Some x``, the value under the key is updated to
+ `x`:
+
+ ```
+ toList
+ (alterWithKey
+ (k x -> Optional.map (v -> v + k) x)
+ 1
+ (fromList ((1, 10) +| [(2, 20)])))
+ ```
+
+ If the key is not present, the function receives {None} for the value, and
+ can insert the key by returning {Some} of the new value.
+
+ ```
+ toList
+ (alterWithKey
+ (k x -> Optional.orElse x (Some (k * 10)))
+ 3
+ (fromList ((1, 10) +| [(2, 20)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.alter} for a version of this that does not receive the
+ key.
+ * {Nonempty.updateWithKey} for a version that does nothing if the key is
+ not present.
+ }}
+
+data.NatMap.Nonempty.breakOffMax : NatMap.Nonempty a -> ((Nat, a), NatMap a)
+data.NatMap.Nonempty.breakOffMax = cases
+ t@(NatMap.Nonempty.Bin p m sz l r) ->
+ use NatMap.internal bin
+ up = cases
+ NatMap.Nonempty.Bin p m sz l r ->
+ (result, r') = up r
+ (result, bin p m (toNatMap l) r')
+ NatMap.Nonempty.Tip k v -> ((k, v), NatMap.empty)
+ let
+ (result, r') = up r
+ (result, bin p m (toNatMap l) r')
+ NatMap.Nonempty.Tip k v -> ((k, v), NatMap.empty)
+
+data.NatMap.Nonempty.breakOffMax.doc : Doc
+data.NatMap.Nonempty.breakOffMax.doc =
+ {{
+ Returns the maximum key and value in the {type NatMap.Nonempty}, and the
+ (possibly empty) {type NatMap} without that key.
+
+ # Example
+
+ ```
+ Tuple.second
+ NatMap.toList
+ (NatMap.Nonempty.breakOffMax
+ (NatMap.Nonempty.fromList ((1, "foo") +| [(2, "bar")])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.breakOffMin} for the opposite operation, returning the
+ minimum key and value.
+ * {NatMap.Nonempty.deleteMax} for a version that only returns the updated
+ map.
+ * {NatMap.Nonempty.getMax} for a version that does not remove the key.
+ }}
+
+data.NatMap.Nonempty.breakOffMin : NatMap.Nonempty a -> ((Nat, a), NatMap a)
+data.NatMap.Nonempty.breakOffMin = cases
+ t@(NatMap.Nonempty.Bin p m sz l r) ->
+ use NatMap.internal bin
+ up = cases
+ NatMap.Nonempty.Bin p m sz l r ->
+ (result, l') = up l
+ (result, bin p m l' (toNatMap r))
+ NatMap.Nonempty.Tip k v -> ((k, v), NatMap.empty)
+ let
+ (result, l') = up l
+ (result, bin p m l' (toNatMap r))
+ NatMap.Nonempty.Tip k v -> ((k, v), NatMap.empty)
+
+data.NatMap.Nonempty.breakOffMin.doc : Doc
+data.NatMap.Nonempty.breakOffMin.doc =
+ {{
+ Returns the minimum key and value in the {type NatMap.Nonempty}, and the map
+ without that key.
+
+ # Example
+
+ ```
+ Tuple.second
+ NatMap.toList
+ (NatMap.Nonempty.breakOffMin
+ (NatMap.Nonempty.fromList ((1, "foo") +| [(2, "bar")])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.breakOffMax} for the opposite operation, returning the
+ maximum key and value.
+ * {NatMap.Nonempty.deleteMin} for a version that only returns the updated
+ map.
+ * {NatMap.Nonempty.getMin} for a version that does not remove the key.
+ }}
+
+data.NatMap.Nonempty.compareBy :
+ (a ->{g} a ->{g} Ordering)
+ -> NatMap.Nonempty a
+ -> NatMap.Nonempty a
+ ->{g} Ordering
+data.NatMap.Nonempty.compareBy f m1 m2 =
+ use Nat + - < <= > >=
+ use NatMap.Nonempty Bin Tip size split
+ use Nonempty maxKey
+ use Universal ordering
+ go = cases
+ Tip k1 v1, Tip k2 v2 -> Ordering.andThen (ordering k1 k2) (f v1 v2)
+ t1@(Bin p1 mask1 sz1 l1 r1), t2@(Bin p2 mask2 sz2 l2 r2) ->
+ largest1 = p1 + mask1 - 1
+ largest2 = p2 + mask2 - 1
+ if largest1 < p2 then Less
+ else
+ if largest2 < p1 then Greater
+ else
+ match go l1 l2 with
+ Equal
+ | size l1 < size l2 ->
+ match split (maxKey l1) l2 with
+ (_, _, NatMap None) -> Equal
+ (_, _, NatMap (Some l2r)) ->
+ match go r1 l2r with
+ Equal
+ | size r1 <= size l2r -> Less
+ | otherwise ->
+ match split (maxKey l2) r1 with
+ (_, _, NatMap None) -> Equal
+ (_, _, NatMap (Some r1r)) -> go r1r r2
+ x -> x
+ | size l1 > size l2 ->
+ match split (maxKey l2) l1 with
+ (_, _, NatMap None) -> Equal
+ (_, _, NatMap (Some l1r)) ->
+ match go l1r r2 with
+ Equal
+ | size l1r >= size r2 -> Greater
+ | otherwise ->
+ match split (maxKey l1) r2 with
+ (_, _, NatMap None) -> Equal
+ (_, _, NatMap (Some r2r)) -> go r1 r2r
+ x -> x
+ | otherwise -> go r1 r2
+ x -> x
+ t1@(Tip _ _), Bin _ _ _ l2 _ -> go t1 l2
+ Bin _ _ _ l1 _, t2@(Tip _ _) -> go l1 t2
+ match go m1 m2 with
+ Equal -> ordering (size m1) (size m2)
+ x -> x
+
+data.NatMap.Nonempty.compareBy.doc : Doc
+data.NatMap.Nonempty.compareBy.doc =
+ use NatMap.Nonempty fromList
+ use Nonempty compareBy
+ use Universal ordering
+ {{
+ Compares two {type NatMap.Nonempty}s using the given comparison function.
+
+ Defines a [total order](https://en.wikipedia.org/wiki/Total_order) on
+ {type NatMap.Nonempty}s.
+
+ The entries are compared in ascending order of key, and the given comparison
+ function is applied to the values of corresponding entries.
+
+ Returns:
+
+ * `` Less `` for any of the following:
+ * The first map is a proper prefix of the second map
+ * The first entry that differs between the maps has a lower key in the
+ first map.
+ * The first entry that differs between the maps has a lower value in the
+ first map, according to the given comparison function.
+ * `` Equal `` when the maps are equal.
+ * `` Greater `` for any of the following:
+ * The second map is a proper prefix of the first map
+ * The first entry that differs between the maps has a lower key in the
+ second map.
+ * The first entry that differs between the maps has a lower value in the
+ second map, according to the given comparison function.
+
+ # Examples
+
+ The maps are equal:
+
+ ```
+ compareBy
+ ordering
+ (fromList ((1, "foo") +| [(2, "bar")]))
+ (fromList ((1, "foo") +| [(2, "bar")]))
+ ```
+
+ The first map is a prefix of the second map:
+
+ ```
+ compareBy
+ ordering
+ (fromList ((1, "foo") +| [(2, "bar")]))
+ (fromList ((1, "foo") +| [(2, "bar"), (3, "baz")]))
+ ```
+
+ The second map is a prefix of the first map:
+
+ ```
+ compareBy
+ ordering
+ (fromList ((1, "foo") +| [(2, "bar")]))
+ (fromList ((1, "foo") +| []))
+ ```
+
+ The first value that differs is larger in the second map:
+
+ ```
+ compareBy
+ ordering
+ (fromList ((1, "foo") +| [(2, "bar")]))
+ (fromList ((1, "foo") +| [(2, "baz")]))
+ ```
+
+ The first key that differs is larger in the second map:
+
+ ```
+ compareBy
+ ordering
+ (fromList ((1, "foo") +| [(2, "bar")]))
+ (fromList ((1, "foo") +| [(3, "bar")]))
+ ```
+
+ The first key that differs is larger in the first map:
+
+ ```
+ compareBy
+ ordering
+ (fromList ((1, "foo") +| [(3, "bar")]))
+ (fromList ((1, "foo") +| [(2, "bar")]))
+ ```
+
+ The first value that differs is larger in the first map:
+
+ ```
+ compareBy
+ ordering
+ (fromList ((1, "foo") +| [(2, "baz")]))
+ (fromList ((1, "foo") +| [(2, "bar")]))
+ ```
+
+ # See also
+
+ * {Nonempty.equalBy} to compare for equality only.
+ * {Nonempty.submapCompareBy} to check if one map is a submap of another,
+ using a comparison function.
+ }}
+
+data.NatMap.Nonempty.contains : Nat -> NatMap.Nonempty a -> Boolean
+data.NatMap.Nonempty.contains k m = match NatMap.Nonempty.get k m with
+ None -> false
+ Some _ -> true
+
+data.NatMap.Nonempty.contains.doc : Doc
+data.NatMap.Nonempty.contains.doc =
+ {{
+ Checks if the given key is present in the {type NatMap.Nonempty}.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.contains
+ 1 (NatMap.Nonempty.fromList ((1, "foo") +| [(2, "bar")]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.get} to get the value if it is present.
+ * {Nonempty.isSubmapOf} to check if one map contains all the keys of
+ another.
+ }}
+
+data.NatMap.Nonempty.delete : Nat -> NatMap.Nonempty a -> NatMap a
+data.NatMap.Nonempty.delete k = cases
+ t@(NatMap.Nonempty.Bin prefix mask size l r)
+ | nomatch k prefix mask -> toNatMap t
+ | Nat.and k mask Nat.== 0 ->
+ NatMap.internal.bin
+ prefix mask (data.NatMap.Nonempty.delete k l) (toNatMap r)
+ | otherwise ->
+ NatMap.internal.bin
+ prefix mask (toNatMap l) (data.NatMap.Nonempty.delete k r)
+ t@(NatMap.Nonempty.Tip k' _) ->
+ if k Nat.== k' then NatMap.empty else toNatMap t
+
+data.NatMap.Nonempty.delete.doc : Doc
+data.NatMap.Nonempty.delete.doc =
+ {{
+ Deletes the given key from the {type NatMap.Nonempty}.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.Nonempty.delete
+ 1 (NatMap.Nonempty.fromList ((1, "foo") +| [(2, "bar")])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.deleteMax} to delete the largest key.
+ * {NatMap.Nonempty.deleteMin} to delete the smallest key.
+ * {NatMap.Nonempty.difference} to delete all keys in one map from another.
+ }}
+
+data.NatMap.Nonempty.deleteMax : NatMap.Nonempty a -> NatMap a
+data.NatMap.Nonempty.deleteMax t =
+ (_, t') = NatMap.Nonempty.breakOffMax t
+ t'
+
+data.NatMap.Nonempty.deleteMax.doc : Doc
+data.NatMap.Nonempty.deleteMax.doc =
+ {{
+ Deletes the largest key from the {type NatMap.Nonempty}.
+
+ If the map is empty, this returns an empty map.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.Nonempty.deleteMax
+ (NatMap.Nonempty.fromList ((1, "foo") +| [(2, "bar")])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.delete} to delete a specific key.
+ * {NatMap.Nonempty.deleteMin} to delete the smallest key.
+ * {NatMap.Nonempty.difference} to delete all keys in one map from another.
+ }}
+
+data.NatMap.Nonempty.deleteMin : NatMap.Nonempty a -> NatMap a
+data.NatMap.Nonempty.deleteMin t =
+ (_, t') = NatMap.Nonempty.breakOffMin t
+ t'
+
+data.NatMap.Nonempty.deleteMin.doc : Doc
+data.NatMap.Nonempty.deleteMin.doc =
+ {{
+ Deletes the smallest key from the {type NatMap.Nonempty}.
+
+ If the map is empty, this calls {abort}.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.Nonempty.deleteMin
+ (NatMap.Nonempty.fromList ((1, "foo") +| [(2, "bar")])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.delete} to delete a specific key.
+ * {NatMap.Nonempty.deleteMax} to delete the largest key.
+ * {NatMap.Nonempty.difference} to delete all keys in one map from another.
+ }}
+
+data.NatMap.Nonempty.difference :
+ NatMap.Nonempty a -> NatMap.Nonempty b -> NatMap a
+data.NatMap.Nonempty.difference = Nonempty.differenceWith do do None
+
+data.NatMap.Nonempty.difference.doc : Doc
+data.NatMap.Nonempty.difference.doc =
+ use NatMap.Nonempty fromList
+ {{
+ Deletes all keys in the second {type NatMap.Nonempty} from the first.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.Nonempty.difference
+ (fromList ((1, "foo") +| [(2, "bar")]))
+ (fromList (List.Nonempty.singleton (1, "foo"))))
+ ```
+
+ # See also
+
+ * {Nonempty.differenceWith} to more finely control which keys are deleted,
+ or replace specific values instead of deleting them.
+ * {NatMap.Nonempty.delete} to delete a specific key.
+ * {NatMap.Nonempty.deleteMax} to delete the largest key.
+ * {NatMap.Nonempty.deleteMin} to delete the smallest key.
+ * {NatMap.Nonempty.intersect} to delete all keys not in both maps.
+ }}
+
+data.NatMap.Nonempty.differenceWith :
+ (a ->{g} b ->{g} Optional a)
+ -> NatMap.Nonempty a
+ -> NatMap.Nonempty b
+ ->{g} NatMap a
+data.NatMap.Nonempty.differenceWith f =
+ Nonempty.differenceWithKey do x y -> f x y
+
+data.NatMap.Nonempty.differenceWith.doc : Doc
+data.NatMap.Nonempty.differenceWith.doc =
+ use NatMap.Nonempty fromList
+ use Text ==
+ {{
+ Deletes keys in the second {type NatMap.Nonempty} from the first, or replaces
+ their values with the result of the given function.
+
+ # Example
+
+ Delete all keys in the second map from the first unless the values under
+ those keys are equal in both maps:
+
+ ```
+ NatMap.toList
+ (Nonempty.differenceWith
+ (x y -> (if x == y then Some x else None))
+ (fromList ((1, "foo") +| [(2, "bar")]))
+ (fromList ((1, "foo") +| [(2, "baz")])))
+ ```
+
+ # See also
+
+ * {Nonempty.differenceWithKey} for a version that also receives the key.
+ * {NatMap.Nonempty.difference} to delete all keys in one map from another.
+ * {NatMap.Nonempty.intersectWith} to delete all keys not in both maps,
+ using a function to combine the remaining values.
+ }}
+
+data.NatMap.Nonempty.differenceWithKey :
+ (Nat ->{g} a ->{g} b ->{g} Optional a)
+ -> NatMap.Nonempty a
+ -> NatMap.Nonempty b
+ ->{g} NatMap a
+data.NatMap.Nonempty.differenceWithKey f = cases
+ t1@(NatMap.Nonempty.Bin p1 m1 sz1 l1 r1),
+ t2@(NatMap.Nonempty.Bin p2 m2 sz2 l2 r2)
+ | shorter m1 m2 ->
+ if nomatch p2 p1 m1 then toNatMap t1
+ else
+ if Nat.and p2 m1 Nat.== 0 then
+ NatMap.internal.bin
+ p1
+ m1
+ (data.NatMap.Nonempty.differenceWithKey f l1 t2)
+ (toNatMap r1)
+ else
+ NatMap.internal.bin
+ p1
+ m1
+ (toNatMap l1)
+ (data.NatMap.Nonempty.differenceWithKey f r1 t2)
+ | shorter m2 m1 ->
+ if nomatch p1 p2 m2 then toNatMap t1
+ else
+ if Nat.and p1 m2 Nat.== 0 then
+ data.NatMap.Nonempty.differenceWithKey f t1 l2
+ else data.NatMap.Nonempty.differenceWithKey f t1 r2
+ | p1 Nat.== p2 ->
+ NatMap.internal.bin
+ p1
+ m1
+ (data.NatMap.Nonempty.differenceWithKey f l1 l2)
+ (data.NatMap.Nonempty.differenceWithKey f r1 r2)
+ | otherwise -> toNatMap t1
+ t1@(NatMap.Nonempty.Tip k v), t2 ->
+ match NatMap.Nonempty.get k t2 with
+ Some v' ->
+ match f k v v' with
+ Some x -> toNatMap (NatMap.Nonempty.Tip k x)
+ None -> NatMap.empty
+ None -> toNatMap t1
+ t, NatMap.Nonempty.Tip k v -> Nonempty.updateWithKey (k' x -> f k' x v) k t
+
+data.NatMap.Nonempty.differenceWithKey.doc : Doc
+data.NatMap.Nonempty.differenceWithKey.doc =
+ use Nat ==
+ use NatMap.Nonempty fromList
+ {{
+ Deletes keys in the second {type NatMap.Nonempty} from the first, or replaces
+ their values with the result of the given function.
+
+ # Example
+
+ Delete all keys in the second map from the first unless the values under
+ those keys are both equal to the key:
+
+ ```
+ NatMap.toList
+ (Nonempty.differenceWithKey
+ (k x y -> (if x == y && x == k then Some x else None))
+ (fromList ((1, 1) +| [(2, 2)]))
+ (fromList ((1, 1) +| [(2, 3)])))
+ ```
+
+ # See also
+
+ * {Nonempty.differenceWith} for a version that does not receive the key.
+ * {NatMap.Nonempty.difference} to delete all keys in one map from another.
+ * {NatMap.Nonempty.intersectWithKey} to delete all keys not in both maps,
+ using a function to combine the remaining values.
+ }}
+
+data.NatMap.Nonempty.doc : Doc
+data.NatMap.Nonempty.doc =
+ {{
+ An efficient implementation of nonempty maps from keys of type {type Nat} to
+ values of some type. This type is parameterised by the type of the values.
+ This specialized map is much more efficient than the general map type,
+ {type Map}, when the keys are of type {type Nat} or can be encoded as
+ {type Nat}s.
+
+ This is the nonempty version of {type NatMap}. It's guaranteed to have at
+ least one entry.
+
+ Most of the functions here have a counterpart in the {type NatMap} namespace.
+
+ Many operations on {type NatMap} return a {type NatMap.Nonempty} when they
+ are guaranteed to return a nonempty map. Correspondingly, many operations on
+ {type NatMap.Nonempty} return a {type NatMap} when they may return an empty
+ map.
+
+ # Constructing nonempty maps
+
+ A map with a single key/value pair:
+
+ @signature{NatMap.Nonempty.singleton}
+
+ Construct a map from a list of key/value pairs:
+
+ @signature{NatMap.Nonempty.fromList}
+
+ Construct a map from a list of key/value pairs, using a combining function
+ to resolve conflicts:aaaaawwdawdawd
+
+ @signature{NatMap.Nonempty.fromListWith}
+
+ Construct a map from a list of key/value pairs, using a combining function
+ to resolve conflicts, and passing the key to the combining function:
+
+ @signature{NatMap.Nonempty.fromListWithKey}
+
+ # Querying
+
+ Look up the value at a key:
+
+ @signature{NatMap.Nonempty.get}
+
+ Look up the value at a key, or return a default value if the key is not
+ present:
+
+ @signature{NatMap.Nonempty.getOrElse}
+
+ Check if a key is in the map:
+
+ @signature{NatMap.Nonempty.contains}
+
+ Get the size of the map:
+
+ @signature{NatMap.Nonempty.size}
+
+ # Inserting
+
+ Insert a key/value pair into the map:
+
+ @signature{NatMap.Nonempty.insert}
+
+ Insert a key/value pair into the map, using a combining function to resolve
+ conflicts:
+
+ @signature{Nonempty.insertWith}
+
+ Insert a key/value pair into the map, using a combining function to resolve
+ conflicts, and passing the key to the combining function:
+
+ @signature{Nonempty.insertWithKey}
+
+ Insert a key/value pair into the map, using a combining function to resolve
+ conflicts, and passing the key to the combining function, and returning the
+ old value if it was present:
+
+ @signature{Nonempty.insertGetWithKey}
+
+ # Deleting and updating values
+
+ Delete a key and its value from the map:
+
+ @signature{NatMap.Nonempty.delete}
+
+ Update the value at a key with a function, or remove the key if the
+ function returns {None}:
+
+ @signature{NatMap.Nonempty.update}
+
+ Update the value at a key with a function, or remove the key if the
+ function returns {None}, passing the key to the function:
+
+ @signature{Nonempty.updateWithKey}
+
+ Update the value at a key with a function, or remove the key if the
+ function returns {None}, passing the key to the function, and returning the
+ old value if it was present:
+
+ @signature{Nonempty.updateGetWithKey}
+
+ Update, insert, or delete the value at a key with a function, depending on
+ whether the key is present in the map:
+
+ @signature{NatMap.Nonempty.alter}
+
+ Update, insert, or delete the value at a key with a function, passing the
+ key to the function.
+
+ @signature{Nonempty.alterWithKey}
+
+ # Combining maps
+
+ ## Unions
+
+ Add the entries from one map to another, preferring the entries from the
+ first map if there are conflicts:
+
+ @signature{NatMap.Nonempty.union}
+
+ Merge two maps, using a combining function to resolve conflicts:
+
+ @signature{NatMap.Nonempty.unionWith}
+
+ Merge two maps, using a combining function to resolve conflicts, and
+ passing the key to the combining function:
+
+ @signature{NatMap.Nonempty.unionWithKey}
+
+ Combine the entries from a list of maps, preferring the entries from the
+ first map if there are conflicts:
+
+ @signature{NatMap.Nonempty.unions}
+
+ Merge list of maps, using a combining function to resolve conflicts:
+
+ @signature{Nonempty.unionsWith}
+
+ ## Difference
+
+ Remove the entries from one map that are present in another map:
+
+ @signature{NatMap.Nonempty.difference}
+
+ Remove the entries from one map that are present in another map, using a
+ combining function to resolve conflicts:
+
+ @signature{Nonempty.differenceWith}
+
+ Remove the entries from one map that are present in another map, using a
+ combining function to resolve conflicts, and passing the key to the
+ combining function:
+
+ @signature{Nonempty.differenceWithKey}
+
+ ## Intersection
+
+ Keep only the entries from one map that are present in another map:
+
+ @signature{NatMap.Nonempty.intersect}
+
+ Keep only entries present in both of two maps, using a combining
+ function to resolve conflicts:
+
+ @signature{NatMap.Nonempty.intersectWith}
+
+ Keep only entries present in both of two maps, using a combining
+ function to resolve conflicts, and passing the key to the combining
+ function:
+
+ @signature{NatMap.Nonempty.intersectWithKey}
+
+ # Transforming maps
+
+ Apply a function to every value in a map:
+
+ @signature{NatMap.Nonempty.map}
+
+ Apply a function to every value in a map, passing the key to the function:
+
+ @signature{NatMap.Nonempty.mapWithKey}
+
+ Apply a partial function to every value in a map, removing entries for
+ which the function returns {None}:
+
+ @signature{Nonempty.mapOptional}
+
+ # Summarizing maps
+
+ Fold a function over the entries in a map, from lowest key to highest key:
+
+ @signature{Nonempty.fold}
+
+ Fold a function over the entries in a map, from lowest key to highest key,
+ passing the key to the function:
+
+ @signature{Nonempty.foldWithKey}
+
+ # Filtering and partitioning
+
+ Filter a map, keeping only entries for which the predicate returns
+ ``true``:
+
+ @signature{NatMap.Nonempty.filter}
+
+ Filter a map, keeping only entries for which the predicate returns
+ ``true``, passing the key to the predicate:
+
+ @signature{NatMap.Nonempty.filterWithKey}
+
+ Partition a map into two maps, one containing entries for which the
+ predicate returns ``true``, and one containing entries for which the
+ predicate returns ``false``:
+
+ @signature{NatMap.Nonempty.partition}
+
+ Partition a map into two maps, one containing entries for which the
+ predicate returns ``true``, and one containing entries for which the
+ predicate returns ``false``, passing the key to the predicate:
+
+ @signature{Nonempty.partitionWithKey}
+
+ Partition a map into two maps, one containing entries for which the
+ function returns {Left} and one containing entries for which the function
+ returns {Right}:
+
+ @signature{Nonempty.mapEither}
+
+ Partition a map into two maps, one containing entries for which the
+ function returns {Left} and one containing entries for which the function
+ returns {Right}, passing the key to the function:
+
+ @signature{Nonempty.mapEitherWithKey}
+
+ Split a map into two maps at a given key:
+
+ @signature{NatMap.Nonempty.split}
+
+ # Comparing maps
+
+ Check if two maps are equal according to a given comparison on the values:
+
+ @signature{Nonempty.equalBy}
+
+ Get a partial order for two maps given a total order on the values:
+
+ @signature{Nonempty.compareBy}
+
+ Check if all entries in one map are present in another map, using a given
+ comparison function to compare the values:
+
+ @signature{Nonempty.isSubmapOfBy}
+
+ Check if all entries in one map are present in another map, using {===} to
+ compare the values:
+
+ @signature{Nonempty.isSubmapOf}
+
+ Check that one map is a proper submap of another map, using a given
+ comparison function to compare the values:
+
+ @signature{Nonempty.isProperSubmapOfBy}
+
+ Check that one map is a proper submap of another map, using {===} to
+ compare the values:
+
+ @signature{Nonempty.isProperSubmapOf}
+
+ # Operations on the minimum or maximum key
+
+ Get the minimum key in a map:
+
+ @signature{NatMap.Nonempty.getMin}
+
+ Get the maximum key in a map:
+
+ @signature{NatMap.Nonempty.getMax}
+
+ Break a map into its minimum key and value, and the rest of the map:
+
+ @signature{NatMap.Nonempty.breakOffMin}
+
+ Break a map into its maximum key and value, and the rest of the map:
+
+ @signature{NatMap.Nonempty.breakOffMax}
+
+ Break a map into the value at the minimum key, and the rest of the map:
+
+ @signature{NatMap.Nonempty.minView}
+
+ Break a map into the value at the maximum key, and the rest of the map:
+
+ @signature{NatMap.Nonempty.maxView}
+
+ Remove the minimum key from a map:
+
+ @signature{NatMap.Nonempty.deleteMin}
+
+ Remove the maximum key from a map:
+
+ @signature{NatMap.Nonempty.deleteMax}
+
+ Update the value at the minimum key with a function:
+
+ @signature{Nonempty.updateMin}
+
+ Update the value at the maximum key with a function:
+
+ @signature{Nonempty.updateMax}
+
+ Update the value at the minimum key with a function, passing the key to the
+ function:
+
+ @signature{Nonempty.updateMinWithKey}
+
+ Update the value at the maximum key with a function, passing the key to the
+ function:
+
+ @signature{Nonempty.updateMaxWithKey}
+
+ # Convertings maps to other types
+
+ Convert a map to a list of key/value pairs:
+
+ @signature{NatMap.Nonempty.toList}
+
+ Get a list of the keys in a map:
+
+ @signature{NatMap.Nonempty.keys}
+
+ Get a list of the values in a map:
+
+ @signature{NatMap.Nonempty.values}
+ }}
+
+data.NatMap.Nonempty.equalBy :
+ (a ->{g} a ->{g} Boolean)
+ -> NatMap.Nonempty a
+ -> NatMap.Nonempty a
+ ->{g} Boolean
+data.NatMap.Nonempty.equalBy f = cases
+ NatMap.Nonempty.Bin p m sz l r, NatMap.Nonempty.Bin p' m' sz' l' r' ->
+ data.NatMap.Nonempty.equalBy f l l' && data.NatMap.Nonempty.equalBy f r r'
+ NatMap.Nonempty.Tip k v, NatMap.Nonempty.Tip k' v' -> k Nat.== k' && f v v'
+ _, _ -> false
+
+data.NatMap.Nonempty.equalBy.doc : Doc
+data.NatMap.Nonempty.equalBy.doc =
+ use Nat ==
+ use NatMap.Nonempty fromList
+ use Nonempty equalBy
+ {{
+ Checks if two {type NatMap.Nonempty}s are equal by comparing their values
+ using the given function.
+
+ # Examples
+
+ ```
+ equalBy
+ (x y -> x == y)
+ (fromList ((1, 1) +| [(2, 2)]))
+ (fromList ((1, 1) +| [(2, 2)]))
+ ```
+
+ ```
+ equalBy
+ (x y -> x == y)
+ (fromList ((1, 1) +| [(2, 2)]))
+ (fromList ((1, 1) +| [(2, 3)]))
+ ```
+
+ # See also
+
+ * {Nonempty.compareBy} to order two {type NatMap.Nonempty}s using a custom
+ comparison function.
+ * {NatMap.Nonempty.difference} to see where two {type NatMap.Nonempty}s
+ differ.
+ }}
+
+data.NatMap.Nonempty.filter :
+ (a ->{g} Boolean) -> NatMap.Nonempty a ->{g} NatMap a
+data.NatMap.Nonempty.filter f = NatMap.Nonempty.filterWithKey do x -> f x
+
+data.NatMap.Nonempty.filter.doc : Doc
+data.NatMap.Nonempty.filter.doc =
+ {{
+ Filters a {type NatMap.Nonempty} by retaining only values that satisfy the
+ given predicate.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.Nonempty.filter
+ Nat.isEven
+ (NatMap.Nonempty.fromList ((1, 11) +| [(2, 22), (3, 33), (4, 44)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.filterWithKey} for a version where the predicate also
+ receives the key.
+ * {NatMap.Nonempty.partition} to split a {type NatMap.Nonempty} into two
+ {type NatMap.Nonempty}s based on a predicate.
+ * {NatMap.Nonempty.intersect} to retain only keys that occur in another
+ map.
+ }}
+
+data.NatMap.Nonempty.filterWithKey :
+ (Nat ->{g} a ->{g} Boolean) -> NatMap.Nonempty a ->{g} NatMap a
+data.NatMap.Nonempty.filterWithKey f = cases
+ NatMap.Nonempty.Bin p m sz l r ->
+ use data.NatMap.Nonempty filterWithKey
+ l' = filterWithKey f l
+ r' = filterWithKey f r
+ NatMap.internal.bin p m l' r'
+ NatMap.Nonempty.Tip k v
+ | f k v -> toNatMap (NatMap.Nonempty.singleton k v)
+ | otherwise -> NatMap.empty
+
+data.NatMap.Nonempty.filterWithKey.doc : Doc
+data.NatMap.Nonempty.filterWithKey.doc =
+ use Nat ==
+ {{
+ Filters a {type NatMap.Nonempty} by retaining only entries that satisfy the
+ given predicate.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.Nonempty.filterWithKey
+ (k x -> k == x)
+ (NatMap.Nonempty.fromList ((1, 1) +| [(2, 22), (3, 3), (4, 44)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.filter} for a version that does not receive the key.
+ * {Nonempty.partitionWithKey} to split a {type NatMap.Nonempty} into two
+ {type NatMap.Nonempty}s based on a predicate.
+ * {NatMap.Nonempty.intersectWithKey} to retain only keys that occur in
+ another map.
+ }}
+
+data.NatMap.Nonempty.fold :
+ (a ->{g} b ->{g} b) -> b -> NatMap.Nonempty a ->{g} b
+data.NatMap.Nonempty.fold f = Nonempty.foldWithKey do x y -> f x y
+
+data.NatMap.Nonempty.fold.doc : Doc
+data.NatMap.Nonempty.fold.doc =
+ use Nat +
+ {{
+ Summarizes the values in a {type NatMap.Nonempty} using the given function.
+ The function is applied to each value in the {type NatMap.Nonempty} and the
+ function combines the result with the summary so far.
+
+ # Example
+
+ ```
+ Nonempty.fold
+ (+) 0 (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {Nonempty.foldWithKey} for a version that also receives the key.
+ }}
+
+data.NatMap.Nonempty.foldMap :
+ (b ->{g} b ->{g} b) -> (a ->{g} b) -> NatMap.Nonempty a ->{g} b
+data.NatMap.Nonempty.foldMap f g = foldMapWithKey f do g
+
+data.NatMap.Nonempty.foldMap.doc : Doc
+data.NatMap.Nonempty.foldMap.doc =
+ use Text ++
+ {{
+ Summarizes the values in a {type NatMap.Nonempty} using the given functions.
+ The second function is applied to each value in the {type NatMap.Nonempty}
+ and the first function combines the results.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.foldMap
+ (++)
+ Nat.toText
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {foldMapWithKey} for a version that receives the key.
+ * {Nonempty.fold} for a version that folds into an accumulator.
+ }}
+
+data.NatMap.Nonempty.foldMapWithKey :
+ (b ->{g} b ->{g} b) -> (Nat ->{g} a ->{g} b) -> NatMap.Nonempty a ->{g} b
+data.NatMap.Nonempty.foldMapWithKey f g = cases
+ NatMap.Nonempty.Bin p m sz l r ->
+ f
+ (data.NatMap.Nonempty.foldMapWithKey f g l)
+ (data.NatMap.Nonempty.foldMapWithKey f g r)
+ NatMap.Nonempty.Tip k v -> g k v
+
+data.NatMap.Nonempty.foldMapWithKey.doc : Doc
+data.NatMap.Nonempty.foldMapWithKey.doc =
+ use Nat toText
+ use Text ++
+ {{
+ Summarizes the values in a {type NatMap.Nonempty} using the given functions.
+ The first function is applied to each key-value pair in the
+ {type NatMap.Nonempty} and the second function combines the results.
+
+ # Example
+
+ ```
+ foldMapWithKey
+ (++)
+ (k x -> toText k ++ toText x)
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {Nonempty.foldWithKey} for a version that folds into an accumulator.
+ * {NatMap.Nonempty.foldMap} for a version that does not receive the key.
+ }}
+
+data.NatMap.Nonempty.foldWithKey :
+ (Nat ->{g} a ->{g} b ->{g} b) -> b -> NatMap.Nonempty a ->{g} b
+data.NatMap.Nonempty.foldWithKey f z = cases
+ NatMap.Nonempty.Bin p m sz l r ->
+ data.NatMap.Nonempty.foldWithKey
+ f (data.NatMap.Nonempty.foldWithKey f z r) l
+ NatMap.Nonempty.Tip k v -> f k v z
+
+data.NatMap.Nonempty.foldWithKey.doc : Doc
+data.NatMap.Nonempty.foldWithKey.doc =
+ use Nat +
+ {{
+ Summarizes the entries in a {type NatMap.Nonempty} using the given function.
+ The function is applied to each entry in the {type NatMap.Nonempty} and the
+ function combines the result with the summary so far.
+
+ The second argument is the initial summary that the entries are then combined
+ with.
+
+ # Example
+
+ ```
+ Nonempty.foldWithKey
+ (k x y -> k + x + y)
+ 0
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {Nonempty.fold} for a version that does not receive the key.
+ }}
+
+data.NatMap.Nonempty.fromList : List.Nonempty (Nat, a) -> NatMap.Nonempty a
+data.NatMap.Nonempty.fromList =
+ List.Nonempty.foldMap
+ NatMap.Nonempty.union (uncurry NatMap.Nonempty.singleton)
+
+data.NatMap.Nonempty.fromList.doc : Doc
+data.NatMap.Nonempty.fromList.doc =
+ use NatMap.Nonempty fromList toList
+ {{
+ Creates a {type NatMap.Nonempty} from a list of key-value pairs.
+
+ If the list contains duplicate keys, the last value for a given key is
+ retained.
+
+ # Examples
+
+ ```
+ toList (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ ```
+ toList (fromList ((1, 10) +| [(2, 20), (1, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.fromListWith} to specify how to combine values for
+ duplicate keys.
+ * {NatMap.Nonempty.fromListWithKey} to specify how to combine values for
+ duplicate keys, taking the key into account.
+ * {toList} to convert a {type NatMap.Nonempty} to a list of key-value
+ pairs.
+ }}
+
+data.NatMap.Nonempty.fromListWith :
+ (a ->{g} a ->{g} a) -> List.Nonempty (Nat, a) ->{g} NatMap.Nonempty a
+data.NatMap.Nonempty.fromListWith f =
+ List.Nonempty.foldMap
+ (NatMap.Nonempty.unionWith f) (uncurry NatMap.Nonempty.singleton)
+
+data.NatMap.Nonempty.fromListWith.doc : Doc
+data.NatMap.Nonempty.fromListWith.doc =
+ use Nat +
+ use NatMap.Nonempty toList
+ {{
+ Creates a {type NatMap.Nonempty} from a {type List.Nonempty} of key-value
+ pairs, combining duplicate keys using the given function.
+
+ # Example
+
+ ```
+ toList (NatMap.Nonempty.fromListWith (+) ((1, 10) +| [(2, 20), (1, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.fromList} for a version that resolves duplicate keys by
+ retaining the last value for that key.
+ * {NatMap.Nonempty.fromListWithKey} for a version that also takes the key
+ into account when combining duplicates.
+ * {toList} to convert a {type NatMap.Nonempty} to a list of key-value
+ pairs.
+ }}
+
+data.NatMap.Nonempty.fromListWithKey :
+ (Nat ->{g} a ->{g} a ->{g} a)
+ -> List.Nonempty (Nat, a)
+ ->{g} NatMap.Nonempty a
+data.NatMap.Nonempty.fromListWithKey f =
+ List.Nonempty.foldMap
+ (NatMap.Nonempty.unionWithKey f) (uncurry NatMap.Nonempty.singleton)
+
+data.NatMap.Nonempty.fromListWithKey.doc : Doc
+data.NatMap.Nonempty.fromListWithKey.doc =
+ use Nat +
+ use NatMap.Nonempty toList
+ {{
+ Creates a {type NatMap.Nonempty} from a list of key-value pairs, combining
+ duplicate keys using the given function.
+
+ # Example
+
+ ```
+ toList
+ (NatMap.Nonempty.fromListWithKey
+ (k x y -> k + x + y) ((1, 10) +| [(2, 20), (1, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.fromList} for a version that resolves duplicate keys by
+ retaining the last value for that key.
+ * {NatMap.Nonempty.fromListWith} for a version that does not take the key
+ into account when combining duplicates.
+ * {toList} to convert a {type NatMap.Nonempty} to a list of key-value
+ pairs.
+ }}
+
+data.NatMap.Nonempty.get : Nat -> NatMap.Nonempty a -> Optional a
+data.NatMap.Nonempty.get k = cases
+ NatMap.Nonempty.Bin _ mask _ l r
+ | Nat.and k mask Nat.== 0 -> data.NatMap.Nonempty.get k l
+ | otherwise -> data.NatMap.Nonempty.get k r
+ NatMap.Nonempty.Tip k' v -> if k Nat.== k' then Some v else None
+
+data.NatMap.Nonempty.get.doc : Doc
+data.NatMap.Nonempty.get.doc =
+ {{
+ Retrieves the value associated with the given key, if it exists.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.get
+ 2 (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.contains} to check if a key is present in a
+ {type NatMap.Nonempty}.
+ * {NatMap.Nonempty.delete} to delete a key from a {type NatMap.Nonempty}.
+ * {NatMap.Nonempty.insert} to insert a key-value pair into a
+ {type NatMap.Nonempty}.
+ }}
+
+data.NatMap.Nonempty.getAbove : Nat -> NatMap.Nonempty v -> Optional (Nat, v)
+data.NatMap.Nonempty.getAbove k m =
+ (below, eq, above) = NatMap.Nonempty.split k m
+ toOptional! do
+ ((k, v), _) = NatMap.breakOffMin above
+ (k, v)
+
+data.NatMap.Nonempty.getAbove.doc : Doc
+data.NatMap.Nonempty.getAbove.doc =
+ use NatMap Nonempty.getAbove
+ use NatMap.Nonempty fromList
+ {{
+ Returns the key-value pair in a {type NatMap.Nonempty} where the key is the
+ smallest one that is strictly larger than a given key.
+
+ # Examples
+
+ ```
+ Nonempty.getAbove 2 (fromList ((1, ?a) +| [(2, ?b), (3, ?c)]))
+ ```
+
+ ```
+ Nonempty.getAbove 4 (fromList ((1, ?a) +| [(2, ?b), (3, ?c)]))
+ ```
+
+ # See also
+
+ * {NatMap.getAbove} for the version of this that operates on a (possibly
+ empty) {type NatMap}.
+ * {NatMap.Nonempty.getAtLeast} to get the key-value pair where the key is
+ the smallest one that is larger than __or equal__ to a given key.
+ * {NatMap.Nonempty.getBelow} to get the key-value pair where the key is the
+ largest one that is strictly smaller than a given key.
+ * {NatMap.Nonempty.getAtMost} to get the key-value pair where the key is
+ the largest one that is smaller than or equal to a given key.
+ }}
+
+data.NatMap.Nonempty.getAtLeast : Nat -> NatMap.Nonempty v -> Optional (Nat, v)
+data.NatMap.Nonempty.getAtLeast k m =
+ (below, eq, above) = NatMap.Nonempty.split k m
+ match eq with
+ Some v -> Some (k, v)
+ None ->
+ toOptional! do
+ ((k, v), _) = NatMap.breakOffMin above
+ (k, v)
+
+data.NatMap.Nonempty.getAtLeast.doc : Doc
+data.NatMap.Nonempty.getAtLeast.doc =
+ use NatMap Nonempty.getAtLeast
+ use NatMap.Nonempty fromList
+ {{
+ Returns the key-value pair in a {type NatMap.Nonempty} where the key is the
+ smallest one that is larger than or equal to a given key.
+
+ # Examples
+
+ ```
+ Nonempty.getAtLeast 2 (fromList ((1, ?a) +| [(2, ?b), (3, ?c)]))
+ ```
+
+ ```
+ Nonempty.getAtLeast 4 (fromList ((1, ?a) +| [(2, ?b), (3, ?c)]))
+ ```
+
+ # See also
+
+ * {NatMap.getAtLeast} for the version of this that operates on a (possibly
+ empty) {type NatMap}.
+ * {NatMap.Nonempty.getAbove} to get the key-value pair where the key is the
+ smallest one that is strictly larger than a given key.
+ * {NatMap.Nonempty.getBelow} to get the key-value pair where the key is the
+ largest one that is strictly smaller than a given key.
+ * {NatMap.Nonempty.getAtMost} to get the key-value pair where the key is
+ the largest one that is smaller than or equal to a given key.
+ }}
+
+data.NatMap.Nonempty.getAtMost : Nat -> NatMap.Nonempty v -> Optional (Nat, v)
+data.NatMap.Nonempty.getAtMost k m =
+ (below, eq, above) = NatMap.Nonempty.split k m
+ match eq with
+ Some v -> Some (k, v)
+ None ->
+ toOptional! do
+ ((k, v), _) = NatMap.breakOffMax below
+ (k, v)
+
+data.NatMap.Nonempty.getAtMost.doc : Doc
+data.NatMap.Nonempty.getAtMost.doc =
+ use NatMap Nonempty.getAtMost
+ use NatMap.Nonempty fromList
+ {{
+ Returns the key-value pair in a {type NatMap.Nonempty} where the key is the
+ largest one that is smaller than or equal to a given key.
+
+ # Examples
+
+ ```
+ Nonempty.getAtMost 2 (fromList ((1, ?a) +| [(2, ?b), (3, ?c)]))
+ ```
+
+ ```
+ Nonempty.getAtMost 4 (fromList ((1, ?a) +| [(2, ?b), (3, ?c)]))
+ ```
+
+ # See also
+
+ * {NatMap.getAtMost} for the version of this that operates on a (possibly
+ empty) {type NatMap}.
+ * {NatMap.Nonempty.getBelow} to get the key-value pair where the key is the
+ largest one that is strictly smaller than a given key.
+ * {NatMap.Nonempty.getAbove} to get the key-value pair where the key is the
+ smallest one that is strictly larger than a given key.
+ * {NatMap.Nonempty.getAtLeast} to get the key-value pair where the key is
+ the smallest one that is larger than or equal to a given key.
+ }}
+
+data.NatMap.Nonempty.getBelow : Nat -> NatMap.Nonempty v -> Optional (Nat, v)
+data.NatMap.Nonempty.getBelow k m =
+ (below, eq, above) = NatMap.Nonempty.split k m
+ toOptional! do
+ ((k, v), _) = NatMap.breakOffMax below
+ (k, v)
+
+data.NatMap.Nonempty.getBelow.doc : Doc
+data.NatMap.Nonempty.getBelow.doc =
+ use NatMap Nonempty.getBelow
+ use NatMap.Nonempty fromList
+ {{
+ Returns the key-value pair in a {type NatMap.Nonempty} where the key is the
+ largest one that is strictly smaller than a given key.
+
+ # Examples
+
+ ```
+ Nonempty.getBelow 2 (fromList ((1, ?a) +| [(2, ?b), (3, ?c)]))
+ ```
+
+ ```
+ Nonempty.getBelow 4 (fromList ((1, ?a) +| [(2, ?b), (3, ?c)]))
+ ```
+
+ # See also
+
+ * {NatMap.getBelow} for the version of this that operates on a (possibly
+ empty) {type NatMap}.
+ * {NatMap.Nonempty.getAtMost} to get the key-value pair where the key is
+ the largest one that is smaller than or equal to a given key.
+ * {NatMap.Nonempty.getAbove} to get the key-value pair where the key is the
+ smallest one that is strictly larger than a given key.
+ * {NatMap.Nonempty.getAtLeast} to get the key-value pair where the key is
+ the smallest one that is larger than or equal to a given key.
+ }}
+
+data.NatMap.Nonempty.getMax : NatMap.Nonempty a -> a
+data.NatMap.Nonempty.getMax t =
+ (v, _) = NatMap.Nonempty.maxView t
+ v
+
+data.NatMap.Nonempty.getMax.doc : Doc
+data.NatMap.Nonempty.getMax.doc =
+ {{
+ Retrieves the value associated with the largest key in the
+ {type NatMap.Nonempty}.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.getMax
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.maxView} for a version of this that also returns the map
+ with the largest key removed.
+ * {NatMap.Nonempty.getMin} to retrieve the value associated with the
+ smallest key.
+ * {NatMap.Nonempty.deleteMax} to remove the largest key from a
+ {type NatMap.Nonempty}.
+ }}
+
+data.NatMap.Nonempty.getMin : NatMap.Nonempty a -> a
+data.NatMap.Nonempty.getMin t =
+ (v, _) = NatMap.Nonempty.minView t
+ v
+
+data.NatMap.Nonempty.getMin.doc : Doc
+data.NatMap.Nonempty.getMin.doc =
+ {{
+ Retrieves the value associated with the smallest key in the
+ {type NatMap.Nonempty}.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.getMin
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.minView} for a version of this that also returns the map
+ with the smallest key removed.
+ * {NatMap.Nonempty.getMax} to retrieve the value associated with the
+ largest key.
+ * {NatMap.Nonempty.deleteMin} to remove the smallest key.
+ }}
+
+data.NatMap.Nonempty.getOrAbort : Nat -> NatMap.Nonempty a ->{Abort} a
+data.NatMap.Nonempty.getOrAbort k m = match NatMap.Nonempty.get k m with
+ None -> abort
+ Some v -> v
+
+data.NatMap.Nonempty.getOrAbort.doc : Doc
+data.NatMap.Nonempty.getOrAbort.doc =
+ use NatMap.Nonempty fromList getOrAbort
+ {{
+ Retrieves the value associated with the given key, or calls {abort} if the
+ key is not present.
+
+ # Examples
+
+ ```
+ toOptional! do getOrAbort 2 (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ ```
+ toOptional! do getOrAbort 4 (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.get} for a version of this that returns {type Optional}
+ instead of aborting.
+ * {NatMap.Nonempty.getOrElse} to return a default value if the key is not
+ present.
+ }}
+
+data.NatMap.Nonempty.getOrElse : Nat -> a -> NatMap.Nonempty a -> a
+data.NatMap.Nonempty.getOrElse k def = cases
+ NatMap.Nonempty.Bin _ mask _ l r
+ | Nat.and k mask Nat.== 0 -> data.NatMap.Nonempty.getOrElse k def l
+ | otherwise -> data.NatMap.Nonempty.getOrElse k def r
+ NatMap.Nonempty.Tip k' v -> if k Nat.== k' then v else def
+
+data.NatMap.Nonempty.getOrElse.doc : Doc
+data.NatMap.Nonempty.getOrElse.doc =
+ use NatMap.Nonempty fromList getOrElse
+ {{
+ Retrieves the value associated with the given key, or returns the given
+ default value if the key is not present.
+
+ # Examples
+
+ ```
+ getOrElse 2 0 (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ ```
+ getOrElse 4 0 (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.get} for a version of this that returns {type Optional}
+ instead of a default value.
+ * {NatMap.Nonempty.getOrAbort} to {abort} if the key is not present.
+ }}
+
+data.NatMap.Nonempty.insert :
+ Nat -> a -> NatMap.Nonempty a -> NatMap.Nonempty a
+data.NatMap.Nonempty.insert k v = cases
+ t@(NatMap.Nonempty.Bin prefix mask size l r)
+ | nomatch k prefix mask ->
+ internal.join k (NatMap.Nonempty.Tip k v) prefix t
+ | Nat.and k mask Nat.== 0 ->
+ NatMap.internal.bim prefix mask (data.NatMap.Nonempty.insert k v l) r
+ | otherwise ->
+ NatMap.internal.bim prefix mask l (data.NatMap.Nonempty.insert k v r)
+ t@(NatMap.Nonempty.Tip k' _)
+ | k Nat.== k' -> NatMap.Nonempty.Tip k v
+ | otherwise -> internal.join k (NatMap.Nonempty.Tip k v) k' t
+
+data.NatMap.Nonempty.insert.doc : Doc
+data.NatMap.Nonempty.insert.doc =
+ use NatMap.Nonempty fromList insert toList
+ {{
+ Inserts a key-value pair into a {type NatMap.Nonempty}, replacing the
+ existing value if the key is already present.
+
+ # Examples
+
+ ```
+ toList (insert 2 20 (fromList ((1, 10) +| [(2, 30), (3, 30)])))
+ ```
+
+ ```
+ toList (insert 4 40 (fromList ((1, 10) +| [(2, 30), (3, 30)])))
+ ```
+
+ # See also
+
+ * {Nonempty.insertWith} for a version of this that allows you to specify a
+ function to combine the new and existing values.
+ * {Nonempty.insertWithKey} for a version that also provides the key to the
+ combining function.
+ * {Nonempty.insertGetWithKey} for a version that also returns the old value
+ if the key was already present.
+ * {NatMap.Nonempty.delete} to delete a key.
+ * {NatMap.Nonempty.get} to retrieve the value associated with a key.
+ }}
+
+data.NatMap.Nonempty.insertGetWithKey :
+ (Nat ->{g} a ->{g} a ->{g} a)
+ -> Nat
+ -> a
+ -> NatMap.Nonempty a
+ ->{g} (Optional a, NatMap.Nonempty a)
+data.NatMap.Nonempty.insertGetWithKey f k v = cases
+ t@(NatMap.Nonempty.Bin prefix mask size l r)
+ | nomatch k prefix mask ->
+ (None, internal.join k (NatMap.Nonempty.Tip k v) prefix t)
+ | Nat.and k mask Nat.== 0 ->
+ (found, l') = data.NatMap.Nonempty.insertGetWithKey f k v l
+ (found, NatMap.internal.bim prefix mask l' r)
+ | otherwise ->
+ (found, r') = data.NatMap.Nonempty.insertGetWithKey f k v r
+ (found, NatMap.internal.bim prefix mask l r')
+ t@(NatMap.Nonempty.Tip k' v')
+ | k Nat.== k' -> (Some v', NatMap.Nonempty.Tip k (f k v v'))
+ | otherwise -> (None, internal.join k (NatMap.Nonempty.Tip k v) k' t)
+
+data.NatMap.Nonempty.insertGetWithKey.doc : Doc
+data.NatMap.Nonempty.insertGetWithKey.doc =
+ use Nat isEven
+ use NatMap.Nonempty fromList toList
+ use Nonempty insertGetWithKey
+ {{
+ Inserts a key-value pair into a {type NatMap.Nonempty}, returning the old
+ value if the key was already present.
+
+ Takes a combining function that is passed the key, the new value, and the old
+ value if the key was already present. The combining function should return
+ the new value to be stored in the map.
+
+ # Examples
+
+ ```
+ (oldKey, newMap) =
+ insertGetWithKey
+ (key old new -> (if isEven key then old else new))
+ 2
+ 20
+ (fromList ((1, 10) +| [(2, 30), (3, 30)]))
+ (oldKey, toList newMap)
+ ```
+
+ ```
+ (oldKey, newMap) =
+ insertGetWithKey
+ (key old new -> (if isEven key then old else new))
+ 4
+ 40
+ (fromList ((1, 10) +| [(2, 30), (3, 30)]))
+ (oldKey, toList newMap)
+ ```
+
+ # See also
+
+ * {Nonempty.insertWithKey} for a version of this that does not return the
+ old value.
+ }}
+
+data.NatMap.Nonempty.insertWith :
+ (a ->{g} a ->{g} a) -> Nat -> a -> NatMap.Nonempty a ->{g} NatMap.Nonempty a
+data.NatMap.Nonempty.insertWith f k v t =
+ Nonempty.insertWithKey (do x' y' -> f x' y') k v t
+
+data.NatMap.Nonempty.insertWith.doc : Doc
+data.NatMap.Nonempty.insertWith.doc =
+ use Nat +
+ use NatMap.Nonempty fromList toList
+ use Nonempty insertWith
+ {{
+ Inserts a key-value pair into a {type NatMap.Nonempty}, combining the new and
+ existing values if the key is already present.
+
+ Takes a combining function that is passed the new and existing values. The
+ combining function should return the new value to be stored in the map.
+
+ # Examples
+
+ ```
+ toList (insertWith (+) 2 20 (fromList ((1, 10) +| [(2, 30), (3, 30)])))
+ ```
+
+ ```
+ toList (insertWith (+) 4 40 (fromList ((1, 10) +| [(2, 30), (3, 30)])))
+ ```
+
+ # See also
+
+ * {Nonempty.insertWithKey} for a version of this that also provides the key
+ to the combining function.
+ * {NatMap.Nonempty.insert} for a version that just replaces the existing
+ value.
+ * {Nonempty.insertGetWithKey} for a version that also returns the old value
+ if the key was already present.
+ }}
+
+data.NatMap.Nonempty.insertWithKey :
+ (Nat ->{g} a ->{g} a ->{g} a)
+ -> Nat
+ -> a
+ -> NatMap.Nonempty a
+ ->{g} NatMap.Nonempty a
+data.NatMap.Nonempty.insertWithKey f k v = cases
+ t@(NatMap.Nonempty.Bin prefix mask size l r)
+ | nomatch k prefix mask ->
+ internal.join k (NatMap.Nonempty.Tip k v) prefix t
+ | Nat.and k mask Nat.== 0 ->
+ NatMap.internal.bim
+ prefix mask (data.NatMap.Nonempty.insertWithKey f k v l) r
+ | otherwise ->
+ NatMap.internal.bim
+ prefix mask l (data.NatMap.Nonempty.insertWithKey f k v r)
+ t@(NatMap.Nonempty.Tip k' v')
+ | k Nat.== k' -> NatMap.Nonempty.Tip k (f k v v')
+ | otherwise -> internal.join k (NatMap.Nonempty.Tip k v) k' t
+
+data.NatMap.Nonempty.insertWithKey.doc : Doc
+data.NatMap.Nonempty.insertWithKey.doc =
+ use Nat + - isEven
+ use NatMap.Nonempty fromList toList
+ use Nonempty insertWithKey
+ {{
+ Inserts a key-value pair into a {type NatMap.Nonempty}, combining the new and
+ existing values if the key is already present.
+
+ Takes a combining function that is passed the key as well as the new and
+ existing values. The combining function should return the new value to be
+ stored in the map.
+
+ # Examples
+
+ ```
+ toList
+ (insertWithKey
+ (key x y -> (if isEven key then x + y else x - y))
+ 2
+ 20
+ (fromList ((1, 10) +| [(2, 30), (3, 30)])))
+ ```
+
+ ```
+ toList
+ (insertWithKey
+ (key x y -> (if isEven key then x + y else x - y))
+ 4
+ 40
+ (fromList ((1, 10) +| [(2, 30), (3, 30)])))
+ ```
+
+ # See also
+
+ * {Nonempty.insertWith} for a version of this that does not provide the key
+ to the combining function.
+ * {NatMap.Nonempty.insert} for a version that just replaces the existing
+ value.
+ * {Nonempty.insertGetWithKey} for a version that also returns the old value
+ if the key was already present.
+ }}
+
+data.NatMap.Nonempty.intersect :
+ NatMap.Nonempty a -> NatMap.Nonempty b -> NatMap a
+data.NatMap.Nonempty.intersect = NatMap.Nonempty.intersectWith const
+
+data.NatMap.Nonempty.intersect.doc : Doc
+data.NatMap.Nonempty.intersect.doc =
+ use NatMap.Nonempty fromList
+ {{
+ Returns a {type NatMap.Nonempty} containing only the keys that are present in
+ both of the given {type NatMap.Nonempty}s.
+
+ The values of the resulting {type NatMap} are the values of the first
+ {type NatMap.Nonempty}.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.Nonempty.intersect
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((2, 20) +| [(3, 30), (4, 40)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.intersectWith} for a version that also combines the
+ values of the two {type NatMap.Nonempty}s.
+ * {NatMap.Nonempty.intersectWithKey} for a version that combines the values
+ and additionally provides the key to the combining function.
+ }}
+
+data.NatMap.Nonempty.intersectWith :
+ (a ->{g} b ->{g} a) -> NatMap.Nonempty a -> NatMap.Nonempty b ->{g} NatMap a
+data.NatMap.Nonempty.intersectWith f =
+ NatMap.Nonempty.intersectWithKey do x y -> f x y
+
+data.NatMap.Nonempty.intersectWith.doc : Doc
+data.NatMap.Nonempty.intersectWith.doc =
+ use Nat +
+ use NatMap.Nonempty fromList
+ {{
+ Returns a {type NatMap.Nonempty} containing only the keys that are present in
+ both of the given {type NatMap.Nonempty}s. The values of the resulting
+ {type NatMap.Nonempty} are computed by combining the values of the two
+ {type NatMap.Nonempty}s using the given combining function.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.Nonempty.intersectWith
+ (+)
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((2, 20) +| [(3, 30), (4, 40)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.intersectWithKey} for a version that additionally
+ provides
+ * {NatMap.Nonempty.intersect} for a version that throws away the values of
+ the second {type NatMap.Nonempty}. the key to the combining function.
+ }}
+
+data.NatMap.Nonempty.intersectWithKey :
+ (Nat ->{g} a ->{g} b ->{g} a)
+ -> NatMap.Nonempty a
+ -> NatMap.Nonempty b
+ ->{g} NatMap a
+data.NatMap.Nonempty.intersectWithKey f = cases
+ t1@(NatMap.Nonempty.Bin p1 m1 sz1 l1 r1),
+ t2@(NatMap.Nonempty.Bin p2 m2 sz2 l2 r2)
+ | shorter m1 m2 ->
+ if nomatch p2 p1 m1 then NatMap.empty
+ else
+ if Nat.and p2 m1 Nat.== 0 then
+ data.NatMap.Nonempty.intersectWithKey f l1 t2
+ else data.NatMap.Nonempty.intersectWithKey f r1 t2
+ | shorter m2 m1 ->
+ if nomatch p1 p2 m2 then NatMap.empty
+ else
+ if Nat.and p1 m2 Nat.== 0 then
+ data.NatMap.Nonempty.intersectWithKey f t1 l2
+ else data.NatMap.Nonempty.intersectWithKey f t1 r2
+ | p1 Nat.== p2 ->
+ NatMap.internal.bin
+ p1
+ m1
+ (data.NatMap.Nonempty.intersectWithKey f l1 l2)
+ (data.NatMap.Nonempty.intersectWithKey f r1 r2)
+ | otherwise -> NatMap.empty
+ t1@(NatMap.Nonempty.Tip k v), t2 ->
+ match NatMap.Nonempty.get k t2 with
+ None -> NatMap.empty
+ Some v' -> toNatMap (NatMap.Nonempty.singleton k (f k v v'))
+ t1, t2@(NatMap.Nonempty.Tip k v) ->
+ match NatMap.Nonempty.get k t1 with
+ None -> NatMap.empty
+ Some v' -> toNatMap (NatMap.Nonempty.singleton k (f k v' v))
+
+data.NatMap.Nonempty.intersectWithKey.doc : Doc
+data.NatMap.Nonempty.intersectWithKey.doc =
+ use Nat +
+ use NatMap.Nonempty fromList
+ {{
+ Returns a {type NatMap.Nonempty} containing only the keys that are present in
+ both of the given {type NatMap.Nonempty}s. The values of the resulting
+ {type NatMap.Nonempty} are computed by combining the values of the two
+ {type NatMap.Nonempty}s using the given combining function. The combining
+ function can take the key into account.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (NatMap.Nonempty.intersectWithKey
+ (k x y -> k + x + y)
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((2, 20) +| [(3, 30), (4, 40)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.intersectWith} for a version that does not provide the
+ key to the combining function.
+ * {NatMap.Nonempty.intersect} for a version that throws away the values of
+ the second {type NatMap.Nonempty}.
+ }}
+
+data.NatMap.Nonempty.isProperSubmapOf :
+ NatMap.Nonempty a -> NatMap.Nonempty a -> Boolean
+data.NatMap.Nonempty.isProperSubmapOf = Nonempty.isProperSubmapOfBy (===)
+
+data.NatMap.Nonempty.isProperSubmapOf.doc : Doc
+data.NatMap.Nonempty.isProperSubmapOf.doc =
+ use NatMap.Nonempty fromList
+ use Nonempty isProperSubmapOf
+ {{
+ Checks whether the first {type NatMap.Nonempty} is a proper submap of the
+ second {type NatMap.Nonempty}.
+
+ A {type NatMap.Nonempty} is a proper submap of another {type NatMap.Nonempty}
+ if the second {type NatMap.Nonempty} contains all the keys of the first
+ {type NatMap.Nonempty} and at least one more key, and the values under the
+ shared keys are equal according to the {===} function.
+
+ # Examples
+
+ ```
+ isProperSubmapOf
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((1, 10) +| [(2, 20), (3, 30), (4, 40)]))
+ ```
+
+ ```
+ isProperSubmapOf
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ ```
+ isProperSubmapOf
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((1, 10) +| [(2, 20)]))
+ ```
+
+ # See also
+
+ * {Nonempty.isSubmapOf} for a version that returns `` true `` also if the
+ two {type NatMap.Nonempty}s are identical.
+ * {Nonempty.isProperSubmapOfBy} for a version that allows you to compare
+ the values under the keys with a custom function.
+ }}
+
+data.NatMap.Nonempty.isProperSubmapOfBy :
+ (a ->{g} a ->{g} Boolean)
+ -> NatMap.Nonempty a
+ -> NatMap.Nonempty a
+ ->{g} Boolean
+data.NatMap.Nonempty.isProperSubmapOfBy f t1 t2 =
+ Nonempty.submapCompareBy f t1 t2 === Some Less
+
+data.NatMap.Nonempty.isProperSubmapOfBy.doc : Doc
+data.NatMap.Nonempty.isProperSubmapOfBy.doc =
+ use Nat ==
+ use NatMap.Nonempty fromList
+ use Nonempty isProperSubmapOfBy
+ {{
+ Checks whether the first {type NatMap.Nonempty} is a proper submap of the
+ second {type NatMap.Nonempty}, and compares the values under the keys with a
+ custom function.
+
+ Returns `` true `` if the second {type NatMap.Nonempty} contains all the keys
+ of the first {type NatMap.Nonempty} and at least one more key, and the values
+ under the common keys are equal according to the given function.
+
+ # Examples
+
+ ```
+ isProperSubmapOfBy
+ (==)
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((1, 10) +| [(2, 20), (3, 30), (4, 40)]))
+ ```
+
+ ```
+ isProperSubmapOfBy
+ (==)
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ ```
+ isProperSubmapOfBy
+ (==)
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((1, 10) +| [(2, 20)]))
+ ```
+
+ # See also
+
+ * {Nonempty.isSubmapOfBy} for a version that returns `` true `` also if the
+ two {type NatMap.Nonempty}s have exactly the same keys.
+ * {Nonempty.isProperSubmapOf} for a version that compares the values under
+ the keys using the {===} function.
+ }}
+
+data.NatMap.Nonempty.isSubmapOf :
+ NatMap.Nonempty a -> NatMap.Nonempty a -> Boolean
+data.NatMap.Nonempty.isSubmapOf = Nonempty.isSubmapOfBy (===)
+
+data.NatMap.Nonempty.isSubmapOf.doc : Doc
+data.NatMap.Nonempty.isSubmapOf.doc =
+ use NatMap.Nonempty fromList
+ use Nonempty isSubmapOf
+ {{
+ Checks whether the first {type NatMap.Nonempty} is a submap of the second
+ {type NatMap.Nonempty}.
+
+ A {type NatMap.Nonempty} is a submap of another {type NatMap.Nonempty} if the
+ second {type NatMap.Nonempty} contains all the keys of the first
+ {type NatMap.Nonempty}, and the values under the shared keys in one map are
+ equal to the values under the shared keys in the other map according to the
+ {===} function.
+
+ # Examples
+
+ ```
+ isSubmapOf
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((1, 10) +| [(2, 20), (3, 30), (4, 40)]))
+ ```
+
+ ```
+ isSubmapOf
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ ```
+ isSubmapOf
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((1, 10) +| [(2, 20)]))
+ ```
+
+ # See also
+
+ * {Nonempty.isProperSubmapOf} for a version that returns `` true `` only if
+ the second {type NatMap.Nonempty} contains at least one more key than the
+ first.
+ * {Nonempty.isSubmapOfBy} for a version that allows you to compare the
+ values under the keys with a custom function.
+ }}
+
+data.NatMap.Nonempty.isSubmapOfBy :
+ (a ->{g} a ->{g} Boolean)
+ -> NatMap.Nonempty a
+ -> NatMap.Nonempty a
+ ->{g} Boolean
+data.NatMap.Nonempty.isSubmapOfBy f t1 t2 =
+ match Nonempty.submapCompareBy f t1 t2 with
+ Some Less -> true
+ Some Equal -> true
+ _ -> false
+
+data.NatMap.Nonempty.isSubmapOfBy.doc : Doc
+data.NatMap.Nonempty.isSubmapOfBy.doc =
+ use Nat ==
+ use NatMap.Nonempty fromList
+ use Nonempty isSubmapOfBy
+ {{
+ Checks whether the first {type NatMap.Nonempty} is a submap of the second
+ {type NatMap.Nonempty}, and compares the values under the keys with a custom
+ function.
+
+ Returns `` true `` if the second {type NatMap.Nonempty} contains all the keys
+ of the first {type NatMap.Nonempty}, and the values under the common keys are
+ equal according to the given function.
+
+ # Examples
+
+ ```
+ isSubmapOfBy
+ (==)
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((1, 10) +| [(2, 20), (3, 30), (4, 40)]))
+ ```
+
+ ```
+ isSubmapOfBy
+ (==)
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ ```
+ isSubmapOfBy
+ (==)
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((1, 10) +| [(2, 20)]))
+ ```
+
+ # See also
+
+ * {Nonempty.isProperSubmapOfBy} for a version that returns `` true `` only
+ if the second {type NatMap.Nonempty} contains at least one more key than
+ the first.
+ * {Nonempty.isSubmapOf} for a version that compares the values under the
+ keys using the {===} function.
+ }}
+
+data.NatMap.Nonempty.keys : NatMap.Nonempty a -> List.Nonempty Nat
+data.NatMap.Nonempty.keys =
+ use Nonempty ++
+ foldMapWithKey (++) (k _ -> List.Nonempty.singleton k)
+
+data.NatMap.Nonempty.keys.doc : Doc
+data.NatMap.Nonempty.keys.doc =
+ {{
+ Returns the keys of a {type NatMap.Nonempty} as a list.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.keys
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.values} returns the values rather than the keys.
+ * {NatMap.Nonempty.toList} returns both the keys and values.
+ }}
+
+data.NatMap.Nonempty.keySet : NatMap.Nonempty a -> NatSet.Nonempty
+data.NatMap.Nonempty.keySet =
+ foldMapWithKey NatSet.Nonempty.union (k _ -> NatSet.singleton k)
+
+data.NatMap.Nonempty.keySet.doc : Doc
+data.NatMap.Nonempty.keySet.doc =
+ {{
+ Returns the {type NatSet.Nonempty} of keys in a {type NatMap.Nonempty}.
+
+ # Example
+
+ ```
+ Nonempty.toListAscending
+ (Nonempty.keySet
+ (NatMap.Nonempty.fromList ((1, "a") +| [(2, "b"), (3, "c")])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.keys} to get the {type List.Nonempty} of keys.
+ * {NatMap.Nonempty.values} to get the {type List.Nonempty} of values.
+ * {NatMap.Nonempty.toList} to convert a {type NatMap.Nonempty} to a
+ {type List.Nonempty} of key-value pairs.
+ }}
+
+data.NatMap.Nonempty.map :
+ (a ->{g} b) -> NatMap.Nonempty a ->{g} NatMap.Nonempty b
+data.NatMap.Nonempty.map f = NatMap.Nonempty.mapWithKey do x -> f x
+
+data.NatMap.Nonempty.map.doc : Doc
+data.NatMap.Nonempty.map.doc =
+ use Nat +
+ {{
+ Applies a function to every value in a {type NatMap.Nonempty}.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.toList
+ (NatMap.Nonempty.map
+ (x -> x + 1) (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.mapWithKey} applies a function to both the keys and
+ values.
+ * {Nonempty.mapOptional} applies a function to the values, removing the
+ ones that return {None}.
+ * {Nonempty.mapEither} applies an {type Either}-valued function to the
+ values, partitioning the {type NatMap.Nonempty} into two {type NatMap}s.
+ * {NatMap.Nonempty.adjust} applies a function to the value under a specific
+ key.
+ }}
+
+data.NatMap.Nonempty.mapEither :
+ (a ->{g} Either b c) -> NatMap.Nonempty a ->{g} (NatMap b, NatMap c)
+data.NatMap.Nonempty.mapEither f = Nonempty.mapEitherWithKey do x -> f x
+
+data.NatMap.Nonempty.mapEither.doc : Doc
+data.NatMap.Nonempty.mapEither.doc =
+ {{
+ Applies an {type Either}-valued function to every value in a
+ {type NatMap.Nonempty}, partitioning the {type NatMap.Nonempty} into two
+ {type NatMap}s.
+
+ # Example
+
+ ```
+ Tuple.bimap
+ NatMap.toList
+ (Nonempty.mapEither
+ (x -> (if Nat.isEven x then Left x else Right x))
+ (NatMap.Nonempty.fromList ((1, 11) +| [(2, 22), (3, 33)])))
+ ```
+
+ # See also
+
+ * {Nonempty.mapEitherWithKey} for a version of this that applies the
+ function to both the keys and values.
+ * {Nonempty.mapOptional} applies a function to the values, removing the
+ ones that return {None}.
+ * {NatMap.Nonempty.map} just applies a function to all the values.
+ * {NatMap.Nonempty.partition} applies a predicate to the values,
+ partitioning the {type NatMap.Nonempty} into two {type NatMap}s.
+ }}
+
+data.NatMap.Nonempty.mapEitherWithKey :
+ (Nat ->{g} a ->{g} Either b c)
+ -> NatMap.Nonempty a
+ ->{g} (NatMap b, NatMap c)
+data.NatMap.Nonempty.mapEitherWithKey f = cases
+ NatMap.Nonempty.Bin p m sz l r ->
+ (l1, l2) = data.NatMap.Nonempty.mapEitherWithKey f l
+ (r1, r2) = data.NatMap.Nonempty.mapEitherWithKey f r
+ (NatMap.internal.bin p m l1 r1, NatMap.internal.bin p m l2 r2)
+ NatMap.Nonempty.Tip k v ->
+ match f k v with
+ Left v' -> (toNatMap (NatMap.Nonempty.Tip k v'), NatMap.empty)
+ Right v' -> (NatMap.empty, toNatMap (NatMap.Nonempty.Tip k v'))
+
+data.NatMap.Nonempty.mapEitherWithKey.doc : Doc
+data.NatMap.Nonempty.mapEitherWithKey.doc =
+ {{
+ Applies an {type Either}-valued function to every value in a
+ {type NatMap.Nonempty}, partitioning the {type NatMap.Nonempty} into two
+ {type NatMap.Nonempty}s.
+
+ # Example
+
+ ```
+ Tuple.bimap
+ NatMap.toList
+ (Nonempty.mapEitherWithKey
+ (k x -> (if Nat.isEven k then Left x else Right x))
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)])))
+ ```
+
+ # See also
+
+ * {Nonempty.mapEither} for a version of this that applies the function only
+ to the values.
+ * {Nonempty.mapOptionalWithKey} applies a function to the entries, removing
+ the ones that return {None}.
+ * {NatMap.Nonempty.mapWithKey} just applies a function to all the entries.
+ * {Nonempty.partitionWithKey} applies a predicate to the entries,
+ partitioning the {type NatMap.Nonempty} into two {type NatMap}s.
+ }}
+
+data.NatMap.Nonempty.mapOptional :
+ (a ->{g} Optional b) -> NatMap.Nonempty a ->{g} NatMap b
+data.NatMap.Nonempty.mapOptional f = Nonempty.mapOptionalWithKey do x -> f x
+
+data.NatMap.Nonempty.mapOptional.doc : Doc
+data.NatMap.Nonempty.mapOptional.doc =
+ {{
+ Applies a function to every value in a {type NatMap.Nonempty}, removing the
+ ones that return {None}.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (Nonempty.mapOptional
+ (x -> (if Nat.isEven x then Some x else None))
+ (NatMap.Nonempty.fromList ((1, 11) +| [(2, 22), (3, 33)])))
+ ```
+
+ # See also
+
+ * {Nonempty.mapOptionalWithKey} for a version of this that applies the
+ function to both the keys and values.
+ * {Nonempty.mapEither} applies an {type Either}-valued function to the
+ values, partitioning the {type NatMap.Nonempty} into two {type NatMap}s.
+ * {NatMap.Nonempty.map} just applies a function to all the values.
+ * {NatMap.Nonempty.filter} applies a predicate to the values, removing the
+ ones that return {false}.
+ }}
+
+data.NatMap.Nonempty.mapOptionalWithKey :
+ (Nat ->{g} a ->{g} Optional b) -> NatMap.Nonempty a ->{g} NatMap b
+data.NatMap.Nonempty.mapOptionalWithKey f = cases
+ NatMap.Nonempty.Bin p m sz l r ->
+ use data.NatMap.Nonempty mapOptionalWithKey
+ l' = mapOptionalWithKey f l
+ r' = mapOptionalWithKey f r
+ NatMap.internal.bin p m l' r'
+ NatMap.Nonempty.Tip k v ->
+ match f k v with
+ Some v' -> toNatMap (NatMap.Nonempty.singleton k v')
+ None -> NatMap.empty
+
+data.NatMap.Nonempty.mapOptionalWithKey.doc : Doc
+data.NatMap.Nonempty.mapOptionalWithKey.doc =
+ {{
+ Applies a function to every key-value pair in a {type NatMap.Nonempty},
+ removing the ones that return {None}.
+
+ # Example
+
+ ```
+ NatMap.toList
+ (Nonempty.mapOptionalWithKey
+ (k x -> (if Nat.isEven k then Some x else None))
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)])))
+ ```
+
+ # See also
+
+ * {Nonempty.mapOptional} for a version of this that applies the function
+ only to the values.
+ * {Nonempty.mapEitherWithKey} applies an {type Either}-valued function to
+ the entries, partitioning the {type NatMap.Nonempty} into two
+ {type NatMap}s.
+ * {NatMap.Nonempty.mapWithKey} just applies a function to all the entries.
+ * {NatMap.Nonempty.filterWithKey} applies a predicate to the entries,
+ removing the ones that return {false}.
+ }}
+
+data.NatMap.Nonempty.mapWithKey :
+ (Nat ->{g} a ->{g} b) -> NatMap.Nonempty a ->{g} NatMap.Nonempty b
+data.NatMap.Nonempty.mapWithKey f = cases
+ NatMap.Nonempty.Bin p m sz l r ->
+ NatMap.internal.bim
+ p
+ m
+ (data.NatMap.Nonempty.mapWithKey f l)
+ (data.NatMap.Nonempty.mapWithKey f r)
+ NatMap.Nonempty.Tip k v -> NatMap.Nonempty.Tip k (f k v)
+
+data.NatMap.Nonempty.mapWithKey.doc : Doc
+data.NatMap.Nonempty.mapWithKey.doc =
+ use Nat +
+ {{
+ Applies a function to every key-value pair in a {type NatMap.Nonempty}.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.toList
+ (NatMap.Nonempty.mapWithKey
+ (+) (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.map} for a version of this that applies the function
+ only to the values.
+ * {Nonempty.mapOptionalWithKey} applies a function to the entries, removing
+ the ones that return {None}.
+ * {Nonempty.mapEitherWithKey} applies an {type Either}-valued function to
+ the entries, partitioning the {type NatMap.Nonempty} into two
+ {type NatMap}s.
+ }}
+
+data.NatMap.Nonempty.maxKey : NatMap.Nonempty a -> Nat
+data.NatMap.Nonempty.maxKey m =
+ ((k, _), _) = NatMap.Nonempty.breakOffMax m
+ k
+
+data.NatMap.Nonempty.maxKey.doc : Doc
+data.NatMap.Nonempty.maxKey.doc =
+ {{
+ Returns the largest key in the map.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.fromList ((1, "a") +| [(2, "b"), (3, "c")])
+ |> Nonempty.maxKey
+ ```
+ }}
+
+data.NatMap.Nonempty.maxView : NatMap.Nonempty a -> (a, NatMap a)
+data.NatMap.Nonempty.maxView t =
+ ((_, v), t') = NatMap.Nonempty.breakOffMax t
+ (v, t')
+
+data.NatMap.Nonempty.maxView.doc : Doc
+data.NatMap.Nonempty.maxView.doc =
+ {{
+ Returns the value of the largest key in the {type NatMap.Nonempty} and the
+ {type NatMap} with that key removed.
+
+ # Example
+
+ ```
+ Tuple.second
+ NatMap.toList
+ (NatMap.Nonempty.maxView
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.breakOffMax} for a version of this that returns the key
+ as well as the value.
+ * {NatMap.Nonempty.minView} for the value under the smallest key.
+ * {NatMap.Nonempty.breakOffMin} for the key and value under the smallest
+ key.
+ }}
+
+data.NatMap.Nonempty.minKey : NatMap.Nonempty a -> Nat
+data.NatMap.Nonempty.minKey m =
+ ((k, _), _) = NatMap.Nonempty.breakOffMin m
+ k
+
+data.NatMap.Nonempty.minView : NatMap.Nonempty a -> (a, NatMap a)
+data.NatMap.Nonempty.minView t =
+ ((_, v), t') = NatMap.Nonempty.breakOffMin t
+ (v, t')
+
+data.NatMap.Nonempty.minView.doc : Doc
+data.NatMap.Nonempty.minView.doc =
+ {{
+ Returns the value of the smallest key in the {type NatMap.Nonempty} and the
+ {type NatMap} with that key removed.
+
+ # Example
+
+ ```
+ Tuple.second
+ NatMap.toList
+ (NatMap.Nonempty.minView
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.breakOffMin} for a version of this that returns the key
+ as well as the value.
+ * {NatMap.Nonempty.maxView} for the value under the largest key.
+ * {NatMap.Nonempty.breakOffMax} for the key and value under the largest
+ key.
+ }}
+
+data.NatMap.Nonempty.nth : Nat -> NatMap.Nonempty v -> Optional (Nat, v)
+data.NatMap.Nonempty.nth index = cases
+ NatMap.Nonempty.Tip k v -> if index Nat.== 0 then Some (k, v) else None
+ NatMap.Nonempty.Bin p m sz l r ->
+ use Nat - <
+ use data.NatMap.Nonempty nth
+ leftSize = NatMap.Nonempty.size l
+ if index < leftSize then nth index l else nth (index - leftSize) r
+
+data.NatMap.Nonempty.nth.doc : Doc
+data.NatMap.Nonempty.nth.doc =
+ {{
+ Returns the key-value pair in the given {type NatMap.Nonempty} with the
+ `i`-th smallest key, where `i`=0 is the smallest key (according to
+ {Universal.ordering}).
+
+ Is the same as {{
+ docExample 2 do
+ i as ->
+ List.at i (sortBy at1 (List.Nonempty.toList (NatMap.Nonempty.toList as)))
+ }} but doesn't require instantiating the intermediate {type List}.
+
+ ```
+ s =
+ NatMap.Nonempty.fromList
+ (Nonempty.Nonempty
+ (6, "six") [(5, "five"), (4, "four"), (2, "two"), (1, "one")])
+ List.map
+ (i -> NatMap.Nonempty.nth i s) (List.range 0 (NatMap.Nonempty.size s))
+ ```
+ }}
+
+test> data.NatMap.Nonempty.nth.tests =
+ test.verify do
+ use Random natIn
+ Each.repeat 100
+ s =
+ (natIn 0 20, natIn 0 10)
+ +| (List.replicate (natIn 0 19) do (natIn 0 20, natIn 0 10))
+ |> NatMap.Nonempty.fromList
+ ensure
+ (List.somes
+ (List.map
+ (i -> NatMap.Nonempty.nth i s)
+ (List.range 0 (NatMap.Nonempty.size s)))
+ === (NatMap.Nonempty.toList s |> List.Nonempty.toList))
+
+data.NatMap.Nonempty.partition :
+ (a ->{g} Boolean) -> NatMap.Nonempty a ->{g} (NatMap a, NatMap a)
+data.NatMap.Nonempty.partition f = Nonempty.partitionWithKey do x -> f x
+
+data.NatMap.Nonempty.partition.doc : Doc
+data.NatMap.Nonempty.partition.doc =
+ {{
+ Partitions a {type NatMap.Nonempty} into two {type NatMap}s, one containing
+ the values that satisfy the predicate and one containing the values that do
+ not.
+
+ # Example
+
+ ```
+ Tuple.bimap
+ NatMap.toList
+ (NatMap.Nonempty.partition
+ Nat.isEven (NatMap.Nonempty.fromList ((1, 11) +| [(2, 22), (3, 33)])))
+ ```
+
+ # See also
+
+ * {Nonempty.partitionWithKey} for a version of this that applies the
+ predicate to both the keys and values.
+ * {NatMap.Nonempty.filter} for a version that only keeps the values that
+ satisfy the predicate.
+ * {Nonempty.mapEither} for partitioning into two {type NatMap}s based on an
+ {type Either}-valued function.
+ * {NatMap.Nonempty.split} for partitioning into two {type NatMap}s around a
+ pivot key.
+ }}
+
+data.NatMap.Nonempty.partitionWithKey :
+ (Nat ->{g} a ->{g} Boolean) -> NatMap.Nonempty a ->{g} (NatMap a, NatMap a)
+data.NatMap.Nonempty.partitionWithKey f = cases
+ NatMap.Nonempty.Bin p m sz l r ->
+ (l1, l2) = data.NatMap.Nonempty.partitionWithKey f l
+ (r1, r2) = data.NatMap.Nonempty.partitionWithKey f r
+ (NatMap.internal.bin p m l1 r1, NatMap.internal.bin p m l2 r2)
+ NatMap.Nonempty.Tip k v
+ | f k v -> (toNatMap (NatMap.Nonempty.Tip k v), NatMap.empty)
+ | otherwise -> (NatMap.empty, toNatMap (NatMap.Nonempty.Tip k v))
+
+data.NatMap.Nonempty.partitionWithKey.doc : Doc
+data.NatMap.Nonempty.partitionWithKey.doc =
+ use Nat >
+ {{
+ Partitions a {type NatMap.Nonempty} into two {type NatMap}s, one containing
+ the entries that satisfy the predicate and one containing the entries that do
+ not.
+
+ # Example
+
+ ```
+ Tuple.bimap
+ NatMap.toList
+ (Nonempty.partitionWithKey
+ (k v -> Nat.isEven k && v > 20)
+ (NatMap.Nonempty.fromList ((1, 11) +| [(2, 22), (3, 33)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.partition} for a version of this that applies the
+ predicate only to the values.
+ * {NatMap.Nonempty.filterWithKey} for a version that only keeps the entries
+ that satisfy the predicate.
+ * {Nonempty.mapEitherWithKey} for partitioning into two {type NatMap}s
+ based on an {type Either}-valued function.
+ }}
+
+data.NatMap.Nonempty.randomChoice : NatMap.Nonempty v ->{Random} (Nat, v)
+data.NatMap.Nonempty.randomChoice map =
+ randomIndex = Random.natIn 0 (NatMap.Nonempty.size map)
+ NatMap.Nonempty.nth randomIndex map
+ |> getOrBug "NatMap.Nonempty.randomChoice: index out of bounds"
+
+data.NatMap.Nonempty.randomChoice.doc : Doc
+data.NatMap.Nonempty.randomChoice.doc =
+ use NatMap.Nonempty fromList randomChoice
+ use Nonempty Nonempty
+ {{
+ Picks a random key-value pair from the given {type NatMap.Nonempty}.
+
+ # Examples
+
+ ```
+ lcg 4096 do
+ randomChoice
+ (fromList (Nonempty (5, "five") [(4, "four"), (2, "two"), (1, "one")]))
+ ```
+
+ ```
+ lcg 2510 do
+ randomChoice
+ (fromList (Nonempty (5, "five") [(4, "four"), (2, "two"), (1, "one")]))
+ ```
+ }}
+
+test> data.NatMap.Nonempty.randomChoice.test = test.verify do
+ map = NatMap.Nonempty.fromList ((0, 0) +| [(1, 1), (2, 2), (3, 3), (4, 4)])
+ Each.repeat 1000
+ e = NatMap.Nonempty.randomChoice map
+ ensure (NatMap.Nonempty.contains (at1 e) map)
+
+data.NatMap.Nonempty.randomKey : NatMap.Nonempty v ->{Random} Nat
+data.NatMap.Nonempty.randomKey map = NatMap.Nonempty.randomChoice map |> at1
+
+data.NatMap.Nonempty.randomKey.doc : Doc
+data.NatMap.Nonempty.randomKey.doc =
+ use NatMap.Nonempty fromList randomKey
+ use Nonempty Nonempty
+ {{
+ Picks a random key from the given {type NatMap.Nonempty}.
+
+ # Examples
+
+ ```
+ lcg 4096 do
+ randomKey
+ (fromList
+ (Nonempty
+ (6, "six") [(5, "five"), (4, "four"), (2, "two"), (1, "one")]))
+ ```
+
+ ```
+ lcg 2510 do
+ randomKey
+ (fromList
+ (Nonempty
+ (6, "six") [(5, "five"), (4, "four"), (2, "two"), (1, "one")]))
+ ```
+ }}
+
+data.NatMap.Nonempty.randomValue : NatMap.Nonempty v ->{Random} v
+data.NatMap.Nonempty.randomValue map = NatMap.Nonempty.randomChoice map |> at2
+
+data.NatMap.Nonempty.randomValue.doc : Doc
+data.NatMap.Nonempty.randomValue.doc =
+ use NatMap.Nonempty fromList randomValue
+ use Nonempty Nonempty
+ {{
+ Picks a random value from the given {type NatMap.Nonempty}.
+
+ # Examples
+
+ ```
+ lcg 4096 do
+ randomValue
+ (fromList
+ (Nonempty
+ (6, "six") [(5, "five"), (4, "four"), (2, "two"), (1, "one")]))
+ ```
+
+ ```
+ lcg 2510 do
+ randomValue
+ (fromList
+ (Nonempty
+ (6, "six") [(5, "five"), (4, "four"), (2, "two"), (1, "one")]))
+ ```
+ }}
+
+data.NatMap.Nonempty.restrict : Nat -> Nat -> NatMap.Nonempty v -> NatMap v
+data.NatMap.Nonempty.restrict min max m =
+ (belowMin, eqMin, aboveMin) = NatMap.Nonempty.split min m
+ (between, eqMax, aboveMax) = NatMap.split max aboveMin
+ match eqMin with
+ None -> between
+ Some v -> NatMap.insert min v between
+
+data.NatMap.Nonempty.restrict.doc : Doc
+data.NatMap.Nonempty.restrict.doc =
+ use NatMap toList
+ use NatMap.Nonempty fromList
+ {{
+ Restricts a {type NatMap.Nonempty} to a given range of keys.
+
+ `` Nonempty.restrict min max map `` drops all entries from `map` that are
+ outside the range between `min` and `max`.
+
+ The range is inclusive of the `min` and exclusive of the `max`. That is, a
+ key that is exactly `min` will be kept, but a key that is exactly `max` will
+ be removed.
+
+ # Examples
+
+ ```
+ (1, ?a) +| [(2, ?b), (3, ?c), (4, ?d), (5, ?e)] |> fromList
+ |> Nonempty.restrict 2 4
+ |> toList
+ ```
+
+ ```
+ (1, ?a) +| [(3, ?c), (5, ?e)] |> fromList |> Nonempty.restrict 2 4
+ |> toList
+ ```
+
+ # See also
+
+ * {NatMap.restrict} for the version of this that operates on a (possibly
+ empty) {type NatMap}.
+ * {Nonempty.restrictAbove} to keep only keys above a given key.
+ * {Nonempty.restrictBelow} to keep only keys below a given key.
+ }}
+
+data.NatMap.Nonempty.restrictAbove : Nat -> NatMap.Nonempty v -> NatMap v
+data.NatMap.Nonempty.restrictAbove min m =
+ (_, _, aboveMin) = NatMap.Nonempty.split min m
+ aboveMin
+
+data.NatMap.Nonempty.restrictAbove.doc : Doc
+data.NatMap.Nonempty.restrictAbove.doc =
+ {{
+ Drops keys from a {type NatMap.Nonempty}, keeping only keys that are strictly
+ larger than a given key.
+
+ # Example
+
+ ```
+ (1, ?a) +| [(2, ?b), (3, ?c), (4, ?d), (5, ?e)] |> NatMap.Nonempty.fromList
+ |> Nonempty.restrictAbove 2
+ |> NatMap.toList
+ ```
+
+ # See also
+
+ * {NatMap.restrictAbove} for the version of this that operates on a
+ (possibly empty) {type NatMap}.
+ * {Nonempty.restrict} to restrict to a range of keys.
+ * {Nonempty.restrictBelow} to restrict to a range of keys, keeping only
+ keys that are strictly smaller than a given key.
+ }}
+
+data.NatMap.Nonempty.restrictBelow : Nat -> NatMap.Nonempty v -> NatMap v
+data.NatMap.Nonempty.restrictBelow max m =
+ (belowMax, _, _) = NatMap.Nonempty.split max m
+ belowMax
+
+data.NatMap.Nonempty.restrictBelow.doc : Doc
+data.NatMap.Nonempty.restrictBelow.doc =
+ {{
+ Drops keys from a {type NatMap.Nonempty}, keeping only keys that are strictly
+ smaller than a given key.
+
+ # Example
+
+ ```
+ (1, ?a) +| [(2, ?b), (3, ?c), (4, ?d), (5, ?e)] |> NatMap.Nonempty.fromList
+ |> Nonempty.restrictBelow 4
+ |> NatMap.toList
+ ```
+
+ # See also
+
+ * {NatMap.restrictBelow} for the version of this that operates on a
+ (possibly empty) {type NatMap}.
+ * {Nonempty.restrict} to restrict to a range of keys.
+ * {Nonempty.restrictAbove} to restrict to a range of keys, keeping only
+ keys that are strictly larger than a given key.
+ }}
+
+data.NatMap.Nonempty.singleton.doc : Doc
+data.NatMap.Nonempty.singleton.doc =
+ {{
+ Creates a {type NatMap.Nonempty} with a single entry.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.toList (NatMap.Nonempty.singleton 1 10)
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.fromList} to create a {type NatMap.Nonempty} from a
+ {type List.Nonempty} of entries.
+ }}
+
+data.NatMap.Nonempty.size : NatMap.Nonempty a -> Nat
+data.NatMap.Nonempty.size = cases
+ NatMap.Nonempty.Bin _ _ sz l r -> sz
+ NatMap.Nonempty.Tip _ _ -> 1
+
+data.NatMap.Nonempty.size.doc : Doc
+data.NatMap.Nonempty.size.doc =
+ {{
+ Returns the number of entries in the {type NatMap.Nonempty}.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.size
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+ }}
+
+data.NatMap.Nonempty.split :
+ Nat -> NatMap.Nonempty a -> (NatMap a, Optional a, NatMap a)
+data.NatMap.Nonempty.split k = cases
+ t@(NatMap.Nonempty.Bin p m sz l r)
+ | nomatch k p m ->
+ if k Nat.> p then (toNatMap t, None, NatMap.empty)
+ else (NatMap.empty, None, toNatMap t)
+ | Nat.and k m Nat.== 0 ->
+ (lt, result, gt) = data.NatMap.Nonempty.split k l
+ (lt, result, NatMap.union gt (toNatMap r))
+ | otherwise ->
+ (lt, result, gt) = data.NatMap.Nonempty.split k r
+ (NatMap.union (toNatMap l) lt, result, gt)
+ t@(NatMap.Nonempty.Tip k' v)
+ | k Nat.> k' -> (toNatMap t, None, NatMap.empty)
+ | k Nat.< k' -> (NatMap.empty, None, toNatMap t)
+ | otherwise -> (NatMap.empty, Some v, NatMap.empty)
+
+data.NatMap.Nonempty.split.doc : Doc
+data.NatMap.Nonempty.split.doc =
+ {{
+ Splits a {type NatMap.Nonempty} into three parts based on the given key. The
+ first part contains all entries with keys less than the given key, the second
+ part contains the entry with that key (if present) and the third part
+ contains all entries with keys greater than the key.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.split
+ 2 (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.partition} to partition a {type NatMap.Nonempty} into
+ two parts based on a predicate.
+ }}
+
+data.NatMap.Nonempty.submapCompareBy :
+ (a ->{g} a ->{g} Boolean)
+ -> NatMap.Nonempty a
+ -> NatMap.Nonempty a
+ ->{g} Optional Ordering
+data.NatMap.Nonempty.submapCompareBy f = cases
+ t1@(NatMap.Nonempty.Bin p1 m1 sz1 l1 r1),
+ t2@(NatMap.Nonempty.Bin p2 m2 sz2 l2 r2)
+ | shorter m1 m2 -> Some Greater
+ | shorter m2 m1 ->
+ if nomatch p1 p2 m2 then Some Greater
+ else
+ if Nat.and p1 m2 Nat.== 0 then
+ data.NatMap.Nonempty.submapCompareBy f t1 l2
+ else data.NatMap.Nonempty.submapCompareBy f t1 r2
+ | p1 Nat.== p2 ->
+ match ( data.NatMap.Nonempty.submapCompareBy f l1 l2
+ , data.NatMap.Nonempty.submapCompareBy f r1 r2
+ ) with
+ (Some Greater, _) -> Some Greater
+ (_, Some Greater) -> Some Greater
+ (Some Equal, Some Equal) -> Some Equal
+ _ -> Some Less
+ | otherwise -> None
+ NatMap.Nonempty.Tip k v, NatMap.Nonempty.Tip k' v' ->
+ if k Nat.== k' && f v v' then Some Equal else None
+ NatMap.Nonempty.Tip k v, t2 ->
+ match NatMap.Nonempty.get k t2 with
+ Some v' | f v v' -> Some Less
+ _ -> None
+ t1, NatMap.Nonempty.Tip k v ->
+ match NatMap.Nonempty.get k t1 with
+ Some v' | f v' v -> Some Greater
+ _ -> None
+
+data.NatMap.Nonempty.submapCompareBy.doc : Doc
+data.NatMap.Nonempty.submapCompareBy.doc =
+ use Nat ==
+ use NatMap.Nonempty fromList
+ use Nonempty submapCompareBy
+ {{
+ Checks if the first {type NatMap.Nonempty} is a submap of the second one,
+ using the given function to compare the values.
+
+ This function returns {Less} if the second {type NatMap.Nonempty} contains
+ all the keys of the first {type NatMap.Nonempty} and comparing the values
+ under those keys using the given function returns `` true `` for all of them.
+
+ It returns {Equal} if the two {type NatMap.Nonempty}s contain the same keys
+ and comparing the values under those keys using the given function returns ``
+ true `` for all of them.
+
+ It returns {Greater} if the first {type NatMap.Nonempty} contains all the
+ keys of the second {type NatMap.Nonempty} and comparing the values under
+ those keys using the given function returns `` true `` for all of them.
+
+ It returns {None} if the two {type NatMap.Nonempty}s contain different keys
+ or comparing the values under those keys using the given function returns ``
+ false `` for any of them.
+
+ # Examples
+
+ ```
+ submapCompareBy
+ (==)
+ (fromList ((1, 10) +| [(2, 20)]))
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ ```
+
+ ```
+ submapCompareBy
+ (==) (fromList ((1, 10) +| [(2, 20)])) (fromList ((1, 10) +| [(2, 20)]))
+ ```
+
+ ```
+ submapCompareBy
+ (==) (fromList ((1, 10) +| [(2, 20)])) (fromList ((1, 10) +| [(2, 30)]))
+ ```
+
+ # See also
+
+ * {Nonempty.isSubmapOfBy} for a version of this that returns a
+ {type Boolean} instead of an {type Ordering}.
+ * {Nonempty.isSubmapOf} returns a {type Boolean} and uses {===} to compare
+ the values.
+ }}
+
+test> data.NatMap.Nonempty.test.diff =
+ runs 100 do
+ use List Nonempty.toList
+ use NatMap Nonempty.fromList
+ use Set ==
+ use gen nat
+ xs = atLeastOne (pairOf nat do ()) ()
+ ys = atLeastOne (pairOf nat do ()) ()
+ diff =
+ Set.fromList
+ (NatMap.toList
+ (NatMap.Nonempty.difference
+ (Nonempty.fromList xs) (Nonempty.fromList ys)))
+ setDiff =
+ Set.deletes (Nonempty.toList ys) (Set.fromList (Nonempty.toList xs))
+ if diff == setDiff then expect true
+ else bug (xs, ys, Set.toList diff, Set.toList setDiff)
+
+data.NatMap.Nonempty.test.gen : '{Gen} t -> '{Gen} NatMap.Nonempty t
+data.NatMap.Nonempty.test.gen g =
+ do NatMap.Nonempty.fromList (atLeastOne (pairOf gen.nat g) ())
+
+test> data.NatMap.Nonempty.test.unionAssociative =
+ runs 100 do
+ use Nat ==
+ use NatMap.Nonempty union
+ use Nonempty.test gen
+ use gen nat
+ map1 = gen nat ()
+ map2 = gen nat ()
+ map3 = gen nat ()
+ expect
+ (Nonempty.equalBy
+ (==) (union map1 (union map2 map3)) (union (union map1 map2) map3))
+
+test> data.NatMap.Nonempty.test.unionCommutative =
+ runs 100 do
+ use Nat ==
+ use Nonempty.test gen
+ use gen nat
+ map2 = gen nat ()
+ map1 = gen nat ()
+ expect
+ (Nonempty.equalBy
+ (==)
+ (NatMap.Nonempty.union map1 map2)
+ (NatMap.Nonempty.unionWith (flip const) map2 map1))
+
+test> data.NatMap.Nonempty.test.unionInsert =
+ runs 100 do
+ use Nat ==
+ use gen nat
+ map = Nonempty.test.gen nat ()
+ key = nat()
+ value = nat()
+ expect
+ (Nonempty.equalBy
+ (==)
+ (NatMap.Nonempty.insert key value map)
+ (NatMap.Nonempty.union (NatMap.Nonempty.singleton key value) map))
+
+test> data.NatMap.Nonempty.test.updateDelete = runs 100 do
+ use Nat ==
+ use gen nat
+ map = Nonempty.test.gen nat ()
+ key = nat()
+ updated = NatMap.Nonempty.update (const None) key map
+ deleted = NatMap.Nonempty.delete key map
+ p = NatMap.equalBy (==) updated deleted
+ if p then expect p else bug (map, key, updated, deleted)
+
+data.NatMap.Nonempty.toList : NatMap.Nonempty a -> List.Nonempty (Nat, a)
+data.NatMap.Nonempty.toList =
+ use Nonempty ++
+ foldMapWithKey (++) (k v -> List.Nonempty.singleton (k, v))
+
+data.NatMap.Nonempty.toList.doc : Doc
+data.NatMap.Nonempty.toList.doc =
+ use NatMap.Nonempty fromList
+ {{
+ Converts a {type NatMap.Nonempty} to a {type List.Nonempty} of {type Tuple}s
+ containing the keys and values.
+
+ # Example
+
+ ```
+ NatMap.Nonempty.toList (fromList ((1, 10) +| [(2, 20)]))
+ ```
+
+ # See also
+
+ * {fromList} for converting a {type List.Nonempty} of {type Tuple}s to a
+ {type NatMap.Nonempty}.
+ }}
+
+data.NatMap.Nonempty.toMap : NatMap.Nonempty a -> Map.Nonempty Nat a
+data.NatMap.Nonempty.toMap =
+ foldMapWithKey Map.Nonempty.union (k v -> Map.Nonempty.singleton k v)
+
+data.NatMap.Nonempty.toMap.doc : Doc
+data.NatMap.Nonempty.toMap.doc =
+ {{
+ Converts a {type NatMap.Nonempty} to a {type Map.Nonempty} of {type Nat}s.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList
+ (NatMap.Nonempty.toMap (NatMap.Nonempty.fromList ((1, 2) +| [(3, 4)])))
+ ```
+ }}
+
+data.NatMap.Nonempty.toNatMap : NatMap.Nonempty a -> NatMap a
+data.NatMap.Nonempty.toNatMap t = NatMap (Some t)
+
+data.NatMap.Nonempty.union :
+ NatMap.Nonempty a -> NatMap.Nonempty a -> NatMap.Nonempty a
+data.NatMap.Nonempty.union = NatMap.Nonempty.unionWith const
+
+data.NatMap.Nonempty.union.doc : Doc
+data.NatMap.Nonempty.union.doc =
+ use NatMap.Nonempty fromList
+ {{
+ Constructs a {type NatMap.Nonempty} containing all the keys and values from
+ both of two {type NatMap.Nonempty}s.
+
+ This function is biased towards the first {type NatMap.Nonempty}. If a key is
+ present in both {type NatMap.Nonempty}s, the value from the first
+ {type NatMap.Nonempty} is used.
+
+ # Examples
+
+ ```
+ NatMap.Nonempty.toList
+ (NatMap.Nonempty.union
+ (fromList ((1, 10) +| [(2, 20)])) (fromList ((1, 30) +| [(3, 40)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.unionWith} for a version of this that takes a combining
+ function for values with the same key.
+ * {NatMap.Nonempty.unions} for combining a {type List.Nonempty} of
+ {type NatMap.Nonempty}s.
+ * {NatMap.Nonempty.intersect} to keep only the values that are in both
+ {type NatMap.Nonempty}s.
+ * {NatMap.Nonempty.difference} to keep the values that are only in the
+ first {type NatMap.Nonempty}.
+ }}
+
+data.NatMap.Nonempty.unions :
+ List.Nonempty (NatMap.Nonempty a) -> NatMap.Nonempty a
+data.NatMap.Nonempty.unions = Nonempty.unionsWith const
+
+data.NatMap.Nonempty.unions.doc : Doc
+data.NatMap.Nonempty.unions.doc =
+ use NatMap.Nonempty fromList
+ {{
+ Constructs a {type NatMap.Nonempty} containing all the keys and values from a
+ {type List.Nonempty} of {type NatMap.Nonempty}s.
+
+ This function is biased towards the first {type NatMap.Nonempty} in the
+ {type List}. If a key is present in multiple {type NatMap.Nonempty}s, the
+ value from the first {type NatMap.Nonempty} is used.
+
+ # Examples
+
+ ```
+ NatMap.Nonempty.toList
+ (NatMap.Nonempty.unions
+ (fromList ((1, 10) +| [(2, 20)]) +| [fromList ((1, 30) +| [(3, 40)])]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.union} for a version of this that combines only two
+ {type NatMap.Nonempty}s.
+ * {Nonempty.unionsWith} for a version of this that takes a combining
+ function for values with the same key.
+ }}
+
+data.NatMap.Nonempty.unionsWith :
+ (a ->{g} a ->{g} a)
+ -> List.Nonempty (NatMap.Nonempty a)
+ ->{g} NatMap.Nonempty a
+data.NatMap.Nonempty.unionsWith f =
+ List.Nonempty.foldMap (NatMap.Nonempty.unionWith f) id
+
+data.NatMap.Nonempty.unionsWith.doc : Doc
+data.NatMap.Nonempty.unionsWith.doc =
+ use Nat +
+ use NatMap.Nonempty fromList
+ {{
+ Constructs a {type NatMap.Nonempty} containing all the keys and values from a
+ {type List} of {type NatMap.Nonempty}s.
+
+ If a key is present in multiple {type NatMap.Nonempty}s, the given combining
+ function is used to combine the values.
+
+ # Examples
+
+ ```
+ NatMap.Nonempty.toList
+ (Nonempty.unionsWith
+ (+)
+ (fromList ((1, 10) +| [(2, 20)]) +| [fromList ((1, 30) +| [(3, 40)])]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.unionWith} for a version of this that combines only two
+ {type NatMap.Nonempty}s.
+ * {NatMap.Nonempty.unions} for a version of this that doesn't take a
+ combining function and just ignores duplicate keys.
+ }}
+
+data.NatMap.Nonempty.unionWith :
+ (a ->{g} a ->{g} a)
+ -> NatMap.Nonempty a
+ -> NatMap.Nonempty a
+ ->{g} NatMap.Nonempty a
+data.NatMap.Nonempty.unionWith f = NatMap.Nonempty.unionWithKey do x y -> f x y
+
+data.NatMap.Nonempty.unionWith.doc : Doc
+data.NatMap.Nonempty.unionWith.doc =
+ use Nat +
+ use NatMap.Nonempty fromList
+ {{
+ Constructs a {type NatMap.Nonempty} containing all the keys and values from
+ both of two {type NatMap.Nonempty}s.
+
+ If a key is present in both {type NatMap.Nonempty}s, the given combining
+ function is used to combine the values.
+
+ # Examples
+
+ ```
+ NatMap.Nonempty.toList
+ (NatMap.Nonempty.unionWith
+ (+) (fromList ((1, 10) +| [(2, 20)])) (fromList ((1, 30) +| [(3, 40)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.union} for a version of this that doesn't take a
+ combining function and just ignores duplicate keys.
+ * {NatMap.Nonempty.unionWithKey} for a version of this that also gives the
+ key to the combining function.
+ * {Nonempty.unionsWith} for a version of this that combines a {type List}
+ of {type NatMap.Nonempty}s.
+ }}
+
+data.NatMap.Nonempty.unionWithKey :
+ (Nat ->{g} a ->{g} a ->{g} a)
+ -> NatMap.Nonempty a
+ -> NatMap.Nonempty a
+ ->{g} NatMap.Nonempty a
+data.NatMap.Nonempty.unionWithKey f = cases
+ t1@(NatMap.Nonempty.Bin p1 m1 sz1 l1 r1),
+ t2@(NatMap.Nonempty.Bin p2 m2 sz2 l2 r2)
+ | shorter m1 m2 ->
+ if nomatch p2 p1 m1 then internal.join p1 t1 p2 t2
+ else
+ if Nat.and p2 m1 Nat.== 0 then
+ NatMap.internal.bim
+ p1 m1 (data.NatMap.Nonempty.unionWithKey f l1 t2) r1
+ else
+ NatMap.internal.bim
+ p1 m1 l1 (data.NatMap.Nonempty.unionWithKey f r1 t2)
+ | shorter m2 m1 ->
+ if nomatch p1 p2 m2 then internal.join p1 t1 p2 t2
+ else
+ if Nat.and p1 m2 Nat.== 0 then
+ NatMap.internal.bim
+ p2 m2 (data.NatMap.Nonempty.unionWithKey f t1 l2) r2
+ else
+ NatMap.internal.bim
+ p2 m2 l2 (data.NatMap.Nonempty.unionWithKey f t1 r2)
+ | p1 Nat.== p2 ->
+ NatMap.internal.bim
+ p1
+ m1
+ (data.NatMap.Nonempty.unionWithKey f l1 l2)
+ (data.NatMap.Nonempty.unionWithKey f r1 r2)
+ | otherwise -> internal.join p1 t1 p2 t2
+ NatMap.Nonempty.Tip k v, t -> Nonempty.insertWithKey f k v t
+ t, NatMap.Nonempty.Tip k v ->
+ Nonempty.insertWithKey (k' x' y' -> f k' y' x') k v t
+
+data.NatMap.Nonempty.unionWithKey.doc : Doc
+data.NatMap.Nonempty.unionWithKey.doc =
+ use NatMap.Nonempty fromList
+ {{
+ Constructs a {type NatMap.Nonempty} containing all the keys and values from
+ both of two {type NatMap.Nonempty}s.
+
+ If a key is present in both {type NatMap.Nonempty}s, the given combining
+ function is used to combine the values. The combining function is given the
+ key as well as the values from each {type NatMap.Nonempty}.
+
+ # Examples
+
+ ```
+ NatMap.Nonempty.toList
+ (NatMap.Nonempty.unionWithKey
+ (k v1 v2 -> (if Nat.isEven k then v1 else v2))
+ (fromList ((1, 10) +| [(2, 20), (3, 30)]))
+ (fromList ((1, 30) +| [(2, 40), (4, 40)])))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.unionWith} for a version of this that doesn't give the
+ key to the combining function.
+ * {NatMap.Nonempty.union} for a version of this that doesn't take a
+ combining function and just ignores duplicate keys.
+ }}
+
+data.NatMap.Nonempty.update :
+ (a ->{g} Optional a) -> Nat -> NatMap.Nonempty a ->{g} NatMap a
+data.NatMap.Nonempty.update f k t = Nonempty.updateWithKey (do x -> f x) k t
+
+data.NatMap.Nonempty.update.doc : Doc
+data.NatMap.Nonempty.update.doc =
+ use Nat +
+ {{
+ Updates or removes a value at a given key in a {type NatMap.Nonempty}.
+ Returns a (possibly empty) {type NatMap}.
+
+ If the key is not present in the {type NatMap.Nonempty}, the map is returned
+ unchanged, but still cast to a {type NatMap}.
+
+ If the key is present in the {type NatMap.Nonempty}, the given function is
+ applied to the value at that key. If the function returns {None}, the key is
+ removed from the {type NatMap}. Otherwise the value is updated with the
+ result of the function.
+
+ # Examples
+
+ ```
+ NatMap.toList
+ (NatMap.Nonempty.update
+ (x -> (if Nat.isEven x then None else Some (x + 1)))
+ 1
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20)])))
+ ```
+
+ # See also
+
+ * {Nonempty.updateWithKey} for a version of this that also gives the key to
+ the update function.
+ * {Nonempty.updateGetWithKey} for a version that gives the key to the
+ update function and returns the old value as well.
+ * {NatMap.Nonempty.alter} for a version that can also insert new values.
+ * {NatMap.Nonempty.map} for a version that can't add or remove values.
+ * {Nonempty.mapOptional} for a version that updates all values in the map.
+ * {Nonempty.updateMax} to update the value under the largest key.
+ * {Nonempty.updateMin} to update the value under the smallest key.
+ }}
+
+data.NatMap.Nonempty.updateGetWithKey :
+ (Nat ->{g} a ->{g} Optional a)
+ -> Nat
+ -> NatMap.Nonempty a
+ ->{g} (Optional a, NatMap a)
+data.NatMap.Nonempty.updateGetWithKey f k = cases
+ t@(NatMap.Nonempty.Bin prefix mask size l r)
+ | nomatch k prefix mask -> (None, toNatMap t)
+ | Nat.and k mask Nat.== 0 ->
+ (found, l') = data.NatMap.Nonempty.updateGetWithKey f k l
+ (found, NatMap.internal.bin prefix mask l' (toNatMap r))
+ | otherwise ->
+ (found, r') = data.NatMap.Nonempty.updateGetWithKey f k r
+ (found, NatMap.internal.bin prefix mask (toNatMap l) r')
+ t@(NatMap.Nonempty.Tip k' v)
+ | k Nat.== k' ->
+ match f k v with
+ Some v' -> (Some v, toNatMap (NatMap.Nonempty.Tip k v'))
+ None -> (Some v, NatMap.empty)
+ | otherwise -> (None, toNatMap t)
+
+data.NatMap.Nonempty.updateGetWithKey.doc : Doc
+data.NatMap.Nonempty.updateGetWithKey.doc =
+ use Nat +
+ {{
+ Updates or removes a value at a given key in a {type NatMap.Nonempty},
+ returning the old value as well. Returns a (possibly empty) {type NatMap}.
+
+ If the key is not present in the {type NatMap.Nonempty}, the map is returned
+ unchanged and {None} is returned for the value.
+
+ If the key is present in the {type NatMap.Nonempty}, the given function is
+ applied to the value at that key. If the function returns {None}, the key is
+ removed from the {type NatMap}. Otherwise the value is updated with the
+ result of the function.
+
+ # Examples
+
+ ```
+ Tuple.second
+ NatMap.toList
+ (Nonempty.updateGetWithKey
+ (k v -> (if Nat.isEven k then None else Some (v + 1)))
+ 1
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20)])))
+ ```
+
+ # See also
+
+ * {Nonempty.updateWithKey} for a version of this that doesn't return the
+ old value.
+ }}
+
+data.NatMap.Nonempty.updateMax :
+ (a ->{g} a) -> NatMap.Nonempty a ->{g} NatMap.Nonempty a
+data.NatMap.Nonempty.updateMax f = Nonempty.updateMaxWithKey do x -> f x
+
+data.NatMap.Nonempty.updateMax.doc : Doc
+data.NatMap.Nonempty.updateMax.doc =
+ use Nat +
+ {{
+ Updates the value under the largest key in a {type NatMap.Nonempty}.
+
+ # Examples
+
+ ```
+ NatMap.Nonempty.toList
+ (Nonempty.updateMax
+ (x -> x + 1) (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20)])))
+ ```
+
+ # See also
+
+ * {Nonempty.updateMaxWithKey} for a version of this that also gives the key
+ to the update function.
+ * {Nonempty.updateMin} to update the value under the smallest key.
+ }}
+
+data.NatMap.Nonempty.updateMaxWithKey :
+ (Nat ->{g} a ->{g} a) -> NatMap.Nonempty a ->{g} NatMap.Nonempty a
+data.NatMap.Nonempty.updateMaxWithKey f = cases
+ t@(NatMap.Nonempty.Bin p m sz l r) ->
+ use NatMap.Nonempty Tip
+ use NatMap.internal bim
+ up = cases
+ NatMap.Nonempty.Bin p m sz l r ->
+ r' = up r
+ bim p m l r'
+ Tip k v -> Tip k (f k v)
+ r' = up r
+ bim p m l r'
+ NatMap.Nonempty.Tip k v -> NatMap.Nonempty.Tip k (f k v)
+
+data.NatMap.Nonempty.updateMaxWithKey.doc : Doc
+data.NatMap.Nonempty.updateMaxWithKey.doc =
+ use Nat +
+ {{
+ Updates the value under the largest key in a {type NatMap.Nonempty}, giving
+ the key to the update function.
+
+ If the {type NatMap.Nonempty} is empty, an {type Abort} is raised.
+
+ # Examples
+
+ ```
+ NatMap.Nonempty.toList
+ (Nonempty.updateMaxWithKey
+ (k v -> k + v) (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20)])))
+ ```
+
+ # See also
+
+ * {Nonempty.updateMax} for a version of this that doesn't give the key to
+ the update function.
+ * {Nonempty.updateMinWithKey} to update the value under the smallest key.
+ }}
+
+data.NatMap.Nonempty.updateMin :
+ (a ->{g} a) -> NatMap.Nonempty a ->{g} NatMap.Nonempty a
+data.NatMap.Nonempty.updateMin f = Nonempty.updateMinWithKey do x -> f x
+
+data.NatMap.Nonempty.updateMin.doc : Doc
+data.NatMap.Nonempty.updateMin.doc =
+ use Nat +
+ {{
+ Updates the value under the smallest key in a {type NatMap.Nonempty}.
+
+ # Examples
+
+ ```
+ NatMap.Nonempty.toList
+ (Nonempty.updateMin
+ (x -> x + 1) (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20)])))
+ ```
+
+ # See also
+
+ * {Nonempty.updateMinWithKey} for a version of this that also gives the key
+ to the update function.
+ * {Nonempty.updateMax} to update the value under the largest key.
+ }}
+
+data.NatMap.Nonempty.updateMinWithKey :
+ (Nat ->{g} a ->{g} a) -> NatMap.Nonempty a ->{g} NatMap.Nonempty a
+data.NatMap.Nonempty.updateMinWithKey f = cases
+ t@(NatMap.Nonempty.Bin p m sz l r) ->
+ use NatMap.Nonempty Tip
+ use NatMap.internal bim
+ up = cases
+ NatMap.Nonempty.Bin p m sz l r ->
+ l' = up l
+ bim p m l' r
+ Tip k v -> Tip k (f k v)
+ l' = up l
+ bim p m l' r
+ NatMap.Nonempty.Tip k v -> NatMap.Nonempty.Tip k (f k v)
+
+data.NatMap.Nonempty.updateMinWithKey.doc : Doc
+data.NatMap.Nonempty.updateMinWithKey.doc =
+ use Nat +
+ {{
+ Updates the value under the smallest key in a {type NatMap.Nonempty}, giving
+ the key to the update function.
+
+ # Examples
+
+ ```
+ NatMap.Nonempty.toList
+ (Nonempty.updateMinWithKey
+ (k v -> k + v) (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20)])))
+ ```
+
+ # See also
+
+ * {Nonempty.updateMin} for a version of this that doesn't give the key to
+ the update function.
+ * {Nonempty.updateMaxWithKey} to update the value under the largest key.
+ }}
+
+data.NatMap.Nonempty.updateWithKey :
+ (Nat ->{g} a ->{g} Optional a) -> Nat -> NatMap.Nonempty a ->{g} NatMap a
+data.NatMap.Nonempty.updateWithKey f k = cases
+ t@(NatMap.Nonempty.Bin prefix mask size l r)
+ | nomatch k prefix mask -> toNatMap t
+ | Nat.and k mask Nat.== 0 ->
+ NatMap.internal.bin
+ prefix mask (data.NatMap.Nonempty.updateWithKey f k l) (toNatMap r)
+ | otherwise ->
+ NatMap.internal.bin
+ prefix mask (toNatMap l) (data.NatMap.Nonempty.updateWithKey f k r)
+ t@(NatMap.Nonempty.Tip k' v)
+ | k Nat.== k' ->
+ match f k v with
+ None -> NatMap.empty
+ Some v' -> toNatMap (NatMap.Nonempty.Tip k v')
+ | otherwise -> toNatMap t
+
+data.NatMap.Nonempty.updateWithKey.doc : Doc
+data.NatMap.Nonempty.updateWithKey.doc =
+ use Nat +
+ {{
+ Updates or removes the value under a key in a {type NatMap.Nonempty}, using a
+ function. The function is given the key and the old value, and returns the
+ new value, or {None} to remove the key.
+
+ Returns a {type NatMap} because the {type NatMap.Nonempty} may become empty.
+
+ If the key is not present in the {type NatMap.Nonempty}, the map is
+ unchanged.
+
+ # Examples
+
+ ```
+ NatMap.toList
+ (Nonempty.updateWithKey
+ (k v -> (if Nat.isEven k then None else Some (v + 1)))
+ 1
+ (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20)])))
+ ```
+
+ # See also
+
+ * {Nonempty.updateGetWithKey} for a version of this that also returns the
+ old value.
+ * {NatMap.Nonempty.update} for a version of this that doesn't give the key
+ to the updating function.
+ }}
+
+data.NatMap.Nonempty.values : NatMap.Nonempty a -> List.Nonempty a
+data.NatMap.Nonempty.values =
+ use Nonempty ++
+ NatMap.Nonempty.foldMap (++) List.Nonempty.singleton
+
+data.NatMap.Nonempty.values.doc : Doc
+data.NatMap.Nonempty.values.doc =
+ {{
+ Returns the values in a {type NatMap.Nonempty} as a {type List.Nonempty}.
+
+ # Examples
+
+ ```
+ NatMap.Nonempty.values (NatMap.Nonempty.fromList ((1, 10) +| [(2, 20)]))
+ ```
+
+ # See also
+
+ * {NatMap.Nonempty.keys} to get the keys.
+ * {NatMap.Nonempty.toList} to get the key-value pairs.
+ }}
+
+data.NatMap.nth : Nat -> NatMap v -> Optional (Nat, v)
+data.NatMap.nth index = cases
+ NatMap map -> Optional.flatMap (NatMap.Nonempty.nth index) map
+
+data.NatMap.nth.doc : Doc
+data.NatMap.nth.doc =
+ use NatMap nth
+ {{
+ {{ docExample 2 do i m -> nth i m }} returns the key-value pair in `m` with
+ the `i`-th smallest key, where `i`=0 is the smallest key (according to
+ {Universal.ordering}).
+
+ Is the same as {{
+ docExample 2 do i as -> List.at i (sortBy at1 (NatMap.toList as)) }} but
+ doesn't require instantiating the intermediate {type List}.
+
+ ```
+ s =
+ NatMap.fromList
+ [(6, "six"), (5, "five"), (4, "four"), (2, "two"), (1, "one")]
+ List.map (i -> nth i s) (List.range 0 (NatMap.size s))
+ ```
+ }}
+
+test> data.NatMap.nth.tests =
+ test.verify do
+ use Random natIn
+ Each.repeat 100
+ s =
+ (List.replicate (natIn 0 20) do (natIn 0 20, natIn 0 10))
+ |> NatMap.fromList
+ ensure
+ (List.somes
+ (List.map (i -> NatMap.nth i s) (List.range 0 (NatMap.size s)))
+ === NatMap.toList s)
+
+data.NatMap.partition :
+ (a ->{g} Boolean) -> NatMap a ->{g} (NatMap a, NatMap a)
+data.NatMap.partition f = NatMap.partitionWithKey do x -> f x
+
+data.NatMap.partition.doc : Doc
+data.NatMap.partition.doc =
+ {{
+ Partitions a {type NatMap} into two {type NatMap}s, one containing the values
+ that satisfy the predicate and one containing the values that do not.
+
+ # Example
+
+ ```
+ NatMap.partition Nat.isEven (NatMap.fromList [(1, 11), (2, 22), (3, 33)])
+ ```
+
+ # See also
+
+ * {NatMap.partitionWithKey} for a version of this that applies the
+ predicate to both the keys and values.
+ * {NatMap.filter} for a version that only keeps the values that satisfy the
+ predicate.
+ * {NatMap.mapEither} for partitioning into two {type NatMap}s based on an
+ {type Either}-valued function.
+ * {NatMap.split} for partitioning into two {type NatMap}s around a pivot
+ key.
+ }}
+
+data.NatMap.partitionWithKey :
+ (Nat ->{g} a ->{g} Boolean) -> NatMap a ->{g} (NatMap a, NatMap a)
+data.NatMap.partitionWithKey f = cases
+ NatMap (Some t) -> Nonempty.partitionWithKey f t
+ NatMap None -> (NatMap.empty, NatMap.empty)
+
+data.NatMap.partitionWithKey.doc : Doc
+data.NatMap.partitionWithKey.doc =
+ use Nat >
+ {{
+ Partitions a {type NatMap} into two {type NatMap}s, one containing the
+ entries that satisfy the predicate and one containing the entries that do
+ not.
+
+ # Example
+
+ ```
+ NatMap.partitionWithKey
+ (k v -> Nat.isEven k && v > 20)
+ (NatMap.fromList [(1, 11), (2, 22), (3, 33)])
+ ```
+
+ # See also
+
+ * {NatMap.partition} for a version of this that applies the predicate only
+ to the values.
+ * {NatMap.filterWithKey} for a version that only keeps the entries that
+ satisfy the predicate.
+ * {NatMap.mapEitherWithKey} for partitioning into two {type NatMap}s based
+ on an {type Either}-valued function.
+ }}
+
+data.NatMap.randomChoice : NatMap v ->{Exception, Random} (Nat, v)
+data.NatMap.randomChoice map =
+ randomIndex = Random.natIn 0 (NatMap.size map)
+ NatMap.nth randomIndex map
+ |> Optional.toException
+ "NatMap.randomChoice: empty NatMap" (typeLink NatMap)
+
+data.NatMap.randomChoice.doc : Doc
+data.NatMap.randomChoice.doc =
+ use NatMap fromList randomChoice
+ {{
+ Picks a random key-value pair from the given {type NatMap}. Assumes that the
+ {type NatMap} is not empty, so an empty {type NatMap} will cause a runtime
+ exception.
+
+ # Examples
+
+ ```
+ catch do
+ lcg 4096 do randomChoice (fromList [(0, ?a), (3, ?b), (5, ?c), (7, ?d)])
+ ```
+
+ ```
+ catch do
+ lcg 2510 do randomChoice (fromList [(0, ?a), (3, ?b), (5, ?c), (7, ?d)])
+ ```
+ }}
+
+test> data.NatMap.randomChoice.test = test.verify do
+ map = NatMap.fromList [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
+ Each.repeat 1000
+ e = NatMap.randomChoice map
+ ensure (NatMap.contains (at1 e) map)
+
+data.NatMap.randomKey : NatMap v ->{Exception, Random} Nat
+data.NatMap.randomKey map = NatMap.randomChoice map |> at1
+
+data.NatMap.randomKey.doc : Doc
+data.NatMap.randomKey.doc =
+ use NatMap fromList randomKey
+ {{
+ Picks a random {type Nat} key from the given {type NatMap}. Assumes that the
+ {type NatMap} is not empty, so an empty {type NatMap} will raise an
+ {type Exception}.
+
+ # Examples
+
+ ```
+ catch do
+ lcg 4096 do randomKey (fromList [(0, ?a), (3, ?b), (5, ?c), (7, ?d)])
+ ```
+
+ ```
+ catch do
+ lcg 2510 do randomKey (fromList [(0, ?a), (3, ?b), (5, ?c), (7, ?d)])
+ ```
+ }}
+
+data.NatMap.randomValue : NatMap v ->{Exception, Random} v
+data.NatMap.randomValue map = NatMap.randomChoice map |> at2
+
+data.NatMap.randomValue.doc : Doc
+data.NatMap.randomValue.doc =
+ use NatMap fromList randomValue
+ {{
+ Picks a random value from the given {type NatMap}. Assumes that the
+ {type NatMap} is not empty, so an empty {type NatMap} will raise an
+ {type Exception}.
+
+ # Examples
+
+ ```
+ catch do
+ lcg 4096 do randomValue (fromList [(0, ?a), (3, ?b), (5, ?c), (7, ?d)])
+ ```
+
+ ```
+ catch do
+ lcg 2510 do randomValue (fromList [(0, ?a), (3, ?b), (5, ?c), (7, ?d)])
+ ```
+ }}
+
+data.NatMap.restrict : Nat -> Nat -> NatMap v -> NatMap v
+data.NatMap.restrict min max = cases
+ NatMap o -> Optional.fold (do NatMap.empty) (Nonempty.restrict min max) o
+
+data.NatMap.restrict.doc : Doc
+data.NatMap.restrict.doc =
+ use NatMap fromList toList
+ {{
+ Restricts a {type NatMap} to a given range of keys.
+
+ `` NatMap.restrict min max map `` drops all entries from `map` that are
+ outside the range between `min` and `max`.
+
+ The range is inclusive of the `min` and exclusive of the `max`. That is, a
+ key that is exactly `min` will be kept, but a key that is exactly `max` will
+ be removed.
+
+ # Examples
+
+ ```
+ toList
+ (NatMap.restrict
+ 2 4 (fromList [(1, ?a), (2, ?b), (3, ?c), (4, ?d), (5, ?e)]))
+ ```
+
+ ```
+ toList (NatMap.restrict 2 4 (fromList [(1, ?a), (3, ?c), (5, ?e)]))
+ ```
+
+ # See also
+
+ * {NatMap.restrictAbove} to keep only keys above a given key.
+ * {NatMap.restrictBelow} to keep only keys below a given key.
+ * {Nonempty.restrict} for the version of this that operates on a
+ {type NatMap.Nonempty}.
+ }}
+
+data.NatMap.restrictAbove : Nat -> NatMap v -> NatMap v
+data.NatMap.restrictAbove k = cases
+ NatMap o -> Optional.fold (do NatMap.empty) (Nonempty.restrictAbove k) o
+
+data.NatMap.restrictAbove.doc : Doc
+data.NatMap.restrictAbove.doc =
+ use NatMap fromList toList
+ {{
+ Restricts a {type NatMap} to keys that are strictly above a given key.
+
+ `` NatMap.restrictAbove k map `` drops all entries from `map` that are less
+ than or equal to `k`.
+
+ # Examples
+
+ ```
+ toList
+ (NatMap.restrictAbove
+ 2 (fromList [(1, ?a), (2, ?b), (3, ?c), (4, ?d), (5, ?e)]))
+ ```
+
+ ```
+ toList
+ (NatMap.restrictAbove
+ 2 (fromList [(1, ?a), (2, ?b), (3, ?c), (4, ?d), (5, ?e)]))
+ ```
+
+ # See also
+
+ * {NatMap.restrict} to restrict to a range of keys.
+ * {NatMap.restrictBelow} to keep only keys below a given key.
+ * {Nonempty.restrictAbove} for the version of this that operates on a
+ {type NatMap.Nonempty}.
+ }}
+
+data.NatMap.restrictBelow : Nat -> NatMap v -> NatMap v
+data.NatMap.restrictBelow k = cases
+ NatMap o -> Optional.fold (do NatMap.empty) (Nonempty.restrictBelow k) o
+
+data.NatMap.restrictBelow.doc : Doc
+data.NatMap.restrictBelow.doc =
+ use NatMap fromList toList
+ {{
+ Restricts a {type NatMap} to keys that are strictly below a given key.
+
+ `` NatMap.restrictBelow k map `` drops all entries from `map` that are
+ greater than or equal to `k`.
+
+ # Examples
+
+ ```
+ toList
+ (NatMap.restrictBelow
+ 4 (fromList [(1, ?a), (2, ?b), (3, ?c), (4, ?d), (5, ?e)]))
+ ```
+
+ ```
+ toList
+ (NatMap.restrictBelow
+ 4 (fromList [(1, ?a), (2, ?b), (3, ?c), (4, ?d), (5, ?e)]))
+ ```
+
+ # See also
+
+ * {NatMap.restrict} to restrict to a range of keys.
+ * {NatMap.restrictAbove} to keep only keys above a given key.
+ * {Nonempty.restrictBelow} for the version of this that operates on a
+ {type NatMap.Nonempty}.
+ }}
+
+data.NatMap.singleton : Nat -> a -> NatMap.Nonempty a
+data.NatMap.singleton k v = NatMap.Nonempty.Tip k v
+
+data.NatMap.singleton.doc : Doc
+data.NatMap.singleton.doc =
+ use NatMap singleton
+ {{
+ Creates a {type NatMap.Nonempty} with a single entry.
+
+ # Example
+
+ ```
+ NatMap.toNonemptyList (singleton 1 10)
+ ```
+
+ # See also
+
+ * {NatMap.fromList} to create a {type NatMap} from a list of entries.
+ * {NatMap.Nonempty.fromList} to create a {type NatMap.Nonempty} from a
+ {type List.Nonempty}.
+ * {NatMap.empty} to create an empty {type NatMap}.
+ * {singleton} to create a {type NatMap.Nonempty} with a single entry.
+ }}
+
+data.NatMap.size : NatMap a -> Nat
+data.NatMap.size = cases
+ NatMap (Some t) -> NatMap.Nonempty.size t
+ NatMap None -> 0
+
+data.NatMap.size.doc : Doc
+data.NatMap.size.doc =
+ {{
+ Returns the number of entries in the {type NatMap}.
+
+ # Example
+
+ ```
+ NatMap.size (NatMap.fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.isEmpty} to check if a {type NatMap} is empty.
+ }}
+
+test> data.NatMap.size.test = test.verify do
+ use Random nat
+ len = Random.natIn 0 1000
+ kvs = Random.listOf (do (nat(), nat)) do len
+ m = NatMap.fromList kvs
+ ensureEqual (NatMap.size m) (List.size (NatMap.toList m))
+
+data.NatMap.split : Nat -> NatMap a -> (NatMap a, Optional a, NatMap a)
+data.NatMap.split k = cases
+ NatMap (Some t) -> NatMap.Nonempty.split k t
+ NatMap None -> (NatMap.empty, None, NatMap.empty)
+
+data.NatMap.split.doc : Doc
+data.NatMap.split.doc =
+ {{
+ Splits a {type NatMap} into three parts based on the given key. The first
+ part contains all entries with keys less than the given key, the second part
+ contains the entry with that key (if present) and the third part contains all
+ entries with keys greater than the key.
+
+ # Example
+
+ ```
+ NatMap.split 2 (NatMap.fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.partition} to partition a {type NatMap} into two parts based on a
+ predicate.
+ }}
+
+data.NatMap.submapCompareBy :
+ (a ->{g} a ->{g} Boolean) -> NatMap a -> NatMap a ->{g} Optional Ordering
+data.NatMap.submapCompareBy f = cases
+ NatMap (Some t1), NatMap (Some t2) -> Nonempty.submapCompareBy f t1 t2
+ NatMap None, NatMap None -> Some Equal
+ NatMap None, NatMap (Some _) -> Some Less
+ NatMap (Some _), NatMap None -> Some Greater
+
+data.NatMap.submapCompareBy.doc : Doc
+data.NatMap.submapCompareBy.doc =
+ use Nat ==
+ use NatMap fromList submapCompareBy
+ {{
+ Checks if the first {type NatMap} is a submap of the second one, using the
+ given function to compare the values.
+
+ This function returns {Less} if the second {type NatMap} contains all the
+ keys of the first {type NatMap} and comparing the values under those keys
+ using the given function returns `` true `` for all of them.
+
+ It returns {Equal} if the two {type NatMap}s contain the same keys and
+ comparing the values under those keys using the given function returns ``
+ true `` for all of them.
+
+ It returns {Greater} if the first {type NatMap} contains all the keys of the
+ second {type NatMap} and comparing the values under those keys using the
+ given function returns `` true `` for all of them.
+
+ It returns {None} if the two {type NatMap}s contain different keys or
+ comparing the values under those keys using the given function returns ``
+ false `` for any of them.
+
+ # Examples
+
+ ```
+ submapCompareBy
+ (==) (fromList [(1, 10), (2, 20)]) (fromList [(1, 10), (2, 20), (3, 30)])
+ ```
+
+ ```
+ submapCompareBy
+ (==) (fromList [(1, 10), (2, 20)]) (fromList [(1, 10), (2, 20)])
+ ```
+
+ ```
+ submapCompareBy
+ (==) (fromList [(1, 10), (2, 20)]) (fromList [(1, 10), (2, 30)])
+ ```
+
+ # See also
+
+ * {NatMap.isSubmapOfBy} for a version of this that returns a {type Boolean}
+ instead of an {type Ordering}.
+ * {NatMap.isSubmapOf} returns a {type Boolean} and uses {===} to compare
+ the values.
+ }}
+
+test> data.NatMap.test.diff =
+ runs 100 do
+ use Set ==
+ use gen listOf nat
+ xs = listOf (pairOf nat do ()) ()
+ ys = listOf (pairOf nat do ()) ()
+ diff =
+ Set.fromList
+ (NatMap.toList
+ (NatMap.difference (NatMap.fromList xs) (NatMap.fromList ys)))
+ setDiff = Set.deletes ys (Set.fromList xs)
+ if diff == setDiff then expect true
+ else bug (xs, ys, Set.toList diff, Set.toList setDiff)
+
+data.NatMap.test.gen : '{Gen} t -> '{Gen} NatMap t
+data.NatMap.test.gen g = do NatMap.fromList (gen.listOf (pairOf gen.nat g) ())
+
+test> data.NatMap.test.insertDelete = runs 100 do
+ use Nat ==
+ use gen nat
+ key = nat()
+ value = nat()
+ map = NatMap.test.gen nat ()
+ present = Boolean.not (NatMap.contains key map)
+ added = NatMap.insert.nonempty key value map
+ removed = NatMap.Nonempty.delete key added
+ p = implies present (NatMap.equalBy (==) map removed)
+ if p then expect p else bug (map, key, value, present, added, removed)
+
+test> data.NatMap.test.single = runs 100 do
+ use Nat ==
+ use gen nat
+ key = nat()
+ value = nat()
+ map1 = NatMap.singleton key value
+ map2 = NatMap.insert.nonempty key value NatMap.empty
+ expect (Nonempty.equalBy (==) map1 map2)
+
+test> data.NatMap.test.unionAssociative =
+ runs 100 do
+ use Nat ==
+ use NatMap union
+ use NatMap.test gen
+ use gen nat
+ map1 = gen nat ()
+ map2 = gen nat ()
+ map3 = gen nat ()
+ expect
+ (NatMap.equalBy
+ (==) (union map1 (union map2 map3)) (union (union map1 map2) map3))
+
+test> data.NatMap.test.unionCommutative =
+ runs 100 do
+ use Nat ==
+ use NatMap.test gen
+ use gen nat
+ map1 = gen nat ()
+ map2 = gen nat ()
+ expect
+ (NatMap.equalBy
+ (==) (NatMap.union map1 map2) (NatMap.unionWith (flip const) map2 map1))
+
+test> data.NatMap.test.unionInsert =
+ runs 100 do
+ use Nat ==
+ use gen nat
+ key = nat()
+ value = nat()
+ map = NatMap.test.gen nat ()
+ expect
+ (NatMap.equalBy
+ (==)
+ (toNatMap (NatMap.insert.nonempty key value map))
+ (NatMap.union (toNatMap (NatMap.singleton key value)) map))
+
+test> data.NatMap.test.updateDelete = runs 100 do
+ use Nat ==
+ use gen nat
+ key = nat()
+ map = NatMap.test.gen nat ()
+ updated = NatMap.update (const None) key map
+ deleted = NatMap.delete key map
+ p = NatMap.equalBy (==) updated deleted
+ if p then expect p else bug (map, key, updated, deleted)
+
+data.NatMap.toList : NatMap a -> [(Nat, a)]
+data.NatMap.toList =
+ use List +:
+ NatMap.foldWithKey (k v ks -> (k, v) +: ks) []
+
+data.NatMap.toList.doc : Doc
+data.NatMap.toList.doc =
+ use NatMap fromList toList
+ {{
+ {toList} converts a {type NatMap} to a {type List} of {type Tuple}s
+ containing the keys and values.
+
+ # Example
+
+ ```
+ toList (fromList [(1, 10), (2, 20)])
+ ```
+
+ # See also
+
+ * {fromList} for converting a {type List} of {type Tuple}s to a
+ {type NatMap}.
+ }}
+
+data.NatMap.toMap : NatMap a -> Map Nat a
+data.NatMap.toMap = NatMap.foldWithKey (k v m -> Map.insert k v m) Map.empty
+
+data.NatMap.toNonemptyList : NatMap.Nonempty a -> List.Nonempty (Nat, a)
+data.NatMap.toNonemptyList = cases
+ NatMap.Nonempty.Tip k v -> List.Nonempty.singleton (k, v)
+ NatMap.Nonempty.Bin _ _ _ t1 t2 ->
+ data.NatMap.toNonemptyList t1 Nonempty.++ data.NatMap.toNonemptyList t2
+
+data.NatMap.union : NatMap a -> NatMap a -> NatMap a
+data.NatMap.union = NatMap.unionWith const
+
+data.NatMap.union.doc : Doc
+data.NatMap.union.doc =
+ use NatMap fromList
+ {{
+ Constructs a {type NatMap} containing all the keys and values from both of
+ two {type NatMap}s.
+
+ This function is biased towards the first {type NatMap}. If a key is present
+ in both {type NatMap}s, the value from the first {type NatMap} is used.
+
+ # Examples
+
+ ```
+ NatMap.toList
+ (NatMap.union (fromList [(1, 10), (2, 20)]) (fromList [(1, 30), (3, 40)]))
+ ```
+
+ # See also
+
+ * {NatMap.unionWith} for a version of this that takes a combining function
+ for values with the same key.
+ * {NatMap.unions} for combining a {type List} of {type NatMap}s.
+ * {NatMap.intersect} to keep only the values that are in both
+ {type NatMap}s.
+ * {NatMap.difference} to keep the values that are only in the first
+ {type NatMap}.
+ }}
+
+data.NatMap.unions : [NatMap a] -> NatMap a
+data.NatMap.unions = NatMap.unionsWith const
+
+data.NatMap.unions.doc : Doc
+data.NatMap.unions.doc =
+ use NatMap fromList
+ {{
+ Constructs a {type NatMap} containing all the keys and values from a
+ {type List} of {type NatMap}s.
+
+ This function is biased towards the first {type NatMap} in the {type List}.
+ If a key is present in multiple {type NatMap}s, the value from the first
+ {type NatMap} is used.
+
+ # Examples
+
+ ```
+ NatMap.toList
+ (NatMap.unions [fromList [(1, 10), (2, 20)], fromList [(1, 30), (3, 40)]])
+ ```
+
+ # See also
+
+ * {NatMap.union} for a version of this that combines only two
+ {type NatMap}s.
+ * {NatMap.unionsWith} for a version of this that takes a combining function
+ for values with the same key.
+ }}
+
+data.NatMap.unionsWith : (a ->{g} a ->{g} a) -> [NatMap a] ->{g} NatMap a
+data.NatMap.unionsWith f = List.foldLeft (NatMap.unionWith f) NatMap.empty
+
+data.NatMap.unionsWith.doc : Doc
+data.NatMap.unionsWith.doc =
+ use Nat +
+ use NatMap fromList
+ {{
+ Constructs a {type NatMap} containing all the keys and values from a
+ {type List} of {type NatMap}s.
+
+ If a key is present in multiple {type NatMap}s, the given combining function
+ is used to combine the values.
+
+ # Examples
+
+ ```
+ NatMap.toList
+ (NatMap.unionsWith
+ (+) [fromList [(1, 10), (2, 20)], fromList [(1, 30), (3, 40)]])
+ ```
+
+ # See also
+
+ * {NatMap.unionWith} for a version of this that combines only two
+ {type NatMap}s.
+ * {NatMap.unions} for a version of this that doesn't take a combining
+ function and just ignores duplicate keys.
+ }}
+
+data.NatMap.unionWith :
+ (a ->{g} a ->{g} a) -> NatMap a -> NatMap a ->{g} NatMap a
+data.NatMap.unionWith f = NatMap.unionWithKey do x y -> f x y
+
+data.NatMap.unionWith.doc : Doc
+data.NatMap.unionWith.doc =
+ use Nat +
+ use NatMap fromList
+ {{
+ Constructs a {type NatMap} containing all the keys and values from both of
+ two {type NatMap}s.
+
+ If a key is present in both {type NatMap}s, the given combining function is
+ used to combine the values.
+
+ # Examples
+
+ ```
+ NatMap.toList
+ (NatMap.unionWith
+ (+) (fromList [(1, 10), (2, 20)]) (fromList [(1, 30), (3, 40)]))
+ ```
+
+ # See also
+
+ * {NatMap.union} for a version of this that doesn't take a combining
+ function and just ignores duplicate keys.
+ * {NatMap.unionWithKey} for a version of this that also gives the key to
+ the combining function.
+ * {NatMap.unionsWith} for a version of this that combines a {type List} of
+ {type NatMap}s.
+ }}
+
+data.NatMap.unionWithKey :
+ (Nat ->{g} a ->{g} a ->{g} a) -> NatMap a -> NatMap a ->{g} NatMap a
+data.NatMap.unionWithKey f = cases
+ NatMap (Some t1), NatMap (Some t2) ->
+ toNatMap (NatMap.Nonempty.unionWithKey f t1 t2)
+ NatMap None, NatMap t2 -> NatMap t2
+ NatMap t1, NatMap None -> NatMap t1
+
+data.NatMap.unionWithKey.doc : Doc
+data.NatMap.unionWithKey.doc =
+ use NatMap fromList
+ {{
+ Constructs a {type NatMap} containing all the keys and values from both of
+ two {type NatMap}s.
+
+ If a key is present in both {type NatMap}s, the given combining function is
+ used to combine the values. The combining function is given the key as well
+ as the values from each {type NatMap}.
+
+ # Examples
+
+ ```
+ NatMap.toList
+ (NatMap.unionWithKey
+ (k v1 v2 -> (if Nat.isEven k then v1 else v2))
+ (fromList [(1, 10), (2, 20), (3, 30)])
+ (fromList [(1, 30), (2, 40), (4, 40)]))
+ ```
+
+ # See also
+
+ * {NatMap.unionWith} for a version of this that doesn't give the key to the
+ combining function.
+ * {NatMap.union} for a version of this that doesn't take a combining
+ function and just ignores duplicate keys.
+ }}
+
+data.NatMap.update : (a ->{g} Optional a) -> Nat -> NatMap a ->{g} NatMap a
+data.NatMap.update f k t = NatMap.updateWithKey (do x -> f x) k t
+
+data.NatMap.update.doc : Doc
+data.NatMap.update.doc =
+ use Nat +
+ {{
+ Updates or removes a value at a given key in a {type NatMap}.
+
+ If the key is not present in the {type NatMap}, the {type NatMap} is returned
+ unchanged.
+
+ If the key is present in the {type NatMap}, the given function is applied to
+ the value at that key. If the function returns {None}, the key is removed
+ from the {type NatMap}. Otherwise the value is updated with the result of the
+ function.
+
+ # Examples
+
+ ```
+ NatMap.toList
+ (NatMap.update
+ (x -> (if Nat.isEven x then None else Some (x + 1)))
+ 1
+ (NatMap.fromList [(1, 10), (2, 20)]))
+ ```
+
+ # See also
+
+ * {NatMap.updateWithKey} for a version of this that also gives the key to
+ the update function.
+ * {NatMap.updateGetWithKey} for a version that gives the key to the update
+ function and returns the old value as well.
+ * {NatMap.alter} for a version that can also insert new values.
+ * {NatMap.map} for a version that can't add or remove values.
+ * {NatMap.mapOptional} for a version that updates all values in the
+ {type NatMap}.
+ * {NatMap.updateMax} to update the value under the largest key.
+ * {NatMap.updateMin} to update the value under the smallest key.
+ }}
+
+data.NatMap.updateGetWithKey :
+ (Nat ->{g} a ->{g} Optional a)
+ -> Nat
+ -> NatMap a
+ ->{g} (Optional a, NatMap a)
+data.NatMap.updateGetWithKey f k = cases
+ NatMap (Some t) -> Nonempty.updateGetWithKey f k t
+ NatMap None -> (None, NatMap.empty)
+
+data.NatMap.updateGetWithKey.doc : Doc
+data.NatMap.updateGetWithKey.doc =
+ use Nat +
+ {{
+ Updates or removes a value at a given key in a {type NatMap}, returning the
+ old value as well.
+
+ If the key is not present in the {type NatMap}, the {type NatMap} is returned
+ unchanged and {None} is returned for the value.
+
+ If the key is present in the {type NatMap}, the given function is applied to
+ the value at that key. If the function returns {None}, the key is removed
+ from the {type NatMap}. Otherwise the value is updated with the result of the
+ function.
+
+ # Examples
+
+ ```
+ Tuple.second
+ NatMap.toList
+ (NatMap.updateGetWithKey
+ (k v -> (if Nat.isEven k then None else Some (v + 1)))
+ 1
+ (NatMap.fromList [(1, 10), (2, 20)]))
+ ```
+
+ # See also
+
+ * {NatMap.updateWithKey} for a version of this that doesn't return the old
+ value.
+ }}
+
+data.NatMap.updateMax : (a ->{g} a) -> NatMap a ->{g, Abort} NatMap a
+data.NatMap.updateMax f = NatMap.updateMaxWithKey do x -> f x
+
+data.NatMap.updateMax.doc : Doc
+data.NatMap.updateMax.doc =
+ use Nat +
+ {{
+ Updates the value under the largest key in a {type NatMap}.
+
+ If the {type NatMap} is empty, an {type Abort} is raised.
+
+ # Examples
+
+ ```
+ toOptional! do
+ NatMap.toList
+ (NatMap.updateMax (x -> x + 1) (NatMap.fromList [(1, 10), (2, 20)]))
+ ```
+
+ # See also
+
+ * {NatMap.updateMaxWithKey} for a version of this that also gives the key
+ to the update function.
+ * {NatMap.updateMin} to update the value under the smallest key.
+ }}
+
+data.NatMap.updateMaxWithKey :
+ (Nat ->{g} a ->{g} a) -> NatMap a ->{g, Abort} NatMap a
+data.NatMap.updateMaxWithKey f = cases
+ NatMap (Some t) -> toNatMap (Nonempty.updateMaxWithKey f t)
+ NatMap None -> abort
+
+data.NatMap.updateMaxWithKey.doc : Doc
+data.NatMap.updateMaxWithKey.doc =
+ use Nat +
+ {{
+ Updates the value under the largest key in a {type NatMap}, giving the key to
+ the update function.
+
+ If the {type NatMap} is empty, an {type Abort} is raised.
+
+ # Examples
+
+ ```
+ toOptional! do
+ NatMap.toList
+ (NatMap.updateMaxWithKey
+ (k v -> k + v) (NatMap.fromList [(1, 10), (2, 20)]))
+ ```
+
+ # See also
+
+ * {NatMap.updateMax} for a version of this that doesn't give the key to the
+ update function.
+ * {NatMap.updateMinWithKey} to update the value under the smallest key.
+ }}
+
+data.NatMap.updateMin : (a ->{g} a) -> NatMap a ->{g, Abort} NatMap a
+data.NatMap.updateMin f = NatMap.updateMinWithKey do x -> f x
+
+data.NatMap.updateMin.doc : Doc
+data.NatMap.updateMin.doc =
+ use Nat +
+ {{
+ Updates the value under the smallest key in a {type NatMap}.
+
+ If the {type NatMap} is empty, an {type Abort} is raised.
+
+ # Examples
+
+ ```
+ toOptional! do
+ NatMap.toList
+ (NatMap.updateMin (x -> x + 1) (NatMap.fromList [(1, 10), (2, 20)]))
+ ```
+
+ # See also
+
+ * {NatMap.updateMinWithKey} for a version of this that also gives the key
+ to the update function.
+ * {NatMap.updateMax} to update the value under the largest key.
+ }}
+
+data.NatMap.updateMinWithKey :
+ (Nat ->{g} a ->{g} a) -> NatMap a ->{g, Abort} NatMap a
+data.NatMap.updateMinWithKey f = cases
+ NatMap (Some t) -> toNatMap (Nonempty.updateMinWithKey f t)
+ NatMap None -> abort
+
+data.NatMap.updateMinWithKey.doc : Doc
+data.NatMap.updateMinWithKey.doc =
+ use Nat +
+ {{
+ Updates the value under the smallest key in a {type NatMap}, giving the key
+ to the update function.
+
+ If the {type NatMap} is empty, an {type Abort} is raised.
+
+ # Examples
+
+ ```
+ toOptional! do
+ NatMap.toList
+ (NatMap.updateMinWithKey
+ (k v -> k + v) (NatMap.fromList [(1, 10), (2, 20)]))
+ ```
+
+ # See also
+
+ * {NatMap.updateMin} for a version of this that doesn't give the key to the
+ update function.
+ * {NatMap.updateMaxWithKey} to update the value under the largest key.
+ }}
+
+data.NatMap.updateWithKey :
+ (Nat ->{g} a ->{g} Optional a) -> Nat -> NatMap a ->{g} NatMap a
+data.NatMap.updateWithKey f k = cases
+ NatMap (Some t) -> Nonempty.updateWithKey f k t
+ NatMap None -> NatMap.empty
+
+data.NatMap.updateWithKey.doc : Doc
+data.NatMap.updateWithKey.doc =
+ use Nat +
+ {{
+ Updates or removes the value under a key in a {type NatMap}, using a
+ function. The function is given the key and the old value, and returns the
+ new value, or {None} to remove the key.
+
+ If the key is not present in the {type NatMap}, the {type NatMap} is
+ unchanged.
+
+ # Examples
+
+ ```
+ NatMap.toList
+ (NatMap.updateWithKey
+ (k v -> (if Nat.isEven k then None else Some (v + 1)))
+ 1
+ (NatMap.fromList [(1, 10), (2, 20)]))
+ ```
+
+ # See also
+
+ * {NatMap.updateGetWithKey} for a version of this that also returns the old
+ value.
+ * {NatMap.update} for a version of this that doesn't give the key to the
+ updating function.
+ }}
+
+data.NatMap.values : NatMap a -> [a]
+data.NatMap.values =
+ use List +:
+ NatMap.foldWithKey (const (+:)) []
+
+data.NatMap.values.doc : Doc
+data.NatMap.values.doc =
+ {{
+ Returns the values in a {type NatMap} as a list.
+
+ # Examples
+
+ ```
+ NatMap.values (NatMap.fromList [(1, 10), (2, 20)])
+ ```
+
+ # See also
+
+ * {NatMap.keys} to get the keys.
+ * {NatMap.toList} to get the key-value pairs.
+ }}
+
+(data.NatSet.==) : NatSet -> NatSet -> Boolean
+(data.NatSet.==) = cases
+ NatSet (Some t1), NatSet (Some t2) -> t1 NatSet.Nonempty.== t2
+ NatSet None, NatSet None -> true
+ _, _ -> false
+
+data.NatSet.all : (Nat ->{g} Boolean) ->{g} NatSet ->{g} Boolean
+data.NatSet.all p = NatSet.foldRight (x b -> p x && b) true
+
+data.NatSet.all.doc : Doc
+data.NatSet.all.doc =
+ use Nat isEven
+ use NatSet all fromList
+ {{
+ Determines whether all {type Nat}s in a {type NatSet} satisfy a predicate.
+
+ # Examples
+
+ ```
+ all isEven (fromList [1, 2, 3, 4])
+ ```
+
+ ```
+ all isEven (fromList [2, 4, 6])
+ ```
+
+ # See also
+
+ * {NatSet.any} to determine whether any {type Nat} in a {type NatSet}
+ satisfies a predicate.
+ }}
+
+test> data.NatSet.all.test = test.verify do
+ use Nat isEven
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ s = NatSet.fromList xs
+ ensure (iff (NatSet.all isEven s) (List.all isEven xs))
+
+data.NatSet.alter : (Boolean ->{g} Boolean) -> Nat -> NatSet ->{g} NatSet
+data.NatSet.alter f k = cases
+ NatSet (Some t) -> NatSet.Nonempty.alter f k t
+ NatSet None ->
+ if f false then NatSet (Some (NatSet.singleton k)) else NatSet None
+
+data.NatSet.alter.doc : Doc
+data.NatSet.alter.doc =
+ use NatSet alter
+ {{
+ Inserts or deletes an element from a {type NatSet} depending on the result of
+ a function.
+
+ The function receives a {type Boolean} indicating whether the element is
+ present in the {type NatSet} and returns a {type Boolean} indicating whether
+ the element should be present in the result. The function is allowed to use
+ any [ability](https://unison-lang.org/docs/abilities) it needs.
+
+ # Examples
+
+ Insert an element regardless:
+
+ @typecheck ```
+ alter (const true)
+ ```
+
+ Delete an element regardless:
+
+ @typecheck ```
+ alter (const false)
+ ```
+
+ Toggle an element:
+
+ @typecheck ```
+ alter Boolean.not
+ ```
+
+ # See also
+
+ * {NatSet.insert.nonempty} to insert an element into a {type NatSet}.
+ * {NatSet.delete} to delete an element from a {type NatSet}.
+ * {NatSet.filter} to delete all elements from a {type NatSet} that do not
+ satisfy a predicate.
+ }}
+
+test> data.NatSet.alter.test =
+ test.verify do
+ use NatSet == alter delete
+ use NatSet.insert nonempty
+ use Random nat
+ _ = Each.range 0 100
+ xs = Random.listOf nat do Random.natIn 0 100
+ n = nat()
+ s = NatSet.fromList xs
+ ensure (alter (const true) n s == NatSet (Some (nonempty n s)))
+ ensure (alter (const false) n s == delete n s)
+ ensure
+ (alter Boolean.not n s
+ == (if NatSet.contains n s then delete n s
+ else NatSet (Some (nonempty n s))))
+
+data.NatSet.any : (Nat ->{g} Boolean) ->{g} NatSet ->{g} Boolean
+data.NatSet.any p = NatSet.foldRight (x b -> p x || b) false
+
+data.NatSet.any.doc : Doc
+data.NatSet.any.doc =
+ use Nat isEven
+ use NatSet any fromList
+ {{
+ Determines whether any {type Nat} in a {type NatSet} satisfies a predicate.
+
+ # Examples
+
+ ```
+ any isEven (fromList [1, 2, 3, 4])
+ ```
+
+ ```
+ any isEven (fromList [1, 3, 5])
+ ```
+
+ # See also
+
+ * {NatSet.all} to determine whether all {type Nat}s in a {type NatSet}
+ satisfy a predicate.
+ }}
+
+test> data.NatSet.any.test = test.verify do
+ use Nat isEven
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 1000) do natIn 0 100
+ s = NatSet.fromList xs
+ ensure (iff (NatSet.any isEven s) (List.any isEven xs))
+
+data.NatSet.contains : Nat -> NatSet -> Boolean
+data.NatSet.contains k = cases
+ NatSet (Some t) -> NatSet.Nonempty.contains k t
+ NatSet None -> false
+
+data.NatSet.contains.doc : Doc
+data.NatSet.contains.doc =
+ use NatSet contains fromList
+ {{
+ Checks if a {type NatSet} contains a particular element.
+
+ # Examples
+
+ ```
+ contains 1 (fromList [1, 2, 3])
+ ```
+
+ ```
+ contains 4 (fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {NatSet.size} to get the number of elements in a {type NatSet}.
+ * {NatSet.isEmpty} to check if a {type NatSet} is empty.
+ }}
+
+test> data.NatSet.contains.test = test.verify do
+ use Random nat
+ _ = Each.range 0 100
+ xs = Random.listOf nat do Random.natIn 0 100
+ s = NatSet.fromList xs
+ n = nat()
+ ensure (NatSet.Nonempty.contains n (NatSet.insert.nonempty n s))
+ ensure (Boolean.not (NatSet.contains n (NatSet.delete n s)))
+
+data.NatSet.delete : Nat -> NatSet -> NatSet
+data.NatSet.delete k = cases
+ NatSet (Some t) -> NatSet.Nonempty.delete k t
+ NatSet None -> NatSet.empty
+
+data.NatSet.delete.doc : Doc
+data.NatSet.delete.doc =
+ {{
+ Deletes an element from a {type NatSet}.
+
+ # Example
+
+ ```
+ NatSet.toList (NatSet.delete 1 (NatSet.fromList [1, 2]))
+ ```
+
+ # See also
+
+ * {NatSet.difference} to delete a whole {type NatSet} from another.
+ * {NatSet.intersect} to delete all elements from a {type NatSet} that are
+ not in another.
+ * {NatSet.Nonempty.delete} to delete an element from a
+ {type NatSet.Nonempty}.
+ * {NatSet.insert.nonempty} to insert an element into a {type NatSet}.
+ }}
+
+data.NatSet.deleteMax : NatSet -> NatSet
+data.NatSet.deleteMax = cases
+ NatSet (Some t) -> NatSet.Nonempty.deleteMax t
+ NatSet None -> NatSet None
+
+data.NatSet.deleteMax.doc : Doc
+data.NatSet.deleteMax.doc =
+ {{
+ Returns the set without the maximum element.
+
+ # Example
+
+ ```
+ NatSet.toList (NatSet.deleteMax (NatSet.fromList [1, 2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatSet.deleteMin} to get the set without the minimum element.
+ * {NatSet.delete} to get the set without a specific element.
+ * {NatSet.maxView} to get the maximum element and the set without that
+ element.
+ }}
+
+test> data.NatSet.deleteMax.test =
+ test.verify do
+ use Nat !=
+ use NatSet fromList
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 1 100
+ r =
+ toOptional! do
+ NatSet.deleteMax (fromList xs)
+ === fromList
+ (List.filter
+ (flip (!=) (Optional.toAbort (List.maximum xs))) xs)
+ ensure (r === Some true)
+
+data.NatSet.deleteMin : NatSet -> NatSet
+data.NatSet.deleteMin = cases
+ NatSet (Some t) -> NatSet.Nonempty.deleteMin t
+ NatSet None -> NatSet None
+
+data.NatSet.deleteMin.doc : Doc
+data.NatSet.deleteMin.doc =
+ {{
+ Returns the set without the minimum element.
+
+ # Example
+
+ ```
+ NatSet.toList (NatSet.deleteMin (NatSet.fromList [1, 2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatSet.deleteMax} to get the set without the maximum element.
+ * {NatSet.delete} to get the set without a specific element.
+ * {NatSet.minView} to get the minimum element and the set without that
+ element.
+ }}
+
+test> data.NatSet.deleteMin.test =
+ test.verify do
+ use Nat !=
+ use NatSet fromList
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 1 100
+ r =
+ toOptional! do
+ NatSet.deleteMin (fromList xs)
+ === fromList
+ (List.filter
+ (flip (!=) (Optional.toAbort (List.minimum xs))) xs)
+ ensure (r === Some true)
+
+data.NatSet.difference : NatSet -> NatSet -> NatSet
+data.NatSet.difference = cases
+ NatSet (Some t1), NatSet (Some t2) -> NatSet.Nonempty.difference t1 t2
+ NatSet None, NatSet (Some t2) -> NatSet None
+ NatSet (Some t1), NatSet None -> NatSet (Some t1)
+ _, _ -> NatSet None
+
+data.NatSet.difference.doc : Doc
+data.NatSet.difference.doc =
+ use NatSet fromList
+ {{
+ Removes all elements from one {type NatSet} that are in another. If an
+ element is present in the first input but not the second, it will be present
+ in the result.
+
+ # Example
+
+ ```
+ NatSet.toList (NatSet.difference (fromList [1, 2]) (fromList [2, 3]))
+ ```
+
+ # See also
+
+ * {NatSet.union} to add all elements from a {type NatSet} that are in
+ another.
+ * {NatSet.intersect} to remove all elements from a {type NatSet} that are
+ not in another.
+ * {NatSet.delete} to remove one element from a {type NatSet}.
+ }}
+
+test> data.NatSet.difference.test =
+ test.verify do
+ use Random listOf nat natIn
+ _ = Each.range 0 100
+ xs = listOf nat do natIn 0 100
+ ys = listOf nat do natIn 0 100
+ ensureEqual
+ (NatSet.toList
+ (NatSet.difference (NatSet.fromList xs) (NatSet.fromList ys)))
+ (Set.toList (Set.deletes ys (Set.fromList xs)))
+
+data.NatSet.disjoint : NatSet -> NatSet -> Boolean
+data.NatSet.disjoint t1 t2 = NatSet.isEmpty (NatSet.intersect t1 t2)
+
+data.NatSet.disjoint.doc : Doc
+data.NatSet.disjoint.doc =
+ use NatSet disjoint fromList
+ {{
+ Checks if two {type NatSet} values have no elements in common.
+
+ # Examples
+
+ Two sets with no elements in common:
+
+ ```
+ disjoint (fromList [1, 2]) (fromList [3, 4])
+ ```
+
+ Two sets with elements in common:
+
+ ```
+ disjoint (fromList [1, 2]) (fromList [2, 3])
+ ```
+
+ # See also
+
+ * {NatSet.intersect} to find all elements in common between two sets.
+ * {NatSet.subsetCompare} to compare two sets to see if either one is a
+ subset of the other.
+ * {NatSet.subset} to check if one set is a superset of another.
+ * {NatSet.properSubset} to check if one set is a proper superset of
+ another.
+ }}
+
+data.NatSet.doc : Doc
+data.NatSet.doc =
+ use NatSet ==
+ {{
+ An efficient implementation of sets of {type Nat} values. This specialized
+ type is much more efficient than the generic {type Set} type when the
+ elements are of type {type Nat} or can be encoded as {type Nat} values.
+
+ {{
+ docAside
+ {{
+ The implementation of {type NatSet} is based on a
+ [patricia tree](https://en.wikipedia.org/wiki/Radix_tree). The code is
+ largely transliterated from the Haskell implementation by Daan Leijen and
+ Andriy Palamarchuk, which is based on the paper "Fast Mergeable Integer
+ Maps" by Chris Okasaki and Andy Gill.
+ }} }}
+
+ # Constructing sets
+
+ The empty set:
+
+ @signature{NatSet.empty}
+
+ A set with a single value in it. Note that this returns
+ {type NatSet.Nonempty} rather than {type NatSet}:
+
+ @signature{NatSet.singleton}
+
+ Construct a set from a list of values:
+
+ @signature{NatSet.fromList}
+
+ # Querying sets
+
+ Check if a set is empty:
+
+ @signature{NatSet.isEmpty}
+
+ Check if a set contains a value:
+
+ @signature{NatSet.contains}
+
+ Get the number of elements in a set:
+
+ @signature{NatSet.size}
+
+ Get the smallest element above a given value:
+
+ @signature{NatSet.getAbove}
+
+ Get the largest element below a given value:
+
+ @signature{NatSet.getBelow}
+
+ Get the smallest element above or equal to a given value:
+
+ @signature{NatSet.getAtLeast}
+
+ Get the largest element below or equal to a given value:
+
+ @signature{NatSet.getAtMost}
+
+ # Inserting and removing elements
+
+ Insert a value into a set:
+
+ @signature{NatSet.insert}
+
+ Insert a value into a set, returning a nonempty set:
+
+ @signature{NatSet.insert.nonempty}
+
+ Remove a value from a set:
+
+ @signature{NatSet.delete}
+
+ # Combining sets
+
+ Union of two sets:
+
+ @signature{NatSet.union}
+
+ Union of a list of sets:
+
+ @signature{NatSet.unions}
+
+ Intersection of two sets:
+
+ @signature{NatSet.intersect}
+
+ Difference between two sets:
+
+ @signature{NatSet.difference}
+
+ # Transforming sets
+
+ Apply function to every element of a set:
+
+ @signature{NatSet.map}
+
+ Apply function to every element of a set, removing elements for which the
+ function returns {None}:
+
+ @signature{NatSet.filterMap}
+
+ Change the membership of a key in a set:
+
+ @signature{NatSet.alter}
+
+ # Partitioning and filtering
+
+ Partition a set into two sets based on a predicate:
+
+ @signature{NatSet.partition}
+
+ Filter a set based on a predicate:
+
+ @signature{NatSet.filter}
+
+ Split a set into two sets around a pivot value:
+
+ @signature{NatSet.split}
+
+ Split a set into two sets around a pivot value, returning whether the pivot
+ value was in the set:
+
+ @signature{NatSet.splitContains}
+
+ # Summarizing sets
+
+ Summarize a set with a left-associative function:
+
+ @signature{NatSet.foldLeft}
+
+ Summarize a set with a right-associative function:
+
+ @signature{NatSet.foldRight}
+
+ Summarize a set by applying a function to each element and combining the
+ results with a binary function:
+
+ @signature{NatSet.foldMap}
+
+ # Comparing sets
+
+ Check if two sets are equal:
+
+ @signature{==}
+
+ Check if a set is a subset of another set:
+
+ @signature{NatSet.subset}
+
+ Check if a set is a superset of another set:
+
+ @signature{NatSet.superset}
+
+ Check if a set is a proper subset of another set:
+
+ @signature{NatSet.properSubset}
+
+ Check if a set is a proper superset of another set:
+
+ @signature{NatSet.properSuperset}
+
+ Compare two sets for subset ordering:
+
+ @signature{NatSet.subsetCompare}
+
+ Order two sets lexicographically:
+
+ @signature{NatSet.ordering}
+
+ Check if two sets have any elements in common:
+
+ @signature{NatSet.disjoint}
+
+ # Converting sets
+
+ Get the elements of a set as a list, in ascending order:
+
+ @signature{NatSet.toList}
+
+ Get the elements of a set as a list, in descending order:
+
+ @signature{NatSet.toListDescending}
+
+ # Minimum and maximum elements
+
+ Get the smallest element in a set:
+
+ @signature{NatSet.getMin}
+
+ Get the largest element in a set:
+
+ @signature{NatSet.getMax}
+
+ Break off the smallest element in a set:
+
+ @signature{NatSet.minView}
+
+ Break off the largest element in a set:
+
+ @signature{NatSet.maxView}
+
+ Remove the smallest element in a set:
+
+ @signature{NatSet.deleteMin}
+
+ Remove the largest element in a set:
+
+ @signature{NatSet.deleteMax}
+ }}
+
+data.NatSet.empty : NatSet
+data.NatSet.empty = NatSet None
+
+data.NatSet.empty.doc : Doc
+data.NatSet.empty.doc = {{ The empty {type NatSet}. }}
+
+data.NatSet.equals.doc : Doc
+data.NatSet.equals.doc =
+ use NatSet fromList
+ {{
+ Checks if two {type NatSet} are equal.
+
+ Two {type NatSet} are equal if they contain the same elements.
+
+ # Example
+
+ ```
+ NatSet.equals (fromList [1, 2, 3, 4]) (fromList [1, 2, 3, 4])
+ ```
+
+ # See also
+
+ * {NatSet.ordering} to compare two {type NatSet} lexicographically.
+ * {NatSet.subsetCompare} to compare two {type NatSet} for subset ordering.
+ * {NatSet.subset} to check if one {type NatSet} is a subset of another.
+ * {NatSet.disjoint} to check if two {type NatSet} have no elements in
+ common.
+ }}
+
+test> data.NatSet.equals.tests.reflexive = test.verify do
+ _ = Each.range 0 100
+ xs = Random.listOf Random.nat do Random.natIn 0 100
+ s = NatSet.fromList xs
+ ensure (NatSet.equals s s)
+
+test> data.NatSet.equals.tests.symmetric = test.verify do
+ use NatSet equals fromList
+ use Random listOf nat natIn
+ _ = Each.range 0 100
+ xs = listOf nat do natIn 0 100
+ ys = listOf nat do natIn 0 100
+ s1 = fromList xs
+ s2 = fromList ys
+ ensureEqual (equals s1 s2) (equals s2 s1)
+
+test> data.NatSet.equals.tests.transitive = test.verify do
+ use NatSet equals fromList
+ use Random listOf nat natIn
+ _ = Each.range 0 100
+ xs = listOf nat do natIn 0 100
+ ys = listOf nat do natIn 0 100
+ zs = listOf nat do natIn 0 100
+ s1 = fromList xs
+ s2 = fromList ys
+ s3 = fromList zs
+ ensureEqual (equals s1 s2 && equals s2 s3) (equals s1 s3)
+
+data.NatSet.filter : (Nat ->{g} Boolean) -> NatSet ->{g} NatSet
+data.NatSet.filter f = cases
+ NatSet (Some t) -> NatSet.Nonempty.filter f t
+ NatSet None -> NatSet None
+
+data.NatSet.filter.doc : Doc
+data.NatSet.filter.doc =
+ {{
+ Filters a {type NatSet} by a predicate. Returns a new set containing only the
+ elements for which the predicate returns ``true``.
+
+ # Example
+
+ ```
+ NatSet.toList (NatSet.filter Nat.isEven (NatSet.fromList [1, 2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatSet.partition} to split a set into two sets, one containing all
+ elements that satisfy the predicate, and one containing all elements that
+ do not.
+ * {NatSet.filterMap} to filter a set by a function that returns an
+ {type Optional} value.
+ * {NatSet.map} to apply a function to every element in a set, and return a
+ new set with the results.
+ }}
+
+test> data.NatSet.filter.test =
+ test.verify do
+ use NatSet fromList
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 0 100
+ f = natIn 0 100
+ ensure
+ (NatSet.filter ((Nat.==) f) (fromList xs)
+ NatSet.== fromList (List.filter ((Nat.==) f) xs))
+
+data.NatSet.filterMap : (Nat ->{g} Optional Nat) -> NatSet ->{g} NatSet
+data.NatSet.filterMap f = cases
+ NatSet (Some t) -> NatSet.Nonempty.filterMap f t
+ NatSet None -> NatSet None
+
+data.NatSet.filterMap.doc : Doc
+data.NatSet.filterMap.doc =
+ use Nat *
+ {{
+ Filters a {type NatSet} by a function that returns an {type Optional} value.
+ Returns a new set containing only the elements for which the function returns
+ ``Some``.
+
+ # Example
+
+ ```
+ NatSet.toList
+ (NatSet.filterMap
+ (n -> (if Nat.isEven n then Some (n * 2) else None))
+ (NatSet.fromList [1, 2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatSet.filter} to filter a set by a predicate that returns a
+ {type Boolean}.
+ * {NatSet.partition} to split a set into two sets, one containing all
+ elements that satisfy the predicate, and one containing all elements that
+ do not.
+ * {NatSet.map} to apply a function to every element in a set, and return a
+ new set with the results.
+ }}
+
+test> data.NatSet.filterMap.test =
+ test.verify do
+ use Nat *
+ use NatSet fromList
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 0 100
+ f = natIn 0 100
+ ensure
+ (NatSet.filterMap
+ (n -> (if n Nat.== f then Some (n * 2) else None)) (fromList xs)
+ NatSet.== fromList
+ (List.filterMap
+ (n -> (if n Nat.== f then Some (n * 2) else None)) xs))
+
+data.NatSet.foldLeft : (a ->{g} Nat ->{g} a) -> a ->{g} NatSet ->{g} a
+data.NatSet.foldLeft f z = cases
+ NatSet (Some t) -> NatSet.Nonempty.foldLeft f z t
+ NatSet None -> z
+
+data.NatSet.foldLeft.doc : Doc
+data.NatSet.foldLeft.doc =
+ use Nat +
+ use NatSet foldLeft fromList
+ use Text ++
+ {{
+ `` foldLeft f z s `` summarizes a set `s` by starting with the value `z` and
+ applying the binary function `f` to each element of `s` with the result so
+ far, associating to the left.
+
+ # Examples
+
+ Sum all the values in the set:
+
+ ```
+ foldLeft (+) 0 (fromList [1, 2, 3, 4])
+ ```
+
+ Concatenate the {type Text} values of all the elements in the set:
+
+ ```
+ foldLeft (acc n -> acc ++ Nat.toText n) "" (fromList [1, 2, 3, 4])
+ ```
+
+ # See also
+
+ * {NatSet.foldRight} to associate to the right.
+ * {NatSet.foldMap} to apply a function to every element and combine the
+ results with a binary function.
+ * {NatSet.map} to apply a function to every element.
+ * {NatSet.filter} to filter out elements that don't satisfy a predicate.
+ * {NatSet.filterMap} to apply a function to every element and filter out
+ the results that are {None}.
+ }}
+
+test> data.NatSet.foldLeft.test = test.verify do
+ use Nat + ==
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 1 100
+ s = NatSet.fromList xs
+ f acc x = acc + x
+ r = NatSet.foldLeft f 0 s == List.foldLeft f 0 (distinct xs)
+ ensure r
+
+data.NatSet.foldMap :
+ (a ->{g} a ->{g} a) -> (Nat ->{g} a) -> a -> NatSet ->{g} a
+data.NatSet.foldMap f g z = cases
+ NatSet (Some t) -> NatSet.Nonempty.foldMap f g t
+ NatSet None -> z
+
+data.NatSet.foldMap.doc : Doc
+data.NatSet.foldMap.doc =
+ use Nat +
+ use NatSet foldMap foldRight fromList
+ use Text ++
+ {{
+ `` foldMap f g s `` applies the function `g` to every element of the set `s`
+ and combines the results with the binary function `f`.
+
+ # Examples
+
+ Sum all the values in the set:
+
+ ```
+ foldMap (+) id 0 (fromList [1, 2, 3, 4])
+ ```
+
+ Concatenate the {type Text} values of all the elements in the set:
+
+ ```
+ foldMap (++) Nat.toText "" (fromList [1, 2, 3, 4])
+ ```
+
+ # See also
+
+ * {foldRight} to accumulate results of a single binary function applied to
+ every element and the result so far, associating to the right.
+ * {NatSet.foldLeft} same as {foldRight} but associating to the left.
+ * {NatSet.map} to apply a function to every element without combining the
+ results.
+ * {NatSet.filter} to filter out elements that don't satisfy a predicate.
+ * {NatSet.filterMap} to apply a function to every element and filter out
+ the results that are {None}.
+ }}
+
+data.NatSet.foldRight : (Nat ->{g} a ->{g} a) -> a ->{g} NatSet ->{g} a
+data.NatSet.foldRight f z = cases
+ NatSet (Some t) -> NatSet.Nonempty.foldRight f z t
+ NatSet None -> z
+
+data.NatSet.foldRight.doc : Doc
+data.NatSet.foldRight.doc =
+ use Nat +
+ use NatSet foldRight fromList
+ use Text ++
+ {{
+ `` foldRight f z s `` summarizes a set `s` by starting with the value `z` and
+ applying the binary function `f` to each element of `s` with the result so
+ far, associating to the right.
+
+ # Examples
+
+ Sum all the values in the set:
+
+ ```
+ foldRight (+) 0 (fromList [1, 2, 3, 4])
+ ```
+
+ Concatenate the {type Text} values of all the elements in the set:
+
+ ```
+ foldRight (n acc -> Nat.toText n ++ acc) "" (fromList [1, 2, 3, 4])
+ ```
+
+ # See also
+
+ * {NatSet.foldLeft} to associate to the left.
+ * {NatSet.foldMap} to apply a function to every element and combine the
+ results with a binary function.
+ * {NatSet.map} to apply a function to every element.
+ * {NatSet.filter} to filter out elements that don't satisfy a predicate.
+ * {NatSet.filterMap} to apply a function to every element and filter out
+ the results that are {None}.
+ }}
+
+test> data.NatSet.foldRight.test = test.verify do
+ use Nat + ==
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 1 100
+ s = NatSet.fromList xs
+ f x acc = acc + x
+ r = NatSet.foldRight f 0 s == List.foldRight f 0 (distinct xs)
+ ensure r
+
+data.NatSet.fromList : [Nat] -> NatSet
+data.NatSet.fromList =
+ List.foldBalanced
+ (NatSet << Some << NatSet.singleton) NatSet.union NatSet.empty
+
+data.NatSet.fromList.doc : Doc
+data.NatSet.fromList.doc =
+ use NatSet toList
+ {{
+ Converts a {type List} of {type Nat} to a {type NatSet}.
+
+ # Example
+
+ ```
+ toList (NatSet.fromList [1, 2, 3, 4])
+ ```
+
+ # See also
+
+ * {toList} to convert the other way.
+ * {NatSet.singleton} to create a {type NatSet.Nonempty} with a single
+ element.
+ }}
+
+data.NatSet.getAbove : Nat -> NatSet -> Optional Nat
+data.NatSet.getAbove k = cases
+ NatSet (Some t) -> NatSet.Nonempty.getAbove k t
+ NatSet None -> None
+
+data.NatSet.getAbove.doc : Doc
+data.NatSet.getAbove.doc =
+ use NatSet fromList getAbove
+ {{
+ Returns the smallest element in a {type NatSet} that is strictly larger than
+ a given one.
+
+ Returns {None} if there is no such element.
+
+ # Examples
+
+ ```
+ getAbove 2 (fromList [1, 2, 3])
+ ```
+
+ ```
+ getAbove 4 (fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {NatSet.getBelow} to get the largest element that is smaller than the
+ key.
+ * {NatSet.getAtMost} to get the largest element that is smaller than or
+ equal to the key.
+ * {NatSet.getAtLeast} to get the smallest element that is larger than or
+ equal to the key.
+ * {NatSet.getMax} to get the largest element.
+ * {NatSet.getMin} to get the smallest element.
+ }}
+
+data.NatSet.getAtLeast : Nat -> NatSet -> Optional Nat
+data.NatSet.getAtLeast k = cases
+ NatSet (Some t) -> NatSet.Nonempty.getAtLeast k t
+ NatSet None -> None
+
+data.NatSet.getAtLeast.doc : Doc
+data.NatSet.getAtLeast.doc =
+ use NatSet fromList getAtLeast
+ {{
+ Returns the smallest element in a {type NatSet} that is larger than or equal
+ to a given one.
+
+ Returns {None} if there is no such element.
+
+ # Examples
+
+ ```
+ getAtLeast 2 (fromList [1, 2, 3])
+ ```
+
+ ```
+ getAtLeast 4 (fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {NatSet.getAbove} to get the smallest element that is strictly larger
+ than the key.
+ * {NatSet.getAtMost} to get the largest element that is smaller than or
+ equal to the key.
+ * {NatSet.getBelow} to get the largest element that is strictly smaller
+ than the key.
+ * {NatSet.getMax} to get the largest element.
+ * {NatSet.getMin} to get the smallest element.
+ }}
+
+data.NatSet.getAtMost : Nat -> NatSet -> Optional Nat
+data.NatSet.getAtMost k = cases
+ NatSet (Some t) -> NatSet.Nonempty.getAtMost k t
+ NatSet None -> None
+
+data.NatSet.getAtMost.doc : Doc
+data.NatSet.getAtMost.doc =
+ use NatSet fromList getAtMost
+ {{
+ Returns the largest element in a {type NatSet} that is smaller than or equal
+ to a given one.
+
+ Returns {None} if there is no such element.
+
+ # Examples
+
+ ```
+ getAtMost 2 (fromList [1, 2, 3])
+ ```
+
+ ```
+ getAtMost 4 (fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {NatSet.getBelow} to get the largest element that is strictly smaller
+ than the key.
+ * {NatSet.getAbove} to get the smallest element that is larger than the
+ key.
+ * {NatSet.getAtLeast} to get the smallest element that is larger than or
+ equal to the key.
+ * {NatSet.getMax} to get the largest element.
+ * {NatSet.getMin} to get the smallest element.
+ }}
+
+data.NatSet.getBelow : Nat -> NatSet -> Optional Nat
+data.NatSet.getBelow k = cases
+ NatSet (Some t) -> NatSet.Nonempty.getBelow k t
+ NatSet None -> None
+
+data.NatSet.getBelow.doc : Doc
+data.NatSet.getBelow.doc =
+ use NatSet fromList getBelow
+ {{
+ Returns the largest element in a {type NatSet} that is strictly smaller than
+ a given one.
+
+ Returns {None} if there is no such element.
+
+ # Examples
+
+ ```
+ getBelow 2 (fromList [1, 2, 3])
+ ```
+
+ ```
+ getBelow 4 (fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {NatSet.getAbove} to get the smallest element that is larger than the
+ key.
+ * {NatSet.getAtMost} to get the largest element that is smaller than or
+ equal to the key.
+ * {NatSet.getAtLeast} to get the smallest element that is larger than or
+ equal to the key.
+ * {NatSet.getMax} to get the largest element.
+ * {NatSet.getMin} to get the smallest element.
+ }}
+
+data.NatSet.getMax : NatSet -> Optional Nat
+data.NatSet.getMax = cases
+ NatSet (Some t) -> Some (NatSet.Nonempty.getMax t)
+ NatSet None -> None
+
+data.NatSet.getMax.doc : Doc
+data.NatSet.getMax.doc =
+ {{
+ Returns the largest element in a {type NatSet}, or {None} if the set is
+ empty.
+
+ # Example
+
+ ```
+ NatSet.getMax (NatSet.fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {NatSet.getMin} to get the smallest element.
+ * {NatSet.getBelow} to get the largest element that is strictly smaller
+ than a given key.
+ * {NatSet.getAbove} to get the smallest element that is larger than a given
+ key.
+ * {NatSet.getAtMost} to get the largest element that is smaller than or
+ equal to a given key.
+ * {NatSet.getAtLeast} to get the smallest element that is larger than or
+ equal to a given key.
+ }}
+
+data.NatSet.getMin : NatSet -> Optional Nat
+data.NatSet.getMin = cases
+ NatSet (Some t) -> Some (NatSet.Nonempty.getMin t)
+ NatSet None -> None
+
+data.NatSet.getMin.doc : Doc
+data.NatSet.getMin.doc =
+ {{
+ Returns the smallest element in a {type NatSet}, or {None} if the set is
+ empty.
+
+ # Example
+
+ ```
+ NatSet.getMin (NatSet.fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {NatSet.getMax} to get the largest element.
+ * {NatSet.getBelow} to get the largest element that is strictly smaller
+ than a given key.
+ * {NatSet.getAbove} to get the smallest element that is larger than a given
+ key.
+ * {NatSet.getAtMost} to get the largest element that is smaller than or
+ equal to a given key.
+ * {NatSet.getAtLeast} to get the smallest element that is larger than or
+ equal to a given key.
+ }}
+
+data.NatSet.insert : Nat -> NatSet -> NatSet
+data.NatSet.insert n s = NatSet.Nonempty.toNatSet (NatSet.insert.nonempty n s)
+
+data.NatSet.insert.doc : Doc
+data.NatSet.insert.doc =
+ use NatSet fromList
+ {{
+ Inserts an element into a {type NatSet}.
+
+ # Example
+
+ ```
+ NatSet.toList (NatSet.insert 1 (fromList [2, 3]))
+ ```
+
+ # See also
+
+ * {NatSet.insert.nonempty} for a version of this that returns a
+ {type NatSet.Nonempty}.
+ * {NatSet.union} to add a whole {type NatSet} into another.
+ * {NatSet.Nonempty.insert} to insert an element into a
+ {type NatSet.Nonempty}.
+ * {NatSet.delete} to delete an element from a {type NatSet}.
+ * {fromList} to create a {type NatSet} from a {type List} of elements.
+ * {NatSet.singleton} to create a {type NatSet.Nonempty} with a single
+ element.
+ }}
+
+data.NatSet.insert.nonempty : Nat -> NatSet -> NatSet.Nonempty
+data.NatSet.insert.nonempty k = cases
+ NatSet (Some t) -> NatSet.Nonempty.insert k t
+ NatSet None -> NatSet.singleton k
+
+data.NatSet.insert.nonempty.doc : Doc
+data.NatSet.insert.nonempty.doc =
+ use NatSet fromList
+ {{
+ Inserts an element into a {type NatSet}.
+
+ # Example
+
+ ```
+ Nonempty.toListAscending (NatSet.insert.nonempty 1 (fromList [2, 3]))
+ ```
+
+ # See also
+
+ * {NatSet.union} to add a whole {type NatSet} into another.
+ * {NatSet.Nonempty.insert} to insert an element into a
+ {type NatSet.Nonempty}.
+ * {NatSet.delete} to delete an element from a {type NatSet}.
+ * {fromList} to create a {type NatSet} from a {type List} of elements.
+ * {NatSet.singleton} to create a {type NatSet.Nonempty} with a single
+ element.
+ }}
+
+data.NatSet.internal.bim :
+ Nat -> Nat -> NatSet.Nonempty -> NatSet.Nonempty -> NatSet.Nonempty
+data.NatSet.internal.bim p m l r =
+ use Nat +
+ use NatSet.Nonempty size
+ NatSet.Nonempty.Bin p m (size l + size r) l r
+
+data.NatSet.internal.bin : Nat -> Nat -> NatSet -> NatSet -> NatSet
+data.NatSet.internal.bin p m = cases
+ NatSet (Some l), NatSet (Some r) ->
+ NatSet (Some (NatSet.internal.bim p m l r))
+ NatSet None, NatSet (Some r) -> NatSet (Some r)
+ NatSet (Some l), NatSet None -> NatSet (Some l)
+ _, _ -> NatSet None
+
+data.NatSet.internal.bin.doc : Doc
+data.NatSet.internal.bin.doc =
+ {{
+ Internal function to combine two {type NatSet} trees ensuring that the result
+ has no empty branches.
+ }}
+
+data.NatSet.internal.bitmapOf : Nat -> Nat
+data.NatSet.internal.bitmapOf k = Nat.shiftLeft 1 (suffixOf k)
+
+data.NatSet.internal.bitmapOf.doc : Doc
+data.NatSet.internal.bitmapOf.doc =
+ {{
+ Internal function to get the bitmap of a key. The bitmap is a bit vector
+ where the bit at position `k` is set.
+ }}
+
+data.NatSet.internal.bitPred :
+ (Nat ->{g} Boolean) -> Nat -> Nat -> Nat ->{g} Nat
+data.NatSet.internal.bitPred f p m bi =
+ use Nat +
+ if f (p + bi) then Nat.or m (Nat.shiftLeft 1 bi) else m
+
+data.NatSet.internal.bitPred.doc : Doc
+data.NatSet.internal.bitPred.doc =
+ {{ Helper function to set a bit in a bitmap if a predicate is true. }}
+
+data.NatSet.internal.foldBitsLeft :
+ Nat -> (a ->{g} Nat ->{g} a) -> a -> Nat ->{g} a
+data.NatSet.internal.foldBitsLeft prefix f z bitmap =
+ go bm acc =
+ use Nat + ==
+ bitmask = lowestBitMask bm
+ if bm == 0 then acc
+ else go (Nat.xor bm bitmask) (f acc (prefix + Nat.trailingZeros bitmask))
+ go bitmap z
+
+data.NatSet.internal.foldBitsLeft.doc : Doc
+data.NatSet.internal.foldBitsLeft.doc =
+ {{
+ Helper function to fold over the bits in a bitmap, starting from the least
+ significant bit.
+ }}
+
+data.NatSet.internal.foldBitsRight :
+ Nat -> (Nat ->{g} a ->{g} a) -> a ->{g} Nat ->{g} a
+data.NatSet.internal.foldBitsRight p f z m =
+ use Nat + - ==
+ go m z =
+ if m == 0 then z
+ else
+ bitmask = lowestBitMask m
+ bi = Nat.trailingZeros bitmask
+ go (Nat.xor m bitmask) (f (p + 63 - bi) z)
+ go (reverseBits m) z
+
+data.NatSet.internal.foldBitsRight.doc : Doc
+data.NatSet.internal.foldBitsRight.doc =
+ {{ Internal helper for {NatSet.Nonempty.foldRight} and {NatSet.foldRight}. }}
+
+data.NatSet.internal.highBit : Nat -> Nat
+data.NatSet.internal.highBit n =
+ use Nat -
+ 63 - Nat.leadingZeros n
+
+data.NatSet.internal.highBit.doc : Doc
+data.NatSet.internal.highBit.doc =
+ {{
+ Internal function to get the position of the highest bit in a bitmap. Note
+ that this function is undefined if the bitmap is 0.
+ }}
+
+data.NatSet.internal.intersectBitmap : Nat -> Nat -> NatSet.Nonempty -> NatSet
+data.NatSet.internal.intersectBitmap p m = cases
+ t@(NatSet.Nonempty.Bin p2 m2 sz2 l2 r2)
+ | nomatch p p2 m2 -> NatSet None
+ | internal.zero p m2 -> data.NatSet.internal.intersectBitmap p m l2
+ | otherwise -> data.NatSet.internal.intersectBitmap p m r2
+ t@(NatSet.Nonempty.Tip p2 m2) ->
+ if p Nat.== p2 then tip p (Nat.and m m2) else NatSet None
+
+data.NatSet.internal.intersectBitmap.doc : Doc
+data.NatSet.internal.intersectBitmap.doc =
+ {{
+ Internal function used by {NatSet.Nonempty.intersect}. Takes a prefix and
+ bitmap, and a {type NatSet.Nonempty}, and masks the leaves of the tree with
+ the bitmap where the prefix matches.
+ }}
+
+data.NatSet.internal.link :
+ Nat -> NatSet.Nonempty -> Nat -> NatSet.Nonempty -> NatSet.Nonempty
+data.NatSet.internal.link p1 t1 p2 t2 =
+ linkWithMask (branchMask p1 p2) p1 t1 t2
+
+data.NatSet.internal.link.doc : Doc
+data.NatSet.internal.link.doc =
+ {{ Internal function to combine two {type NatSet.Nonempty} trees. }}
+
+data.NatSet.internal.linkWithMask :
+ Nat -> Nat -> NatSet.Nonempty -> NatSet.Nonempty -> NatSet.Nonempty
+data.NatSet.internal.linkWithMask m p1 t1 t2 =
+ use NatSet.internal bim
+ p = mask p1 m
+ if internal.zero p1 m then bim p m t1 t2 else bim p m t2 t1
+
+data.NatSet.internal.linkWithMask.doc : Doc
+data.NatSet.internal.linkWithMask.doc =
+ {{
+ Internal function to combine two {type NatSet.Nonempty} trees when the branch
+ mask is known.
+ }}
+
+data.NatSet.internal.lowestBitMask : Nat -> Nat
+data.NatSet.internal.lowestBitMask n = Nat.and n (twosComplement n)
+
+data.NatSet.internal.lowestBitMask.doc : Doc
+data.NatSet.internal.lowestBitMask.doc =
+ {{ Helper function to get the bitmask for the lowest set bit in a bitmap. }}
+
+data.NatSet.internal.prefixOf : Nat -> Nat
+data.NatSet.internal.prefixOf k = Nat.and k 18446744073709551552
+
+data.NatSet.internal.prefixOf.doc : Doc
+data.NatSet.internal.prefixOf.doc =
+ {{
+ Internal function to get the prefix of a key. The prefix is the high-order
+ bits of the key, with the last 6 bits set to 0.
+ }}
+
+data.NatSet.internal.suffixOf : Nat -> Nat
+data.NatSet.internal.suffixOf k = Nat.and k 63
+
+data.NatSet.internal.suffixOf.doc : Doc
+data.NatSet.internal.suffixOf.doc =
+ {{
+ Internal function to get the suffix of a key. The suffix is the low-order 6
+ bits of the key.
+ }}
+
+data.NatSet.internal.tip : Nat -> Nat -> NatSet
+data.NatSet.internal.tip p bm =
+ use Nat ==
+ if bm == 0 then NatSet None else NatSet (Some (NatSet.Nonempty.Tip p bm))
+
+data.NatSet.internal.tip.doc : Doc
+data.NatSet.internal.tip.doc =
+ {{
+ Internal function to create a {type NatSet} tree from a prefix and bitmap,
+ ensuring that the bitmap is non-zero.
+ }}
+
+data.NatSet.intersect : NatSet -> NatSet -> NatSet
+data.NatSet.intersect = cases
+ NatSet (Some t1), NatSet (Some t2) -> NatSet.Nonempty.intersect t1 t2
+ _, _ -> NatSet None
+
+data.NatSet.intersect.doc : Doc
+data.NatSet.intersect.doc =
+ use NatSet fromList
+ {{
+ Removes all elements from one {type NatSet} that are not in another.
+
+ Returns a {type NatSet} containing all elements that are present in both
+ inputs.
+
+ # Example
+
+ ```
+ NatSet.toList (NatSet.intersect (fromList [1, 2]) (fromList [2, 3]))
+ ```
+
+ # See also
+
+ * {NatSet.union} to return elements in __either__ input.
+ * {NatSet.difference} to return elements in the first input but not the
+ second.
+ * {NatSet.delete} to remove one element from a {type NatSet}.
+ }}
+
+test> data.NatSet.intersect.test =
+ test.verify do
+ use Random listOf nat natIn
+ _ = Each.range 0 100
+ xs = listOf nat do natIn 0 100
+ ys = listOf nat do natIn 0 100
+ ensureEqual
+ (NatSet.toList
+ (NatSet.intersect (NatSet.fromList xs) (NatSet.fromList ys)))
+ (Set.toList (Set.intersect (Set.fromList xs) (Set.fromList ys)))
+
+data.NatSet.isEmpty : NatSet -> Boolean
+data.NatSet.isEmpty = cases
+ NatSet (Some _) -> false
+ NatSet None -> true
+
+data.NatSet.isEmpty.doc : Doc
+data.NatSet.isEmpty.doc =
+ use NatSet fromList isEmpty
+ {{
+ Checks if a {type NatSet} is empty.
+
+ # Examples
+
+ ```
+ isEmpty (fromList [1, 2, 3])
+ ```
+
+ ```
+ isEmpty (fromList [])
+ ```
+
+ # See also
+
+ * {NatSet.size} to get the number of elements in a {type NatSet}.
+ * {NatSet.contains} to check if a {type NatSet} contains a particular
+ element.
+ }}
+
+data.NatSet.map : (Nat ->{g} Nat) -> NatSet ->{g} NatSet
+data.NatSet.map f = cases
+ NatSet (Some t) -> NatSet (Some (NatSet.Nonempty.map f t))
+ NatSet None -> NatSet None
+
+data.NatSet.map.doc : Doc
+data.NatSet.map.doc =
+ use Nat *
+ {{
+ Applies a function to every element in a {type NatSet}.
+
+ # Example
+
+ ```
+ NatSet.toList (NatSet.map ((*) 2) (NatSet.fromList [1, 2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatSet.foldMap} to apply a function to every element and combine the
+ results with a binary function.
+ * {NatSet.filterMap} to apply a function to every element and filter out
+ the results that are {None}.
+ * {NatSet.filter} to filter out elements that don't satisfy a predicate.
+ * {NatSet.foldLeft} and {NatSet.foldRight} to summarize the elements of a
+ set.
+ }}
+
+test> data.NatSet.map.tests.functor.homomorphism = test.verify do
+ use Nat * +
+ use NatSet == map
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 1 100
+ s = NatSet.fromList xs
+ f x = x + 1
+ g x = x * 2
+ r = map (f >> g) s == map g (map f s)
+ ensure r
+
+test> data.NatSet.map.tests.functor.identity = test.verify do
+ use NatSet ==
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 1 100
+ s = NatSet.fromList xs
+ r = NatSet.map id s == s
+ ensure r
+
+test> data.NatSet.map.tests.noninjective = test.verify do
+ use Nat /
+ use NatSet == fromList
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 1 100
+ s = fromList xs
+ f x = x / 2
+ r = NatSet.map f s == fromList (List.map f xs)
+ ensure r
+
+data.NatSet.maxView : NatSet ->{Abort} (Nat, NatSet)
+data.NatSet.maxView = cases
+ NatSet (Some t) -> NatSet.Nonempty.maxView t
+ NatSet None -> abort
+
+data.NatSet.maxView.doc : Doc
+data.NatSet.maxView.doc =
+ {{
+ Returns the maximum element in a {type NatSet}, and the set without that
+ element.
+
+ Calls {abort} if the set is empty.
+
+ # Example
+
+ ```
+ toOptional! do
+ Tuple.second
+ NatSet.toList (NatSet.maxView (NatSet.fromList [1, 2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatSet.minView} to get the minimum element and the set without that
+ element.
+ * {NatSet.getMax} to just get the maximum element.
+ * {NatSet.deleteMax} to get the set without the maximum element.
+ }}
+
+test> data.NatSet.maxView.test =
+ test.verify do
+ use List maximum
+ use Nat !=
+ use NatSet fromList
+ use Optional toAbort
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 1 100
+ r =
+ toOptional! do
+ NatSet.maxView (fromList xs)
+ === ( toAbort (maximum xs)
+ , fromList (List.filter (flip (!=) (toAbort (maximum xs))) xs)
+ )
+ ensure (r === Some true)
+
+data.NatSet.minView : NatSet ->{Abort} (Nat, NatSet)
+data.NatSet.minView = cases
+ NatSet (Some t) -> NatSet.Nonempty.minView t
+ NatSet None -> abort
+
+data.NatSet.minView.doc : Doc
+data.NatSet.minView.doc =
+ {{
+ Returns the minimum element in a {type NatSet}, and the set without that
+ element.
+
+ Calls {abort} if the set is empty.
+
+ # Example
+
+ ```
+ toOptional! do
+ Tuple.second
+ NatSet.toList (NatSet.minView (NatSet.fromList [1, 2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatSet.maxView} to get the maximum element and the set without that
+ element.
+ * {NatSet.getMin} to just get the minimum element.
+ * {NatSet.deleteMin} to get the set without the minimum element.
+ }}
+
+test> data.NatSet.minView.test =
+ test.verify do
+ use List minimum
+ use Nat !=
+ use NatSet fromList
+ use Optional toAbort
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 1 100
+ r =
+ toOptional! do
+ NatSet.minView (fromList xs)
+ === ( toAbort (minimum xs)
+ , fromList (List.filter (flip (!=) (toAbort (minimum xs))) xs)
+ )
+ ensure (r === Some true)
+
+(data.NatSet.Nonempty.==) : NatSet.Nonempty -> NatSet.Nonempty -> Boolean
+(data.NatSet.Nonempty.==) = cases
+ NatSet.Nonempty.Bin p1 m1 sz1 l1 r1, NatSet.Nonempty.Bin p2 m2 sz2 l2 r2 ->
+ m1 Nat.== m2 && p1 Nat.== p2 && l1 data.NatSet.Nonempty.== l2
+ && r1 data.NatSet.Nonempty.== r2
+ NatSet.Nonempty.Tip p1 m1, NatSet.Nonempty.Tip p2 m2 ->
+ m1 Nat.== m2 && p1 Nat.== p2
+ _, _ -> false
+
+data.NatSet.Nonempty.alter :
+ (Boolean ->{g} Boolean) -> Nat -> NatSet.Nonempty ->{g} NatSet
+data.NatSet.Nonempty.alter f k s =
+ present = NatSet.Nonempty.contains k s
+ let
+ (inserted, deleted) =
+ if present then (NatSet (Some s), NatSet.Nonempty.delete k s)
+ else (NatSet (Some (NatSet.Nonempty.insert k s)), NatSet (Some s))
+ if f present then inserted else deleted
+
+data.NatSet.Nonempty.alter.doc : Doc
+data.NatSet.Nonempty.alter.doc =
+ use NatSet.Nonempty alter
+ {{
+ Inserts or deletes an element from a {type NatSet.Nonempty} depending on the
+ result of a function.
+
+ The function receives a {type Boolean} indicating whether the element is
+ present in the {type NatSet.Nonempty} and returns a {type Boolean} indicating
+ whether the element should be present in the result. The function is allowed
+ to use any [ability](https://unison-lang.org/docs/abilities) it needs.
+
+ # Examples
+
+ Insert an element regardless:
+
+ @typecheck ```
+ alter (const true)
+ ```
+
+ Delete an element regardless:
+
+ @typecheck ```
+ alter (const false)
+ ```
+
+ Toggle an element:
+
+ @typecheck ```
+ alter Boolean.not
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.insert} to insert an element into a
+ {type NatSet.Nonempty}.
+ * {NatSet.Nonempty.delete} to delete an element from a
+ {type NatSet.Nonempty}.
+ * {NatSet.Nonempty.filter} to delete all elements from a
+ {type NatSet.Nonempty} that do not satisfy a predicate.
+ }}
+
+data.NatSet.Nonempty.compare : NatSet.Nonempty -> NatSet.Nonempty -> Ordering
+data.NatSet.Nonempty.compare xs ys =
+ use Nonempty toListAscending
+ Universal.ordering (toListAscending xs) (toListAscending ys)
+
+data.NatSet.Nonempty.contains : Nat -> NatSet.Nonempty -> Boolean
+data.NatSet.Nonempty.contains k = cases
+ NatSet.Nonempty.Bin p m _ l r
+ | nomatch k p m -> false
+ | internal.zero k m -> data.NatSet.Nonempty.contains k l
+ | otherwise -> data.NatSet.Nonempty.contains k r
+ NatSet.Nonempty.Tip p bm
+ | prefixOf k Nat.== p -> isSetBit (suffixOf k) bm
+ | otherwise -> false
+
+data.NatSet.Nonempty.contains.doc : Doc
+data.NatSet.Nonempty.contains.doc =
+ use NatSet.Nonempty contains fromList
+ {{
+ Checks if a {type NatSet.Nonempty} contains a particular element.
+
+ # Examples
+
+ ```
+ contains 1 (fromList (1 +| [2, 3]))
+ ```
+
+ ```
+ contains 4 (fromList (1 +| [2, 3]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.size} to get the number of elements in a
+ {type NatSet.Nonempty}.
+ }}
+
+data.NatSet.Nonempty.delete : Nat -> NatSet.Nonempty -> NatSet
+data.NatSet.Nonempty.delete k = deleteBitmap (prefixOf k) (bitmapOf k)
+
+data.NatSet.Nonempty.delete.doc : Doc
+data.NatSet.Nonempty.delete.doc =
+ {{
+ Deletes an element from a {type NatSet.Nonempty}, returning a {type NatSet}.
+
+ # Example
+
+ ```
+ NatSet.toList
+ (NatSet.Nonempty.delete 1 (NatSet.Nonempty.fromList (1 +| [2])))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.difference} to delete a whole {type NatSet.Nonempty}
+ from another.
+ * {NatSet.Nonempty.intersect} to delete all elements from a
+ {type NatSet.Nonempty} that are not in another.
+ * {NatSet.delete} to delete an element from a {type NatSet}.
+ * {NatSet.Nonempty.insert} to insert an element into a
+ {type NatSet.Nonempty}.
+ }}
+
+test> data.NatSet.Nonempty.delete.test.deletedIsAbsent =
+ test.verify do
+ use Random nat
+ _ = Each.range 0 100
+ xs = Random.listOf nat do Random.natIn 0 100
+ n = nat()
+ s = NatSet.fromList xs
+ ensure
+ (Boolean.not
+ (NatSet.contains
+ n (NatSet.Nonempty.delete n (NatSet.insert.nonempty n s))))
+
+data.NatSet.Nonempty.deleteMax : NatSet.Nonempty -> NatSet
+data.NatSet.Nonempty.deleteMax = at2 << NatSet.Nonempty.maxView
+
+data.NatSet.Nonempty.deleteMax.doc : Doc
+data.NatSet.Nonempty.deleteMax.doc =
+ {{
+ Returns the set without the maximum element.
+
+ # Example
+
+ ```
+ NatSet.toList
+ (NatSet.Nonempty.deleteMax (NatSet.Nonempty.fromList (1 +| [2, 3, 4])))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.deleteMin} to get the set without the minimum element.
+ * {NatSet.Nonempty.delete} to get the set without a specific element.
+ * {NatSet.Nonempty.maxView} to get the maximum element and the set without
+ that element.
+ }}
+
+data.NatSet.Nonempty.deleteMin : NatSet.Nonempty -> NatSet
+data.NatSet.Nonempty.deleteMin = at2 << NatSet.Nonempty.minView
+
+data.NatSet.Nonempty.deleteMin.doc : Doc
+data.NatSet.Nonempty.deleteMin.doc =
+ {{
+ Returns the set without the minimum element.
+
+ # Example
+
+ ```
+ NatSet.toList
+ (NatSet.Nonempty.deleteMin (NatSet.Nonempty.fromList (1 +| [2, 3, 4])))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.deleteMax} to get the set without the maximum element.
+ * {NatSet.Nonempty.delete} to get the set without a specific element.
+ * {NatSet.Nonempty.minView} to get the minimum element and the set without
+ that element.
+ }}
+
+data.NatSet.Nonempty.difference : NatSet.Nonempty -> NatSet.Nonempty -> NatSet
+data.NatSet.Nonempty.difference = cases
+ t1@(NatSet.Nonempty.Bin p1 m1 _ l1 r1),
+ t2@(NatSet.Nonempty.Bin p2 m2 _ l2 r2)
+ | shorter m1 m2 ->
+ if nomatch p2 p1 m1 then NatSet (Some t1)
+ else
+ if internal.zero p2 m1 then
+ NatSet.internal.bin
+ p1 m1 (data.NatSet.Nonempty.difference l1 t2) (NatSet (Some r1))
+ else
+ NatSet.internal.bin
+ p1 m1 (NatSet (Some l1)) (data.NatSet.Nonempty.difference r1 t2)
+ | shorter m2 m1 ->
+ if nomatch p1 p2 m2 then NatSet (Some t1)
+ else
+ if internal.zero p1 m2 then data.NatSet.Nonempty.difference t1 l2
+ else data.NatSet.Nonempty.difference t1 r2
+ | p1 Nat.== p2 ->
+ NatSet.internal.bin
+ p1
+ m1
+ (data.NatSet.Nonempty.difference l1 l2)
+ (data.NatSet.Nonempty.difference r1 r2)
+ | otherwise -> NatSet (Some t1)
+ t@(NatSet.Nonempty.Bin _ _ _ _ _), NatSet.Nonempty.Tip p m ->
+ deleteBitmap p m t
+ t@(NatSet.Nonempty.Tip p m), NatSet.Nonempty.Bin p2 m2 _ l2 r2 ->
+ if nomatch p p2 m2 then NatSet (Some t)
+ else
+ if internal.zero p m2 then data.NatSet.Nonempty.difference t l2
+ else data.NatSet.Nonempty.difference t r2
+ t@(NatSet.Nonempty.Tip p1 m1), NatSet.Nonempty.Tip p2 m2 ->
+ if p1 Nat.== p2 then tip p1 (Nat.and m1 (Nat.complement m2))
+ else NatSet (Some t)
+
+data.NatSet.Nonempty.difference.doc : Doc
+data.NatSet.Nonempty.difference.doc =
+ use NatSet.Nonempty fromList
+ {{
+ Removes all elements from one {type NatSet.Nonempty} that are in another. If
+ an element is present in the first input but not the second, it will be
+ present in the result.
+
+ # Example
+
+ ```
+ NatSet.toList
+ (NatSet.Nonempty.difference (fromList (1 +| [2])) (fromList (2 +| [3])))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.union} to add all elements from a {type NatSet.Nonempty}
+ that are in another.
+ * {NatSet.Nonempty.intersect} to remove all elements from a
+ {type NatSet.Nonempty} that are not in another.
+ * {NatSet.Nonempty.delete} to remove one element from a
+ {type NatSet.Nonempty}.
+ }}
+
+data.NatSet.Nonempty.disjoint : NatSet.Nonempty -> NatSet.Nonempty -> Boolean
+data.NatSet.Nonempty.disjoint t1 t2 =
+ NatSet.isEmpty (NatSet.Nonempty.intersect t1 t2)
+
+data.NatSet.Nonempty.disjoint.doc : Doc
+data.NatSet.Nonempty.disjoint.doc =
+ use NatSet.Nonempty fromList
+ use Nonempty disjoint
+ {{
+ Checks if two {type NatSet.Nonempty} values have no elements in common.
+
+ # Examples
+
+ Two sets with no elements in common:
+
+ ```
+ disjoint (fromList (1 +| [2])) (fromList (3 +| [4]))
+ ```
+
+ Two sets with elements in common:
+
+ ```
+ disjoint (fromList (1 +| [2])) (fromList (2 +| [3]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.intersect} to find all elements in common between two
+ sets.
+ * {Nonempty.subsetCompare} to compare two sets to see if either one is a
+ subset of the other.
+ * {NatSet.Nonempty.subset} to check if one set is a superset of another.
+ * {Nonempty.properSubset} to check if one set is a proper superset of
+ another.
+ }}
+
+test> data.NatSet.Nonempty.disjoint.test =
+ test.verify do
+ use List all
+ use Nat !=
+ use NatSet fromList
+ use Random listOf natIn
+ _ = Each.range 0 100
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ d = NatSet.disjoint (fromList xs) (fromList ys)
+ if all (x -> all (y -> x != y) ys) xs then ensure d
+ else ensure (Boolean.not d)
+
+data.NatSet.Nonempty.doc : Doc
+data.NatSet.Nonempty.doc =
+ use NatSet.Nonempty ==
+ {{
+ A nonempty set of {type Nat} values. This specialized type is much more
+ efficient than the generic {type Set} type when the elements are of type
+ {type Nat} or can be encoded as {type Nat} values.
+
+ {{
+ docAside
+ {{
+ The implementation of {type NatSet.Nonempty} is based on a
+ [patricia tree](https://en.wikipedia.org/wiki/Radix_tree). The code is
+ largely transliterated from the Haskell implementation by Daan Leijen and
+ Andriy Palamarchuk, which is based on the paper "Fast Mergeable Integer
+ Maps" by Chris Okasaki and Andy Gill.
+ }} }}
+
+ Note that certain operations on {type NatSet.Nonempty} return (possibly
+ empty) {type NatSet} values, where the presence of at least one element
+ cannot be guaranteed by the type. Conversely, certain operations on
+ {type NatSet} return {type NatSet.Nonempty} values, where the presence of at
+ least one element is guaranteed.
+
+ # Constructing sets
+
+ A set with a single value in it:
+
+ @signature{NatSet.Nonempty.singleton}
+
+ Construct a set from a {type List.Nonempty} of values:
+
+ @signature{NatSet.Nonempty.fromList}
+
+ # Querying sets
+
+ Check if a set contains a value:
+
+ @signature{NatSet.Nonempty.contains}
+
+ Get the number of elements in a set:
+
+ @signature{NatSet.Nonempty.size}
+
+ Get the smallest element above a given value:
+
+ @signature{NatSet.Nonempty.getAbove}
+
+ Get the largest element below a given value:
+
+ @signature{NatSet.Nonempty.getBelow}
+
+ Get the smallest element above or equal to a given value:
+
+ @signature{NatSet.Nonempty.getAtLeast}
+
+ Get the largest element below or equal to a given value:
+
+ @signature{NatSet.Nonempty.getAtMost}
+
+ # Inserting and removing elements
+
+ Insert a value into a set:
+
+ @signature{NatSet.Nonempty.insert}
+
+ Remove a value from a set:
+
+ @signature{NatSet.Nonempty.delete}
+
+ # Combining sets
+
+ Union of two sets:
+
+ @signature{NatSet.Nonempty.union}
+
+ Union of a {type List.Nonempty} of sets:
+
+ @signature{NatSet.Nonempty.unions}
+
+ Intersection of two sets:
+
+ @signature{NatSet.Nonempty.intersect}
+
+ Difference between two sets:
+
+ @signature{NatSet.Nonempty.difference}
+
+ # Transforming sets
+
+ Apply function to every element of a set:
+
+ @signature{NatSet.Nonempty.map}
+
+ Apply function to every element of a set, removing elements for which the
+ function returns {None}:
+
+ @signature{NatSet.Nonempty.filterMap}
+
+ Change the membership of a key in a set:
+
+ @signature{NatSet.Nonempty.alter}
+
+ # Partitioning and filtering
+
+ Partition a set into two sets based on a predicate:
+
+ @signature{NatSet.Nonempty.partition}
+
+ Filter a set based on a predicate:
+
+ @signature{NatSet.Nonempty.filter}
+
+ Split a set into two sets around a pivot value:
+
+ @signature{NatSet.Nonempty.split}
+
+ Split a set into two sets around a pivot value, returning whether the pivot
+ value was in the set:
+
+ @signature{Nonempty.splitContains}
+
+ # Summarizing sets
+
+ Summarize a set with a left-associative function:
+
+ @signature{NatSet.Nonempty.foldLeft}
+
+ Summarize a set with a right-associative function:
+
+ @signature{NatSet.Nonempty.foldRight}
+
+ Summarize a set by applying a function to each element and combining the
+ results with a binary function:
+
+ @signature{NatSet.Nonempty.foldMap}
+
+ # Comparing sets
+
+ Check if two sets are equal:
+
+ @signature{==}
+
+ Check if a set is a subset of another set:
+
+ @signature{NatSet.Nonempty.subset}
+
+ Check if a set is a superset of another set:
+
+ @signature{NatSet.Nonempty.superset}
+
+ Check if a set is a proper subset of another set:
+
+ @signature{Nonempty.properSubset}
+
+ Check if a set is a proper superset of another set:
+
+ @signature{Nonempty.properSuperset}
+
+ Compare two sets for subset ordering:
+
+ @signature{Nonempty.subsetCompare}
+
+ Order two sets lexicographically:
+
+ @signature{Nonempty.ordering}
+
+ Check if two sets have any elements in common:
+
+ @signature{Nonempty.disjoint}
+
+ # Converting sets
+
+ Get the elements of a set as a {type List.Nonempty}, in ascending order:
+
+ @signature{Nonempty.toListAscending}
+
+ Get the elements of a set as a {type List.Nonempty}, in descending order:
+
+ @signature{Nonempty.toListDescending}
+
+ # Minimum and maximum elements
+
+ Get the smallest element in a set:
+
+ @signature{NatSet.Nonempty.getMin}
+
+ Get the largest element in a set:
+
+ @signature{NatSet.Nonempty.getMax}
+
+ Break off the smallest element in a set:
+
+ @signature{NatSet.Nonempty.minView}
+
+ Break off the largest element in a set:
+
+ @signature{NatSet.Nonempty.maxView}
+
+ Remove the smallest element in a set:
+
+ @signature{NatSet.Nonempty.deleteMin}
+
+ Remove the largest element in a set:
+
+ @signature{NatSet.Nonempty.deleteMax}
+ }}
+
+data.NatSet.Nonempty.equals.doc : Doc
+data.NatSet.Nonempty.equals.doc =
+ use NatSet.Nonempty fromList
+ {{
+ Checks if two {type NatSet.Nonempty} are equal.
+
+ Two {type NatSet.Nonempty} are equal if they contain the same elements.
+
+ # Example
+
+ ```
+ NatSet.Nonempty.equals
+ (fromList (1 +| [2, 3, 4])) (fromList (1 +| [2, 3, 4]))
+ ```
+
+ # See also
+
+ * {Nonempty.ordering} to compare two {type NatSet.Nonempty}
+ lexicographically.
+ * {Nonempty.subsetCompare} to compare two {type NatSet.Nonempty} for subset
+ ordering.
+ * {NatSet.Nonempty.subset} to check if one {type NatSet.Nonempty} is a
+ subset of another.
+ * {Nonempty.disjoint} to check if two {type NatSet.Nonempty} have no
+ elements in common.
+ }}
+
+data.NatSet.Nonempty.filter :
+ (Nat ->{g} Boolean) -> NatSet.Nonempty ->{g} NatSet
+data.NatSet.Nonempty.filter f = cases
+ NatSet.Nonempty.Bin p m _ l r ->
+ NatSet.internal.bin
+ p m (data.NatSet.Nonempty.filter f l) (data.NatSet.Nonempty.filter f r)
+ NatSet.Nonempty.Tip p m -> tip p (foldBitsLeft 0 (bitPred f p) 0 m)
+
+data.NatSet.Nonempty.filter.doc : Doc
+data.NatSet.Nonempty.filter.doc =
+ {{
+ Filters a {type NatSet.Nonempty} by a predicate. Returns a new set containing
+ only the elements for which the predicate returns ``true``.
+
+ # Example
+
+ ```
+ NatSet.toList
+ (NatSet.Nonempty.filter
+ Nat.isEven (NatSet.Nonempty.fromList (1 +| [2, 3, 4])))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.partition} to split a set into two sets, one containing
+ all elements that satisfy the predicate, and one containing all elements
+ that do not.
+ * {NatSet.Nonempty.filterMap} to filter a set by a function that returns an
+ {type Optional} value.
+ * {NatSet.Nonempty.map} to apply a function to every element in a set, and
+ return a new set with the results.
+ }}
+
+data.NatSet.Nonempty.filterMap :
+ (Nat ->{g} Optional Nat) ->{g} NatSet.Nonempty ->{g} NatSet
+data.NatSet.Nonempty.filterMap f =
+ NatSet.Nonempty.foldLeft
+ (acc n -> (match f n with
+ Some n' -> NatSet (Some (NatSet.insert.nonempty n' acc))
+ None -> acc)) NatSet.empty
+
+data.NatSet.Nonempty.filterMap.doc : Doc
+data.NatSet.Nonempty.filterMap.doc =
+ use Nat *
+ {{
+ Filters a {type NatSet.Nonempty} by a function that returns an
+ {type Optional} value. Returns a new set containing only the elements for
+ which the function returns ``Some``.
+
+ # Example
+
+ ```
+ NatSet.toList
+ (NatSet.Nonempty.filterMap
+ (n -> (if Nat.isEven n then Some (n * 2) else None))
+ (NatSet.Nonempty.fromList (1 +| [2, 3, 4])))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.filter} to filter by a predicate that returns a
+ {type Boolean}.
+ * {NatSet.Nonempty.partition} to split a set into two sets, one containing
+ all elements that satisfy the predicate, and one containing all elements
+ that do not.
+ * {NatSet.Nonempty.map} to apply a function to every element in a set, and
+ return a new set with the results.
+ }}
+
+data.NatSet.Nonempty.foldLeft :
+ (a ->{g} Nat ->{g} a) -> a ->{g} NatSet.Nonempty ->{g} a
+data.NatSet.Nonempty.foldLeft f z =
+ go z' = cases
+ NatSet.Nonempty.Tip p m -> foldBitsLeft p f z' m
+ NatSet.Nonempty.Bin p m _ l r -> go (go z' l) r
+ go z
+
+data.NatSet.Nonempty.foldLeft.doc : Doc
+data.NatSet.Nonempty.foldLeft.doc =
+ use Nat +
+ use NatSet.Nonempty foldLeft fromList
+ use Text ++
+ {{
+ `` foldLeft f z s `` summarizes a set `s` by starting with the value `z` and
+ applying the binary function `f` to each element of `s` with the result so
+ far, associating to the left.
+
+ # Examples
+
+ Sum all the values in the set:
+
+ ```
+ foldLeft (+) 0 (fromList (1 +| [2, 3, 4]))
+ ```
+
+ Concatenate the {type Text} values of all the elements in the set:
+
+ ```
+ foldLeft (acc n -> acc ++ Nat.toText n) "" (fromList (1 +| [2, 3, 4]))
+ ```
+
+ Find the maximum element in the set:
+
+ ```
+ foldLeft Nat.max 0 (fromList (1 +| [2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.foldRight} to associate to the right.
+ * {NatSet.Nonempty.foldMap} to apply a function to every element and
+ combine the results with a binary function.
+ * {NatSet.Nonempty.map} to apply a function to every element.
+ * {NatSet.Nonempty.filter} to filter out elements that don't satisfy a
+ predicate.
+ * {NatSet.Nonempty.filterMap} to apply a function to every element and
+ filter out the results that are {None}.
+ }}
+
+data.NatSet.Nonempty.foldMap :
+ (a ->{g} a ->{g} a) -> (Nat ->{g} a) -> NatSet.Nonempty ->{g} a
+data.NatSet.Nonempty.foldMap f g = cases
+ NatSet.Nonempty.Bin p m _ l r ->
+ f (data.NatSet.Nonempty.foldMap f g l) (data.NatSet.Nonempty.foldMap f g r)
+ NatSet.Nonempty.Tip p m ->
+ use Nat +
+ first = Nat.trailingZeros m
+ firstMask = Nat.shiftLeft 1 first
+ foldBitsLeft
+ p
+ (a n -> f a (g n))
+ (g (first + p))
+ (Nat.and m (Nat.complement firstMask))
+
+data.NatSet.Nonempty.foldMap.doc : Doc
+data.NatSet.Nonempty.foldMap.doc =
+ use Nat +
+ use NatSet.Nonempty foldMap foldRight fromList
+ use Text ++
+ {{
+ `` foldMap f g s `` applies the function `g` to every element of the set `s`
+ and combines the results with the binary function `f`.
+
+ # Examples
+
+ Sum all the values in the set:
+
+ ```
+ foldMap (+) id (fromList (1 +| [2, 3, 4]))
+ ```
+
+ Concatenate the {type Text} values of all the elements in the set:
+
+ ```
+ foldMap (++) Nat.toText (fromList (1 +| [2, 3, 4]))
+ ```
+
+ # See also
+
+ * {foldRight} to accumulate results of a single binary function applied to
+ every element and the result so far, associating to the right.
+ * {NatSet.Nonempty.foldLeft} same as {foldRight} but associating to the
+ left.
+ * {NatSet.Nonempty.map} to apply a function to every element without
+ combining the results.
+ * {NatSet.Nonempty.filter} to filter out elements that don't satisfy a
+ predicate.
+ * {NatSet.Nonempty.filterMap} to apply a function to every element and
+ filter out the results that are {None}.
+ }}
+
+data.NatSet.Nonempty.foldRight :
+ (Nat ->{g} a ->{g} a) -> a ->{g} NatSet.Nonempty ->{g} a
+data.NatSet.Nonempty.foldRight f z =
+ go z' = cases
+ NatSet.Nonempty.Tip p m -> foldBitsRight p f z' m
+ NatSet.Nonempty.Bin p m _ l r -> go (go z' r) l
+ go z
+
+data.NatSet.Nonempty.foldRight.doc : Doc
+data.NatSet.Nonempty.foldRight.doc =
+ use Nat +
+ use NatSet.Nonempty foldRight fromList
+ use Text ++
+ {{
+ `` foldRight f z s `` summarizes a set `s` by starting with the value `z` and
+ applying the binary function `f` to each element of `s` with the result so
+ far, associating to the right.
+
+ # Examples
+
+ Sum all the values in the set:
+
+ ```
+ foldRight (+) 0 (fromList (1 +| [2, 3, 4]))
+ ```
+
+ Concatenate the {type Text} values of all the elements in the set:
+
+ ```
+ foldRight (n acc -> Nat.toText n ++ acc) "" (fromList (1 +| [2, 3, 4]))
+ ```
+
+ Find the maximum element in the set:
+
+ ```
+ foldRight Nat.max 0 (fromList (1 +| [2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.foldLeft} to associate to the left.
+ * {NatSet.Nonempty.foldMap} to apply a function to every element and
+ combine the results with a binary function.
+ * {NatSet.Nonempty.map} to apply a function to every element.
+ * {NatSet.Nonempty.filter} to filter out elements that don't satisfy a
+ predicate.
+ * {NatSet.Nonempty.filterMap} to apply a function to every element and
+ filter out the results that are {None}.
+ }}
+
+data.NatSet.Nonempty.fromList : List.Nonempty Nat -> NatSet.Nonempty
+data.NatSet.Nonempty.fromList =
+ List.Nonempty.foldMap NatSet.Nonempty.union NatSet.Nonempty.singleton
+
+data.NatSet.Nonempty.fromList.doc : Doc
+data.NatSet.Nonempty.fromList.doc =
+ use Nonempty toListAscending
+ {{
+ Converts a {type List.Nonempty} of {type Nat} to a {type NatSet.Nonempty}.
+
+ # Example
+
+ ```
+ toListAscending (NatSet.Nonempty.fromList (1 +| [2, 3, 4]))
+ ```
+
+ # See also
+
+ * {toListAscending} to convert the other way.
+ * {NatSet.Nonempty.singleton} to create a {type NatSet.Nonempty} with a
+ single element.
+ }}
+
+data.NatSet.Nonempty.getAbove : Nat -> NatSet.Nonempty -> Optional Nat
+data.NatSet.Nonempty.getAbove k = cases
+ NatSet.Nonempty.Bin p m _ l r
+ | nomatch k p m ->
+ if k Nat.> p then None else Some (NatSet.Nonempty.getMin l)
+ | internal.zero k m ->
+ match data.NatSet.Nonempty.getAbove k l with
+ Some x -> Some x
+ None -> data.NatSet.Nonempty.getAbove k r
+ | otherwise -> data.NatSet.Nonempty.getAbove k r
+ NatSet.Nonempty.Tip p bm
+ | prefixOf k Nat.< p -> Some (p Nat.+ Nat.trailingZeros bm)
+ | otherwise ->
+ use Nat != + ==
+ maskGT = Nat.and bm (twosComplement (Nat.shiftLeft (bitmapOf k) 1))
+ if prefixOf k == p && maskGT != 0 then
+ Some (p + Nat.trailingZeros maskGT)
+ else None
+
+data.NatSet.Nonempty.getAbove.doc : Doc
+data.NatSet.Nonempty.getAbove.doc =
+ use NatSet.Nonempty fromList getAbove
+ {{
+ Returns the smallest element in a {type NatSet.Nonempty} that is larger than
+ a given one.
+
+ Returns {None} if there is no such element.
+
+ # Examples
+
+ ```
+ getAbove 2 (fromList (1 +| [2, 3]))
+ ```
+
+ ```
+ getAbove 4 (fromList (1 +| [2, 3]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.getBelow} to get the largest element that is smaller
+ than the key.
+ * {NatSet.Nonempty.getAtMost} to get the largest element that is smaller
+ than or equal to the key.
+ * {NatSet.Nonempty.getAtLeast} to get the smallest element that is larger
+ than or equal to the key.
+ * {NatSet.Nonempty.getMax} to get the largest element.
+ * {NatSet.Nonempty.getMin} to get the smallest element.
+ }}
+
+test> data.NatSet.Nonempty.getAbove.test = test.verify do
+ use Nat < > >=
+ use Random nat
+ _ = Each.range 0 100
+ xs = Random.listOf nat do Random.natIn 1 100
+ s = NatSet.Nonempty.fromList (Abort.toBug do List.nonempty xs)
+ n = nat()
+ match NatSet.Nonempty.getAbove n s with
+ Some x ->
+ ensure (x > n)
+ ys = List.filter (y -> y > n) xs
+ ensure (List.all (y -> y >= x) ys)
+ None -> ensure (NatSet.Nonempty.getMax s < n)
+
+data.NatSet.Nonempty.getAtLeast : Nat -> NatSet.Nonempty -> Optional Nat
+data.NatSet.Nonempty.getAtLeast k = cases
+ NatSet.Nonempty.Bin p m _ l r
+ | nomatch k p m ->
+ if k Nat.> p then None else Some (NatSet.Nonempty.getMin l)
+ | internal.zero k m ->
+ match data.NatSet.Nonempty.getAtLeast k l with
+ Some x -> Some x
+ None -> data.NatSet.Nonempty.getAtLeast k r
+ | otherwise -> data.NatSet.Nonempty.getAtLeast k r
+ NatSet.Nonempty.Tip p bm
+ | prefixOf k Nat.< p -> Some (p Nat.+ Nat.trailingZeros bm)
+ | otherwise ->
+ use Nat != + ==
+ maskGE = Nat.and bm (twosComplement (bitmapOf k))
+ if prefixOf k == p && maskGE != 0 then
+ Some (p + Nat.trailingZeros maskGE)
+ else None
+
+data.NatSet.Nonempty.getAtLeast.doc : Doc
+data.NatSet.Nonempty.getAtLeast.doc =
+ use NatSet.Nonempty fromList getAtLeast
+ {{
+ Returns the smallest element in a {type NatSet.Nonempty} that is larger than
+ or equal to a given one.
+
+ Returns {None} if there is no such element.
+
+ # Examples
+
+ ```
+ getAtLeast 2 (fromList (1 +| [2, 3]))
+ ```
+
+ ```
+ getAtLeast 4 (fromList (1 +| [2, 3]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.getBelow} to get the largest element that is strictly
+ smaller than the key.
+ * {NatSet.Nonempty.getAbove} to get the smallest element that is larger
+ than the key.
+ * {NatSet.Nonempty.getAtMost} to get the largest element that is smaller
+ than or equal to the key.
+ * {NatSet.Nonempty.getMax} to get the largest element.
+ * {NatSet.Nonempty.getMin} to get the smallest element.
+ }}
+
+test> data.NatSet.Nonempty.getAtLeast.test = test.verify do
+ use Nat < >=
+ use Random nat
+ _ = Each.range 0 100
+ xs = Random.listOf nat do Random.natIn 1 100
+ s = NatSet.Nonempty.fromList (Abort.toBug do List.nonempty xs)
+ n = nat()
+ match NatSet.Nonempty.getAtLeast n s with
+ Some x ->
+ ensure (x >= n)
+ ys = List.filter (y -> y >= n) xs
+ ensure (List.all (y -> y >= x) ys)
+ None -> ensure (NatSet.Nonempty.getMax s < n)
+
+data.NatSet.Nonempty.getAtMost : Nat -> NatSet.Nonempty -> Optional Nat
+data.NatSet.Nonempty.getAtMost k = cases
+ NatSet.Nonempty.Bin p m _ l r
+ | nomatch k p m ->
+ if k Nat.< p then None else Some (NatSet.Nonempty.getMax r)
+ | internal.zero k m -> data.NatSet.Nonempty.getAtMost k l
+ | otherwise ->
+ match data.NatSet.Nonempty.getAtMost k r with
+ Some x -> Some x
+ None -> data.NatSet.Nonempty.getAtMost k l
+ NatSet.Nonempty.Tip p bm
+ | prefixOf k Nat.> p -> Some (p Nat.+ highBit bm)
+ | otherwise ->
+ use Nat != + - ==
+ maskLE = Nat.and bm (Nat.shiftLeft (bitmapOf k) 1 - 1)
+ if prefixOf k == p && maskLE != 0 then Some (p + highBit maskLE)
+ else None
+
+data.NatSet.Nonempty.getAtMost.doc : Doc
+data.NatSet.Nonempty.getAtMost.doc =
+ use NatSet.Nonempty fromList getAtMost
+ {{
+ Returns the largest element in a {type NatSet.Nonempty} that is smaller than
+ or equal to a given one.
+
+ Returns {None} if there is no such element.
+
+ # Examples
+
+ ```
+ getAtMost 2 (fromList (1 +| [2, 3]))
+ ```
+
+ ```
+ getAtMost 4 (fromList (1 +| [2, 3]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.getBelow} to get the largest element that is strictly
+ smaller than the key.
+ * {NatSet.Nonempty.getAbove} to get the smallest element that is larger
+ than the key.
+ * {NatSet.Nonempty.getAtLeast} to get the smallest element that is larger
+ than or equal to the key.
+ * {NatSet.Nonempty.getMax} to get the largest element.
+ * {NatSet.Nonempty.getMin} to get the smallest element.
+ }}
+
+test> data.NatSet.Nonempty.getAtMost.test = test.verify do
+ use Nat <= >
+ use Random nat
+ _ = Each.range 0 100
+ xs = Random.listOf nat do Random.natIn 1 100
+ s = NatSet.Nonempty.fromList (Abort.toBug do List.nonempty xs)
+ n = nat()
+ match NatSet.Nonempty.getAtMost n s with
+ Some x ->
+ ensure (x <= n)
+ ys = List.filter (y -> y <= n) xs
+ ensure (List.all (y -> y <= x) ys)
+ None -> ensure (NatSet.Nonempty.getMin s > n)
+
+data.NatSet.Nonempty.getBelow : Nat -> NatSet.Nonempty -> Optional Nat
+data.NatSet.Nonempty.getBelow k = cases
+ NatSet.Nonempty.Bin p m _ l r
+ | nomatch k p m ->
+ if k Nat.< p then None else Some (NatSet.Nonempty.getMax r)
+ | internal.zero k m -> data.NatSet.Nonempty.getBelow k l
+ | otherwise ->
+ match data.NatSet.Nonempty.getBelow k r with
+ Some x -> Some x
+ None -> data.NatSet.Nonempty.getBelow k l
+ NatSet.Nonempty.Tip p bm
+ | prefixOf k Nat.> p -> Some (p Nat.+ highBit bm)
+ | otherwise ->
+ use Nat != + - ==
+ maskLT = Nat.and bm (bitmapOf k - 1)
+ if prefixOf k == p && maskLT != 0 then Some (p + highBit maskLT)
+ else None
+
+data.NatSet.Nonempty.getBelow.doc : Doc
+data.NatSet.Nonempty.getBelow.doc =
+ use NatSet.Nonempty fromList getBelow
+ {{
+ Returns the largest element in a {type NatSet.Nonempty} that is smaller than
+ a given one.
+
+ Returns {None} if there is no such element.
+
+ # Examples
+
+ ```
+ getBelow 2 (fromList (1 +| [2, 3]))
+ ```
+
+ ```
+ getBelow 4 (fromList (1 +| [2, 3]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.getAbove} to get the smallest element in a
+ {type NatSet.Nonempty} that is larger than the key.
+ * {NatSet.Nonempty.getAtMost} to get the largest element in a
+ {type NatSet.Nonempty} that is smaller than or equal to the key.
+ * {NatSet.Nonempty.getAtLeast} to get the smallest element in a
+ {type NatSet.Nonempty} that is larger than or equal to the key.
+ * {NatSet.Nonempty.getMax} to get the largest element in a
+ {type NatSet.Nonempty}.
+ * {NatSet.Nonempty.getMin} to get the smallest element in a
+ {type NatSet.Nonempty}.
+ }}
+
+test> data.NatSet.Nonempty.getBelow.test = test.verify do
+ use Nat < <= >=
+ use Random nat
+ _ = Each.range 0 100
+ xs = Random.listOf nat do Random.natIn 1 100
+ s = NatSet.Nonempty.fromList (Abort.toBug do List.nonempty xs)
+ n = nat()
+ match NatSet.Nonempty.getBelow n s with
+ Some x ->
+ ensure (x < n)
+ ys = List.filter (y -> y < n) xs
+ ensure (List.all (y -> y <= x) ys)
+ None -> ensure (NatSet.Nonempty.getMin s >= n)
+
+data.NatSet.Nonempty.getMax : NatSet.Nonempty -> Nat
+data.NatSet.Nonempty.getMax = cases
+ NatSet.Nonempty.Tip p bm -> p Nat.+ highBit bm
+ NatSet.Nonempty.Bin p m _ l r -> data.NatSet.Nonempty.getMax r
+
+data.NatSet.Nonempty.getMax.doc : Doc
+data.NatSet.Nonempty.getMax.doc =
+ {{
+ Returns the largest element in a {type NatSet.Nonempty}.
+
+ # Example
+
+ ```
+ NatSet.Nonempty.getMax (NatSet.Nonempty.fromList (1 +| [2, 3]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.getMin} to get the smallest element.
+ * {NatSet.Nonempty.getBelow} to get the largest element that is strictly
+ smaller than a given key.
+ * {NatSet.Nonempty.getAbove} to get the smallest element that is larger
+ than a given key.
+ * {NatSet.Nonempty.getAtMost} to get the largest element that is smaller
+ than or equal to a given key.
+ * {NatSet.Nonempty.getAtLeast} to get the smallest element that is larger
+ than or equal to a given key.
+ }}
+
+test> data.NatSet.Nonempty.getMax.test = test.verify do
+ use Nat <=
+ _ = Each.range 0 100
+ xs = Random.listOf Random.nat do Random.natIn 1 100
+ s = NatSet.Nonempty.fromList (Abort.toBug do List.nonempty xs)
+ x = NatSet.Nonempty.getMax s
+ ensure (List.all (y -> y <= x) xs)
+
+data.NatSet.Nonempty.getMin : NatSet.Nonempty -> Nat
+data.NatSet.Nonempty.getMin = cases
+ NatSet.Nonempty.Tip p bm -> p Nat.+ Nat.trailingZeros bm
+ NatSet.Nonempty.Bin p m _ l r -> data.NatSet.Nonempty.getMin l
+
+data.NatSet.Nonempty.getMin.doc : Doc
+data.NatSet.Nonempty.getMin.doc =
+ {{
+ Returns the smallest element in a {type NatSet.Nonempty}.
+
+ # Example
+
+ ```
+ NatSet.Nonempty.getMin (NatSet.Nonempty.fromList (1 +| [2, 3]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.getMax} to get the largest element.
+ * {NatSet.Nonempty.getBelow} to get the largest element that is strictly
+ smaller than a given key.
+ * {NatSet.Nonempty.getAbove} to get the smallest element that is larger
+ than a given key.
+ * {NatSet.Nonempty.getAtMost} to get the largest element that is smaller
+ than or equal to a given key.
+ * {NatSet.Nonempty.getAtLeast} to get the smallest element that is larger
+ than or equal to a given key.
+ }}
+
+test> data.NatSet.Nonempty.getMin.test = test.verify do
+ use Nat >=
+ _ = Each.range 0 100
+ xs = Random.listOf Random.nat do Random.natIn 1 100
+ s = NatSet.Nonempty.fromList (Abort.toBug do List.nonempty xs)
+ x = NatSet.Nonempty.getMin s
+ ensure (List.all (y -> y >= x) xs)
+
+data.NatSet.Nonempty.insert : Nat -> NatSet.Nonempty -> NatSet.Nonempty
+data.NatSet.Nonempty.insert k = insertBitmap (prefixOf k) (bitmapOf k)
+
+data.NatSet.Nonempty.insert.doc : Doc
+data.NatSet.Nonempty.insert.doc =
+ use NatSet.Nonempty fromList
+ {{
+ Inserts an element into a {type NatSet.Nonempty}.
+
+ # Example
+
+ ```
+ Nonempty.toListAscending (NatSet.Nonempty.insert 1 (fromList (2 +| [3])))
+ ```
+
+ # See also
+
+ * {NatSet.insert.nonempty} to insert an element into a {type NatSet}.
+ * {NatSet.union} to add a whole {type NatSet} into another.
+ * {NatSet.Nonempty.delete} to delete an element from a
+ {type NatSet.Nonempty}.
+ * {fromList} to create a {type NatSet.Nonempty} from a {type List.Nonempty}
+ of elements.
+ * {NatSet.Nonempty.singleton} to create a {type NatSet.Nonempty} with a
+ single element.
+ }}
+
+test> data.NatSet.Nonempty.insert.test.containsInserted = test.verify do
+ use Random nat
+ _ = Each.range 0 100
+ xs = Random.listOf nat do Random.natIn 1 100
+ n = nat()
+ s = NatSet.Nonempty.fromList (Abort.toBug do List.nonempty xs)
+ ensure (NatSet.Nonempty.contains n (NatSet.Nonempty.insert n s))
+
+data.NatSet.Nonempty.internal.deleteBitmap :
+ Nat -> Nat -> NatSet.Nonempty -> NatSet
+data.NatSet.Nonempty.internal.deleteBitmap prefix bitmap = cases
+ t@(NatSet.Nonempty.Bin p m _ l r)
+ | nomatch prefix p m -> NatSet (Some t)
+ | internal.zero prefix m ->
+ NatSet.internal.bin
+ p
+ m
+ (data.NatSet.Nonempty.internal.deleteBitmap prefix bitmap l)
+ (NatSet (Some r))
+ | otherwise ->
+ NatSet.internal.bin
+ p
+ m
+ (NatSet (Some l))
+ (data.NatSet.Nonempty.internal.deleteBitmap prefix bitmap r)
+ t@(NatSet.Nonempty.Tip p bm)
+ | prefix Nat.== p -> tip p (Nat.and bm (Nat.complement bitmap))
+ | otherwise -> NatSet (Some t)
+
+data.NatSet.Nonempty.internal.deleteBitmap.doc : Doc
+data.NatSet.Nonempty.internal.deleteBitmap.doc =
+ {{
+ Internal function to delete a bitmap from a {type NatSet.Nonempty} tree.
+ }}
+
+data.NatSet.Nonempty.internal.insertBitmap :
+ Nat -> Nat -> NatSet.Nonempty -> NatSet.Nonempty
+data.NatSet.Nonempty.internal.insertBitmap prefix bitmap = cases
+ t@(NatSet.Nonempty.Bin p m _ l r)
+ | nomatch prefix p m ->
+ NatSet.internal.link prefix (NatSet.Nonempty.Tip prefix bitmap) p t
+ | internal.zero prefix m ->
+ NatSet.internal.bim
+ p m (data.NatSet.Nonempty.internal.insertBitmap prefix bitmap l) r
+ | otherwise ->
+ NatSet.internal.bim
+ p m l (data.NatSet.Nonempty.internal.insertBitmap prefix bitmap r)
+ t@(NatSet.Nonempty.Tip p bm)
+ | prefix Nat.== p -> NatSet.Nonempty.Tip p (Nat.or bm bitmap)
+ | otherwise ->
+ NatSet.internal.link prefix (NatSet.Nonempty.Tip prefix bitmap) p t
+
+data.NatSet.Nonempty.internal.insertBitmap.doc : Doc
+data.NatSet.Nonempty.internal.insertBitmap.doc =
+ {{
+ Internal function to insert a bitmap into a {type NatSet.Nonempty} tree.
+ }}
+
+data.NatSet.Nonempty.intersect : NatSet.Nonempty -> NatSet.Nonempty -> NatSet
+data.NatSet.Nonempty.intersect = cases
+ t1@(NatSet.Nonempty.Bin p1 m1 _ l1 r1),
+ t2@(NatSet.Nonempty.Bin p2 m2 _ l2 r2)
+ | shorter m1 m2 ->
+ if nomatch p2 p1 m1 then NatSet None
+ else
+ if internal.zero p2 m1 then data.NatSet.Nonempty.intersect l1 t2
+ else data.NatSet.Nonempty.intersect r1 t2
+ | shorter m2 m1 ->
+ if nomatch p1 p2 m2 then NatSet None
+ else
+ if internal.zero p1 m2 then data.NatSet.Nonempty.intersect t1 l2
+ else data.NatSet.Nonempty.intersect t1 r2
+ | p1 Nat.== p2 ->
+ NatSet.internal.bin
+ p1
+ m1
+ (data.NatSet.Nonempty.intersect l1 l2)
+ (data.NatSet.Nonempty.intersect r1 r2)
+ | otherwise -> NatSet None
+ t@(NatSet.Nonempty.Bin _ _ _ _ _), NatSet.Nonempty.Tip p m ->
+ intersectBitmap p m t
+ NatSet.Nonempty.Tip p m, t2 -> intersectBitmap p m t2
+
+data.NatSet.Nonempty.intersect.doc : Doc
+data.NatSet.Nonempty.intersect.doc =
+ use NatSet.Nonempty fromList
+ {{
+ Removes all elements from one {type NatSet.Nonempty} that are not in another.
+
+ Returns a {type NatSet} containing all elements that are present in both
+ inputs.
+
+ # Example
+
+ ```
+ NatSet.toList
+ (NatSet.Nonempty.intersect (fromList (1 +| [2])) (fromList (2 +| [3])))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.union} to return elements in __either__ input.
+ * {NatSet.Nonempty.difference} to return elements in the first input but
+ not the second.
+ * {NatSet.Nonempty.delete} to remove one element from a
+ {type NatSet.Nonempty}.
+ }}
+
+data.NatSet.Nonempty.map :
+ (Nat ->{g} Nat) -> NatSet.Nonempty ->{g} NatSet.Nonempty
+data.NatSet.Nonempty.map f =
+ NatSet.Nonempty.foldMap
+ NatSet.Nonempty.union (NatSet.Nonempty.singleton << f)
+
+data.NatSet.Nonempty.map.doc : Doc
+data.NatSet.Nonempty.map.doc =
+ use Nat *
+ {{
+ Applies a function to every element in a {type NatSet.Nonempty}.
+
+ # Example
+
+ ```
+ Nonempty.toListAscending
+ (NatSet.Nonempty.map ((*) 2) (NatSet.Nonempty.fromList (1 +| [2, 3, 4])))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.foldMap} to apply a function to every element and
+ combine the results with a binary function.
+ * {NatSet.Nonempty.filterMap} to apply a function to every element and
+ filter out the results that are {None}.
+ * {NatSet.Nonempty.filter} to filter out elements that don't satisfy a
+ predicate.
+ * {NatSet.Nonempty.foldLeft} and {NatSet.Nonempty.foldRight} to summarize
+ the elements of a set.
+ }}
+
+data.NatSet.Nonempty.maxView : NatSet.Nonempty -> (Nat, NatSet)
+data.NatSet.Nonempty.maxView = cases
+ NatSet.Nonempty.Bin p m _ l r ->
+ (x, r') = data.NatSet.Nonempty.maxView r
+ (x, NatSet.internal.bin p m (NatSet (Some l)) r')
+ NatSet.Nonempty.Tip p m ->
+ use Nat +
+ bi = highBit m
+ (p + bi, tip p (Nat.and m (Nat.complement (Nat.shiftLeft 1 bi))))
+
+data.NatSet.Nonempty.maxView.doc : Doc
+data.NatSet.Nonempty.maxView.doc =
+ {{
+ Returns the maximum element in a {type NatSet.Nonempty}, and the set without
+ that element.
+
+ # Example
+
+ ```
+ Tuple.second
+ NatSet.toList
+ (NatSet.Nonempty.maxView (NatSet.Nonempty.fromList (1 +| [2, 3, 4])))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.minView} to get the minimum element and the set without
+ that element.
+ * {NatSet.Nonempty.getMax} to just get the maximum element.
+ * {NatSet.Nonempty.deleteMax} to get the set without the maximum element.
+ }}
+
+data.NatSet.Nonempty.minView : NatSet.Nonempty -> (Nat, NatSet)
+data.NatSet.Nonempty.minView = cases
+ NatSet.Nonempty.Bin p m _ l r ->
+ (x, l') = data.NatSet.Nonempty.minView l
+ (x, NatSet.internal.bin p m l' (NatSet (Some r)))
+ NatSet.Nonempty.Tip p m ->
+ use Nat +
+ bi = Nat.trailingZeros m
+ (p + bi, tip p (Nat.and m (Nat.complement (Nat.shiftLeft 1 bi))))
+
+data.NatSet.Nonempty.minView.doc : Doc
+data.NatSet.Nonempty.minView.doc =
+ {{
+ Returns the minimum element in a {type NatSet.Nonempty}, and the set without
+ that element.
+
+ # Example
+
+ ```
+ Tuple.second
+ NatSet.toList
+ (NatSet.Nonempty.minView (NatSet.Nonempty.fromList (1 +| [2, 3, 4])))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.maxView} to get the maximum element and the set without
+ that element.
+ * {NatSet.Nonempty.getMin} to just get the minimum element.
+ * {NatSet.Nonempty.deleteMin} to get the set without the minimum element.
+ }}
+
+data.NatSet.Nonempty.nth : Nat -> NatSet.Nonempty -> Optional Nat
+data.NatSet.Nonempty.nth index = cases
+ NatSet.Nonempty.Tip p bm ->
+ if index Nat.< Nat.popCount bm then
+ use Nat + - trailingZeros
+ loop : Nat -> Nat -> Nat -> Nat
+ loop bm count acc = match count with
+ 0 -> acc + trailingZeros bm
+ otherwise ->
+ zeros = trailingZeros bm
+ newBM = Nat.shiftRight bm (zeros + 1)
+ loop newBM (count - 1) (acc + zeros + 1)
+ Some (p + loop bm index 0)
+ else None
+ NatSet.Nonempty.Bin prefix mask sz l r ->
+ use Nat - <
+ use data.NatSet.Nonempty nth
+ leftSize = NatSet.Nonempty.size l
+ if index < leftSize then nth index l else nth (index - leftSize) r
+
+data.NatSet.Nonempty.nth.doc : Doc
+data.NatSet.Nonempty.nth.doc =
+ use NatSet.Nonempty nth
+ {{
+ {{ docExample 2 do i s -> nth i s }} returns the `i`-th smallest value in
+ `s`, where `i`=0 is the smallest value (according to {Universal.ordering}).
+
+ Is the same as {{
+ docExample 2 do i as -> List.at i (List.sort (NatSet.toList as)) }} but
+ doesn't require instantiating the intermediate {type List}.
+
+ ```
+ s = NatSet.Nonempty.fromList (Nonempty.Nonempty 6 [5, 4, 2, 1])
+ List.map (i -> nth i s) (List.range 0 (NatSet.Nonempty.size s))
+ ```
+ }}
+
+test> data.NatSet.Nonempty.nth.tests =
+ test.verify do
+ use Random natIn
+ Each.repeat 100
+ s =
+ natIn 0 10 +| (List.replicate (natIn 0 19) do natIn 0 10)
+ |> NatSet.Nonempty.fromList
+ ensure
+ (List.somes
+ (List.map
+ (i -> NatSet.Nonempty.nth i s)
+ (List.range 0 (NatSet.Nonempty.size s)))
+ === (Nonempty.toListAscending s |> List.Nonempty.toList))
+
+data.NatSet.Nonempty.ordering : NatSet.Nonempty -> NatSet.Nonempty -> Ordering
+data.NatSet.Nonempty.ordering m1 m2 =
+ use Nat + - < <= == > >= and
+ use NatSet.Nonempty Bin Tip getMax size split
+ use Universal ordering
+ go = cases
+ Tip p1 m1, Tip p2 m2 ->
+ Ordering.andThen
+ (ordering p1 p2) (if Nat.isPrefixOf m1 m2 then Equal
+ else
+ diff = Nat.xor m1 m2
+ lowest = and diff (twosComplement diff)
+ if and lowest m2 == 0 then Less else Greater)
+ t1@(Bin p1 mask1 sz1 l1 r1), t2@(Bin p2 mask2 sz2 l2 r2) ->
+ largest1 = p1 + mask1 - 1
+ largest2 = p2 + mask2 - 1
+ if largest1 < p2 then Less
+ else
+ if largest2 < p1 then Greater
+ else
+ match go l1 l2 with
+ Equal
+ | size l1 < size l2 ->
+ match split (getMax l1) l2 with
+ (_, NatSet None) -> Equal
+ (_, NatSet (Some l2r)) ->
+ match go r1 l2r with
+ Equal
+ | size r1 <= size l2r -> Less
+ | otherwise ->
+ match split (getMax l2) r1 with
+ (_, NatSet None) -> Equal
+ (_, NatSet (Some r1r)) -> go r1r r2
+ x -> x
+ | size l1 > size l2 ->
+ match split (getMax l2) l1 with
+ (_, NatSet None) -> Equal
+ (_, NatSet (Some l1r)) ->
+ match go l1r r2 with
+ Equal
+ | size l1r >= size r2 -> Greater
+ | otherwise ->
+ match split (getMax l1) r2 with
+ (_, NatSet None) -> Equal
+ (_, NatSet (Some r2r)) -> go r1 r2r
+ x -> x
+ | otherwise -> go r1 r2
+ x -> x
+ t1@(Tip p1 _), Bin p2 m2 _ l2 r2 -> go t1 l2
+ Bin p1 m1 _ l1 r1, t2@(Tip p2 _) -> go l1 t2
+ match go m1 m2 with
+ Equal -> ordering (size m1) (size m2)
+ x -> x
+
+data.NatSet.Nonempty.partition :
+ (Nat ->{g} Boolean) -> NatSet.Nonempty ->{g} (NatSet, NatSet)
+data.NatSet.Nonempty.partition f = cases
+ NatSet.Nonempty.Bin p m _ l r ->
+ (l1, l2) = data.NatSet.Nonempty.partition f l
+ (r1, r2) = data.NatSet.Nonempty.partition f r
+ (NatSet.internal.bin p m l1 r1, NatSet.internal.bin p m l2 r2)
+ NatSet.Nonempty.Tip p m ->
+ bm1 = foldBitsLeft 0 (bitPred f p) 0 m
+ (tip p bm1, tip p (Nat.xor m bm1))
+
+data.NatSet.Nonempty.partition.doc : Doc
+data.NatSet.Nonempty.partition.doc =
+ {{
+ Partitions a {type NatSet.Nonempty} by a predicate. Returns a pair of sets,
+ the first containing all elements that satisfy the predicate, and the second
+ containing all elements that do not.
+
+ # Example
+
+ ```
+ Tuple.bimap
+ NatSet.toList
+ (NatSet.Nonempty.partition
+ Nat.isEven (NatSet.Nonempty.fromList (1 +| [2, 3, 4])))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.filter} to remove all elements that do not satisfy the
+ predicate.
+ * {NatSet.Nonempty.filterMap} to filter a set by a function that returns an
+ {type Optional} value.
+ * {NatSet.Nonempty.map} to apply a function to every element in a set, and
+ return a new set with the results.
+ * {NatSet.Nonempty.split} to split a set based on whether elements are less
+ than or greater than a given element.
+ }}
+
+data.NatSet.Nonempty.properSubset :
+ NatSet.Nonempty -> NatSet.Nonempty -> Boolean
+data.NatSet.Nonempty.properSubset = cases
+ t1, t2 ->
+ match Nonempty.subsetCompare t1 t2 with
+ Some Less -> true
+ _ -> false
+
+data.NatSet.Nonempty.properSubset.doc : Doc
+data.NatSet.Nonempty.properSubset.doc =
+ use NatSet.Nonempty fromList
+ use Nonempty properSubset
+ {{
+ Checks if one {type NatSet.Nonempty} is a proper subset of another.
+
+ A set is a proper subset of another if it contains only elements that are
+ also in the other set, and the two sets are not equal.
+
+ # Examples
+
+ A set is not a proper subset of itself:
+
+ ```
+ properSubset (fromList (1 +| [2])) (fromList (1 +| [2]))
+ ```
+
+ A set is a proper subset of another if it contains only elements that are
+ also in the other set:
+
+ ```
+ properSubset (fromList (1 +| [2])) (fromList (1 +| [2, 3]))
+ ```
+
+ A set is not a proper subset of another if it contains elements that are
+ not in the other set:
+
+ ```
+ properSubset (fromList (1 +| [2, 3])) (fromList (1 +| [2]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.subset} for a version of this that returns `` true ``
+ when the inputs are equal.
+ * {Nonempty.subsetCompare} to compare two sets to see if either one is a
+ subset of the other.
+ * {Nonempty.disjoint} to check if two sets have no elements in common.
+ }}
+
+data.NatSet.Nonempty.properSuperset :
+ NatSet.Nonempty -> NatSet.Nonempty -> Boolean
+data.NatSet.Nonempty.properSuperset t1 t2 = Nonempty.properSubset t2 t1
+
+data.NatSet.Nonempty.properSuperset.doc : Doc
+data.NatSet.Nonempty.properSuperset.doc =
+ use NatSet.Nonempty fromList
+ use Nonempty properSuperset
+ {{
+ Checks if one {type NatSet.Nonempty} is a proper superset of another.
+
+ A set is a proper superset of another if it contains all elements in the
+ other set, and the two sets are not equal.
+
+ # Examples
+
+ A set is not a proper superset of itself:
+
+ ```
+ properSuperset (fromList (1 +| [2])) (fromList (1 +| [2]))
+ ```
+
+ A set is a proper superset of another if it contains all elements in the
+ other set:
+
+ ```
+ properSuperset (fromList (1 +| [2, 3])) (fromList (1 +| [2]))
+ ```
+
+ A set is not a proper superset of another it doesn't contain all elements
+ in the other set:
+
+ ```
+ properSuperset (fromList (1 +| [2])) (fromList (1 +| [2, 3]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.superset} for a version of this that returns `` true ``
+ when the inputs are equal.
+ * {Nonempty.subsetCompare} to compare two sets to see if either one is a
+ superset of the other.
+ * {Nonempty.disjoint} to check if two sets have no elements in common.
+ }}
+
+data.NatSet.Nonempty.randomChoice : NatSet.Nonempty ->{Random} Nat
+data.NatSet.Nonempty.randomChoice set =
+ randomIndex = Random.natIn 0 (NatSet.Nonempty.size set)
+ NatSet.Nonempty.nth randomIndex set
+ |> getOrBug "NatSet.Nonempty.randomChoice: index out of bounds"
+
+data.NatSet.Nonempty.randomChoice.doc : Doc
+data.NatSet.Nonempty.randomChoice.doc =
+ use NatSet.Nonempty fromList randomChoice
+ use Nonempty Nonempty
+ {{
+ Returns a random {type Nat} from the given {type NatSet.Nonempty}.
+
+ # Examples
+
+ ```
+ lcg 4096 do randomChoice (fromList (Nonempty 0 [3, 5, 7]))
+ ```
+
+ ```
+ lcg 2510 do randomChoice (fromList (Nonempty 0 [3, 5, 7]))
+ ```
+ }}
+
+test> data.NatSet.Nonempty.randomChoice.test = test.verify do
+ set = NatSet.Nonempty.fromList (0 +| [1, 2, 3, 4, 5, 6, 7, 8, 9])
+ Each.repeat 1000
+ e = NatSet.Nonempty.randomChoice set
+ ensure (NatSet.Nonempty.contains e set)
+
+data.NatSet.Nonempty.size : NatSet.Nonempty -> Nat
+data.NatSet.Nonempty.size = cases
+ NatSet.Nonempty.Bin _ _ sz l r -> sz
+ NatSet.Nonempty.Tip _ bm -> Nat.popCount bm
+
+data.NatSet.Nonempty.size.doc : Doc
+data.NatSet.Nonempty.size.doc =
+ {{
+ Returns the number of elements in a {type NatSet.Nonempty}.
+
+ The complexity of this function is `O(n)` where `n` is the number of elements
+ in the set. However, the constant factor `1/64` is very small, so this
+ function is quite fast for small sets.
+
+ # Example
+
+ ```
+ NatSet.Nonempty.size (NatSet.Nonempty.fromList (1 +| [2]))
+ ```
+ }}
+
+data.NatSet.Nonempty.split : Nat -> NatSet.Nonempty -> (NatSet, NatSet)
+data.NatSet.Nonempty.split x = cases
+ t@(NatSet.Nonempty.Bin p m _ l r)
+ | mask x m Nat.== p ->
+ if internal.zero x m then
+ (lt, gt) = data.NatSet.Nonempty.split x l
+ (lt, NatSet.union gt (NatSet (Some r)))
+ else
+ (lt, gt) = data.NatSet.Nonempty.split x r
+ (NatSet.union (NatSet (Some l)) lt, gt)
+ | otherwise ->
+ if x Nat.< p then (NatSet None, NatSet (Some t))
+ else (NatSet (Some t), NatSet None)
+ t@(NatSet.Nonempty.Tip p m)
+ | p Nat.> x -> (NatSet None, NatSet (Some t))
+ | p Nat.< prefixOf x -> (NatSet (Some t), NatSet None)
+ | otherwise ->
+ use Nat + - and
+ lowerBitmap = bitmapOf x - 1
+ upperBitmap = Nat.complement (lowerBitmap + bitmapOf x)
+ (tip p (and m lowerBitmap), tip p (and m upperBitmap))
+
+data.NatSet.Nonempty.split.doc : Doc
+data.NatSet.Nonempty.split.doc =
+ {{
+ Splits a {type NatSet.Nonempty} into two sets based on whether elements are
+ less than or greater than a given element.
+
+ The first set contains all elements that are less than the given element, and
+ the second set contains all elements that are greater than the given element.
+ The given element is not included in either set.
+
+ # Example
+
+ ```
+ Tuple.bimap
+ NatSet.toList
+ (NatSet.Nonempty.split 3 (NatSet.Nonempty.fromList (1 +| [2, 3, 4])))
+ ```
+
+ # See also
+
+ * {Nonempty.splitContains} to also check whether the given element is in
+ the set.
+ * {NatSet.Nonempty.partition} to split a set into two sets based on whether
+ elements satisfy a predicate.
+ * {NatSet.Nonempty.filter} to remove all elements that do not satisfy a
+ predicate.
+ * {NatSet.Nonempty.filterMap} to filter a set by a function that returns an
+ {type Optional} value.
+ * {NatSet.Nonempty.map} to apply a function to every element in a set, and
+ return a new set with the results.
+ }}
+
+data.NatSet.Nonempty.splitContains :
+ Nat -> NatSet.Nonempty -> (NatSet, Boolean, NatSet)
+data.NatSet.Nonempty.splitContains x = cases
+ t@(NatSet.Nonempty.Bin p m _ l r)
+ | mask x m Nat.== p ->
+ if internal.zero x m then
+ (lt, b, gt) = data.NatSet.Nonempty.splitContains x l
+ (lt, b, NatSet.union gt (NatSet (Some r)))
+ else
+ (lt, b, gt) = data.NatSet.Nonempty.splitContains x r
+ (NatSet.union (NatSet (Some l)) lt, b, gt)
+ | otherwise ->
+ if x Nat.< p then (NatSet None, false, NatSet (Some t))
+ else (NatSet (Some t), false, NatSet None)
+ t@(NatSet.Nonempty.Tip p m)
+ | p Nat.> x -> (NatSet None, false, NatSet (Some t))
+ | p Nat.< prefixOf x -> (NatSet (Some t), false, NatSet None)
+ | otherwise ->
+ use Nat != + - and
+ lowerBitmap = bitmapOf x - 1
+ upperBitmap = Nat.complement (lowerBitmap + bitmapOf x)
+ ( tip p (and m lowerBitmap)
+ , and m (bitmapOf x) != 0
+ , tip p (and m upperBitmap)
+ )
+
+data.NatSet.Nonempty.splitContains.doc : Doc
+data.NatSet.Nonempty.splitContains.doc =
+ use NatSet toList
+ {{
+ Splits a {type NatSet.Nonempty} into two sets based on whether elements are
+ less than or greater than a given element, and returns whether the given
+ element is in the set.
+
+ The first set in the result contains all elements that are less than the
+ given element, and the other set contains all elements that are greater than
+ the given element. The element is not included in either set, but the middle
+ element of the result is a {type Boolean} indicating whether the element is
+ in the set.
+
+ # Example
+
+ ```
+ (l, b, r) =
+ Nonempty.splitContains 3 (NatSet.Nonempty.fromList (1 +| [2, 3, 4]))
+ (toList l, b, toList r)
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.split} for a version of this function that does not
+ return whether the element is in the set.
+ * {NatSet.Nonempty.partition} to split a set into two sets based on whether
+ elements satisfy a predicate.
+ * {NatSet.Nonempty.filter} to remove all elements that do not satisfy a
+ predicate.
+ * {NatSet.Nonempty.filterMap} to filter a set by a function that returns an
+ {type Optional} value.
+ * {NatSet.Nonempty.map} to apply a function to every element in a set, and
+ return a new set with the results.
+ }}
+
+data.NatSet.Nonempty.subset : NatSet.Nonempty -> NatSet.Nonempty -> Boolean
+data.NatSet.Nonempty.subset = cases
+ t1, t2 ->
+ match Nonempty.subsetCompare t1 t2 with
+ Some Less -> true
+ Some Equal -> true
+ _ -> false
+
+data.NatSet.Nonempty.subset.doc : Doc
+data.NatSet.Nonempty.subset.doc =
+ use NatSet.Nonempty fromList subset
+ {{
+ Checks if one {type NatSet.Nonempty} is a subset of another.
+
+ A set is a subset of another if it contains only elements that are also in
+ the other set.
+
+ # Examples
+
+ Every set is a subset of itself:
+
+ ```
+ subset (fromList (1 +| [2])) (fromList (1 +| [2]))
+ ```
+
+ A set is a subset of another if it contains only elements that are also in
+ the other set:
+
+ ```
+ subset (fromList (1 +| [2])) (fromList (1 +| [2, 3]))
+ ```
+
+ A set is not a subset of another if it contains elements that are not in
+ the other set:
+
+ ```
+ subset (fromList (1 +| [2, 3])) (fromList (1 +| [2]))
+ ```
+
+ # See also
+
+ * {Nonempty.properSubset} for a version of this that returns `` false ``
+ when the inputs are equal.
+ * {Nonempty.subsetCompare} to compare two sets to see if either one is a
+ subset of the other.
+ * {Nonempty.disjoint} to check if two sets have no elements in common.
+ }}
+
+data.NatSet.Nonempty.subsetCompare :
+ NatSet.Nonempty -> NatSet.Nonempty -> Optional Ordering
+data.NatSet.Nonempty.subsetCompare = cases
+ t1@(NatSet.Nonempty.Bin p1 m1 _ l1 r1),
+ t2@(NatSet.Nonempty.Bin p2 m2 _ l2 r2)
+ | shorter m1 m2 ->
+ if nomatch p2 p1 m1 then None
+ else
+ if internal.zero p2 m1 then
+ match data.NatSet.Nonempty.subsetCompare l1 t2 with
+ Some Greater -> Some Greater
+ Some Equal -> Some Greater
+ _ -> None
+ else
+ match data.NatSet.Nonempty.subsetCompare r1 t2 with
+ Some Greater -> Some Greater
+ Some Equal -> Some Greater
+ _ -> None
+ | shorter m2 m1 ->
+ if nomatch p1 p2 m2 then None
+ else
+ if internal.zero p1 m2 then
+ match data.NatSet.Nonempty.subsetCompare t1 l2 with
+ Some Less -> Some Less
+ Some Equal -> Some Less
+ _ -> None
+ else
+ match data.NatSet.Nonempty.subsetCompare t1 r2 with
+ Some Less -> Some Less
+ Some Equal -> Some Less
+ _ -> None
+ | p1 Nat.== p2 ->
+ use data.NatSet.Nonempty subsetCompare
+ lc = subsetCompare l1 l2
+ rc = subsetCompare r1 r2
+ match (lc, rc) with
+ (Some Equal, Some Equal) -> Some Equal
+ (Some Less, Some Less) -> Some Less
+ (Some Greater, Some Greater) -> Some Greater
+ (Some Less, Some Equal) -> Some Less
+ (Some Equal, Some Less) -> Some Less
+ (Some Greater, Some Equal) -> Some Greater
+ (Some Equal, Some Greater) -> Some Greater
+ _ -> None
+ | otherwise -> None
+ NatSet.Nonempty.Tip p1 m1, NatSet.Nonempty.Tip p2 m2
+ | p1 Nat.!= p2 -> None
+ | m1 Nat.== m2 -> Some Equal
+ | Nat.and m1 (Nat.complement m2) Nat.== 0 -> Some Less
+ | Nat.and m2 (Nat.complement m1) Nat.== 0 -> Some Greater
+ | otherwise -> None
+ t@(NatSet.Nonempty.Tip p1 _), NatSet.Nonempty.Bin p2 m2 _ l2 r2
+ | nomatch p1 p2 m2 -> None
+ | internal.zero p1 m2 ->
+ match data.NatSet.Nonempty.subsetCompare t l2 with
+ Some Less -> Some Less
+ Some Equal -> Some Less
+ _ -> None
+ | otherwise ->
+ match data.NatSet.Nonempty.subsetCompare t r2 with
+ Some Less -> Some Less
+ Some Equal -> Some Less
+ _ -> None
+ t1@(NatSet.Nonempty.Bin _ _ _ _ _), t2@(NatSet.Nonempty.Tip _ _) ->
+ Optional.map Ordering.inverse (data.NatSet.Nonempty.subsetCompare t2 t1)
+
+data.NatSet.Nonempty.subsetCompare.doc : Doc
+data.NatSet.Nonempty.subsetCompare.doc =
+ use NatSet.Nonempty fromList
+ use Nonempty subsetCompare
+ {{
+ Compares two {type NatSet.Nonempty} values to see if one is a subset of the
+ other.
+
+ Returns `` Some Less `` if the first input is a subset of the second, ``
+ Some Greater `` if the second input is a subset of the first, and ``
+ Some Equal `` if the inputs are the same set. Returns `` None `` if the sets
+ are not comparable (i.e. each has elements that are not in the other).
+
+ # Examples
+
+ ```
+ subsetCompare (fromList (1 +| [2])) (fromList (2 +| [3]))
+ ```
+
+ ```
+ subsetCompare (fromList (1 +| [2])) (fromList (1 +| [2]))
+ ```
+
+ ```
+ subsetCompare (fromList (1 +| [2])) (fromList (1 +| [2, 3]))
+ ```
+
+ ```
+ subsetCompare (fromList (1 +| [2, 3])) (fromList (1 +| [2]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.subset} to check if one set is a subset of another.
+ * {Nonempty.disjoint} to check if two {type NatSet.Nonempty} have no
+ elements in common.
+ }}
+
+data.NatSet.Nonempty.superset : NatSet.Nonempty -> NatSet.Nonempty -> Boolean
+data.NatSet.Nonempty.superset t1 t2 = NatSet.Nonempty.subset t2 t1
+
+data.NatSet.Nonempty.superset.doc : Doc
+data.NatSet.Nonempty.superset.doc =
+ use NatSet.Nonempty fromList superset
+ {{
+ Checks if one {type NatSet.Nonempty} is a superset of another.
+
+ A set is a superset of another if it contains all elements in the other set.
+
+ # Examples
+
+ Every set is a superset of itself:
+
+ ```
+ superset (fromList (1 +| [2])) (fromList (1 +| [2]))
+ ```
+
+ A set is a superset of another if it contains all elements in the other
+ set:
+
+ ```
+ superset (fromList (1 +| [2, 3])) (fromList (1 +| [2]))
+ ```
+
+ A set is not a superset of another it doesn't contain all elements in the
+ other set:
+
+ ```
+ superset (fromList (1 +| [2])) (fromList (1 +| [2, 3]))
+ ```
+
+ # See also
+
+ * {Nonempty.properSuperset} for a version of this that returns `` false ``
+ when the inputs are equal.
+ * {Nonempty.subsetCompare} to compare two sets to see if either one is a
+ superset of the other.
+ * {Nonempty.disjoint} to check if two sets have no elements in common.
+ }}
+
+data.NatSet.Nonempty.tips : NatSet.Nonempty -> List.Nonempty NatSet.Nonempty
+data.NatSet.Nonempty.tips = cases
+ NatSet.Nonempty.Bin _ _ _ l r ->
+ data.NatSet.Nonempty.tips l Nonempty.++ data.NatSet.Nonempty.tips r
+ t@(NatSet.Nonempty.Tip _ _) -> List.Nonempty.singleton t
+
+data.NatSet.Nonempty.tips.doc : Doc
+data.NatSet.Nonempty.tips.doc =
+ use Nonempty tips
+ {{
+ Returns a list of all the leaves in a {type NatSet.Nonempty}.
+
+ Each leaf is a {type NatSet.Nonempty} with a prefix and a bitmap. The prefix
+ is the 58-bit common prefix of all numbers in the set. The 64-bit bitmap
+ indicates which numbers with that prefix are in the set. If the prefix is `p`
+ and bit `n` is set in the bitmap, then the number `p + n` is in the set.
+
+ # Examples
+
+ ```
+ tips (NatSet.Nonempty.singleton 8)
+ ```
+
+ ```
+ tips (NatSet.Nonempty.fromList (0 +| [1, 2, 4, 8, 16, 32, 64, 128]))
+ ```
+ }}
+
+data.NatSet.Nonempty.toList : NatSet.Nonempty -> List.Nonempty Nat
+data.NatSet.Nonempty.toList =
+ use Nonempty ++
+ NatSet.Nonempty.foldMap (++) List.Nonempty.singleton
+
+data.NatSet.Nonempty.toList.doc : Doc
+data.NatSet.Nonempty.toList.doc =
+ use NatSet.Nonempty foldRight
+ {{
+ Converts a {type NatSet.Nonempty} to a {type List.Nonempty} of its elements.
+
+ # Example
+
+ ```
+ NatSet.Nonempty.toList (NatSet.Nonempty.fromList (1 +| [2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.foldMap} to apply a function to every element and
+ combine the results.
+ * {foldRight} to accumulate results of a single binary function applied to
+ every element and the result so far, associating to the right.
+ * {NatSet.Nonempty.foldLeft} same as {foldRight} but associating to the
+ left.
+ * {NatSet.Nonempty.map} to apply a function to every element without
+ combining the results.
+ * {NatSet.Nonempty.filter} to filter out elements that don't satisfy a
+ predicate.
+ * {NatSet.Nonempty.filterMap} to apply a function to every element and
+ filter out the results that are {None}.
+ }}
+
+data.NatSet.Nonempty.toListDescending : NatSet.Nonempty -> List.Nonempty Nat
+data.NatSet.Nonempty.toListDescending s =
+ Abort.toBug do List.nonempty << NatSet.toListDescending << NatSet <| Some s
+
+data.NatSet.Nonempty.toListDescending.doc : Doc
+data.NatSet.Nonempty.toListDescending.doc =
+ {{
+ Converts a {type NatSet.Nonempty} to a {type List.Nonempty} of its elements
+ in descending order.
+
+ # Example
+
+ ```
+ Nonempty.toListDescending (NatSet.Nonempty.fromList (1 +| [2, 3, 4]))
+ ```
+
+ # See also
+
+ * {Nonempty.toListAscending} to convert to a {type List.Nonempty} in
+ ascending order.
+ }}
+
+data.NatSet.Nonempty.toNatSet : NatSet.Nonempty -> NatSet
+data.NatSet.Nonempty.toNatSet = NatSet << Some
+
+data.NatSet.Nonempty.toNatSet.doc : Doc
+data.NatSet.Nonempty.toNatSet.doc =
+ {{
+ Converts a {type NatSet.Nonempty} to a {type NatSet}.
+
+ # Example
+
+ ```
+ NatSet.isEmpty
+ (NatSet.Nonempty.toNatSet (NatSet.Nonempty.fromList (1 +| [2, 3, 4])))
+ ```
+ }}
+
+data.NatSet.Nonempty.union :
+ NatSet.Nonempty -> NatSet.Nonempty -> NatSet.Nonempty
+data.NatSet.Nonempty.union = cases
+ t1@(NatSet.Nonempty.Bin p1 m1 _ l1 r1),
+ t2@(NatSet.Nonempty.Bin p2 m2 _ l2 r2)
+ | shorter m1 m2 ->
+ if nomatch p2 p1 m1 then NatSet.internal.link p1 t1 p2 t2
+ else
+ if internal.zero p2 m1 then
+ NatSet.internal.bim p1 m1 (data.NatSet.Nonempty.union l1 t2) r1
+ else NatSet.internal.bim p1 m1 l1 (data.NatSet.Nonempty.union r1 t2)
+ | shorter m2 m1 ->
+ if nomatch p1 p2 m2 then NatSet.internal.link p1 t1 p2 t2
+ else
+ if internal.zero p1 m2 then
+ NatSet.internal.bim p2 m2 (data.NatSet.Nonempty.union t1 l2) r2
+ else NatSet.internal.bim p2 m2 l2 (data.NatSet.Nonempty.union t1 r2)
+ | p1 Nat.== p2 ->
+ NatSet.internal.bim
+ p1
+ m1
+ (data.NatSet.Nonempty.union l1 l2)
+ (data.NatSet.Nonempty.union r1 r2)
+ | otherwise -> NatSet.internal.link p1 t1 p2 t2
+ t@(NatSet.Nonempty.Bin _ _ _ _ _), NatSet.Nonempty.Tip p m ->
+ insertBitmap p m t
+ NatSet.Nonempty.Tip p m, t -> insertBitmap p m t
+
+data.NatSet.Nonempty.union.doc : Doc
+data.NatSet.Nonempty.union.doc =
+ use NatSet.Nonempty fromList
+ {{
+ Adds all elements from one {type NatSet.Nonempty} to another. If an element
+ is present in either input, it will be present in the result.
+
+ # Example
+
+ ```
+ Nonempty.toListAscending
+ (NatSet.Nonempty.union (fromList (1 +| [2])) (fromList (2 +| [3])))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.difference} to remove all elements from a
+ {type NatSet.Nonempty} that are in another.
+ * {NatSet.Nonempty.intersect} to remove all elements from a
+ {type NatSet.Nonempty} that are not in another.
+ * {NatSet.Nonempty.insert} to add one element into a
+ {type NatSet.Nonempty}.
+ }}
+
+data.NatSet.Nonempty.unions : List.Nonempty NatSet.Nonempty -> NatSet.Nonempty
+data.NatSet.Nonempty.unions = reduceLeft NatSet.Nonempty.union
+
+data.NatSet.Nonempty.unions.doc : Doc
+data.NatSet.Nonempty.unions.doc =
+ use NatSet.Nonempty fromList
+ {{
+ Puts all elements from a {type List.Nonempty} of {type NatSet.Nonempty}s into
+ a single {type NatSet.Nonempty}. If an element is present in any of the
+ inputs, it will be present in the result.
+
+ # Example
+
+ ```
+ Nonempty.toListAscending
+ (NatSet.Nonempty.unions (fromList (1 +| [2]) +| [fromList (2 +| [3])]))
+ ```
+
+ # See also
+
+ * {NatSet.Nonempty.union} to union two {type NatSet.Nonempty}s.
+ }}
+
+data.NatSet.ordering : NatSet -> NatSet -> Ordering
+data.NatSet.ordering = cases
+ NatSet (Some s1), NatSet (Some s2) -> Nonempty.ordering s1 s2
+ NatSet None, NatSet None -> Equal
+ NatSet None, NatSet (Some _) -> Less
+ NatSet (Some _), NatSet None -> Greater
+
+data.NatSet.ordering.doc : Doc
+data.NatSet.ordering.doc =
+ use NatSet == fromList ordering
+ {{
+ Compares two {type NatSet} for lexicographical {type Ordering}.
+
+ Returns {Less} if the smallest element that differs between the two
+ {type NatSet}s is less in the first {type NatSet}, and {Greater} if it is
+ greater. If one {type NatSet} is a prefix of the other, the smaller
+ {type NatSet} is {Less}. If the two {type NatSet}s have the same elements,
+ returns {Equal}.
+
+ # Examples
+
+ ```
+ ordering (fromList [1, 2, 3, 4]) (fromList [1, 2, 3, 4])
+ ```
+
+ ```
+ ordering (fromList [1, 2, 3, 4]) (fromList [1, 2, 3, 5])
+ ```
+
+ ```
+ ordering (fromList [1, 2, 3, 4]) (fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {==} to check if two {type NatSet} are equal.
+ * {NatSet.subsetCompare} to compare two {type NatSet} for subset ordering.
+ * {NatSet.subset} to check if one {type NatSet} is a subset of another.
+ * {NatSet.disjoint} to check if two {type NatSet} have no elements in
+ common.
+ }}
+
+test> data.NatSet.ordering.test =
+ test.verify do
+ use List size
+ use NatSet fromList toList
+ use Random listOf natIn
+ _ = Each.range 0 100
+ xs = listOf (do natIn 0 1000) do natIn 0 100
+ ys = listOf (do natIn 0 1000) do natIn 0 100
+ s1 = fromList xs
+ s2 = fromList ys
+ l1 = toList s1
+ l2 = toList s2
+ lc = List.zipWith Universal.ordering l1 l2
+ lo =
+ List.foldRight
+ (x acc -> Ordering.andThen x acc)
+ (Universal.ordering (size l1) (size l2))
+ lc
+ so = NatSet.ordering s1 s2
+ ensureEqual lo so
+
+data.NatSet.partition : (Nat ->{g} Boolean) -> NatSet ->{g} (NatSet, NatSet)
+data.NatSet.partition f = cases
+ NatSet (Some t) -> NatSet.Nonempty.partition f t
+ NatSet None -> (NatSet None, NatSet None)
+
+data.NatSet.partition.doc : Doc
+data.NatSet.partition.doc =
+ {{
+ Partitions a {type NatSet} by a predicate. Returns a pair of sets, the first
+ containing all elements that satisfy the predicate, and the second containing
+ all elements that do not.
+
+ # Example
+
+ ```
+ Tuple.bimap
+ NatSet.toList
+ (NatSet.partition Nat.isEven (NatSet.fromList [1, 2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatSet.filter} to remove all elements that do not satisfy the predicate.
+ * {NatSet.filterMap} to filter a set by a function that returns an
+ {type Optional} value.
+ * {NatSet.map} to apply a function to every element in a set, and return a
+ new set with the results.
+ * {NatSet.Nonempty.split} to split a set based on whether elements are less
+ than or greater than a given element.
+ }}
+
+test> data.NatSet.partition.test =
+ test.verify do
+ use List filter
+ use Nat ==
+ use NatSet fromList
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 0 100
+ f = natIn 0 100
+ ensure
+ (NatSet.partition ((==) f) (fromList xs)
+ === ( fromList (filter ((==) f) xs)
+ , fromList (filter (Boolean.not << (==) f) xs)
+ ))
+
+data.NatSet.properSubset : NatSet -> NatSet -> Boolean
+data.NatSet.properSubset = cases
+ NatSet (Some t1), NatSet (Some t2) -> Nonempty.properSubset t1 t2
+ NatSet None, NatSet None -> false
+ NatSet None, NatSet (Some _) -> true
+ NatSet (Some _), NatSet None -> false
+
+data.NatSet.properSubset.doc : Doc
+data.NatSet.properSubset.doc =
+ use NatSet fromList properSubset
+ {{
+ Checks if one {type NatSet} is a proper subset of another.
+
+ A set is a proper subset of another if it contains only elements that are
+ also in the other set, and the two sets are not equal.
+
+ # Examples
+
+ A set is not a proper subset of itself:
+
+ ```
+ properSubset (fromList [1, 2]) (fromList [1, 2])
+ ```
+
+ A set is a proper subset of another if it contains only elements that are
+ also in the other set:
+
+ ```
+ properSubset (fromList [1, 2]) (fromList [1, 2, 3])
+ ```
+
+ A set is not a proper subset of another if it contains elements that are
+ not in the other set:
+
+ ```
+ properSubset (fromList [1, 2, 3]) (fromList [1, 2])
+ ```
+
+ # See also
+
+ * {NatSet.subset} for a version of this that returns `` true `` when the
+ inputs are equal.
+ * {NatSet.subsetCompare} to compare two sets to see if either one is a
+ subset of the other.
+ * {NatSet.disjoint} to check if two sets have no elements in common.
+ }}
+
+data.NatSet.properSuperset : NatSet -> NatSet -> Boolean
+data.NatSet.properSuperset t1 t2 = NatSet.properSubset t2 t1
+
+data.NatSet.properSuperset.doc : Doc
+data.NatSet.properSuperset.doc =
+ use NatSet fromList properSuperset
+ {{
+ Checks if one {type NatSet} is a proper superset of another.
+
+ A set is a proper superset of another if it contains all elements in the
+ other set, and the two sets are not equal.
+
+ # Examples
+
+ A set is not a proper superset of itself:
+
+ ```
+ properSuperset (fromList [1, 2]) (fromList [1, 2])
+ ```
+
+ A set is a proper superset of another if it contains all elements in the
+ other set:
+
+ ```
+ properSuperset (fromList [1, 2, 3]) (fromList [1, 2])
+ ```
+
+ A set is not a proper superset of another it doesn't contain all elements
+ in the other set:
+
+ ```
+ properSuperset (fromList [1, 2]) (fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {NatSet.superset} for a version of this that returns `` true `` when the
+ inputs are equal.
+ * {NatSet.subsetCompare} to compare two sets to see if either one is a
+ superset of the other.
+ * {NatSet.disjoint} to check if two sets have no elements in common.
+ }}
+
+data.NatSet.randomChoice : NatSet ->{Exception, Random} Nat
+data.NatSet.randomChoice = cases
+ NatSet internalSet ->
+ Optional.map (s -> NatSet.Nonempty.randomChoice s) internalSet
+ |> Optional.toException
+ "NatSet.randomChoice: empty set" (typeLink NatSet)
+
+data.NatSet.randomChoice.doc : Doc
+data.NatSet.randomChoice.doc =
+ use NatSet fromList randomChoice
+ {{
+ Returns a random {type Nat} from the given {type NatSet}. Assumes that the
+ {type NatSet} is not empty, so an empty {type NatSet} will cause a runtime
+ exception.
+
+ # Examples
+
+ ```
+ catch do lcg 4096 do randomChoice (fromList [0, 3, 5, 7])
+ ```
+
+ ```
+ catch do lcg 2510 do randomChoice (fromList [0, 3, 5, 7])
+ ```
+ }}
+
+test> data.NatSet.randomChoice.test = test.verify do
+ set = NatSet.fromList [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ Each.repeat 1000
+ e = NatSet.randomChoice set
+ ensure (NatSet.contains e set)
+
+data.NatSet.singleton : Nat -> NatSet.Nonempty
+data.NatSet.singleton k = NatSet.Nonempty.Tip (prefixOf k) (bitmapOf k)
+
+data.NatSet.singleton.doc : Doc
+data.NatSet.singleton.doc =
+ {{
+ Creates a {type NatSet.Nonempty} with a single element.
+
+ # Example
+
+ ```
+ Nonempty.toListAscending (NatSet.singleton 1)
+ ```
+
+ # See also
+
+ * {NatSet.fromList} to create a {type NatSet} from a {type List} of
+ elements.
+ * {NatSet.Nonempty.fromList} to create a {type NatSet.Nonempty} from a
+ {type List.Nonempty} of elements.
+ * {NatSet.empty} for the empty {type NatSet}.
+ * {NatSet.insert.nonempty} to add an element to a {type NatSet}.
+ }}
+
+data.NatSet.size : NatSet -> Nat
+data.NatSet.size = cases
+ NatSet (Some t) -> NatSet.Nonempty.size t
+ NatSet None -> 0
+
+data.NatSet.size.doc : Doc
+data.NatSet.size.doc =
+ {{
+ Returns the number of elements in a {type NatSet}.
+
+ The complexity of this function is `O(n)` where `n` is the number of elements
+ in the set. However, the constant factor `1/64` is very small, so this
+ function is quite fast for small sets.
+
+ # Example
+
+ ```
+ NatSet.size (NatSet.fromList [1, 2])
+ ```
+ }}
+
+test> data.NatSet.size.test = test.verify do
+ len = Random.natIn 0 1000
+ xs = Random.listOf Random.nat do len
+ s = NatSet.fromList xs
+ ensureEqual (NatSet.size s) (List.size (NatSet.toList s))
+
+data.NatSet.split : Nat -> NatSet -> (NatSet, NatSet)
+data.NatSet.split x = cases
+ NatSet (Some t) -> NatSet.Nonempty.split x t
+ NatSet None -> (NatSet None, NatSet None)
+
+data.NatSet.split.doc : Doc
+data.NatSet.split.doc =
+ {{
+ Splits a {type NatSet} into two sets based on whether elements are less than
+ or greater than a given element.
+
+ The first set contains all elements that are less than the given element, and
+ the second set contains all elements that are greater than the given element.
+ The given element is not included in either set.
+
+ # Example
+
+ ```
+ Tuple.bimap NatSet.toList (NatSet.split 3 (NatSet.fromList [1, 2, 3, 4]))
+ ```
+
+ # See also
+
+ * {NatSet.splitContains} to also check whether the given element is in the
+ set.
+ * {NatSet.partition} to split a set into two sets based on a predicate.
+ * {NatSet.filter} to remove all elements that do not satisfy a predicate.
+ * {NatSet.filterMap} to filter a set by a function that returns an
+ {type Optional} value.
+ * {NatSet.map} to apply a function to every element in a set, and return a
+ new set with the results.
+ }}
+
+test> data.NatSet.split.test =
+ test.verify do
+ use List filter
+ use Nat < >
+ use NatSet fromList
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 0 100
+ x = natIn 0 100
+ ensure
+ (NatSet.split x (fromList xs)
+ === ( fromList (filter (flip (<) x) xs)
+ , fromList (filter (flip (>) x) xs)
+ ))
+
+data.NatSet.splitContains : Nat -> NatSet -> (NatSet, Boolean, NatSet)
+data.NatSet.splitContains x = cases
+ NatSet (Some t) -> Nonempty.splitContains x t
+ NatSet None -> (NatSet None, false, NatSet None)
+
+data.NatSet.splitContains.doc : Doc
+data.NatSet.splitContains.doc =
+ use NatSet toList
+ {{
+ Splits a {type NatSet} into two sets based on whether elements are less than
+ or greater than a given element, and returns whether the given element is in
+ the set.
+
+ The first set in the result contains all elements that are less than the
+ given element, and the other set contains all elements that are greater than
+ the given element. The element is not included in either set, but the middle
+ element of the result is a {type Boolean} indicating whether the element is
+ in the set.
+
+ # Example
+
+ ```
+ (l, b, r) = NatSet.splitContains 3 (NatSet.fromList [1, 2, 3, 4])
+ (toList l, b, toList r)
+ ```
+
+ # See also
+
+ * {NatSet.split} for a version of this function that does not return
+ whether the element is in the set.
+ * {NatSet.partition} to split a set into two sets based on a predicate.
+ * {NatSet.filter} to remove all elements that do not satisfy a predicate.
+ * {NatSet.filterMap} to filter a set by a function that returns an
+ {type Optional} value.
+ * {NatSet.map} to apply a function to every element in a set, and return a
+ new set with the results.
+ }}
+
+test> data.NatSet.splitContains.test =
+ test.verify do
+ use List filter
+ use Nat < >
+ use NatSet fromList
+ use Random natIn
+ _ = Each.range 0 100
+ xs = Random.listOf (do natIn 0 100) do natIn 0 100
+ x = natIn 0 100
+ ensure
+ (NatSet.splitContains x (fromList xs)
+ === ( fromList (filter (flip (<) x) xs)
+ , List.contains x xs
+ , fromList (filter (flip (>) x) xs)
+ ))
+
+data.NatSet.subset : NatSet -> NatSet -> Boolean
+data.NatSet.subset = cases
+ NatSet (Some t1), NatSet (Some t2) -> NatSet.Nonempty.subset t1 t2
+ NatSet None, NatSet None -> true
+ NatSet None, NatSet (Some _) -> true
+ NatSet (Some _), NatSet None -> false
+
+data.NatSet.subset.doc : Doc
+data.NatSet.subset.doc =
+ use NatSet fromList subset
+ {{
+ Checks if one {type NatSet} is a subset of another.
+
+ A set is a subset of another if it contains only elements that are also in
+ the other set.
+
+ # Examples
+
+ Every set is a subset of itself:
+
+ ```
+ subset (fromList [1, 2]) (fromList [1, 2])
+ ```
+
+ A set is a subset of another if it contains only elements that are also in
+ the other set:
+
+ ```
+ subset (fromList [1, 2]) (fromList [1, 2, 3])
+ ```
+
+ A set is not a subset of another if it contains elements that are not in
+ the other set:
+
+ ```
+ subset (fromList [1, 2, 3]) (fromList [1, 2])
+ ```
+
+ # See also
+
+ * {NatSet.properSubset} for a version of this that returns `` false `` when
+ the inputs are equal.
+ * {NatSet.subsetCompare} to compare two sets to see if either one is a
+ subset of the other.
+ * {NatSet.disjoint} to check if two sets have no elements in common.
+ }}
+
+data.NatSet.subsetCompare : NatSet -> NatSet -> Optional Ordering
+data.NatSet.subsetCompare = cases
+ NatSet (Some t1), NatSet (Some t2) -> Nonempty.subsetCompare t1 t2
+ NatSet None, NatSet None -> Some Equal
+ NatSet None, NatSet (Some _) -> Some Less
+ NatSet (Some _), NatSet None -> Some Greater
+
+data.NatSet.subsetCompare.doc : Doc
+data.NatSet.subsetCompare.doc =
+ use NatSet fromList subsetCompare
+ {{
+ Compares two {type NatSet} values to see if one is a subset of the other.
+
+ Returns `` Some Less `` if the first input is a subset of the second, ``
+ Some Greater `` if the second input is a subset of the first, and ``
+ Some Equal `` if the inputs are the same set. Returns `` None `` if the sets
+ are not comparable (i.e. each has elements that are not in the other).
+
+ # Examples
+
+ ```
+ subsetCompare (fromList [1, 2]) (fromList [2, 3])
+ ```
+
+ ```
+ subsetCompare (fromList [1, 2]) (fromList [1, 2])
+ ```
+
+ ```
+ subsetCompare (fromList [1, 2]) (fromList [1, 2, 3])
+ ```
+
+ ```
+ subsetCompare (fromList [1, 2, 3]) (fromList [1, 2])
+ ```
+
+ # See also
+
+ * {NatSet.subset} to check if one set is a subset of another.
+ * {NatSet.disjoint} to check if two sets have no elements in common.
+ }}
+
+test> data.NatSet.subsetCompare.test =
+ test.verify do
+ use List all
+ use NatSet contains fromList
+ use Random listOf natIn
+ _ = Each.range 0 100
+ xs = listOf (do natIn 0 100) do natIn 0 100
+ ys = listOf (do natIn 0 100) do natIn 0 100
+ s1 = fromList xs
+ s2 = fromList ys
+ c = NatSet.subsetCompare s1 s2
+ allXinY = all (x -> contains x s2) xs
+ allYinX = all (y -> contains y s1) ys
+ ensure
+ (if allXinY && allYinX then c === Some Equal
+ else
+ if allXinY then c === Some Less
+ else if allYinX then c === Some Greater else c === None)
+
+data.NatSet.superset : NatSet -> NatSet -> Boolean
+data.NatSet.superset t1 t2 = NatSet.subset t2 t1
+
+data.NatSet.superset.doc : Doc
+data.NatSet.superset.doc =
+ use NatSet fromList superset
+ {{
+ Checks if one {type NatSet} is a superset of another.
+
+ A set is a superset of another if it contains all elements in the other set.
+
+ # Examples
+
+ Every set is a superset of itself:
+
+ ```
+ superset (fromList [1, 2]) (fromList [1, 2])
+ ```
+
+ A set is a superset of another if it contains all elements in the other
+ set:
+
+ ```
+ superset (fromList [1, 2, 3]) (fromList [1, 2])
+ ```
+
+ A set is not a superset of another it doesn't contain all elements in the
+ other set:
+
+ ```
+ superset (fromList [1, 2]) (fromList [1, 2, 3])
+ ```
+
+ # See also
+
+ * {NatSet.properSuperset} for a version of this that returns `` false ``
+ when the inputs are equal.
+ * {NatSet.subsetCompare} to compare two sets to see if either one is a
+ superset of the other.
+ * {NatSet.disjoint} to check if two sets have no elements in common.
+ }}
+
+data.NatSet.takeMax : Nat -> NatSet -> NatSet
+data.NatSet.takeMax n natSet =
+ use Nat +
+ go : Nat -> (NatSet, Nat) -> (NatSet, Nat)
+ go nextElem = cases
+ (acc, count) ->
+ if count === n then (acc, n) else (NatSet.insert nextElem acc, count + 1)
+ res : (NatSet, Nat)
+ res = NatSet.foldRight go (NatSet.empty, 0) natSet
+ at1 res
+
+data.NatSet.takeMax.doc : Doc
+data.NatSet.takeMax.doc =
+ {{
+ Returns a {type NatSet} of the `n` largest elements in the given
+ {type NatSet}.
+
+ # Example
+
+ ```
+ NatSet.toList
+ <| takeMax 3 (NatSet.fromList [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
+ ```
+ }}
+
+data.NatSet.takeMin : Nat -> NatSet -> NatSet
+data.NatSet.takeMin n natSet =
+ use Nat +
+ go : (NatSet, Nat) -> Nat -> (NatSet, Nat)
+ go tup nextElem =
+ (acc, count) = tup
+ if count === n then (acc, n) else (NatSet.insert nextElem acc, count + 1)
+ NatSet.foldLeft go (NatSet.empty, 0) natSet |> at1
+
+data.NatSet.takeMin.doc : Doc
+data.NatSet.takeMin.doc =
+ {{
+ Returns a {type NatSet} of the `n` smallest elements in the given
+ {type NatSet}.
+
+ # Example
+
+ ```
+ NatSet.toList
+ <| takeMin 3 (NatSet.fromList [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
+ ```
+ }}
+
+data.NatSet.tips : NatSet -> [NatSet.Nonempty]
+data.NatSet.tips = cases
+ NatSet (Some t) -> List.Nonempty.toList (Nonempty.tips t)
+ NatSet None -> []
+
+data.NatSet.tips.doc : Doc
+data.NatSet.tips.doc =
+ use NatSet fromList tips
+ {{
+ Returns a list of all the leaves in a {type NatSet}.
+
+ Each leaf is a {type NatSet.Nonempty} with a prefix and a bitmap. The prefix
+ is the 58-bit common prefix of all numbers in the set. The 64-bit bitmap
+ indicates which numbers with that prefix are in the set. If the prefix is `p`
+ and bit `n` is set in the bitmap, then the number `p + n` is in the set.
+
+ # Examples
+
+ ```
+ tips (fromList [8])
+ ```
+
+ ```
+ tips (fromList [0, 1, 2, 4, 8, 16, 32, 64, 128])
+ ```
+ }}
+
+data.NatSet.toList : NatSet -> [Nat]
+data.NatSet.toList = cases
+ NatSet (Some t) -> List.Nonempty.toList (Nonempty.toListAscending t)
+ NatSet None -> []
+
+data.NatSet.toList.doc : Doc
+data.NatSet.toList.doc =
+ use NatSet foldRight
+ {{
+ Converts a {type NatSet} to a {type List} of its elements.
+
+ # Example
+
+ ```
+ NatSet.toList (NatSet.fromList [1, 2, 3, 4])
+ ```
+
+ # See also
+
+ * {NatSet.foldMap} to apply a function to every element and combine the
+ results.
+ * {foldRight} to accumulate results of a single binary function applied to
+ every element and the result so far, associating to the right.
+ * {NatSet.foldLeft} same as {foldRight} but associating to the left.
+ * {NatSet.map} to apply a function to every element without combining the
+ results.
+ * {NatSet.filter} to filter out elements that don't satisfy a predicate.
+ * {NatSet.filterMap} to apply a function to every element and filter out
+ the results that are {None}.
+ }}
+
+test> data.NatSet.toList.test = test.verify do
+ _ = Each.range 0 100
+ xs = Random.listOf Random.nat do Random.natIn 0 100
+ ensureEqual (NatSet.toList (NatSet.fromList xs)) (distinct (Heap.sort xs))
+
+data.NatSet.toListDescending : NatSet -> [Nat]
+data.NatSet.toListDescending = cases
+ NatSet (Some t) -> NatSet.Nonempty.foldLeft (flip (List.+:)) [] t
+ NatSet None -> []
+
+data.NatSet.toListDescending.doc : Doc
+data.NatSet.toListDescending.doc =
+ {{
+ Converts a {type NatSet} to a {type List} of its elements in descending
+ order.
+
+ # Example
+
+ ```
+ NatSet.toListDescending (NatSet.fromList [1, 2, 3, 4])
+ ```
+
+ # See also
+
+ * {NatSet.toList} to convert to a {type List} in ascending order.
+ }}
+
+test> data.NatSet.toListDescending.test =
+ test.verify do
+ _ = Each.range 0 100
+ xs = Random.listOf Random.nat do Random.natIn 0 100
+ ensureEqual
+ (NatSet.toListDescending (NatSet.fromList xs))
+ (distinct (sortDescending xs))
+
+data.NatSet.union : NatSet -> NatSet -> NatSet
+data.NatSet.union = cases
+ NatSet (Some t1), NatSet (Some t2) ->
+ NatSet (Some (NatSet.Nonempty.union t1 t2))
+ NatSet None, NatSet (Some t2) -> NatSet (Some t2)
+ NatSet (Some t1), NatSet None -> NatSet (Some t1)
+ _, _ -> NatSet None
+
+data.NatSet.union.doc : Doc
+data.NatSet.union.doc =
+ use NatSet fromList
+ {{
+ Adds all elements from one {type NatSet} to another. If an element is present
+ in either input, it will be present in the result.
+
+ # Example
+
+ ```
+ NatSet.toList (NatSet.union (fromList [1, 2]) (fromList [2, 3]))
+ ```
+
+ # See also
+
+ * {NatSet.difference} to remove all elements from a {type NatSet} that are
+ in another.
+ * {NatSet.intersect} to remove all elements from a {type NatSet} that are
+ not in another.
+ * {NatSet.insert.nonempty} to add one element into a {type NatSet}.
+ }}
+
+test> data.NatSet.union.test = test.verify do
+ use List ++
+ use NatSet == fromList
+ use Random listOf nat natIn
+ _ = Each.range 0 100
+ xs = listOf nat do natIn 0 100
+ ys = listOf nat do natIn 0 100
+ u = NatSet.union (fromList xs) (fromList ys)
+ a = fromList (xs ++ ys)
+ ensure (u == a)
+
+data.NatSet.unions : [NatSet] -> NatSet
+data.NatSet.unions = List.foldLeft NatSet.union NatSet.empty
+
+data.NatSet.unions.doc : Doc
+data.NatSet.unions.doc =
+ use NatSet fromList
+ {{
+ Puts all elements from a {type List} of {type NatSet}s into a single
+ {type NatSet}. If an element is present in any of the inputs, it will be
+ present in the result.
+
+ # Example
+
+ ```
+ NatSet.toList (NatSet.unions [fromList [1, 2], fromList [2, 3]])
+ ```
+
+ # See also
+
+ * {NatSet.union} to union two {type NatSet}s.
+ }}
+
+data.OneOrBoth.doc : Doc
+data.OneOrBoth.doc =
+ use Map fromList mergeWith mergeWithKey
+ use Nat + toText
+ use OneOrBoth fold merge partition
+ use Text ++ size toUppercase
+ {{
+ The {type OneOrBoth} type takes two type parameters, `a` and `b`, and
+ represents a value that is either {This} of type `a`, {That} of type `b`, or
+ {Both} which contains both an `a` and a `b`.
+
+ This type is useful for representing values that can be one of two types, or
+ both types at the same time. For example, it can be used to represent the
+ result of aligning two lists or maps, where each element can be present in
+ only one of the input lists or maps, or in both.
+
+ It's similar to the {type Either} type, but it can also represent the case
+ where both values are present.
+
+ # Constructing values
+
+ You can construct a value of type {type OneOrBoth} using the {This},
+ {That}, or {Both} constructors:
+
+ ```
+ This 42
+ ```
+
+ ```
+ That "hello"
+ ```
+
+ ```
+ Both 42 "hello"
+ ```
+
+ # Inspecting values
+
+ You can use the {isThis}, {isThat}, and {isBoth} functions to check which
+ constructor a value of type {type OneOrBoth} has:
+
+ ```
+ isThis (This 42)
+ ```
+
+ ```
+ isThat (That "hello")
+ ```
+
+ ```
+ isBoth (Both 42 "hello")
+ ```
+
+ # Deconstructing values
+
+ There are several ways of getting rid of a {type OneOrBoth} value.
+
+ ## Pattern matching
+
+ You can use pattern matching to destructure a {type OneOrBoth} value:
+
+ ```
+ match That "hello" with
+ This x -> x
+ That y -> size y
+ Both x y -> x + size y
+ ```
+
+ ## Folding
+
+ Use the {fold} function to apply one of three functions to a value of
+ type {type OneOrBoth}:
+
+ ```
+ fold id size (x y -> x + size y) (That "hello")
+ ```
+
+ ```
+ fold id size (x y -> x + size y) (Both 42 "hello")
+ ```
+
+ ```
+ fold id size (x y -> x + size y) (This 42)
+ ```
+
+ ## Conversion to {type Optional}
+
+ You can turn a {type OneOrBoth} value into an {type Optional} value
+ using the {maybeThis}, {maybeThat}, {justThis}, {justThat}, or
+ {justBoth} functions:
+
+ @signatures{maybeThis, maybeThat, justThis, justThat, justBoth}
+
+ The difference between {maybeThis} and {justThis} is that {maybeThis}
+ returns {Some} in the {Both} case, while {justThis} returns {None} in
+ the {Both} case. Mutatis mutandis for {maybeThat} and {justThat}.
+ {justBoth} returns {Some} only in the {Both} case.
+
+ ## Conversion to a pair
+
+ You can turn a {type OneOrBoth} value into a pair using the {toTuple}
+ function:
+
+ @signature{toTuple}
+
+ The function takes two fallback values that are used if the value is
+ {This} or {That}.
+
+ ## Merging values
+
+ You can merge a {type OneOrBoth} value into a single value using the
+ {merge} function, if the type on both sides is the same:
+
+ @signature{merge}
+
+ ## Getting rid of nested {type OneOrBoth} values
+
+ You can use the {joinThis} and {joinThat} functions to get rid of nested
+ {type OneOrBoth} values:
+
+ ```
+ joinThis (+) (That (This 42))
+ ```
+
+ ```
+ joinThat (++) (This (That "hello"))
+ ```
+
+ # Mapping over values
+
+ You can use the {mapThis}, {mapThat}, and {mapBoth} functions to apply
+ functions to the left, right, or both sides of a {type OneOrBoth}:
+
+ ```
+ mapThis toUppercase (Both "hello" "world")
+ ```
+
+ ```
+ mapThat toUppercase (Both "hello" "world")
+ ```
+
+ ```
+ mapBoth size toUppercase (Both "hello" "world")
+ ```
+
+ # List functions
+
+ ## List alignment
+
+ You can put two lists together into a list of {type OneOrBoth} using
+ {List.align}. The result will have the same length as the longer of the
+ two lists, and each element will be a {type OneOrBoth} containing the
+ corresponding elements from the two input lists. If the first list is
+ shorter than the second, the result will be padded with {That} values
+ from the second list, and if the second list is shorter than the first,
+ the result will be padded with {This} values from the first list:
+
+ ```
+ List.align [1, 2, 3] ["a", "b"]
+ ```
+
+ You can also use {List.alignWith} to apply a function to the aligned
+ elements:
+
+ ```
+ f = cases
+ This a -> toText a ++ " -> Nothing"
+ That b -> "Nothing -> " ++ b
+ Both a b -> toText a ++ " -> " ++ b
+ List.alignWith f [1, 2, 3] ["a", "b"]
+ ```
+
+ ## Lists of {type OneOrBoth}
+
+ You can use {these}, {those}, {justThese}, and {justThose} to extract
+ the {This} and {That} values from a list of {type OneOrBoth} values:
+
+ ```
+ these [This 1, That "hello", Both 42 "world"]
+ ```
+
+ ```
+ justThese [This 1, That "hello", Both 42 "world"]
+ ```
+
+ Partition a list of {type OneOrBoth} values into three lists using
+ {partition}:
+
+ ```
+ partition [This 1, That "hello", Both 42 "world"]
+ ```
+
+ # Map functions
+
+ ## Map alignment
+
+ You can align two maps into a map of {type OneOrBoth} values using
+ {Map.align}. The result will have the same keys as the union of the keys
+ of the two input maps, and each value will be a {type OneOrBoth}
+ containing the corresponding values from the two input maps. If a key is
+ present in only one of the input maps, the result will contain {This} or
+ {That} values accordingly. If a key is present in both input maps, the
+ result will contain a {Both} value:
+
+ ```
+ Map.toList
+ (Map.align
+ (fromList [(1, "hello"), (2, "world")])
+ (fromList [(2, 42), (3, 43)]))
+ ```
+
+ {Map.alignWith} is a variant of {Map.align} where you can specify a
+ function to apply to the values:
+
+ ```
+ f = cases
+ This a -> "only in the first map: " ++ a
+ That b -> "only in the second map: " ++ b
+ Both a b -> "in both maps: " ++ a ++ " and " ++ b
+ Map.values
+ (Map.alignWith
+ f
+ (fromList [(1, "circuit"), (2, "quasar")])
+ (fromList [(2, "voyage"), (3, "harmony")]))
+ ```
+
+ {Map.alignWithKey} is a variant of {Map.alignWith} where the function
+ takes the key as an argument:
+
+ @signature{Map.alignWithKey}
+
+ There are also {type Map.Nonempty} variants of these functions:
+
+ @signatures{Map.Nonempty.align, Map.Nonempty.alignWith, Map.Nonempty.alignWithKey}
+
+ And {type NatMap} and {type NatMap.Nonempty} variants as well:
+
+ @signatures{NatMap.align, NatMap.alignWith, NatMap.alignWithKey, NatMap.Nonempty.align, NatMap.Nonempty.alignWith, NatMap.Nonempty.alignWithKey}
+
+ ## Map merging with a function
+
+ {mergeWith} is a highly versatile function that can be used to merge two
+ maps via {type OneOrBoth}. It can be used to implement map union,
+ intersection, and difference, among other things:
+
+ @signature{mergeWith}
+
+ {mergeWithKey} is a variant of {mergeWith} where the function takes the
+ key into account as well:
+
+ @signature{mergeWithKey}
+ }}
+
+data.OneOrBoth.fold :
+ (a ->{e} c)
+ -> (b ->{f} c)
+ -> (a ->{g} b ->{h} c)
+ -> OneOrBoth a b
+ ->{e, f, g, h} c
+data.OneOrBoth.fold f g h = cases
+ This x -> f x
+ That y -> g y
+ Both x y -> h x y
+
+data.OneOrBoth.fold.doc : Doc
+data.OneOrBoth.fold.doc =
+ use Nat +
+ use OneOrBoth fold
+ use Text size
+ {{
+ Applies one of three functions to a value of type {type OneOrBoth}. If the
+ value is {This}, the first function is applied. If the value is {That}, the
+ second function is applied. If the value is {Both}, the third function is
+ applied.
+
+ # Examples
+
+ ```
+ fold id size (x y -> x + size y) (That "hello")
+ ```
+
+ ```
+ fold id size (x y -> x + size y) (Both 42 "hello")
+ ```
+
+ ```
+ fold id size (x y -> x + size y) (This 42)
+ ```
+ }}
+
+data.OneOrBoth.isBoth : OneOrBoth a b -> Boolean
+data.OneOrBoth.isBoth = cases
+ This _ -> false
+ That _ -> false
+ Both _ _ -> true
+
+data.OneOrBoth.isBoth.doc : Doc
+data.OneOrBoth.isBoth.doc =
+ {{
+ Returns `true` if the value is {Both}, and `false` otherwise.
+
+ # Examples
+
+ ```
+ isBoth (This 42)
+ ```
+
+ ```
+ isBoth (That "hello")
+ ```
+
+ ```
+ isBoth (Both 42 "hello")
+ ```
+
+ # See also
+
+ * {isThis} - returns `true` if the value is {This}.
+ * {isThat} - returns `true` if the value is {That}.
+ }}
+
+data.OneOrBoth.isThat : OneOrBoth a b -> Boolean
+data.OneOrBoth.isThat = cases
+ This _ -> false
+ That _ -> true
+ Both _ _ -> false
+
+data.OneOrBoth.isThat.doc : Doc
+data.OneOrBoth.isThat.doc =
+ {{
+ Returns `true` if the value is {That}, and `false` otherwise.
+
+ # Examples
+
+ ```
+ isThat (This 42)
+ ```
+
+ ```
+ isThat (That "hello")
+ ```
+
+ ```
+ isThat (Both 42 "hello")
+ ```
+
+ # See also
+
+ * {isThis} - exactly the opposite of this function.
+ * {isBoth} - returns `true` if the value is {Both}.
+ }}
+
+data.OneOrBoth.isThis : OneOrBoth a b -> Boolean
+data.OneOrBoth.isThis = cases
+ This _ -> true
+ That _ -> false
+ Both _ _ -> false
+
+data.OneOrBoth.isThis.doc : Doc
+data.OneOrBoth.isThis.doc =
+ {{
+ Returns `true` if the value is {This}, and `false` otherwise.
+
+ # Examples
+
+ ```
+ isThis (This 42)
+ ```
+
+ ```
+ isThis (That "hello")
+ ```
+
+ ```
+ isThis (Both 42 "hello")
+ ```
+
+ # See also
+
+ * {isThat} - exactly the opposite of this function.
+ * {isBoth} - returns `true` if the value is {Both}.
+ }}
+
+data.OneOrBoth.joinThat :
+ (b ->{g1} b ->{g} b) -> OneOrBoth (OneOrBoth a b) b ->{g1, g} OneOrBoth a b
+data.OneOrBoth.joinThat f = cases
+ This x -> x
+ That y -> That y
+ Both (This x) y -> Both x y
+ Both (That x) y -> That (f x y)
+ Both (Both x y) z -> Both x (f y z)
+
+data.OneOrBoth.joinThat.doc : Doc
+data.OneOrBoth.joinThat.doc =
+ use Text ++
+ {{
+ Joins a {That} value of a {type OneOrBoth} with the {That} value of a nested
+ {type OneOrBoth} using a function. If the value is {That}, the value is
+ returned. If the value is {This}, the nested value is returned. If the value
+ is {Both}, the function is applied to the two {That} values.
+
+ # Examples
+
+ ```
+ joinThat (++) (That "hello")
+ ```
+
+ ```
+ joinThat (++) (This (That "hello"))
+ ```
+
+ ```
+ joinThat (++) (Both (This 42) "hello")
+ ```
+
+ ```
+ joinThat (++) (Both (That "hello") "world")
+ ```
+
+ ```
+ joinThat (++) (Both (Both 42 "hello") "world")
+ ```
+
+ # See also
+
+ * {joinThis} - joins a {This} value of a {type OneOrBoth} with the {This}
+ value of a nested {type OneOrBoth} using a function.
+ }}
+
+data.OneOrBoth.joinThis :
+ (a ->{g1} a ->{g} a) -> OneOrBoth a (OneOrBoth a b) ->{g1, g} OneOrBoth a b
+data.OneOrBoth.joinThis f = cases
+ This x -> This x
+ That y -> y
+ Both x (This y) -> This (f x y)
+ Both x (That y) -> Both x y
+ Both x (Both y z) -> Both (f x y) z
+
+data.OneOrBoth.joinThis.doc : Doc
+data.OneOrBoth.joinThis.doc =
+ use Nat +
+ {{
+ Joins a {This} value of a {type OneOrBoth} with the {This} value of a nested
+ {type OneOrBoth} using a function. If the value is {This}, the value is
+ returned. If the value is {That}, the nested value is returned. If the value
+ is {Both}, the function is applied to the two {This} values.
+
+ # Examples
+
+ ```
+ joinThis (+) (This 42)
+ ```
+
+ ```
+ joinThis (+) (That (This 42))
+ ```
+
+ ```
+ joinThis (+) (Both 42 (This 42))
+ ```
+
+ ```
+ joinThis (+) (Both 42 (That "hello"))
+ ```
+
+ ```
+ joinThis (+) (Both 42 (Both 42 "hello"))
+ ```
+
+ # See also
+
+ * {joinThat} - joins a {That} value of a {type OneOrBoth} with the {That}
+ value of a nested {type OneOrBoth} using a function.
+ }}
+
+data.OneOrBoth.justBoth : OneOrBoth a b -> Optional (a, b)
+data.OneOrBoth.justBoth = cases
+ This _ -> None
+ That _ -> None
+ Both x y -> Some (x, y)
+
+data.OneOrBoth.justBoth.doc : Doc
+data.OneOrBoth.justBoth.doc =
+ {{
+ Returns the {Both} value of a {type OneOrBoth} as an {type Optional}. If the
+ value is {This} or {That}, {None} is returned. If the value is {Both}, the
+ values are returned as a pair in {Some}.
+
+ # Examples
+
+ ```
+ justBoth (This 42)
+ ```
+
+ ```
+ justBoth (That "hello")
+ ```
+
+ ```
+ justBoth (Both 42 "hello")
+ ```
+
+ # See also
+
+ * {toTuple} - a variant that uses fallback values for {This} and {That}.
+ * {OneOrBoth.merge} - a variant that merges the values using a function.
+ }}
+
+data.OneOrBoth.justThat : OneOrBoth a b -> Optional b
+data.OneOrBoth.justThat = cases
+ This _ -> None
+ That y -> Some y
+ Both _ _ -> None
+
+data.OneOrBoth.justThat.doc : Doc
+data.OneOrBoth.justThat.doc =
+ {{
+ Returns the {That} value of a {type OneOrBoth} as an {type Optional}. If the
+ value is {That}, the value is returned as {Some}. If the value is {This} or
+ {Both}, {None} is returned.
+
+ # Examples
+
+ ```
+ justThat (This 42)
+ ```
+
+ ```
+ justThat (That "hello")
+ ```
+
+ ```
+ justThat (Both 42 "hello")
+ ```
+
+ # See also
+
+ * {maybeThat} - a variant that returns {Some} in the {Both} case.
+ * {justThis} - exactly the opposite of this function.
+ }}
+
+data.OneOrBoth.justThese : [OneOrBoth a b] -> [a]
+data.OneOrBoth.justThese oob = Each.toList do
+ use Each fail
+ x = each oob
+ match x with
+ This a -> a
+ That _ -> fail()
+ Both _ _ -> fail()
+
+data.OneOrBoth.justThese.doc : Doc
+data.OneOrBoth.justThese.doc =
+ {{
+ Extracts the {This} values from a list of {type OneOrBoth} values. If a value
+ is {That} or {Both}, it is skipped.
+
+ # Examples
+
+ ```
+ justThese [This 1, That "hello", Both 42 "world"]
+ ```
+
+ ```
+ justThese [This 1, This 2, This 3]
+ ```
+
+ ```
+ justThese [That "hello", That "world", That "universe"]
+ ```
+
+ # See also
+
+ * {these} - a variant that extracts the left sides of {Both} values as
+ well.
+ * {justThose} - exactly the opposite of this function.
+ }}
+
+data.OneOrBoth.justThis : OneOrBoth a b -> Optional a
+data.OneOrBoth.justThis = cases
+ This x -> Some x
+ That _ -> None
+ Both _ _ -> None
+
+data.OneOrBoth.justThis.doc : Doc
+data.OneOrBoth.justThis.doc =
+ {{
+ Returns the {This} value of a {type OneOrBoth} as an {type Optional}. If the
+ value is {This}, the value is returned as {Some}. If the value is {That} or
+ {Both}, {None} is returned.
+
+ # Examples
+
+ ```
+ justThis (This 42)
+ ```
+
+ ```
+ justThis (That "hello")
+ ```
+
+ ```
+ justThis (Both 42 "hello")
+ ```
+
+ # See also
+
+ * {maybeThis} - a variant that returns {Some} in the {Both} case.
+ * {justThat} - exactly the opposite of this function.
+ }}
+
+data.OneOrBoth.justThose : [OneOrBoth a b] -> [b]
+data.OneOrBoth.justThose oob = Each.toList do
+ use Each fail
+ x = each oob
+ match x with
+ This _ -> fail()
+ That b -> b
+ Both _ b -> fail()
+
+data.OneOrBoth.justThose.doc : Doc
+data.OneOrBoth.justThose.doc =
+ {{
+ Extracts the {That} values from a list of {type OneOrBoth} values. If a value
+ is {This} or {Both}, it is skipped.
+
+ # Examples
+
+ ```
+ justThose [This 1, That "hello", Both 42 "world"]
+ ```
+
+ ```
+ justThose [That "hello", That "world", That "universe"]
+ ```
+
+ ```
+ justThose [That "hello", That "world", That "universe"]
+ ```
+
+ # See also
+
+ * {those} - a variant that extracts the right sides of {Both} values as
+ well.
+ * {justThese} - exactly the opposite of this function.
+ }}
+
+data.OneOrBoth.mapBoth :
+ (a ->{f} c) -> (b ->{g} d) -> OneOrBoth a b ->{f, g} OneOrBoth c d
+data.OneOrBoth.mapBoth f g = cases
+ This x -> This (f x)
+ That y -> That (g y)
+ Both x y -> Both (f x) (g y)
+
+data.OneOrBoth.mapBoth.doc : Doc
+data.OneOrBoth.mapBoth.doc =
+ use Text size toUppercase
+ {{
+ Applies two functions to a value of type {type OneOrBoth}. If the value is
+ {This}, the first function is applied. If the value is {That}, the second
+ function is applied. If the value is {Both}, both functions are applied.
+
+ # Examples
+
+ ```
+ mapBoth size toUppercase (That "hello")
+ ```
+
+ ```
+ mapBoth size toUppercase (Both "yes" "hello")
+ ```
+
+ ```
+ mapBoth size toUppercase (This "hello")
+ ```
+
+ # See also
+
+ * {mapThis} - applies a function to the {This} value.
+ * {mapThat} - applies a function to the {That} value.
+ }}
+
+data.OneOrBoth.mapThat : (b ->{g} d) -> OneOrBoth a b ->{g} OneOrBoth a d
+data.OneOrBoth.mapThat f = cases
+ This x -> This x
+ That y -> That (f y)
+ Both x y -> Both x (f y)
+
+data.OneOrBoth.mapThat.doc : Doc
+data.OneOrBoth.mapThat.doc =
+ use Text toUppercase
+ {{
+ Applies a function to the {That} value of a {type OneOrBoth}. If the value is
+ {This}, the value is returned unchanged. If the value is {That}, the function
+ is applied. If the value is {Both}, the function is applied to the right
+ value.
+
+ # Examples
+
+ ```
+ mapThat toUppercase (That "hello")
+ ```
+
+ ```
+ mapThat toUppercase (Both "yes" "hello")
+ ```
+
+ ```
+ mapThat toUppercase (This "hello")
+ ```
+
+ # See also
+
+ * {mapThis} - applies a function to the {This} value.
+ * {mapBoth} - applies a function to both values.
+ }}
+
+data.OneOrBoth.mapThis : (a ->{g} c) -> OneOrBoth a b ->{g} OneOrBoth c b
+data.OneOrBoth.mapThis f = cases
+ This x -> This (f x)
+ That y -> That y
+ Both x y -> Both (f x) y
+
+data.OneOrBoth.mapThis.doc : Doc
+data.OneOrBoth.mapThis.doc =
+ use Text toUppercase
+ {{
+ Applies a function to the {This} value of a {type OneOrBoth}. If the value is
+ {This}, the function is applied. If the value is {That}, the value is
+ returned unchanged. If the value is {Both}, the function is applied to the
+ left value.
+
+ # Examples
+
+ ```
+ mapThis toUppercase (That "hello")
+ ```
+
+ ```
+ mapThis toUppercase (Both "yes" "hello")
+ ```
+
+ ```
+ mapThis toUppercase (This "hello")
+ ```
+
+ # See also
+
+ * {mapThat} - applies a function to the {That} value.
+ * {mapBoth} - applies a function to both values.
+ }}
+
+data.OneOrBoth.maybeThat : OneOrBoth a b -> Optional b
+data.OneOrBoth.maybeThat = cases
+ This _ -> None
+ That y -> Some y
+ Both _ y -> Some y
+
+data.OneOrBoth.maybeThat.doc : Doc
+data.OneOrBoth.maybeThat.doc =
+ {{
+ Returns the {That} value of a {type OneOrBoth} as an {type Optional}. If the
+ value is {This}, {None} is returned. If the value is {That}, the value is
+ returned as {Some}. If the value is {Both}, the right value is returned as
+ {Some}.
+
+ # Examples
+
+ ```
+ maybeThat (This 42)
+ ```
+
+ ```
+ maybeThat (That "hello")
+ ```
+
+ ```
+ maybeThat (Both 42 "hello")
+ ```
+
+ # See also
+
+ * {justThat} - a variant that returns {None} in the {Both} case.
+ * {maybeThis} - exactly the opposite of this function.
+ }}
+
+data.OneOrBoth.maybeThis : OneOrBoth a b -> Optional a
+data.OneOrBoth.maybeThis = cases
+ This x -> Some x
+ That _ -> None
+ Both x _ -> Some x
+
+data.OneOrBoth.maybeThis.doc : Doc
+data.OneOrBoth.maybeThis.doc =
+ {{
+ Returns the {This} value of a {type OneOrBoth} as an {type Optional}. If the
+ value is {This}, the value is returned as {Some}. If the value is {That},
+ {None} is returned. If the value is {Both}, the left value is returned as
+ {Some}.
+
+ # Examples
+
+ ```
+ maybeThis (This 42)
+ ```
+
+ ```
+ maybeThis (That "hello")
+ ```
+
+ ```
+ maybeThis (Both 42 "hello")
+ ```
+
+ # See also
+
+ * {justThis} - a variant that returns {None} in the {Both} case.
+ * {maybeThat} - exactly the opposite of this function.
+ }}
+
+data.OneOrBoth.merge : (a ->{f} a ->{g} a) -> OneOrBoth a a ->{f, g} a
+data.OneOrBoth.merge f = cases
+ This x -> x
+ That y -> y
+ Both x y -> f x y
+
+data.OneOrBoth.merge.doc : Doc
+data.OneOrBoth.merge.doc =
+ use Nat +
+ use OneOrBoth merge
+ {{
+ Merges a value of type {type OneOrBoth} into a single value using a function.
+ If the value is {This} or {That} the value is returned. If the value is
+ {Both}, the function is applied to the two values.
+
+ # Examples
+
+ ```
+ merge (+) (That 42)
+ ```
+
+ ```
+ merge (+) (Both 42 42)
+ ```
+
+ ```
+ merge (+) (This 42)
+ ```
+
+ # See also
+
+ * {toTuple} - returns the values as a pair.
+ * {justBoth} - returns the values as a pair in {Some}.
+ }}
+
+data.OneOrBoth.partition : [OneOrBoth a b] -> ([a], [b], [(a, b)])
+data.OneOrBoth.partition oob =
+ use List :+
+ go = cases
+ (as, bs, boths) ->
+ cases
+ This a -> (as :+ a, bs, boths)
+ That b -> (as, bs :+ b, boths)
+ Both a b -> (as, bs, boths :+ (a, b))
+ List.foldLeft go ([], [], []) oob
+
+data.OneOrBoth.partition.doc : Doc
+data.OneOrBoth.partition.doc =
+ use OneOrBoth partition
+ {{
+ Partitions a list of {type OneOrBoth} values into three lists: one containing
+ the {This} values, one containing the {That} values, and one containing the
+ {Both} values.
+
+ # Examples
+
+ ```
+ partition [This 1, That "hello", Both 42 "world"]
+ ```
+
+ ```
+ partition [This 1, This 2, This 3]
+ ```
+
+ ```
+ partition [That "hello", That "world", That "universe"]
+ ```
+
+ # See also
+
+ * {these} - extracts the {This} values and the left sides of the {Both}
+ values.
+ * {those} - extracts the {That} values and the right sides of the {Both}
+ values.
+ * {justThese} - extracts the {This} values but not the {Both} values.
+ {Both} values.
+ * {justThose} - extracts the {That} values but not the {Both} values.
+ }}
+
+data.OneOrBoth.these : [OneOrBoth a b] -> [a]
+data.OneOrBoth.these oob = Each.toList do
+ x = each oob
+ match x with
+ This a -> a
+ That _ -> Each.fail()
+ Both a _ -> a
+
+data.OneOrBoth.these.doc : Doc
+data.OneOrBoth.these.doc =
+ {{
+ Extracts the {This} values from a list of {type OneOrBoth} values. If a value
+ is {That}, it is skipped. If a value is {Both}, the left value is extracted.
+
+ # Examples
+
+ ```
+ these [This 1, That "hello", Both 42 "world"]
+ ```
+
+ ```
+ these [This 1, This 2, This 3]
+ ```
+
+ ```
+ these [That "hello", That "world", That "universe"]
+ ```
+
+ # See also
+
+ * {justThese} - a variant that skips {Both} values.
+ * {those} - exactly the opposite of this function.
+ }}
+
+data.OneOrBoth.those : [OneOrBoth a b] -> [b]
+data.OneOrBoth.those oob = Each.toList do
+ x = each oob
+ match x with
+ This _ -> Each.fail()
+ That b -> b
+ Both _ b -> b
+
+data.OneOrBoth.those.doc : Doc
+data.OneOrBoth.those.doc =
+ {{
+ Extracts the {That} values from a list of {type OneOrBoth} values. If a value
+ is {This}, it is skipped. If a value is {Both}, the right value is extracted.
+
+ # Examples
+
+ ```
+ those [This 1, That "hello", Both 42 "world"]
+ ```
+
+ ```
+ those [That "hello", That "world", That "universe"]
+ ```
+
+ ```
+ those [That "hello", That "world", That "universe"]
+ ```
+
+ # See also
+
+ * {justThose} - a variant that skips {Both} values.
+ * {these} - exactly the opposite of this function.
+ }}
+
+data.OneOrBoth.toTuple : OneOrBoth a b -> a -> b -> (a, b)
+data.OneOrBoth.toTuple = cases
+ This x, _, b -> (x, b)
+ That y, a, _ -> (a, y)
+ Both x y, _, _ -> (x, y)
+
+data.OneOrBoth.toTuple.doc : Doc
+data.OneOrBoth.toTuple.doc =
+ {{
+ Returns a pair of values from a {type OneOrBoth} value and two fallback
+ values. If the value is {This}, the first fallback value is used. If the
+ value is {That}, the second fallback value is used. If the value is {Both},
+ the fallback values are ignored and the values from the {Both} are used.
+
+ # Examples
+
+ ```
+ toTuple (That "hello") 0 ""
+ ```
+
+ ```
+ toTuple (Both 42 "hello") 0 ""
+ ```
+
+ ```
+ toTuple (This 42) 0 ""
+ ```
+
+ # See also
+
+ * {OneOrBoth.merge} - merges the values using a function.
+ * {justBoth} - returns the values as a pair in {Some}.
+ }}
+
+data.SeqView.doc : Doc
+data.SeqView.doc =
+ {{
+ A value of this type is a possibly empty pair. It is used internally by the
+ Unison compiler to represent pattern matching on sequences.
+ }}
+
+data.SeqView.VElem.doc : Doc
+data.SeqView.VElem.doc = {{ The non-empty case of {type SeqView}. }}
+
+data.SeqView.VEmpty.doc : Doc
+data.SeqView.VEmpty.doc = {{ The empty case of {type SeqView}. }}
+
+(data.Set.==) : Set k -> Set k -> Boolean
+x data.Set.== y =
+ use Map ==
+ use Set.internal underlying
+ underlying x == underlying y
+
+data.Set.==.doc : Doc
+data.Set.==.doc =
+ use Set == empty fromText singleton
+ {{
+ Checks if two {type Set}s are equal according to {Universal.ordering} on the
+ elements.
+
+ ```
+ empty == empty
+ ```
+
+ ```
+ fromText "🍎🍊🍐🍇" == fromText "🍇🍐🍊🍎"
+ ```
+
+ ```
+ singleton 4 == singleton 3
+ ```
+ }}
+
+data.Set.all : (a ->{e} Boolean) -> Set a ->{e} Boolean
+data.Set.all p = List.all p << Set.toList
+
+data.Set.all.doc : Doc
+data.Set.all.doc =
+ use Set all fromList
+ {{
+ `` all p s `` returns `` true `` if the {type Boolean} function `p` returns
+ `` true `` for __all__ elements of the {type Set} `s`, or if the {type Set}
+ `s` is empty. Otherwise ``false``.
+
+ # Examples
+
+ ```
+ all Nat.isEven (fromList [2, 4, 6, 8])
+ ```
+
+ ```
+ all Nat.isOdd (fromList [])
+ ```
+
+ ```
+ all
+ isWhitespace (Set.fromText "I'm not always right, but I'm never wrong.")
+ ```
+ }}
+
+data.Set.any : (a ->{e} Boolean) -> Set a ->{e} Boolean
+data.Set.any p = List.any p << Set.toList
+
+data.Set.any.doc : Doc
+data.Set.any.doc =
+ use Nat isEven
+ use Set any fromList
+ {{
+ `` any p s `` returns `` true `` if the {type Boolean} function `p` returns
+ `` true `` for __any__ elements of the {type Set} `s`. Otherwise ``false``,
+ including if the {type Set} is empty.
+
+ # Examples
+
+ ```
+ any isEven (fromList [5, 6, 7])
+ ```
+
+ ```
+ any isEven (fromList [5, 9, 7])
+ ```
+
+ ```
+ any Nat.isOdd (fromList [])
+ ```
+
+ ```
+ any
+ isWhitespace (Set.fromText "I'm not always right, but I'm never wrong.")
+ ```
+ }}
+
+data.Set.contains : k -> Set k -> Boolean
+data.Set.contains k = cases internal.Set m -> Map.contains k m
+
+data.Set.contains.doc : Doc
+data.Set.contains.doc =
+ {{
+ Checks if a {type Set} contains a given element.
+
+ # Example
+
+ ```
+ Set.contains ?🍎 (Set.fromText "🍎🍊🍐🍇")
+ ```
+ }}
+
+data.Set.delete : k -> Set k -> Set k
+data.Set.delete k s = internal.Set (Map.delete k (Set.internal.underlying s))
+
+data.Set.delete.doc : Doc
+data.Set.delete.doc =
+ {{
+ Deletes the given element from the {type Set}, if present.
+
+ # Example
+
+ ```
+ Set.toText (Set.delete ?🍊 (Set.fromText "🍎🍊🍐🍋"))
+ ```
+ }}
+
+test> data.Set.delete.test = runs 100 do
+ xs = setOf natInOrder ()
+ x = natInOrder()
+ expect (Boolean.not (Set.contains x (Set.delete x xs)))
+
+data.Set.deletes : [a] -> Set a -> Set a
+data.Set.deletes as s = List.foldLeft (s a -> Set.delete a s) s as
+
+data.Set.deletes.doc : Doc
+data.Set.deletes.doc =
+ use Set deletes
+ {{
+ `` deletes as s `` deletes multiple elements from `as`.
+
+ ```
+ deletes [1, 2, 3] (Set.fromList [1, 2, 3, 4, 5, 6]) |> Set.toList
+ ```
+ }}
+
+test> data.Set.deletes.test = test.verify do
+ use Set deletes
+ i = Each.range 0 20
+ j = Each.range 0 25
+ as = List.range 0 i
+ s = Set.fromList as
+ ensure (Set.delete j s === deletes [j] s)
+ ensure (deletes as s === Set.empty)
+
+data.Set.difference : Set a -> Set a -> Set a
+data.Set.difference xs ys =
+ use internal Set
+ (Set x) = xs
+ (Set y) = ys
+ z = Map.difference x y
+ Set z
+
+data.Set.difference.doc : Doc
+data.Set.difference.doc =
+ use Set fromList
+ {{
+ {{
+ Aside
+ {{
+ The __set difference__ of `A` and `B` is also known as the
+ __relative complement__ of `B` in `A`.
+ }} }} `Set.difference` returns the
+ [__set difference__](https://en.wikipedia.org/wiki/Complement_(set_theory) of
+ two {type Set}s.
+
+ ```
+ xs = fromList [?a, ?b, ?c, ?d, ?e, ?f]
+ ys = fromList [?d, ?e, ?f]
+ Set.toList (Set.difference xs ys)
+ ```
+ }}
+
+test> data.Set.difference.test =
+ test.verify do
+ use Random listOf nat natIn
+ use Set fromList toList
+ _ = Each.range 0 100
+ xs = listOf nat do natIn 0 100
+ ys = listOf nat do natIn 0 100
+ ensureEqual
+ (toList (Set.difference (fromList xs) (fromList ys)))
+ (toList (Set.deletes ys (fromList xs)))
+
+data.Set.doc : Doc
+data.Set.doc =
+ use Set == all any contains delete deletes elementAt empty flatMap flatten foldLeft foldRight fromList fromText insert intersect intersects map singleton size subset superset toList toMap union unions
+ use Universal ordering
+ {{
+ {type Set} is a sorted finite set, supporting efficient lookup and deletion
+ of elements. Sorting is done according to {ordering}.
+
+ # Constructing sets
+
+ {empty} is the empty {type Set}:
+
+ @signature{empty}
+
+ {singleton} creates a {type Set} with one element:
+
+ @signature{singleton}
+
+ {fromList} creates a {type Set} from a {type List}:
+
+ @signature{fromList}
+
+ {fromText} creates a {type Set} of {type Char} values from a {type Text}
+ value:
+
+ @signature{fromText}
+
+ # Inserting and deleting elements
+
+ {insert} adds an element to a {type Set}:
+
+ @signature{insert}
+
+ {delete} removes an element from a {type Set}:
+
+ @signature{delete}
+
+ {deletes} removes a whole {type List} of elements from a {type Set}:
+
+ @signature{deletes}
+
+ # Accessing and querying elements
+
+ `` elementAt n s `` gets the `n`th smallest element from the {type Set}
+ `s`:
+
+ @signature{elementAt}
+
+ {all} checks if all elements satisfy a predicate:
+
+ @signature{all}
+
+ {any} checks if at least one element satisfies a predicate:
+
+ @signature{any}
+
+ {contains} checks if a specified element is in the {type Set}:
+
+ @signature{contains}
+
+ {size} gets the number of elements in the {type Set}:
+
+ @signature{size}
+
+ # Combining sets
+
+ {intersect} gets the intersection of two {type Set}s:
+
+ @signature{intersect}
+
+ {intersects} gets the intersection of a whole {type List} of {type Set}s:
+
+ @signature{intersects}
+
+ {union} gets the union of two {type Set}s:
+
+ @signature{union}
+
+ {unions} gets the union of a whole {type List} of {type Set}s:
+
+ @signature{unions}
+
+ {flatten} gets the union of a whole {type Set} of {type Set}s:
+
+ @signature{flatten}
+
+ # Comparing sets
+
+ {==} checks if two {type Set}s have all the same elements according to
+ {ordering}:
+
+ @signature{==}
+
+ `` subset x y `` checks if `x` is a subset of `y`:
+
+ @signature{subset}
+
+ `` superset x y `` checks if `x` is a superset of `y`:
+
+ @signature{superset}
+
+ # Set traversals
+
+ {map} applies a function to every element of a {type Set}, collecting the
+ results in a new {type Set}:
+
+ @signature{map}
+
+ {flatMap} applies a {type Set}-valued function to every element, unioning
+ the results:
+
+ @signature{flatMap}
+
+ {foldLeft} passes each element to an accumulating function starting with an
+ initial value, in ascending order:
+
+ @signature{foldLeft}
+
+ {foldRight} passes each element to an accumulating function, starting with
+ an initial value, in descending order.
+
+ @signature{foldRight}
+
+ # Conversions to other types
+
+ {toList} gets the {type List} of elements in a {type Set}:
+
+ @signature{toList}
+
+ {toMap} generates a {type Map} where the elements of the {type Set} are the
+ keys, and the values are a function of the keys.
+
+ @signature{toMap}
+
+ {Set.toText} turns a {type Set} of {type Char} Unicode code points to the
+ {type Text} consisting of those code points in ascending order.
+
+ # Implementation notes
+
+ The type is implemented as a {type Map} whose values are of type
+ {type Unit}.
+
+ ```
+ fromList ["a", "b", "c", "d", "e", "f"]
+ ```
+
+ The size of each subtree is cached at non-leaf nodes, and the entry at each
+ node has a key which is bigger than all entries in the left subtree, and
+ smaller than all entries in the right subtree. Operations like {insert} and
+ {delete} maintain a balanced tree so its depth is logarithmic in the
+ {size}.
+ }}
+
+data.Set.elementAt : Nat -> Set a -> Optional a
+data.Set.elementAt n = cases
+ internal.Set underlying -> Optional.map at1 (Map.nth n underlying)
+
+data.Set.elementAt.doc : Doc
+data.Set.elementAt.doc =
+ use Set elementAt
+ {{
+ `` elementAt i s `` returns the `i`-th (0-based) element of `s`.
+
+ Is the same as {{ docExample 2 do i as -> List.at i (Set.toList as) }} but
+ doesn't require instantiating the intermediate {type List}.
+
+ ```
+ s = Set.fromList [6, 5, 4, 2, 1, 3]
+ List.map (i -> elementAt i s) (List.range 0 (Set.size s))
+ ```
+ }}
+
+test> data.Set.elementAt.tests =
+ test.verify do
+ use Random natIn
+ Each.repeat 100
+ s = (List.replicate (natIn 0 20) do natIn 0 20) |> Set.fromList
+ ensure
+ (List.somes
+ (List.map (i -> Set.elementAt i s) (List.range 0 (Set.size s)))
+ === Set.toList s)
+
+data.Set.empty : Set k
+data.Set.empty = internal.Set Map.empty
+
+data.Set.empty.doc : Doc
+data.Set.empty.doc = {{ The empty {type Set}. }}
+
+data.Set.flatMap : (i ->{g} Set k) -> Set i ->{g} Set k
+data.Set.flatMap f as = Set.foldLeft (b a -> Set.union b (f a)) Set.empty as
+
+data.Set.flatMap.doc : Doc
+data.Set.flatMap.doc =
+ use Set flatMap
+ {{
+ `` flatMap f s `` applies the {type Set}-valued function `f` to every element
+ in the {type Set} `s` and unions the results into a single {type Set}.
+
+ # Example
+
+ ```
+ Set.toText
+ (flatMap Set.fromText (Set.fromList ["please", "and", "thanks"]))
+ ```
+ }}
+
+test> data.Set.flatMap.tests.associative = runs 100 do
+ use Map get
+ use Optional getOrElse
+ use Set empty flatMap
+ use tests mapOf
+ xs = setOf natInOrder ()
+ ys = mapOf natInOrder (setOf natInOrder) ()
+ zs = mapOf natInOrder (setOf natInOrder) ()
+ f x = getOrElse empty (get x ys)
+ g y = getOrElse empty (get y zs)
+ r = flatMap f (flatMap g xs) === flatMap (x -> flatMap f (g x)) xs
+ expect r
+
+test> data.Set.flatMap.tests.unit = runs 100 do
+ use Set == flatMap singleton
+ n = natInOrder()
+ xs = setOf natInOrder ()
+ ys = tests.mapOf natInOrder (setOf natInOrder) ()
+ f x = Optional.getOrElse Set.empty (Map.get x ys)
+ left = flatMap f (singleton n) == f n
+ right = flatMap singleton xs == xs
+ expect (left && right)
+
+data.Set.flatten : Set (Set k) -> Set k
+data.Set.flatten = Set.flatMap id
+
+data.Set.flatten.doc : Doc
+data.Set.flatten.doc =
+ {{
+ Unions all the sets in a {type Set}.
+
+ # Example
+
+ ```
+ Set.toText
+ (Set.flatten
+ (Set.fromList (List.map Set.fromText ["please", "and", "thanks"])))
+ ```
+ }}
+
+test> data.Set.flatten.tests.associative = runs 100 do
+ use Set flatten
+ xs = setOf (setOf (setOf natInOrder)) ()
+ expect (flatten (flatten xs) === flatten (Set.map flatten xs))
+
+test> data.Set.flatten.tests.unit = runs 100 do
+ use Set == flatten singleton
+ xs = setOf natInOrder ()
+ expect (flatten (singleton xs) == flatten (Set.map singleton xs))
+
+data.Set.foldLeft : (b ->{e} a ->{e} b) -> b -> Set a ->{e} b
+data.Set.foldLeft f b s =
+ List.foldLeft f b (Map.keys (Set.internal.underlying s))
+
+data.Set.foldLeft.doc : Doc
+data.Set.foldLeft.doc =
+ use Nat -
+ {{
+ Folds the elements in the {type Set} in ascending order (according to
+ {Universal.ordering}), associating to the left, using the given binary
+ operator.
+
+ # Example
+
+ ```
+ Set.foldLeft (-) 100 (Set.fromList [3, 5, 2])
+ ```
+ }}
+
+test> data.Set.foldLeft.tests.homomorphism = runs 100 do
+ use List :+
+ xs = setOf natInOrder ()
+ expect (Set.foldLeft (:+) [] xs === Set.toList xs)
+
+data.Set.foldRight : (a ->{e} b ->{e} b) -> b -> Set a ->{e} b
+data.Set.foldRight f b s =
+ List.foldRight f b (Map.keys (Set.internal.underlying s))
+
+data.Set.foldRight.doc : Doc
+data.Set.foldRight.doc =
+ use Text ++
+ {{
+ Folds the elements in the {type Set} in ascending order (according to
+ {Universal.ordering}), associating to the right, using the given binary
+ operator.
+
+ # Example
+
+ ```
+ Set.foldRight (++) "!" (Set.fromList ["yes", "no", "maybe"])
+ ```
+ }}
+
+test> data.Set.foldRight.tests.homomorphism = runs 100 do
+ use List +:
+ xs = setOf natInOrder ()
+ expect (Set.foldRight (+:) [] xs === Set.toList xs)
+
+data.Set.fromList : [k] -> Set k
+data.Set.fromList ks = internal.Set (Map.fromList (List.map (k -> (k, ())) ks))
+
+data.Set.fromList.doc : Doc
+data.Set.fromList.doc =
+ use Set fromList toList
+ {{
+ Gets the {type Set} of elements in a {type List}.
+
+ # Examples
+
+ ```
+ toList (fromList [])
+ ```
+
+ ```
+ toList (fromList [5, 6, 7, 5, 9, 7, 6])
+ ```
+ }}
+
+data.Set.inertNonempty.doc : Doc
+data.Set.inertNonempty.doc =
+ use Set insertNonempty
+ use Set.Nonempty toList
+ {{
+ Insert a value into a {type Set}, returning a nonempty set.
+
+ # Example
+
+ ```
+ toList (insertNonempty ?🌸 Set.empty)
+ ```
+
+ ```
+ toList (insertNonempty ?🍎 (Set.fromList [?🍊, ?🍐]))
+ ```
+
+ # See Also
+
+ * {Set.insert}
+ }}
+
+data.Set.insert : k -> Set k -> Set k
+data.Set.insert k = cases internal.Set s -> internal.Set (Map.insert k () s)
+
+data.Set.insert.doc : Doc
+data.Set.insert.doc =
+ use Set fromText insert toText
+ {{
+ `` insert k s `` inserts the element `k` into the {type Set} `s`. Returns `s`
+ if `k` is already in `s`.
+
+ ```
+ toText (insert ?🐶 (fromText "🐱🐭🐹"))
+ ```
+
+ ```
+ toText (insert ?🍎 (fromText "🍎🍊🍐🍇"))
+ ```
+ }}
+
+data.Set.insertNonempty : k -> Set k -> Set.Nonempty k
+data.Set.insertNonempty k = cases
+ internal.Set s -> Nonempty.Set (Map.insertNonempty k () s)
+
+data.Set.insertNonempty.doc : Doc
+data.Set.insertNonempty.doc =
+ use Set insertNonempty
+ {{
+ {insertNonempty} inserts an element into a {type Set}, and returns a
+ {type Set.Nonempty}. If the element was already in the set, the original set
+ is returned as a {type Set.Nonempty}.
+
+ # Examples
+
+ ```
+ Set.Nonempty.toList (insertNonempty 1 (Set.fromList [2, 3, 4]))
+ ```
+
+ ```
+ Nonempty.toText (insertNonempty ?c (Set.fromText "abracadabra"))
+ ```
+ }}
+
+data.Set.internal.underlying : Set k -> Map k ()
+data.Set.internal.underlying = cases internal.Set s -> s
+
+data.Set.intersect : Set k -> Set k -> Set k
+data.Set.intersect s1 s2 =
+ use Set.internal underlying
+ internal.Set (Map.intersect (underlying s1) (underlying s2))
+
+data.Set.intersect.doc : Doc
+data.Set.intersect.doc =
+ use Set empty fromText intersect toList
+ {{
+ `` intersect x y `` returns the {type Set} of elements that are common to
+ both `x` and `y`.
+
+ # Examples
+
+ ```
+ Set.toText (intersect (fromText "🏈🏀⚾️🎾") (fromText "🏐🏈🏉🎱"))
+ ```
+
+ The intersection with the empty {type Set} is always empty:
+
+ ```
+ toList (intersect (fromText "🏠🚿🛁") empty)
+ ```
+
+ ```
+ toList (intersect empty (fromText "🏠🚿🛁"))
+ ```
+ }}
+
+data.Set.intersects : [Set a] -> Set a
+data.Set.intersects = cases
+ [] -> Set.empty
+ s +: ss -> List.foldLeft Set.intersect s ss
+
+data.Set.intersects.doc : Doc
+data.Set.intersects.doc =
+ use Set fromList
+ {{
+ Finds the intersection of all the sets in a list. See {Set.intersect}.
+
+ # Example
+
+ ```
+ Set.intersects [fromList [1, 2, 3], fromList [3, 4, 5]]
+ ```
+ }}
+
+test> data.Set.intersects.test =
+ check
+ ([[1, 2, 3], [3, 4, 5]] |> List.map Set.fromList |> Set.intersects
+ |> Set.toList
+ |> (===) [3])
+
+data.Set.isEmpty : Set k -> Boolean
+data.Set.isEmpty = cases internal.Set m -> Map.isEmpty m
+
+data.Set.isEmpty.doc : Doc
+data.Set.isEmpty.doc =
+ use Set isEmpty
+ {{
+ Returns `` true `` if the provided {type Set} has no elements and `` false ``
+ otherwise.
+
+ # Examples
+
+ ```
+ isEmpty Set.empty
+ ```
+
+ ```
+ isEmpty (Set.singleton 42)
+ ```
+
+ ```
+ isEmpty (Set.fromList [1, 2, 3])
+ ```
+ }}
+
+data.Set.map : (a ->{e} b) -> Set a ->{e} Set b
+data.Set.map f s = internal.Set (Map.mapKeys f (Set.internal.underlying s))
+
+data.Set.map.doc : Doc
+data.Set.map.doc =
+ {{
+ Applies the given function to every element in the {type Set}, collecting the
+ results into a new {type Set}.
+
+ # Example
+
+ ```
+ Set.toText
+ (Set.map ascii.toUpper (Set.fromText "Don't sweat the small stuff."))
+ ```
+ }}
+
+test> data.Set.map.test = runs 100 do
+ use Nat * +
+ use Set map
+ ks = setOf natInOrder ()
+ f x = x * 2
+ g x = x + 2
+ expect (map (f << g) ks === map f (map g ks))
+
+(data.Set.Nonempty.==) : Set.Nonempty k -> Set.Nonempty k -> Boolean
+x data.Set.Nonempty.== y =
+ use Map.Nonempty ==
+ use Nonempty.internal underlying
+ underlying x == underlying y
+
+data.Set.Nonempty.==.doc : Doc
+data.Set.Nonempty.==.doc =
+ use Set.Nonempty ==
+ {{
+ Checks if two sets are equal by comparing their elements using
+ {Universal.ordering}. Two sets are equal if they contain the same elements.
+
+ # Examples
+
+ ```
+ fromListAnd 1 [2, 3] == fromListAnd 1 [3, 2, 2]
+ ```
+
+ ```
+ fromListAnd 1 [2, 3] == fromListAnd 1 [3]
+ ```
+ }}
+
+data.Set.Nonempty.all : (a ->{e} Boolean) -> Set.Nonempty a ->{e} Boolean
+data.Set.Nonempty.all p = List.all p << Set.toList << Set.Nonempty.toSet
+
+data.Set.Nonempty.all.doc : Doc
+data.Set.Nonempty.all.doc =
+ use Nat isEven
+ use Nonempty all
+ {{
+ Checks if all elements in a {type Set.Nonempty} satisfy a predicate.
+
+ # Examples
+
+ ```
+ all isEven (fromListAnd 2 [4, 6])
+ ```
+
+ ```
+ all isEven (fromListAnd 2 [4, 5])
+ ```
+ }}
+
+data.Set.Nonempty.any : (a ->{e} Boolean) -> Set.Nonempty a ->{e} Boolean
+data.Set.Nonempty.any p = List.any p << Set.toList << Set.Nonempty.toSet
+
+data.Set.Nonempty.any.doc : Doc
+data.Set.Nonempty.any.doc =
+ use Nat isEven
+ use Nonempty any
+ {{
+ Checks if any element in a {type Set.Nonempty} satisfies a predicate.
+
+ # Examples
+
+ ```
+ any isEven (fromListAnd 2 [4, 6])
+ ```
+
+ ```
+ any isEven (fromListAnd 2 [3, 5])
+ ```
+ }}
+
+data.Set.Nonempty.contains : k -> Set.Nonempty k -> Boolean
+data.Set.Nonempty.contains k = cases
+ Nonempty.Set m -> Map.Nonempty.contains k m
+
+data.Set.Nonempty.contains.doc : Doc
+data.Set.Nonempty.contains.doc =
+ use Set.Nonempty contains
+ {{
+ Checks if a {type Set.Nonempty} contains an element.
+
+ # Examples
+
+ ```
+ contains 2 (fromListAnd 1 [2, 3])
+ ```
+
+ ```
+ contains 2 (fromListAnd 1 [3])
+ ```
+ }}
+
+data.Set.Nonempty.delete : k -> Set.Nonempty k -> Set k
+data.Set.Nonempty.delete k s =
+ internal.Set (Map.Nonempty.delete k (Nonempty.internal.underlying s))
+
+data.Set.Nonempty.delete.doc : Doc
+data.Set.Nonempty.delete.doc =
+ use Set toList
+ use Set.Nonempty delete
+ {{
+ Deletes an element from a {type Set.Nonempty}, returning a {type Set}.
+
+ # Examples
+
+ ```
+ toList (delete 2 (fromListAnd 1 [2, 3]))
+ ```
+
+ ```
+ toList (delete 2 (fromListAnd 1 [3]))
+ ```
+ }}
+
+data.Set.Nonempty.deletes : [a] -> Set.Nonempty a -> Set a
+data.Set.Nonempty.deletes as s =
+ List.foldLeft (s a -> Set.delete a s) (Set.Nonempty.toSet s) as
+
+data.Set.Nonempty.deletes.doc : Doc
+data.Set.Nonempty.deletes.doc =
+ use Nonempty deletes
+ use Set toList
+ {{
+ Deletes a {type List} of elements from a {type Set.Nonempty}, returning a
+ {type Set}.
+
+ # Examples
+
+ ```
+ toList (deletes [2, 3] (fromListAnd 1 [2, 3]))
+ ```
+
+ ```
+ toList (deletes [2, 3] (fromListAnd 1 [3]))
+ ```
+ }}
+
+data.Set.Nonempty.doc : Doc
+data.Set.Nonempty.doc =
+ use Nonempty all any deletes elementAt flatten intersects
+ use Set insertNonempty
+ use Set.Nonempty == contains delete flatMap foldLeft foldRight fromList insert intersect map singleton size subset superset toList toMap union unions
+ use Universal ordering
+ {{
+ {type Set.Nonempty} is the non-empty version of {type Set}. It is a sorted
+ finite set with at least one element. Sorting is done according to
+ {ordering}.
+
+ # Constructing nonempty sets
+
+ {singleton} creates a {type Set.Nonempty} with one element:
+
+ @signature{singleton}
+
+ {fromList} creates a {type Set.Nonempty} from a {type List.Nonempty}:
+
+ @signature{fromList}
+
+ {fromListAnd} creates a {type Set.Nonempty} from an element and a
+ {type List}:
+
+ @signature{fromListAnd}
+
+ {fromTextAnd} creates a {type Set.Nonempty} from a {type Char} and a
+ {type Text}:
+
+ @signature{fromTextAnd}
+
+ {insertNonempty} creates a {type Set.Nonempty} from a {type Set} and an
+ element:
+
+ @signature{insertNonempty}
+
+ # Inserting and deleting elements
+
+ {insert} adds an element to a {type Set.Nonempty}:
+
+ @signature{insert}
+
+ {delete} removes an element from a {type Set.Nonempty}, returning a
+ (possibly empty) {type Set}.
+
+ @signature{delete}
+
+ {deletes} removes a whole {type List} of elements from a
+ {type Set.Nonempty}, returning a (possibly empty) {type Set}.
+
+ @signature{deletes}
+
+ # Accessing and querying elements
+
+ `` elementAt n s `` gets the `n`th smallest element from the
+ {type Set.Nonempty} `s`:
+
+ @signature{elementAt}
+
+ {all} checks if all elements satisfy a predicate:
+
+ @signature{all}
+
+ {any} checks if at least one element satisfies a predicate:
+
+ @signature{any}
+
+ {contains} checks if a specified element is in the {type Set.Nonempty}:
+
+ @signature{contains}
+
+ {size} gets the number of elements in the {type Set.Nonempty}:
+
+ @signature{size}
+
+ # Combining sets
+
+ {intersect} gets the intersection of two {type Set.Nonempty}s, as a
+ {type Set}:
+
+ @signature{intersect}
+
+ {intersects} gets the intersection of a whole {type List} of
+ {type Set.Nonempty}s, as a {type Set}:
+
+ @signature{intersects}
+
+ {union} gets the union of two {type Set.Nonempty}s:
+
+ @signature{union}
+
+ {unions} gets the union of a whole {type List} of {type Set.Nonempty}s:
+
+ @signature{unions}
+
+ {flatten} gets the union of a whole {type Set.Nonempty} of
+ {type Set.Nonempty}s:
+
+ @signature{flatten}
+
+ # Comparing sets
+
+ {==} checks if two {type Set.Nonempty}s have all the same elements
+ according to {ordering}:
+
+ @signature{==}
+
+ `` subset x y `` checks if `x` is a subset of `y`:
+
+ @signature{subset}
+
+ `` superset x y `` checks if `x` is a superset of `y`:
+
+ @signature{superset}
+
+ # Set.Nonempty traversals
+
+ {map} applies a function to every element of a {type Set.Nonempty},
+ collecting the results in a new {type Set.Nonempty}:
+
+ @signature{map}
+
+ {flatMap} applies a {type Set.Nonempty}-valued function to every element,
+ unioning the results:
+
+ @signature{flatMap}
+
+ {foldLeft} passes each element to an accumulating function starting with an
+ initial value, in ascending order:
+
+ @signature{foldLeft}
+
+ {foldRight} passes each element to an accumulating function, starting with
+ an initial value, in descending order.
+
+ @signature{foldRight}
+
+ # Conversions to other types
+
+ {toList} gets the {type List} of elements in a {type Set.Nonempty}:
+
+ @signature{toList}
+
+ {toMap} generates a {type Map.Nonempty} where the elements of the
+ {type Set.Nonempty} are the keys, and the values are a function of the
+ keys.
+
+ @signature{toMap}
+
+ {Nonempty.toText} turns a {type Set.Nonempty} of {type Char} Unicode code
+ points to the {type Text} consisting of those code points in ascending
+ order.
+
+ # Implementation notes
+
+ The type is implemented as a {type Map.Nonempty} whose values are of type
+ {type Unit}.
+
+ ```
+ fromList ("a" +| ["b", "c", "d", "e", "f"])
+ ```
+
+ The size of each subtree is cached at non-leaf nodes, and the entry at each
+ node has a key which is bigger than all entries in the left subtree, and
+ smaller than all entries in the right subtree. Operations like {insert} and
+ {delete} maintain a balanced tree so its depth is logarithmic in the
+ {size}.
+ }}
+
+data.Set.Nonempty.elementAt : Nat -> Set.Nonempty a -> Optional a
+data.Set.Nonempty.elementAt n = cases
+ Nonempty.Set underlying -> Optional.map at1 (Map.Nonempty.nth n underlying)
+
+data.Set.Nonempty.elementAt.doc : Doc
+data.Set.Nonempty.elementAt.doc =
+ use Nat +
+ use Nonempty elementAt
+ {{
+ `` elementAt n s `` gets the `n`th smallest element from the
+ {type Set.Nonempty} `s`.
+
+ If the {type Set.Nonempty} has fewer than `` n + 1 `` elements, this function
+ returns {None}.
+
+ Otherwise, this function returns {Some} with the `n`th smallest element.
+
+ # Examples
+
+ ```
+ elementAt 0 (fromListAnd 1 [2, 3])
+ ```
+
+ ```
+ elementAt 1 (fromListAnd 1 [2, 3])
+ ```
+
+ ```
+ elementAt 20 (fromListAnd 1 [2, 3])
+ ```
+ }}
+
+data.Set.Nonempty.flatMap :
+ (i ->{g} Set.Nonempty k) -> Set.Nonempty i ->{g} Set.Nonempty k
+data.Set.Nonempty.flatMap f as =
+ use Set.Nonempty toList
+ Set.Nonempty.fromList (List.Nonempty.flatMap (a -> toList (f a)) (toList as))
+
+data.Set.Nonempty.flatMap.doc : Doc
+data.Set.Nonempty.flatMap.doc =
+ use Set.Nonempty flatMap
+ {{
+ {flatMap} applies a {type Set.Nonempty}-valued function to every element,
+ unioning the results.
+
+ # Example
+
+ ```
+ Nonempty.toText
+ (flatMap
+ (a -> fromTextAnd a (Text.toUppercase (Char.toText a)))
+ (fromTextAnd ?a "bracadabra"))
+ ```
+ }}
+
+data.Set.Nonempty.flatten : Set.Nonempty (Set.Nonempty k) -> Set.Nonempty k
+data.Set.Nonempty.flatten = Set.Nonempty.flatMap id
+
+data.Set.Nonempty.flatten.doc : Doc
+data.Set.Nonempty.flatten.doc =
+ use Nonempty flatten
+ {{
+ {flatten} gets the union of a whole {type Set.Nonempty} of
+ {type Set.Nonempty}s.
+
+ # Example
+
+ ```
+ Nonempty.toText
+ (flatten
+ (fromListAnd
+ (fromTextAnd ?a "abc") [fromTextAnd ?b "def", fromTextAnd ?c "ghi"]))
+ ```
+ }}
+
+data.Set.Nonempty.foldLeft : (b ->{e} a ->{e} b) -> b -> Set.Nonempty a ->{e} b
+data.Set.Nonempty.foldLeft f b s =
+ List.Nonempty.foldLeft
+ f b (Map.Nonempty.keys (Nonempty.internal.underlying s))
+
+data.Set.Nonempty.foldLeft.doc : Doc
+data.Set.Nonempty.foldLeft.doc =
+ use List :+
+ use Nat +
+ use Set.Nonempty foldLeft
+ {{
+ {foldLeft} passes each element to an accumulating function starting with an
+ initial value, in ascending order, associating to the left.
+
+ # Examples
+
+ ```
+ foldLeft (a b -> a + b) 0 (fromListAnd 1 [2, 3])
+ ```
+
+ ```
+ foldLeft (a b -> a :+ b) [] (fromListAnd 5 [2, 4, 4, 2, 6])
+ ```
+ }}
+
+data.Set.Nonempty.foldMap :
+ (b ->{e} b ->{e} b) -> (a ->{e} b) -> Set.Nonempty a ->{e} b
+data.Set.Nonempty.foldMap f g s =
+ List.Nonempty.foldMap
+ f g (Map.Nonempty.keys (Nonempty.internal.underlying s))
+
+data.Set.Nonempty.foldMap.doc : Doc
+data.Set.Nonempty.foldMap.doc =
+ use Nat +
+ {{
+ {Set.Nonempty.foldMap} combines all elements of a {type Set.Nonempty} into
+ one using a combining function, and then maps the result using a mapping
+ function.
+
+ # Example
+
+ ```
+ Set.Nonempty.foldMap (+) Nat.increment (fromListAnd 1 [2, 3])
+ ```
+
+ # See also
+
+ * {List.Nonempty.foldMap}
+ }}
+
+data.Set.Nonempty.foldRight :
+ (a ->{e} b ->{e} b) -> b -> Set.Nonempty a ->{e} b
+data.Set.Nonempty.foldRight f b s =
+ List.Nonempty.foldRight
+ f b (Map.Nonempty.keys (Nonempty.internal.underlying s))
+
+data.Set.Nonempty.foldRight.doc : Doc
+data.Set.Nonempty.foldRight.doc =
+ use List +:
+ use Nat +
+ use Set.Nonempty foldRight
+ {{
+ {foldRight} passes each element to an accumulating function, starting with an
+ initial value, in ascending order, associating to the right.
+
+ # Examples
+
+ ```
+ foldRight (a b -> a + b) 0 (fromListAnd 1 [2, 3])
+ ```
+
+ ```
+ foldRight (a b -> a +: b) [] (fromListAnd 5 [2, 4, 4, 2, 6])
+ ```
+ }}
+
+data.Set.Nonempty.fromList : List.Nonempty k -> Set.Nonempty k
+data.Set.Nonempty.fromList ks =
+ Nonempty.Set (toNonemptyMap (List.Nonempty.map (k -> (k, ())) ks))
+
+data.Set.Nonempty.fromList.doc : Doc
+data.Set.Nonempty.fromList.doc =
+ use Set.Nonempty fromList
+ {{
+ {fromList} gets a {type Set.Nonempty} from a {type List.Nonempty}.
+
+ # Example
+
+ ```
+ Set.Nonempty.toList (fromList (5 +| [2, 4, 4, 2, 6]))
+ ```
+ }}
+
+data.Set.Nonempty.fromListAnd : k -> [k] -> Set.Nonempty k
+data.Set.Nonempty.fromListAnd k ks = Set.Nonempty.fromList (k +| ks)
+
+data.Set.Nonempty.fromListAnd.doc : Doc
+data.Set.Nonempty.fromListAnd.doc =
+ use Set.Nonempty toList
+ {{
+ Creates a {type Set.Nonempty} from an element a (possibly empty) {type List}
+ of elements.
+
+ # Examples
+
+ ```
+ toList (fromListAnd 1 [2, 3])
+ ```
+
+ ```
+ toList (fromListAnd 1 [2, 3, 2])
+ ```
+ }}
+
+data.Set.Nonempty.fromTextAnd : Char -> Text -> Set.Nonempty Char
+data.Set.Nonempty.fromTextAnd c t = Set.Nonempty.fromList (c +| toCharList t)
+
+data.Set.Nonempty.fromTextAnd.doc : Doc
+data.Set.Nonempty.fromTextAnd.doc =
+ use Nonempty toText
+ {{
+ Creates a {type Set.Nonempty} from a {type Char} and a {type Text}.
+
+ # Examples
+
+ ```
+ toText (fromTextAnd ?🐯 "🔹🔸🔹")
+ ```
+
+ ```
+ toText (fromTextAnd ?e "spionage")
+ ```
+ }}
+
+data.Set.Nonempty.insert : k -> Set.Nonempty k -> Set.Nonempty k
+data.Set.Nonempty.insert k = cases
+ Nonempty.Set s -> Nonempty.Set (Map.Nonempty.insert k () s)
+
+data.Set.Nonempty.insert.doc : Doc
+data.Set.Nonempty.insert.doc =
+ use Set.Nonempty insert
+ {{
+ {insert} adds an element to a {type Set.Nonempty}.
+
+ # Example
+
+ ```
+ Set.Nonempty.toList (insert 3 (fromListAnd 1 [2, 4]))
+ ```
+ }}
+
+data.Set.Nonempty.internal.underlying : Set.Nonempty k -> Map.Nonempty k ()
+data.Set.Nonempty.internal.underlying = cases Nonempty.Set s -> s
+
+data.Set.Nonempty.intersect : Set.Nonempty k -> Set.Nonempty k -> Set k
+data.Set.Nonempty.intersect s1 s2 =
+ use Map.Nonempty toMap
+ use Nonempty.internal underlying
+ internal.Set (Map.intersect (toMap (underlying s1)) (toMap (underlying s2)))
+
+data.Set.Nonempty.intersect.doc : Doc
+data.Set.Nonempty.intersect.doc =
+ use Set.Nonempty intersect
+ {{
+ {intersect} gets the intersection of two {type Set.Nonempty}s.
+
+ # Example
+
+ ```
+ Set.toList (intersect (fromListAnd 1 [2, 3]) (fromListAnd 2 [3, 4]))
+ ```
+ }}
+
+data.Set.Nonempty.intersects : [Set.Nonempty a] -> Set a
+data.Set.Nonempty.intersects = cases
+ [] -> Set.empty
+ s +: ss ->
+ List.foldLeft
+ (acc s -> Set.intersect acc (Set.Nonempty.toSet s))
+ (Set.Nonempty.toSet s)
+ ss
+
+data.Set.Nonempty.intersects.doc : Doc
+data.Set.Nonempty.intersects.doc =
+ use Nonempty intersects
+ {{
+ {intersects} gets the intersection of a {type List} of {type Set.Nonempty}s.
+
+ # Example
+
+ ```
+ Set.toList
+ (intersects
+ [fromListAnd 1 [2, 3], fromListAnd 2 [3, 4], fromListAnd 3 [4, 5]])
+ ```
+ }}
+
+data.Set.Nonempty.map : (a ->{e} b) -> Set.Nonempty a ->{e} Set.Nonempty b
+data.Set.Nonempty.map f s =
+ Nonempty.Set (Nonempty.mapKeys f (Nonempty.internal.underlying s))
+
+data.Set.Nonempty.map.doc : Doc
+data.Set.Nonempty.map.doc =
+ use Set.Nonempty map
+ {{
+ {map} applies a function to every element of a {type Set.Nonempty}.
+
+ # Example
+
+ ```
+ Nonempty.toText (map Char.toUppercase (fromTextAnd ?a "bracadabra"))
+ ```
+ }}
+
+data.Set.Nonempty.random : Set.Nonempty a ->{Random} a
+data.Set.Nonempty.random s =
+ n = Random.natIn 0 (Set.Nonempty.size s)
+ getOrBug ("index out of bounds", n, s) (Nonempty.elementAt n s)
+
+data.Set.Nonempty.random.doc : Doc
+data.Set.Nonempty.random.doc =
+ use Nonempty random
+ {{
+ {random} gets a random element from a {type Set.Nonempty}.
+
+ # Example
+
+ ```
+ lcg 0 do random (fromListAnd 1 [2, 3, 4, 5])
+ ```
+ }}
+
+data.Set.Nonempty.randomChoice : Set.Nonempty a ->{Random} a
+data.Set.Nonempty.randomChoice = cases
+ Nonempty.Set map ->
+ randomIndex = Random.natIn 0 (Map.Nonempty.size map)
+ Map.Nonempty.nth randomIndex map
+ |> getOrBug "Set.Nonempty.randomChoice: index out of bounds"
+ |> at1
+
+data.Set.Nonempty.randomChoice.doc : Doc
+data.Set.Nonempty.randomChoice.doc =
+ use Nonempty Nonempty
+ use Set.Nonempty fromList randomChoice
+ {{
+ Picks a random element from the given {type Set.Nonempty}.
+
+ # Examples
+
+ ```
+ lcg 4096 do randomChoice (fromList (Nonempty ?a [?b, ?c, ?d]))
+ ```
+
+ ```
+ lcg 2510 do randomChoice (fromList (Nonempty ?a [?b, ?c, ?d]))
+ ```
+ }}
+
+test> data.Set.Nonempty.randomChoice.test = test.verify do
+ set = Set.Nonempty.fromList (0 +| [1, 2, 3, 4, 5, 6, 7, 8, 9])
+ Each.repeat 1000
+ e = Set.Nonempty.randomChoice set
+ ensure (Set.Nonempty.contains e set)
+
+data.Set.Nonempty.reduce : (a ->{e} a ->{e} a) -> Set.Nonempty a ->{e} a
+data.Set.Nonempty.reduce f s =
+ reduceRight f (Map.Nonempty.keys (Nonempty.internal.underlying s))
+
+data.Set.Nonempty.reduce.doc : Doc
+data.Set.Nonempty.reduce.doc =
+ use Nat +
+ {{
+ {reduce} combines all elements of a {type Set.Nonempty} into one using a
+ combining function.
+
+ # Example
+
+ ```
+ reduce (+) (fromListAnd 1 [2, 3])
+ ```
+
+ # See also
+
+ * {reduceRight}
+ }}
+
+data.Set.Nonempty.similarity : Set.Nonempty k -> Set.Nonempty k -> Float
+data.Set.Nonempty.similarity a b =
+ use Float + - / > fromNat
+ use Set Nonempty.size
+ use Set.Nonempty toSet
+ i = fromNat (Set.size (Set.intersect (toSet a) (toSet b)))
+ s = fromNat (Nonempty.size a) + fromNat (Nonempty.size b) - i
+ if s > 0.0 then i / s else 1.0
+
+data.Set.Nonempty.similarity.doc : Doc
+data.Set.Nonempty.similarity.doc =
+ use Nonempty similarity
+ {{
+ Measures the similarity of two {type Set.Nonempty}s. The result is a number
+ between `` 0.0 `` and ``1.0``, where `` 0.0 `` means they have no elements in
+ common, and `` 1.0 `` means they are equal. It is calculated as the size of
+ the intersection divided by the size of the union.
+
+ # Examples
+
+ ```
+ similarity (fromListAnd 1 [2, 3]) (fromListAnd 2 [3, 4])
+ ```
+
+ ```
+ similarity (fromTextAnd ?c "arnivorous") (fromTextAnd ?c "oronavirus")
+ ```
+ }}
+
+data.Set.Nonempty.singleton : a -> Set.Nonempty a
+data.Set.Nonempty.singleton a = Nonempty.Set (Map.Nonempty.singleton a ())
+
+data.Set.Nonempty.singleton.doc : Doc
+data.Set.Nonempty.singleton.doc =
+ use Set.Nonempty singleton
+ {{
+ {singleton} constructs a {type Set.Nonempty} with a single element.
+
+ # Example
+
+ ```
+ Set.Nonempty.toList (singleton 1)
+ ```
+ }}
+
+data.Set.Nonempty.size : Set.Nonempty k -> Nat
+data.Set.Nonempty.size s = Map.Nonempty.size (Nonempty.internal.underlying s)
+
+data.Set.Nonempty.size.doc : Doc
+data.Set.Nonempty.size.doc =
+ use Set.Nonempty size
+ {{
+ {size} gets the number of elements in a {type Set.Nonempty}.
+
+ # Example
+
+ ```
+ size (fromListAnd 1 [2, 3])
+ ```
+ }}
+
+data.Set.Nonempty.subset : Set.Nonempty a -> Set.Nonempty a -> Boolean
+data.Set.Nonempty.subset s1 s2 =
+ Nonempty.all (a -> Set.Nonempty.contains a s2) s1
+
+data.Set.Nonempty.subset.doc : Doc
+data.Set.Nonempty.subset.doc =
+ use Set.Nonempty subset
+ {{
+ {subset} checks if a {type Set.Nonempty} is a subset of another. A
+ {type Set.Nonempty} is a subset of another if it contains no elements that
+ are not in the other.
+
+ Every {type Set.Nonempty} is a subset of itself.
+
+ # Example
+
+ ```
+ subset (fromListAnd 1 [2, 3]) (fromListAnd 2 [3, 4])
+ ```
+
+ ```
+ subset (fromTextAnd ?c "arnivorous") (fromTextAnd ?c "oronavirus")
+ ```
+ }}
+
+data.Set.Nonempty.superset : Set.Nonempty a -> Set.Nonempty a -> Boolean
+data.Set.Nonempty.superset = flip Set.Nonempty.subset
+
+data.Set.Nonempty.superset.doc : Doc
+data.Set.Nonempty.superset.doc =
+ use Set.Nonempty superset
+ {{
+ {superset} checks if a {type Set.Nonempty} is a superset of another. A
+ {type Set.Nonempty} is a superset of another if it contains all elements of
+ the other.
+
+ Every {type Set.Nonempty} is a superset of itself.
+
+ # Example
+
+ ```
+ superset (fromListAnd 1 [2, 3]) (fromListAnd 2 [3, 4])
+ ```
+
+ ```
+ superset (fromTextAnd ?c "arnivorous") (fromTextAnd ?c "oronavirus")
+ ```
+ }}
+
+data.Set.Nonempty.toList : Set.Nonempty k -> List.Nonempty k
+data.Set.Nonempty.toList = Map.Nonempty.keys << Nonempty.internal.underlying
+
+data.Set.Nonempty.toList.doc : Doc
+data.Set.Nonempty.toList.doc =
+ use Set.Nonempty toList
+ {{
+ {toList} converts a {type Set.Nonempty} to a {type List.Nonempty}.
+
+ # Example
+
+ ```
+ toList (fromListAnd 1 [2, 3])
+ ```
+ }}
+
+data.Set.Nonempty.toMap : (k ->{e} v) -> Set.Nonempty k ->{e} Map.Nonempty k v
+data.Set.Nonempty.toMap f =
+ Map.Nonempty.mapWithKey (k _ -> f k) << Nonempty.internal.underlying
+
+data.Set.Nonempty.toMap.doc : Doc
+data.Set.Nonempty.toMap.doc =
+ use Set.Nonempty toMap
+ {{
+ {toMap} converts a {type Set.Nonempty} to a {type Map.Nonempty}. The keys of
+ the map are the elements of the set, and the values are the result of
+ applying the given function to each key.
+
+ # Example
+
+ ```
+ Map.Nonempty.toList (toMap Char.toUppercase (fromTextAnd ?a "bracadabra"))
+ ```
+ }}
+
+data.Set.Nonempty.toSet : Set.Nonempty k -> Set k
+data.Set.Nonempty.toSet =
+ internal.Set << Map.Nonempty.toMap << Nonempty.internal.underlying
+
+data.Set.Nonempty.toSet.doc : Doc
+data.Set.Nonempty.toSet.doc =
+ use Set toList
+ use Set.Nonempty toSet
+ {{
+ Converts a {type Set.Nonempty} to a {type Set}.
+
+ # Examples
+
+ ```
+ toList (toSet (fromListAnd 1 [2, 3]))
+ ```
+
+ ```
+ toList (toSet (fromListAnd 1 [3]))
+ ```
+ }}
+
+data.Set.Nonempty.toText : Set.Nonempty Char -> Text
+data.Set.Nonempty.toText chars =
+ fromCharList (List.Nonempty.toList (Set.Nonempty.toList chars))
+
+data.Set.Nonempty.toText.doc : Doc
+data.Set.Nonempty.toText.doc =
+ use Nonempty toText
+ {{
+ {toText} converts a {type Set.Nonempty} of {type Char} to a {type Text}.
+
+ # Example
+
+ ```
+ toText (fromTextAnd ?c "arnivorous")
+ ```
+ }}
+
+data.Set.Nonempty.union : Set.Nonempty k -> Set.Nonempty k -> Set.Nonempty k
+data.Set.Nonempty.union s1 s2 =
+ use Nonempty.internal underlying
+ Nonempty.Set (Map.Nonempty.union (underlying s1) (underlying s2))
+
+data.Set.Nonempty.union.doc : Doc
+data.Set.Nonempty.union.doc =
+ use Set.Nonempty union
+ {{
+ {union} combines two {type Set.Nonempty}s into one. The result contains all
+ elements from both inputs.
+
+ # Example
+
+ ```
+ Set.Nonempty.toList (union (fromListAnd 1 [2, 3]) (fromListAnd 2 [3, 4]))
+ ```
+
+ ```
+ Nonempty.toText
+ (union (fromTextAnd ?c "arnivorous") (fromTextAnd ?c "oronavirus"))
+ ```
+ }}
+
+data.Set.Nonempty.unions : List.Nonempty (Set.Nonempty a) -> Set.Nonempty a
+data.Set.Nonempty.unions = cases
+ Nonempty.Nonempty s ss -> List.foldLeft Set.Nonempty.union s ss
+
+data.Set.Nonempty.unions.doc : Doc
+data.Set.Nonempty.unions.doc =
+ use Set.Nonempty unions
+ {{
+ {unions} combines a {type List} of {type Set.Nonempty}s into one. The result
+ contains all elements from all inputs.
+
+ # Example
+
+ ```
+ Set.Nonempty.toList
+ (unions (fromListAnd 1 [2, 3] +| [fromListAnd 2 [3, 4]]))
+ ```
+
+ ```
+ Nonempty.toText (unions (fromTextAnd ?b "ee" +| [fromTextAnd ?f "ly"]))
+ ```
+ }}
+
+data.Set.random : Set a ->{Random} Optional a
+data.Set.random s =
+ use Nat ==
+ use Set size
+ if size s == 0 then None else Set.elementAt (Random.natIn 0 (size s)) s
+
+data.Set.random.doc : Doc
+data.Set.random.doc =
+ {{
+ Returns a random element from a {type Set}.
+
+ # Example
+
+ ```
+ splitmix 42 do Set.random (Set.fromList [?🍎, ?🍌, ?🍊])
+ ```
+ }}
+
+data.Set.randomChoice : Set a ->{Exception, Random} a
+data.Set.randomChoice = cases internal.Set map -> Map.randomChoice map |> at1
+
+data.Set.randomChoice.doc : Doc
+data.Set.randomChoice.doc =
+ use Set fromList randomChoice
+ {{
+ Returns a random element from the given {type Set}. Assumes that the
+ {type Set} is not empty, so an empty {type Set} will cause a runtime
+ exception.
+
+ # Examples
+
+ ```
+ catch do lcg 4096 do randomChoice (fromList [0, 3, 5, 7])
+ ```
+
+ ```
+ catch do lcg 2510 do randomChoice (fromList [?x, ?y, ?z])
+ ```
+
+ ```
+ catch do lcg 128 do randomChoice (fromList [char.digit, hex]) ()
+ ```
+ }}
+
+test> data.Set.randomChoice.test = test.verify do
+ set = Set.fromList [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ Each.repeat 1000
+ e = Set.randomChoice set
+ ensure (Set.contains e set)
+
+data.Set.similarity : Set k -> Set k -> Float
+data.Set.similarity a b =
+ use Float + - / > fromNat
+ use Set size
+ i = fromNat (size (Set.intersect a b))
+ s = fromNat (size a) + fromNat (size b) - i
+ if s > 0.0 then i / s else 1.0
+
+data.Set.similarity.doc : Doc
+data.Set.similarity.doc =
+ use Set empty fromText similarity singleton
+ {{
+ Measures the similarity of two {type Set}s. This is the number of elements
+ they have in common, as a proportion of the total number of elements in their
+ union — a number between `` 0.0 `` (no common elements) and `` 1.0 `` (the
+ {type Set}s are identical).
+
+ # Examples
+
+ ```
+ similarity (fromText "France") (fromText "French")
+ ```
+
+ ```
+ similarity (singleton 1) (singleton 2)
+ ```
+
+ ```
+ similarity empty empty
+ ```
+ }}
+
+data.Set.singleton : a -> Set a
+data.Set.singleton a = Set.insert a Set.empty
+
+data.Set.singleton.doc : Doc
+data.Set.singleton.doc =
+ {{
+ Constructs a {type Set} with a single element.
+
+ # Example
+
+ ```
+ Set.toList (Set.singleton ?🤖)
+ ```
+ }}
+
+test> data.Set.singleton.test =
+ check
+ let
+ s = Set.singleton 0
+ Set.contains 0 s && Set.size s === 1
+
+data.Set.size : Set k -> Nat
+data.Set.size s = Map.size (Set.internal.underlying s)
+
+data.Set.size.doc : Doc
+data.Set.size.doc =
+ use Set size
+ {{
+ Gets the number of elements in a {type Set}.
+
+ # Examples
+
+ ```
+ size Set.empty
+ ```
+
+ ```
+ size (Set.fromList [1, 2, 3])
+ ```
+ }}
+
+data.Set.subset : Set a -> Set a -> Boolean
+data.Set.subset s1 s2 = Set.all (a -> Set.contains a s2) s1
+
+data.Set.subset.doc : Doc
+data.Set.subset.doc =
+ use Set fromText subset
+ {{
+ `` subset s1 s2 `` returns `` true `` if every element in `s1` is also in
+ `s2`. Otherwise ``false``.
+
+ # Examples
+
+ ```
+ subset (fromText "abc") (fromText "abcde")
+ ```
+
+ ```
+ subset (fromText "wxy") (fromText "xyz")
+ ```
+
+ Every {type Set} is a subset of itself:
+
+ ```
+ s = fromText "abc"
+ subset s s
+ ```
+ }}
+
+data.Set.superset : Set a -> Set a -> Boolean
+data.Set.superset = flip Set.subset
+
+data.Set.superset.doc : Doc
+data.Set.superset.doc =
+ use Set fromText superset
+ {{
+ `` superset s1 s2 `` returns `` true `` if every element in `s2` is also in
+ `s1`. Otherwise ``false``.
+
+ # Examples
+
+ ```
+ superset (fromText "abcde") (fromText "abc")
+ ```
+
+ ```
+ superset (fromText "wxy") (fromText "xyz")
+ ```
+
+ Every {type Set} is a superset of itself:
+
+ ```
+ s = fromText "abc"
+ superset s s
+ ```
+ }}
+
+data.Set.toList : Set k -> [k]
+data.Set.toList = Map.keys << Set.internal.underlying
+
+data.Set.toList.doc : Doc
+data.Set.toList.doc =
+ use Set toList
+ {{
+ Converts a {type Set} to a {type List} of the elements in that {type Set}.
+
+ # Examples
+
+ ```
+ toList (Set.fromText "🍕🌮🍔🍟")
+ ```
+
+ ```
+ toList Set.empty
+ ```
+ }}
+
+data.Set.toMap : (k ->{e} v) -> Set k ->{e} Map k v
+data.Set.toMap f = Map.mapWithKey (k _ -> f k) << Set.internal.underlying
+
+data.Set.toMap.doc : Doc
+data.Set.toMap.doc =
+ use Set toMap
+ {{
+ `` toMap v ks `` generates a {type Map} where the keys are the elements of
+ the {type Set} `ks`, and the values are generated by passing each key to the
+ function `v`.
+
+ # Examples
+
+ ```
+ Map.toList (toMap Text.size (Set.fromList ["one", "two", "three"]))
+ ```
+ }}
+
+data.Set.union : Set k -> Set k -> Set k
+data.Set.union s1 s2 =
+ use Set.internal underlying
+ internal.Set (Map.union (underlying s1) (underlying s2))
+
+data.Set.union.doc : Doc
+data.Set.union.doc =
+ use Set empty fromText toText union
+ {{
+ `` union x y `` returns the {type Set} of elements that are in either `x` or
+ `y`.
+
+ # Examples
+
+ ```
+ toText (union (fromText "🏈🏀⚾️🎾") (fromText "🏐🏈🏉🎱"))
+ ```
+
+ The union with the empty {type Set} is a no-op:
+
+ ```
+ toText (union (fromText "🏠🚿🛁") empty)
+ ```
+
+ ```
+ toText (union empty (fromText "🏠🚿🛁"))
+ ```
+ }}
+
+data.Set.unions : [Set a] -> Set a
+data.Set.unions = List.foldLeft Set.union Set.empty
+
+data.Set.unions.doc : Doc
+data.Set.unions.doc =
+ {{
+ Takes a {type List} of {type Set}s and unions them all into one {type Set}.
+
+ # Example
+
+ ```
+ Set.toText (Set.unions (List.map Set.fromText ["🍎🍊🍐🍇", "🥕🌽🥒🥦"]))
+ ```
+ }}
+
+test> data.Set.unions.test = runs 100 do
+ use Set ==
+ sets = gen.listOf (setOf natInOrder) ()
+ expect (Set.unions sets == Set.flatten (Set.fromList sets))
+
+(data.Stream.++) :
+ (v1 ->{g, Stream a} v2) -> (v2 ->{g, Stream a} v3) -> v1 ->{g, Stream a} v3
+a data.Stream.++ b = a >> b
+
+data.Stream.++.doc : Doc
+data.Stream.++.doc =
+ {{
+ `s1 ++ s2` appends stream `s2` to the end of `s1`. `(++)` is a special case
+ of `.base.andThen`, for `Stream` computations.
+ }}
+
+(data.Stream.+:) : a -> '{g, Stream a} r -> '{g, Stream a} r
+(data.Stream.+:) a s _ =
+ emit a
+ s()
+
+data.Stream.+:.doc : Doc
+data.Stream.+:.doc =
+ {{ `el +: s` prepends a single element `el` to a stream `s`. }}
+
+data.Stream.all : (a ->{g} Boolean) -> '{g, Stream a} r ->{g} Boolean
+data.Stream.all = compose2 (isNone << Stream.head) Stream.dropWhile
+
+data.Stream.all.doc : Doc
+data.Stream.all.doc =
+ use Nat < <=
+ use Stream all
+ {{
+ `` all p x `` returns `` true `` if every element of the {type Stream} `x`
+ satisfies predicate `p`.
+
+ # Examples
+
+ ```
+ to 10 |> all (x -> x <= 10)
+ ```
+
+ ```
+ to 10 |> all (x -> x < 10)
+ ```
+
+ ## See also
+
+ * {Stream.any}
+ }}
+
+test> data.Stream.all.test = test.verify do
+ use Nat < <=
+ use Stream all
+ s = to 10
+ ensureEqual (all (x -> x <= 10) s) true
+ ensureEqual (all (x -> x < 10) s) false
+
+data.Stream.any : (a ->{g} Boolean) -> '{g, Stream a} r ->{g} Boolean
+data.Stream.any = compose2 isSome Stream.find
+
+data.Stream.any.doc : Doc
+data.Stream.any.doc =
+ use Nat ==
+ use Stream any
+ {{
+ `` any p x `` returns `` true `` if at least one element of the {type Stream}
+ `x` satisfies predicate `p`.
+
+ # Examples
+
+ ```
+ to 10 |> any ((==) 10)
+ ```
+
+ ```
+ to 10 |> any ((==) 11)
+ ```
+
+ ## See also:
+
+ * {Stream.all}
+ }}
+
+test> data.Stream.any.test = test.verify do
+ use Nat ==
+ use Stream any
+ s = to 10
+ ensureEqual (any ((==) 10) s) true
+ ensureEqual (any ((==) 11) s) false
+
+data.Stream.changes : Boolean -> '{g, Stream a} r -> '{g, Stream a} r
+data.Stream.changes emitInitial stream = do changes! emitInitial stream
+
+data.Stream.changes.doc : Doc
+data.Stream.changes.doc =
+ {{
+ `` changes emitInitial stream `` returns a delayed computation of
+ ``changes! emitInitial stream``.
+ }}
+
+test> data.Stream.changes.tests.multiple = test.verify do
+ use Stream toList
+ input = Stream.fromList [1, 1, 1, 2, 3, 3, 4, 4, 4, 4, 5, 5]
+ let
+ res = input |> changes true |> toList
+ expected = [1, 2, 3, 4, 5]
+ ensureEqual res expected
+ res = input |> changes false |> toList
+ expected = [2, 3, 4, 5]
+ ensureEqual res expected
+
+test> data.Stream.changes.tests.pair = test.verify do
+ use Stream toList
+ input = Stream.rangeClosed 1 2
+ let
+ res = input |> changes false |> toList
+ expected = [2]
+ ensureEqual res expected
+ res = input |> changes true |> toList
+ expected = [1, 2]
+ ensureEqual res expected
+
+test> data.Stream.changes.tests.repeatSingle = test.verify do
+ use Stream fromList toList
+ input = [1, 1]
+ let
+ res = fromList [1, 1] |> changes false |> toList
+ expected = []
+ ensureEqual res expected
+ res = fromList [1, 1] |> changes true |> toList
+ expected = [1]
+ ensureEqual res expected
+
+test> data.Stream.changes.tests.single = test.verify do
+ use Stream toList
+ input = do emit 1
+ let
+ res = input |> changes false |> toList
+ expected = []
+ ensureEqual res expected
+ res = input |> changes true |> toList
+ expected = [1]
+ ensureEqual res expected
+
+data.Stream.changes! : Boolean -> '{g, Stream a} r ->{g, Stream a} r
+data.Stream.changes! = changesBy! (===)
+
+data.Stream.changes!.doc : Doc
+data.Stream.changes!.doc =
+ use Stream fromList toList
+ {{
+ `` changes! emitInitial stream `` transforms the input `stream` into a stream
+ that only contains elements that are not equal to the previous element
+ (according to [universal equality]({===})).
+
+ If `emitInitial` is true, the first element of the input stream is always
+ emitted. Otherwise the first element emitted in the output will be the first
+ element in the input that __differs__ from the initial element in the input.
+
+ See {changesBy!} for a version that uses a provided equality function instead
+ of universal equality.
+
+ # Examples
+
+ ```
+ toList do changes! true (fromList [1, 2, 2, 2, 3, 3, 4, 5])
+ ```
+
+ ```
+ toList do changes! false (fromList [1, 2, 2, 2, 3, 3, 4, 5])
+ ```
+
+ ```
+ toList do changes! true do emit 1
+ ```
+
+ ```
+ toList do changes! false do emit 1
+ ```
+ }}
+
+data.Stream.changesBy :
+ (a -> a -> Boolean) -> Boolean -> '{g, Stream a} r -> '{g, Stream a} r
+data.Stream.changesBy eq emitInitial stream =
+ do changesBy! eq emitInitial stream
+
+data.Stream.changesBy.doc : Doc
+data.Stream.changesBy.doc =
+ {{
+ `` changesBy eq emitInitial stream `` returns a delayed computation of
+ ``changesBy! eq emitInitial stream``.
+ }}
+
+data.Stream.changesBy! :
+ (a -> a -> Boolean) -> Boolean -> '{g, Stream a} r ->{g, Stream a} r
+data.Stream.changesBy! eq emitInitial stream =
+ go : a -> Request {Stream a} r ->{g, Stream a} r
+ go prev = cases
+ { r } -> r
+ { emit current -> rest } ->
+ if eq prev current then handle rest() with go current
+ else
+ emit current
+ handle rest() with go current
+ handle stream()
+ with cases
+ { r } -> r
+ { emit a -> k } ->
+ if emitInitial then emit a else ()
+ handle k() with go a
+
+data.Stream.changesBy!.doc : Doc
+data.Stream.changesBy!.doc =
+ use Nat ==
+ use Stream fromList toList
+ {{
+ `` changesBy! eq emitInitial stream `` transforms the input `stream` into a
+ stream that only contains elements that are not equal to the previous element
+ (according to `eq`).
+
+ If `emitInitial` is true, the first element of the input stream is always
+ emitted. Otherwise the first element emitted in the output will be the first
+ element in the input that __differs__ from the initial element in the input.
+
+ See {changes!} for a version that uses universal equality instead of a
+ provided equality function.
+
+ See {changesBy} for a version that returns a delayed result.
+
+ # Examples
+
+ ```
+ toList do changesBy! (==) true (fromList [1, 2, 2, 2, 3, 3, 4, 5])
+ ```
+
+ ```
+ toList do changesBy! (==) false (fromList [1, 2, 2, 2, 3, 3, 4, 5])
+ ```
+
+ ```
+ toList do changesBy! (==) true do emit 1
+ ```
+
+ ```
+ toList do changesBy! (==) false do emit 1
+ ```
+ }}
+
+data.Stream.chunk : Nat -> '{g, Stream a} r -> '{g, Stream (List.Nonempty a)} r
+data.Stream.chunk n = delay (chunk! n)
+
+data.Stream.chunk.doc : Doc
+data.Stream.chunk.doc =
+ {{ {Stream.chunk} returns a delayed computation of {chunk!}. }}
+
+test> data.Stream.chunk.tests =
+ test.verify do
+ use Each repeat run
+ use List.Nonempty singleton
+ use Random natIn
+ use Stream chunk rangeClosed
+ run do
+ repeat 100
+ n = Random.nat!
+ ensureEqual [] (Stream.toList (chunk n do ()))
+ ensureEqual [singleton 1] (Stream.toList (chunk 1 do emit 1))
+ ensureEqual
+ [singleton 1, singleton 2, singleton 3]
+ (Stream.toList (chunk 1 (rangeClosed 1 3)))
+ ensureEqual
+ [1 +| [2], singleton 3] (Stream.toList (chunk 2 (rangeClosed 1 3)))
+ ensureEqual [1 +| [2, 3]] (Stream.toList (chunk 3 (rangeClosed 1 3)))
+ ensureEqual [1 +| [2, 3]] (Stream.toList (chunk 4 (rangeClosed 1 3)))
+ ensureEqual
+ [1 +| [2, 3], 4 +| [5, 6], 7 +| [8, 9]]
+ (Stream.toList (chunk 3 (rangeClosed 1 9)))
+ ensureEqual
+ [1 +| [2, 3], 4 +| [5, 6], singleton 7]
+ (Stream.toList (chunk 3 (rangeClosed 1 7)))
+ run do
+ repeat 20
+ streamSize = do natIn 0 20
+ groupSize = natIn 1 20
+ initialList = Random.listOf Random.nat streamSize
+ finalList =
+ chunk groupSize (Stream.fromList initialList)
+ |> concatMap List.Nonempty.toList
+ |> Stream.toList
+ ensureEqual initialList finalList
+
+data.Stream.chunk! : Nat -> '{g, Stream a} r ->{g, Stream (List.Nonempty a)} r
+data.Stream.chunk! n =
+ use List.Nonempty singleton
+ use Nat == decrement
+ if n == 0 then
+ ((bug ("group size must be greater than 0", n)) :
+ ('{g, Stream a} r ->{g, Stream (List.Nonempty a)} r))
+ else
+ go acc need = cases
+ { r } ->
+ emit acc
+ r
+ { emit a -> k } ->
+ if need == 0 then
+ emit acc
+ handle k() with go (singleton a) (decrement n)
+ else handle k() with go (Nonempty.snoc acc a) (decrement need)
+ thunk ->
+ (handle thunk()
+ with cases
+ { r } -> r
+ { emit a -> k } -> handle k() with go (singleton a) (decrement n))
+
+data.Stream.chunk!.doc : Doc
+data.Stream.chunk!.doc =
+ use Stream chunk rangeClosed toList
+ {{
+ `` chunk! groupSize stream `` transforms a stream of `a` elements into a
+ stream of [nonempty lists]({type List.Nonempty}) of `a` elements. Each list
+ in the stream will have a length of exactly `groupSize`, except for the last
+ emitted list, which may have fewer elements.
+
+ {{
+ docCallout
+ (Some {{ ⚠️ }}) {{ This function will call {bug} if `groupSize` is `0`. }}
+ }}
+
+ # Examples
+
+ ```
+ chunk 3 (rangeClosed 1 10) |> toList
+ ```
+
+ ```
+ chunk 1 (rangeClosed 1 5) |> toList
+ ```
+ }}
+
+data.Stream.collate :
+ '{g1, Stream a} r -> '{g2, Stream a} Void -> '{g1, g2, Stream a} r
+data.Stream.collate finiteStream infiniteStream =
+ do collate! finiteStream infiniteStream
+
+data.Stream.collate.doc : Doc
+data.Stream.collate.doc =
+ {{
+ `data.Stream.intersperse finiteStream infiniteStream` returns a delayed
+ computation of `data.Stream.intersperse! finiteStream infiniteStream`
+ }}
+
+test> data.Stream.collate.test.testIntersperse = test.verify do
+ use Counter nat!
+ use Nat increment
+ use Stream tap
+ commas = (Stream.repeat do ",") |> (tap do comma!)
+ nats n = to n |> Stream.map Nat.toText |> (tap do nat!)
+ expected = "0,1,2,3,4,5"
+ actual =
+ handle collate (nats 5) commas |> Stream.toList |> Text.join ""
+ with
+ go cs ns = cases
+ { comma! -> k } -> handle k() with go (increment cs) ns
+ { nat! -> k } -> handle k() with go cs (increment ns)
+ { txt } ->
+ ensureEqual cs 5
+ ensureEqual ns 6
+ txt
+ go 0 0
+ ensureEqual expected actual
+
+data.Stream.collate! :
+ '{g1, Stream a} r -> '{g2, Stream a} Void ->{g1, g2, Stream a} r
+data.Stream.collate! finiteStream infiniteStream =
+ handle finiteStream() with handler3 infiniteStream
+
+data.Stream.collate!.doc : Doc
+data.Stream.collate!.doc =
+ {{
+ `Stream.intersperce! finiteStream infiniteStream` alternates between emitting
+ elements of the finiteStream and the infiniteStream, The resulting stream
+ terminates after emitting the final element of the finiteStream.
+
+ ```
+ nats = to 5 |> Stream.map Nat.toText
+ commas = Stream.repeat do ","
+ combined = do collate! nats commas
+ combined |> Stream.toList |> Text.join ""
+ ```
+ }}
+
+data.Stream.collate!.handler1 :
+ '{g2} Void -> Request (Stream a) r ->{g2, Stream a} r
+data.Stream.collate!.handler1 k = cases
+ { emit a -> s } -> handle k() with data.Stream.collate!.handler2 a s
+ { done } -> done
+
+data.Stream.collate!.handler2 :
+ a -> '{g2} r -> Request (Stream a) Void ->{g2, Stream a} r
+data.Stream.collate!.handler2 af k = cases
+ { emit ai -> s } ->
+ emit ai
+ emit af
+ handle k() with data.Stream.collate!.handler1 s
+
+data.Stream.collate!.handler3 :
+ '{g2} Void -> Request (Stream a) r ->{g2, Stream a} r
+data.Stream.collate!.handler3 k = cases
+ { emit a -> s } ->
+ emit a
+ handle s() with handler1 k
+ { done } -> done
+
+data.Stream.concatMap : (a ->{e} [b]) -> '{e, Stream a} r -> '{e, Stream b} r
+data.Stream.concatMap f s _ = concatMap! f s
+
+data.Stream.concatMap.doc : Doc
+data.Stream.concatMap.doc =
+ {{
+ `` concatMap f s `` returns a delayed computation of ``concatMap! f s``.
+ }}
+
+data.Stream.concatMap! : (a ->{e} [b]) -> '{e, Stream a} r ->{e, Stream b} r
+data.Stream.concatMap! f = flatMap! (a -> fromList! (f a))
+
+data.Stream.concatMap!.doc : Doc
+data.Stream.concatMap!.doc =
+ {{
+ `` concatMap! f s `` applies `f` to each element emitted by stream `s` in
+ turn and emits the elements of the resulting lists.
+ }}
+
+data.Stream.contains : a -> '{g, Stream a} r ->{g} Boolean
+data.Stream.contains x = isSome << Stream.find ((===) x)
+
+data.Stream.contains.doc : Doc
+data.Stream.contains.doc =
+ use Stream contains
+ {{
+ `` contains x s `` returns `` true `` if the element `x` is found in the
+ {type Stream} `s`.
+
+ # Examples
+
+ ```
+ to 10 |> contains 10
+ ```
+
+ ```
+ to 10 |> contains 11
+ ```
+
+ ## See also:
+
+ * {Stream.find}
+ }}
+
+test> data.Stream.contains.test = test.verify do
+ use Stream contains
+ s = to 10
+ ensureEqual (contains 5 s) true
+ ensureEqual (contains 11 s) false
+
+data.Stream.doc : Doc
+data.Stream.doc =
+ use Stream ++
+ {{
+ The {type Stream} ability provides a way to lazily generate a potentially
+ infinite sequence of values that can use other abilities in their
+ computation. The ability is parameterized by the type of values the stream
+ generates.
+
+ {{
+ docCallout
+ (Some {{ ℹ️ }})
+ {{
+ Operations on {type Stream} generally have a `!` suffix to indicate that
+ they are eager and will immediately emit values, and the same operation
+ without the `!` suffix will return a lazy computation that can be forced
+ later with (e.g. with {force}) to emit values.
+ }} }}
+
+ # Emitting values onto a stream
+
+ Emit a single value:
+
+ @signature{emit}
+
+ Emit a value and return it as the result:
+
+ @signatures{emitAndReturn!, emitAndReturn}
+
+ Emit a list of values:
+
+ @signatures{fromList!, Stream.fromList}
+
+ Emit all the {type Nat} values in order starting from a given value:
+
+ @signatures{Stream.from!, Stream.from}
+
+ Emit all the {type Nat} values in order starting from 0 and up to a given
+ value (inclusive):
+
+ @signatures{to!, to}
+
+ Emit all the {type Nat} values in order starting from 0 and up to a given
+ value (exclusive):
+
+ @signatures{until!, Stream.until}
+
+ Emit all the {type Nat} values in a range (exclusive of the end value):
+
+ @signatures{Stream.range!, Stream.range}
+
+ Emit all the {type Nat} values in a range (inclusive of the end value):
+
+ @signatures{rangeClosed!, Stream.rangeClosed}
+
+ Repeatedly force a computation to produce a stream of values:
+
+ @signature{Stream.repeat}
+
+ Iterate a function over a seed value to produce a stream of values:
+
+ @signatures{iterate!, Stream.iterate}
+
+ Iterate a state transition function over a seed value to produce a stream
+ of values:
+
+ @signature{Stream.unfold}
+
+ # Consuming values from a stream
+
+ Collect all the values from a stream into a list:
+
+ @signatures{Stream.toList, toDelayedList}
+
+ Collect all the values from a stream into a list and also return the final
+ result of the {type Stream} computation:
+
+ @signatures{toListWithResult, toDelayedListWithResult}
+
+ Take the first value from a stream and return it together with the
+ remainder of the stream:
+
+ @signature{Stream.uncons}
+
+ Ignore all the values in the stream, but force the computation to run its
+ effects:
+
+ @signature{drain}
+
+ Accumulate values from a stream with a function, starting with an initial
+ value:
+
+ @signatures{Stream.fold, foldDelayed}
+
+ Fold a stream in a balanced way by recursively splitting the stream in half
+ and folding each half:
+
+ @signature{Stream.foldBalanced}
+
+ Fold a stream, returning both the result of the fold and the result of the
+ {type Stream} computation:
+
+ @signatures{foldWithResult, foldDelayedWithResult}
+
+ Fold stream with a function, starting with an initial value, and emit
+ intermediate results:
+
+ @signatures{scan!, scan}
+
+ Apply an effectful function to each value in a stream:
+
+ @signature{Stream.foreach}
+
+ Pipe values from a stream into a computation that uses the {type Ask}
+ ability to consume them:
+
+ @signatures{pipe!, pipe}
+
+ # Transforming streams
+
+ Take a prefix of a stream:
+
+ @signatures{take!, Stream.take}
+
+ Drop a prefix of a stream:
+
+ @signatures{Stream.drop!, Stream.drop}
+
+ Take a prefix of a stream while a predicate holds:
+
+ @signatures{takeWhile!, Stream.takeWhile}
+
+ Map a function over a stream:
+
+ @signatures{Stream.map!, Stream.map}
+
+ Map a list-valued function over a stream, emitting the values in the list
+ for each input value:
+
+ @signatures{concatMap!, concatMap}
+
+ Remove values from a stream that do not satisfy a predicate:
+
+ @signatures{filter!, Stream.filter}
+
+ Map a stream-valued function over a stream, emitting the values in the
+ stream for each input value:
+
+ @signatures{flatMap!, Stream.flatMap}
+
+ Wrap all the values in a stream in a {Some} and emit a {None} at the end:
+
+ @signatures{terminated!, terminated}
+
+ # Combining streams
+
+ Concatenate two streams:
+
+ @signature{++}
+
+ Interleave two streams by alternating between values from each stream:
+
+ @signatures{interleave!, Stream.interleave}
+
+ Run two streams in parallel and emit values from both streams:
+
+ @signatures{zip!, Stream.zip}
+
+ Run two streams in parallel and apply a function to the values from each
+ stream, emitting the results:
+
+ @signatures{zipWith!, Stream.zipWith}
+ }}
+
+data.Stream.drain : '{g, Stream a} r ->{g} r
+data.Stream.drain s = at2 (foldWithResult (const const()) () s)
+
+data.Stream.drain.doc : Doc
+data.Stream.drain.doc =
+ {{
+ `` drain s `` consumes all elements from the {type Stream} `s` and discards
+ them.
+ }}
+
+data.Stream.drop : Nat -> '{g, Stream a} r -> '{g, Stream a} r
+data.Stream.drop n s _ = Stream.drop! n s
+
+data.Stream.drop.doc : Doc
+data.Stream.drop.doc =
+ {{ `Stream.drop n s` returns a delayed computation of `Stream.drop! n s`. }}
+
+test> data.Stream.drop.test = runs 100 do
+ ns = gen.listOf natInOrder ()
+ lim = gen.natIn 0 (List.size ns) ()
+ expected = (List.drop lim ns, "result")
+ stream = do
+ fromList! ns
+ "result"
+ actual = toListWithResult (Stream.drop lim stream)
+ expect (assertEquals actual expected)
+
+data.Stream.drop! : Nat -> '{g, Stream a} r ->{g, Stream a} r
+data.Stream.drop! n s =
+ go _ =
+ abilities.repeat n do ask
+ forever do emit ask
+ pipe! s go
+
+data.Stream.drop!.doc : Doc
+data.Stream.drop!.doc =
+ {{
+ `Stream.drop! n s` emits all elements of a stream computation `s` after
+ omitting the first `n` elements emitted by `s`.
+ }}
+
+data.Stream.dropWhile :
+ (a ->{e} Boolean) -> '{f, Stream a} r -> '{e, f, Stream a} r
+data.Stream.dropWhile p s = do match Stream.uncons s with
+ Left r -> r
+ Right (a, as) -> if p a then data.Stream.dropWhile p as () else s()
+
+data.Stream.dropWhile.doc : Doc
+data.Stream.dropWhile.doc =
+ {{
+ Drops elements from the stream while the given predicate is true. Returns the
+ rest of the stream. This is the lazy version of {dropWhile!}
+
+ # Example
+
+ ```
+ Stream.toList
+ (Stream.dropWhile Nat.isEven (Stream.fromList [2, 4, 6, 7, 8]))
+ ```
+ }}
+
+data.Stream.dropWhile! :
+ (a ->{e} Boolean) -> '{f, Stream a} r ->{e, f, Stream a} r
+data.Stream.dropWhile! p s = match Stream.uncons s with
+ Left r -> r
+ Right (a, as) -> if p a then data.Stream.dropWhile! p as else s()
+
+data.Stream.dropWhile!.doc : Doc
+data.Stream.dropWhile!.doc =
+ {{
+ Drops elements from the stream while the given predicate is true. Returns the
+ rest of the stream. This is the strict version of {Stream.dropWhile}.
+
+ # Example
+
+ ```
+ Stream.toList do dropWhile! Nat.isEven (Stream.fromList [2, 4, 6, 7, 8])
+ ```
+ }}
+
+data.Stream.emit.doc : Doc
+data.Stream.emit.doc =
+ {{
+ Puts a value onto the {type Stream}. The value will be received by the
+ nearest enclosing handler.
+
+ # Example
+
+ ```
+ Stream.toList do
+ emit 1
+ emit 2
+ emit 3
+ ```
+ }}
+
+data.Stream.emitAndReturn : a -> '{Stream a} a
+data.Stream.emitAndReturn a _ = emitAndReturn! a
+
+data.Stream.emitAndReturn.doc : Doc
+data.Stream.emitAndReturn.doc =
+ {{
+ `Stream.emitAndReturn a` returns a delayed computation of
+ `Stream.emitAndReturn! a`.
+ }}
+
+data.Stream.emitAndReturn! : a ->{Stream a} a
+data.Stream.emitAndReturn! a =
+ emit a
+ a
+
+data.Stream.emitAndReturn!.doc : Doc
+data.Stream.emitAndReturn!.doc =
+ {{ `Stream.emitAndReturn! a` emits `a` and evaluates to `a`. }}
+
+data.Stream.filter : (a ->{g} Boolean) -> '{g, Stream a} r -> '{g, Stream a} r
+data.Stream.filter p s _ = filter! p s
+
+data.Stream.filter.doc : Doc
+data.Stream.filter.doc =
+ {{
+ `Stream.filter p s` returns a delayed computation of `Stream.filter! p s`.
+ }}
+
+test> data.Stream.filter.test = runs 10 do
+ n = natInOrder()
+ p = Nat.isEven
+ expected = if p n then [n] else []
+ actual = Stream.toList (Stream.filter p do emit n)
+ expect (assertEquals actual expected)
+
+data.Stream.filter! : (a ->{g} Boolean) -> '{g, Stream a} r ->{g, Stream a} r
+data.Stream.filter! pred = concatMap! (a -> (if pred a then [a] else []))
+
+data.Stream.filter!.doc : Doc
+data.Stream.filter!.doc =
+ {{
+ `` filter! pred s `` emits the elements emitted by stream `s` for which the
+ predicate `pred` holds.
+ }}
+
+data.Stream.find : (a ->{g} Boolean) -> '{g, Stream a} r ->{g} Optional a
+data.Stream.find pred = Stream.head << Stream.filter pred
+
+data.Stream.find.doc : Doc
+data.Stream.find.doc =
+ use Nat ==
+ use Stream find
+ {{
+ `` find p x `` looks for an element that matches predicate `p` in the
+ {type Stream} `x`. Returns {type Optional} value.
+
+ # Examples
+
+ ```
+ to 10 |> find ((==) 10)
+ ```
+
+ ```
+ to 10 |> find ((==) 11)
+ ```
+
+ ## See also:
+
+ * {Stream.contains}
+ }}
+
+test> data.Stream.find.test = test.verify do
+ use Nat ==
+ use Stream find
+ s = to 10
+ ensureEqual (find ((==) 10) s) (Some 10)
+ ensureEqual (find ((==) 11) s) None
+
+data.Stream.flatMap :
+ (a ->{e, Stream b} any) -> '{e, Stream a} r -> '{e, Stream b} r
+data.Stream.flatMap f s _ = flatMap! f s
+
+data.Stream.flatMap.doc : Doc
+data.Stream.flatMap.doc =
+ {{
+ `` Stream.flatMap f s `` returns a delayed computation of ``flatMap! f s``.
+ }}
+
+test> data.Stream.flatMap.test = runs 10 do
+ use Nat +
+ ns = gen.listOf natInOrder ()
+ expected = List.flatMap (n -> [n, n + 1]) ns
+ go n =
+ emit n
+ emit (n + 1)
+ actual = Stream.toList (Stream.flatMap go (Stream.fromList ns))
+ expect (assertEquals actual expected)
+
+data.Stream.flatMap! :
+ (a ->{e, Stream b} any) -> '{e, Stream a} r ->{e, Stream b} r
+data.Stream.flatMap! f s = pipe! s do forever do f ask
+
+data.Stream.flatMap!.doc : Doc
+data.Stream.flatMap!.doc =
+ {{
+ `` flatMap! f s `` applies `f` to each element emitted by stream `s` in turn
+ and concatenates the resulting streams.
+ }}
+
+data.Stream.fold : (b ->{g} a ->{g} b) -> b -> '{g, Stream a} r ->{g} b
+data.Stream.fold f z s = at1 (handle s() with foldWithResult.handler f z)
+
+data.Stream.fold.doc : Doc
+data.Stream.fold.doc =
+ {{
+ `Stream.fold! op z s` folds over the elements emitted by stream `s`, starting
+ an accumulation at value `z` and using binary operation `op` to add each
+ element emitted by `s` into the accumulation.
+ }}
+
+test> data.Stream.fold.test = runs 10 do
+ use Nat +
+ ns = gen.listOf natInOrder ()
+ expected = List.foldLeft (+) 0 ns
+ actual = Stream.fold (+) 0 (Stream.fromList ns)
+ expect (assertEquals actual expected)
+
+data.Stream.foldBalanced :
+ (a ->{g1} b) -> b -> (b -> b ->{g2} b) -> '{g3, Stream a} r ->{g1, g2, g3} b
+data.Stream.foldBalanced f z op s0 =
+ use List :+
+ use Nat + >=
+ done = cases
+ [] -> z
+ h +: t -> List.foldLeft (acc a -> op acc (at1 a)) (at1 h) t
+ push stack a n = match stack with
+ stack :+ (a0, m) | n >= m -> push stack (op a0 a) (n + m)
+ _ -> stack :+ (a, n)
+ go stack s = match Stream.uncons s with
+ Left _ -> done stack
+ Right (hd, s) -> go (push stack (f hd) 1) s
+ go [] s0
+
+data.Stream.foldBalanced.doc : Doc
+data.Stream.foldBalanced.doc =
+ use Text ++
+ {{
+ Reduce a {type Stream} using a balanced fold, useful for converting a stream
+ to some balanced structure, or when the combining operation isn't constant
+ time.
+
+ ```
+ pair t1 t2 = "(" ++ t1 ++ " " ++ t2 ++ ")"
+ Stream.fromList (List.range 0 8) |> Stream.foldBalanced Nat.toText "" pair
+ ```
+ }}
+
+test> data.Stream.foldBalanced.test = test.verify do
+ use Nat * + / ==
+ n = Each.range 1 32
+ ns = Stream.range 1 (n + 1)
+ ensure (Stream.foldBalanced id 0 (+) ns == n * (n + 1) / 2)
+
+data.Stream.foldDelayed :
+ (b ->{g} a ->{g} b) -> b -> '{g, Stream a} r -> '{g} b
+data.Stream.foldDelayed f z s _ = Stream.fold f z s
+
+data.Stream.foldDelayed.doc : Doc
+data.Stream.foldDelayed.doc =
+ {{
+ `Stream.fold f z s` returns a delayed computation of `Stream.fold! f z s`.
+ }}
+
+data.Stream.foldDelayedRight :
+ (a ->{g} b ->{g} b) -> b -> '{g, Stream a} r -> '{g} b
+data.Stream.foldDelayedRight f z s = do Stream.foldRight f z s
+
+data.Stream.foldDelayedRight.doc : Doc
+data.Stream.foldDelayedRight.doc =
+ use Nat +
+ {{
+ `` foldDelayedRight f z s `` folds the {type Stream} `s` with the function
+ `f` and the initial value `z`, associating to the right.
+
+ Returns an unevaluated computation that will return the result of the fold
+ when forced.
+
+ # Example
+
+ ```
+ foldDelayedRight (+) 0 (Stream.fromList [1, 2, 3]) ()
+ ```
+
+ # See also - {Stream.foldRight} for a version of this that forces the result.
+ - {foldDelayed} associates to the left. - {List.foldRight} folds a
+ {type List}.
+
+
+ }}
+
+data.Stream.foldDelayedWithResult :
+ (b ->{g} a ->{g} b) -> b -> '{g, Stream a} r -> '{g} (b, r)
+data.Stream.foldDelayedWithResult f z s _ = foldWithResult f z s
+
+data.Stream.foldDelayedWithResult.doc : Doc
+data.Stream.foldDelayedWithResult.doc =
+ {{
+ `Stream.foldWithResult f z s` returns a delayed computation of
+ `Stream.foldWithResult! f z s`.
+ }}
+
+data.Stream.foldRight : (a ->{g} b ->{g} b) -> b -> '{g, Stream a} r ->{g} b
+data.Stream.foldRight f z s = Stream.fold (g a b -> g (f a b)) id s z
+
+data.Stream.foldRight.doc : Doc
+data.Stream.foldRight.doc =
+ use Nat +
+ {{
+ `` Stream.foldRight f z s `` folds the {type Stream} `s` with the function
+ `f` and the initial value `z`, associating to the right.
+
+ Returns the result of the fold, forcing the computation.
+
+ # Example
+
+ ```
+ Stream.foldRight (+) 0 (Stream.fromList [1, 2, 3])
+ ```
+
+ # See also - {foldDelayedRight} for a version of this that returns an
+ unevaluated computation. - {Stream.fold} associates to the left. -
+ {List.foldRight} folds a {type List}.
+
+
+ }}
+
+data.Stream.foldWithResult :
+ (b ->{g} a ->{g} b) -> b -> '{g, Stream a} r ->{g} (b, r)
+data.Stream.foldWithResult f z s = handle s() with foldWithResult.handler f z
+
+data.Stream.foldWithResult.doc : Doc
+data.Stream.foldWithResult.doc =
+ {{
+ `Stream.foldWithResult! f z s` folds over the elements emitted by stream `s`
+ and returns a pair containing both the fold result and the value of the
+ stream.
+
+ See also: {foldDelayed}
+ }}
+
+data.Stream.foldWithResult.handler :
+ (b ->{g} a ->{g} b) -> b -> Request (Stream a) r ->{g} (b, r)
+data.Stream.foldWithResult.handler f z = cases
+ { emit a -> k } ->
+ handle k() with data.Stream.foldWithResult.handler f (f z a)
+ { v } -> (z, v)
+
+test> data.Stream.foldWithResult.test = runs 100 do
+ use Nat +
+ use Stream ++
+ ns = gen.listOf natInOrder ()
+ r = alpha()
+ expected = (List.foldLeft (+) 0 ns, r)
+ actual = foldWithResult (+) 0 (Stream.fromList ns ++ (do r))
+ expect (assertEquals actual expected)
+
+data.Stream.foreach : (a ->{g} ()) -> '{g, Stream a} r ->{g} r
+data.Stream.foreach f s =
+ h = cases
+ { emit a -> k } -> handle k (f a) with h
+ { r } -> r
+ handle s() with h
+
+data.Stream.foreach.doc : Doc
+data.Stream.foreach.doc =
+ {{
+ `` Stream.foreach f s `` performs `f` on each element of the {type Stream}
+ `s`, discarding the result.
+ }}
+
+data.Stream.from : Nat -> '{Stream Nat} a
+data.Stream.from n _ = Stream.from! n
+
+data.Stream.from.doc : Doc
+data.Stream.from.doc =
+ {{ `Stream.from n` returns a delayed computation of `Stream.from! n`. }}
+
+data.Stream.from! : Nat ->{Stream Nat} a
+data.Stream.from! n =
+ use Nat +
+ emit n
+ data.Stream.from! (n + 1)
+
+data.Stream.from!.doc : Doc
+data.Stream.from!.doc =
+ {{
+ `Stream.from! n` emits all natural numbers starting from `n` and increasing
+ by ones.
+ }}
+
+data.Stream.fromList : [a] -> '{Stream a} ()
+data.Stream.fromList l _ = fromList! l
+
+data.Stream.fromList.doc : Doc
+data.Stream.fromList.doc =
+ {{ `Stream.fromList` returns a delayed computation of `Stream.fromList!`. }}
+
+data.Stream.fromList! : [a] ->{Stream a} ()
+data.Stream.fromList! l = ignore (List.map emit l)
+
+data.Stream.fromList!.doc : Doc
+data.Stream.fromList!.doc =
+ {{ `Stream.fromList! l` eagerly emits each element of `List` `l`. }}
+
+data.Stream.head : '{g, Stream a} r ->{g} Optional a
+data.Stream.head stream = match Stream.uncons stream with
+ Left _ -> None
+ Right (a, _) -> Some a
+
+data.Stream.head.doc : Doc
+data.Stream.head.doc =
+ {{
+ Returns the first element of a stream, if it exists, or {None} if the stream
+ is empty.
+
+ # Example
+
+ ```
+ Stream.head (Stream.fromList [1, 2, 3])
+ ```
+ }}
+
+data.Stream.indexed : '{g, Stream a} r -> '{g, Stream (a, Nat)} r
+data.Stream.indexed s = do indexed! s
+
+data.Stream.indexed.doc : Doc
+data.Stream.indexed.doc =
+ use Stream fromList indexed toList
+ {{
+ Transforms a stream of elements in a stream of pairs of the form
+ `(element, index)` where `element` is the original element and `index` is the
+ zero-based index of the element in the stream.
+
+ See {indexed!} for a variant in which the output is not delayed.
+
+ # Examples
+
+ ```
+ fromList ["a", "b", "c"] |> indexed |> toList
+ ```
+
+ ```
+ fromList [] |> indexed |> toList
+ ```
+ }}
+
+data.Stream.indexed! : '{g, Stream a} r ->{g, Stream (a, Nat)} r
+data.Stream.indexed! s = zipWith! Tuple.pair s Nat.all
+
+data.Stream.indexed!.doc : Doc
+data.Stream.indexed!.doc = {{ A non-delayed variant of {Stream.indexed}. }}
+
+data.Stream.Int.all : '{Stream Int} a
+data.Stream.Int.all _ =
+ emit +0
+ go : Int ->{Stream Int} a
+ go n =
+ emit n
+ emit (Int.negate n)
+ go (Int.increment n)
+ go +1
+
+data.Stream.Int.all.doc : Doc
+data.Stream.Int.all.doc =
+ {{
+ `Stream.Int.all` is the (delayed) stream of all integers: +0, +1, -1, +2, -2,
+ etc.
+ }}
+
+data.Stream.Int.from : Int -> '{Stream Int} a
+data.Stream.Int.from n _ = Int.from! n
+
+data.Stream.Int.from.doc : Doc
+data.Stream.Int.from.doc =
+ {{
+ `Stream.Int.from n` returns a delayed computation of `Stream.Int.from! n`.
+ }}
+
+data.Stream.Int.from! : Int ->{Stream Int} a
+data.Stream.Int.from! n =
+ emit n
+ data.Stream.Int.from! (Int.increment n)
+
+data.Stream.Int.from!.doc : Doc
+data.Stream.Int.from!.doc =
+ {{
+ `Stream.Int.from! n` eagerly emits all integers starting at `n` and
+ increasing by ones.
+ }}
+
+data.Stream.Int.negatives : '{Stream Int} a
+data.Stream.Int.negatives = Stream.map Int.negate (Int.from +1)
+
+data.Stream.Int.negatives.doc : Doc
+data.Stream.Int.negatives.doc =
+ {{
+ `Stream.Int.negatives` is the (delayed) stream of all negative integers.
+ }}
+
+data.Stream.Int.positives : '{Stream Int} a
+data.Stream.Int.positives = Int.from +1
+
+data.Stream.Int.positives.doc : Doc
+data.Stream.Int.positives.doc =
+ {{
+ `Stream.Int.positives` is the (delayed) stream of all positive integers.
+ }}
+
+data.Stream.Int.range : Int -> Int -> '{Stream Int} ()
+data.Stream.Int.range a b _ = Int.range! a b
+
+data.Stream.Int.range.doc : Doc
+data.Stream.Int.range.doc =
+ {{
+ `Stream.Int.range n m` is the (delayed) stream of integers from `n`
+ (inclusive) until `m` (exclusive).
+ }}
+
+data.Stream.Int.range! : Int ->{Stream Int} Int ->{Stream Int} ()
+data.Stream.Int.range! a b =
+ if Universal.gteq a b then ()
+ else
+ emit a
+ data.Stream.Int.range! (Int.increment a) b
+
+data.Stream.Int.range!.doc : Doc
+data.Stream.Int.range!.doc =
+ {{
+ `Stream.Int.range! n m` is the (eager) stream of integers from `n`
+ (inclusive) until `m` (exclusive).
+ }}
+
+data.Stream.interleave :
+ '{g, Stream a} r -> '{g, Stream a} r -> '{g, Stream a} r
+data.Stream.interleave s1 s2 _ = interleave! s1 s2
+
+data.Stream.interleave.doc : Doc
+data.Stream.interleave.doc =
+ {{
+ `Stream.interleave s1 s2` returns a delayed computation of
+ `Stream.interleave! s1 s2`.
+ }}
+
+data.Stream.interleave! :
+ '{g, Stream a} r -> '{g, Stream a} r ->{g, Stream a} r
+data.Stream.interleave! s1 s2 = handle s1() with interleave!.handler s2
+
+data.Stream.interleave!.doc : Doc
+data.Stream.interleave!.doc =
+ {{
+ `Stream.interleave! s1 s2` alternates between emitting elements of stream
+ `s1` and stream `s2`. The resulting stream terminates when the first of the
+ two streams terminates (and evaluates to the value of that stream).
+ }}
+
+data.Stream.interleave!.handler :
+ '{e} r -> Request (Stream a) r ->{e, Stream a} r
+data.Stream.interleave!.handler k = cases
+ { emit a -> s } ->
+ emit a
+ handle k() with data.Stream.interleave!.handler s
+ { r } -> r
+
+data.Stream.intersperse : a -> '{g, Stream a} r -> '{g, Stream a} r
+data.Stream.intersperse sep stream = do intersperse! sep stream
+
+data.Stream.intersperse.doc : Doc
+data.Stream.intersperse.doc =
+ use Stream intersperse toList
+ {{
+ Intersperse a separator between the elements of a stream.
+
+ This function takes a separator and a stream and returns a new stream with
+ the separator interspersed between the elements of the input stream.
+
+ # Example
+
+ ```
+ toList (intersperse "🙀" (Stream.fromList ["👻", "💀", "🎃"]))
+ ```
+
+ This also works for infinite streams:
+
+ ```
+ toList (Stream.take 7 (intersperse "🍎" (Stream.repeat do "🍊")))
+ ```
+ }}
+
+data.Stream.intersperse! : a -> '{g, Stream a} r ->{g, Stream a} r
+data.Stream.intersperse! sep stream =
+ use Stream uncons
+ go s = match uncons s with
+ Left r -> r
+ Right (head, tail) ->
+ emit sep
+ emit head
+ go tail
+ match uncons stream with
+ Left r -> r
+ Right (head, tail) ->
+ emit head
+ go tail
+
+data.Stream.intersperse!.doc : Doc
+data.Stream.intersperse!.doc =
+ {{
+ A strict version of {Stream.intersperse} that evaluates the stream instead of
+ returning it.
+ }}
+
+data.Stream.iterate : (a ->{g} a) -> a -> '{g, Stream a} Void
+data.Stream.iterate f init = do iterate! f init
+
+data.Stream.iterate.doc : Doc
+data.Stream.iterate.doc =
+ {{
+ `` Stream.iterate f x `` Returns a delayed computation of `` iterate! f x ``
+ }}
+
+test> data.Stream.iterate.tests.ex1 =
+ use Nat *
+ output = Stream.toList (Stream.take 5 <| Stream.iterate (n -> n * 2) 1)
+ expected = [1, 2, 4, 8, 16]
+ check (output === expected)
+
+data.Stream.iterate! : (a ->{g} a) -> a ->{g, Stream a} Void
+data.Stream.iterate! f init =
+ loop x =
+ emit x
+ loop (f x)
+ loop init
+
+data.Stream.iterate!.doc : Doc
+data.Stream.iterate!.doc =
+ {{
+ `` iterate! f x `` returns a infinite Stream consisting of
+
+ `x, f x, f (f x), f (f (f x))...`
+
+ and so on. That is, f applied to x zero times, then once, then twice, etc.
+ }}
+
+data.Stream.map : (a ->{g} b) -> '{g, Stream a} r -> '{g, Stream b} r
+data.Stream.map f s _ = Stream.map! f s
+
+data.Stream.map.doc : Doc
+data.Stream.map.doc =
+ {{ `Stream.map f s` returns a delayed computation of `Stream.map! f s`. }}
+
+test> data.Stream.map.test = runs 10 do
+ use Nat +
+ n = natInOrder()
+ f = (+) 1
+ expected = [f n]
+ actual = Stream.toList (Stream.map f do emit n)
+ expect (assertEquals actual expected)
+
+data.Stream.map! : (a ->{g} b) -> '{g, Stream a} r ->{g, Stream b} r
+data.Stream.map! f s = pipe! s do forever do f ask |> emitAndReturn!
+
+data.Stream.map!.doc : Doc
+data.Stream.map!.doc =
+ {{
+ `Stream.map! f s` applies a function `f` to each element emitted by a stream
+ `s` and emits each result.
+ }}
+
+data.Stream.Nat.all : '{Stream Nat} a
+data.Stream.Nat.all _ = Stream.from! 0
+
+data.Stream.Nat.all.doc : Doc
+data.Stream.Nat.all.doc =
+ {{ `Stream.Nat.all` is the (delayed) stream of all natural numbers. }}
+
+data.Stream.pipe :
+ '{g, Stream a} r -> '{g, Ask a, Stream b} r -> '{g, Stream b} r
+data.Stream.pipe i t _ = pipe! i t
+
+data.Stream.pipe.doc : Doc
+data.Stream.pipe.doc =
+ {{ `Stream.pipe i t` returns a delayed computation of `Stream.pipe! i t`. }}
+
+data.Stream.pipe! :
+ '{g, Stream a} r -> '{g, Ask a, Stream b} r ->{g, Stream b} r
+data.Stream.pipe! i t = handle t() with pipe!.handler i
+
+data.Stream.pipe!.doc : Doc
+data.Stream.pipe!.doc =
+ {{
+ `Stream.pipe! i t` pipes an input stream `i` through a transducer `t`. The
+ transducer may consume any number of elements emitted by the input stream
+ (using `Ask.ask`) and may emit any number of values (using `Stream.emit`).
+ }}
+
+data.Stream.pipe!.handler :
+ '{g, Stream a} r -> Request {Ask a, Stream b} r ->{g, Stream b} r
+data.Stream.pipe!.handler i = cases
+ { r } -> r
+ { ask -> resumeC } ->
+ h : Request {Stream a} r ->{Stream b} r
+ h = cases
+ { r } -> r
+ { emit a -> i' } -> handle resumeC a with data.Stream.pipe!.handler i'
+ handle i() with h
+ { emit b -> k } ->
+ emit b
+ handle k() with data.Stream.pipe!.handler i
+
+data.Stream.range : Nat -> Nat -> '{Stream Nat} ()
+data.Stream.range n m _ = Stream.range! n m
+
+data.Stream.range.doc : Doc
+data.Stream.range.doc =
+ {{
+ `Stream.range n m` returns a delayed computation of `Stream.range! n m`.
+ }}
+
+test> data.Stream.range.test.emptyRange = runs 10 do
+ n = natInOrder()
+ actual = Stream.toList (Stream.range n n)
+ expect (assertEquals actual [])
+
+test> data.Stream.range.test.emptyRange2 = runs 10 do
+ n = natInOrder()
+ m = natInOrder()
+ n' = Universal.max n m
+ m' = Universal.min n m
+ actual = Stream.toList (Stream.range n' m')
+ expect (assertEquals actual [])
+
+test> data.Stream.range.test.listLike = runs 100 do
+ n = natInOrder()
+ m = natInOrder()
+ expected = List.range n m
+ actual = Stream.toList (Stream.range n m)
+ expect (assertEquals actual expected)
+
+test> data.Stream.range.test.singletonRange = runs 10 do
+ use Nat +
+ n = natInOrder()
+ actual = Stream.toList (Stream.range n (n + 1))
+ expect (assertEquals actual [n])
+
+data.Stream.range! : Nat -> Nat ->{Stream Nat} ()
+data.Stream.range! n m =
+ use Nat +
+ if Universal.gteq n m then ()
+ else
+ emit n
+ data.Stream.range! (n + 1) m
+
+data.Stream.range!.doc : Doc
+data.Stream.range!.doc =
+ {{
+ `Stream.range! n m` emits the natural numbers from `n` (inclusive) until `m`
+ (exclusive).
+ }}
+
+data.Stream.rangeClosed : Nat -> Nat -> '{Stream Nat} ()
+data.Stream.rangeClosed n m _ = rangeClosed! n m
+
+data.Stream.rangeClosed.doc : Doc
+data.Stream.rangeClosed.doc =
+ {{
+ `Stream.rangeClosed n m` returns a delayed computation of
+ `Stream.rangeClosed! n m`.
+ }}
+
+data.Stream.rangeClosed! : Nat -> Nat ->{Stream Nat} ()
+data.Stream.rangeClosed! n m =
+ use Nat +
+ Stream.range! n (m + 1)
+
+data.Stream.rangeClosed!.doc : Doc
+data.Stream.rangeClosed!.doc =
+ {{
+ `Stream.rangeClosed! n m` emits the natural numbers from `n` to `m`
+ (inclusive).
+ }}
+
+data.Stream.repeat : '{g} a -> '{g, Stream a} Void
+data.Stream.repeat a =
+ loop = do
+ emit a()
+ loop()
+ loop
+
+data.Stream.repeat.doc : Doc
+data.Stream.repeat.doc =
+ use Stream repeat
+ {{
+ `` repeat x `` builds a {type Stream} that emits `x` forever.
+
+ # Example
+
+ ```
+ lcg 0 (toDelayedList (Stream.take 10 (repeat do Random.natIn 0 100)))
+ ```
+ }}
+
+data.Stream.scan :
+ (b ->{g} a ->{g} b) -> b -> '{g, Stream a} r -> '{g, Stream b} r
+data.Stream.scan f z s _ = scan! f z s
+
+data.Stream.scan.doc : Doc
+data.Stream.scan.doc =
+ {{
+ `Stream.scan f z s` returns a delayed computation of `Stream.scan! f z s`.
+ }}
+
+data.Stream.scan! :
+ (b ->{g} a ->{g} b) -> b -> '{g, Stream a} r ->{g, Stream b} r
+data.Stream.scan! f z s =
+ f' : b ->{g} a ->{g, Stream b} b
+ f' b a = emitAndReturn! (f b a)
+ at2 (foldWithResult f' z s)
+
+data.Stream.scan!.doc : Doc
+data.Stream.scan!.doc =
+ {{
+ `Stream.scan! op z s` folds over the elements emitted by stream `s`, starting
+ an accumulation at value `z` and using binary operation `op` to add each
+ element emitted by `s` into the accumulation. `Stream.scan!` emits the
+ cumulative value as each element of `s` is added to the accumulation.
+
+ See also: {scan!}
+ }}
+
+data.Stream.somes : '{g, Stream (Optional a)} r -> '{g, Stream a} r
+data.Stream.somes s = do somes! s
+
+data.Stream.somes.doc : Doc
+data.Stream.somes.doc =
+ use Stream fromList somes toList
+ {{
+ Returns a {type Stream} of the {Some} values from a {type Stream} of
+ {type Optional} values, skipping the {None} values.
+
+ # Examples
+
+ ```
+ toList (somes (fromList [Some 1, None, Some 2, None, Some 3]))
+ ```
+
+ ```
+ toList (somes (fromList [None, Some 1, Some 2, None, Some 3]))
+ ```
+
+ ```
+ toList (somes (fromList [None, None, None]))
+ ```
+
+ # See also
+
+ * {somes!} - a variant that produces the stream immediately, without
+ creating an unevaluated stream.
+ }}
+
+data.Stream.somes! : '{g, Stream (Optional a)} r ->{g, Stream a} r
+data.Stream.somes! s =
+ h = cases
+ { emit (Some a) -> k } ->
+ emit a
+ handle k() with h
+ { emit None -> k } -> handle k() with h
+ { r } -> r
+ handle s() with h
+
+data.Stream.somes!.doc : Doc
+data.Stream.somes!.doc =
+ use Stream fromList toList
+ {{
+ Produces a {type Stream} of the {Some} values from a {type Stream} of
+ {type Optional} values, skipping the {None} values.
+
+ # Examples
+
+ ```
+ toList do somes! (fromList [Some 1, None, Some 2, None, Some 3])
+ ```
+
+ ```
+ toList do somes! (fromList [None, Some 1, Some 2, None, Some 3])
+ ```
+
+ ```
+ toList do somes! (fromList [None, None, None])
+ ```
+
+ # See also
+
+ * {Stream.somes} - a variant that returns an unevaluated stream.
+ }}
+
+data.Stream.span :
+ (a ->{g} Boolean) -> '{g, Stream a} r ->{g} ([a], '{g, Stream a} r)
+data.Stream.span p thunk =
+ use List :+
+ go : [a] -> Request {Stream a} r ->{g} ([a], '{g, Stream a} r)
+ go acc = cases
+ { r } -> (acc, do r)
+ { emit a -> k } ->
+ if p a then handle k() with go (acc :+ a)
+ else
+ (acc, do
+ emit a
+ k())
+ handle thunk() with go []
+
+data.Stream.span.doc : Doc
+data.Stream.span.doc =
+ {{
+ Returns the longest prefix of the input {type Stream} that satisfies the
+ predicate, together with the rest of the {type Stream}.
+
+ Performs whatever effects are required to produce the prefix tbut does not
+ perform any effects on the rest of the {type Stream}.
+
+ # Example
+
+ ```
+ Tuple.second Stream.toList
+ <| Stream.span Nat.isOdd (Stream.fromList [1, 1, 2, 3, 1, 1])
+ ```
+ }}
+
+data.Stream.splitAt : Nat -> '{g, Stream a} r ->{g} ([a], '{g, Stream a} r)
+data.Stream.splitAt n s =
+ use List :+
+ use Nat - ==
+ h : [a] -> Nat -> Request (Stream a) r ->{g} ([a], '{g, Stream a} r)
+ h acc n = cases
+ { emit a -> k } -> go (acc :+ a) (n - 1) k
+ { r } -> (acc, do r)
+ go : [a] -> Nat -> '{g, Stream a} r ->{g} ([a], '{g, Stream a} r)
+ go acc n s = if n == 0 then (acc, s) else handle s() with h acc n
+ go [] n s
+
+data.Stream.splitAt.doc : Doc
+data.Stream.splitAt.doc =
+ {{
+ Returns first `n` elements of a {type Stream}, as a {type List}, together
+ with the rest of the stream.
+
+ If the {type Stream} has fewer than `n` elements, all the elements are
+ returned as the first element of the pair, and the second element of the pair
+ is the empty {type Stream}.
+
+ Performs whatever effects are required to produce the first `n` elements of
+ the {type Stream}, but does not perform any effects on the rest of the
+ {type Stream}.
+
+ # Example
+
+ ```
+ Tuple.second Stream.toList
+ <| Stream.splitAt 3 (Stream.fromList [1, 2, 3, 4, 5])
+ ```
+ }}
+
+data.Stream.tails : '{g, Stream a} r -> '{g, Stream ('{Stream a, g} r)} r
+data.Stream.tails s = do tails! s
+
+data.Stream.tails.doc : Doc
+data.Stream.tails.doc =
+ use Stream toList
+ {{
+ Returns a {type Stream} of all the suffixes of the input {type Stream}.
+
+ A lazy version of {tails!}.
+
+ # Example
+
+ ```
+ toList (Stream.map toList (Stream.tails (Stream.fromList [1, 2, 3])))
+ ```
+ }}
+
+data.Stream.tails! : '{g, Stream a} r ->{g, Stream ('{Stream a, g} r)} r
+data.Stream.tails! s = match Stream.uncons s with
+ Left r -> r
+ Right (x, s) -> (Stream.+:) (x Stream.+: s) (do data.Stream.tails! s) ()
+
+data.Stream.tails!.doc : Doc
+data.Stream.tails!.doc =
+ use Stream toList
+ {{
+ Emits all the suffixes of the input {type Stream} onto a {type Stream}.
+
+ # Example
+
+ ```
+ toList (Stream.map toList do tails! (Stream.fromList [1, 2, 3]))
+ ```
+ }}
+
+data.Stream.take : Nat -> '{g, Stream a} t -> '{g, Stream a} Optional t
+data.Stream.take n s = do take! n s
+
+data.Stream.take.doc : Doc
+data.Stream.take.doc =
+ {{ `Stream.take n s` returns a delayed computation of `Stream.take! n s`. }}
+
+test> data.Stream.take.tests.completion =
+ runs 100 do
+ use Nat +
+ use Stream ++
+ ns = gen.listOf natInOrder ()
+ lim = List.size ns
+ expected = (List.take lim ns, Some "end")
+ actual =
+ toListWithResult
+ (Stream.take (lim + 1) (Stream.fromList ns ++ (do "end")))
+ expect (assertEquals actual expected)
+
+test> data.Stream.take.tests.earlyTermination = runs 100 do
+ ns = gen.listOf natInOrder ()
+ lim = gen.natIn 0 (List.size ns) ()
+ expected = (List.take lim ns, None)
+ actual = toListWithResult (Stream.take lim (Stream.fromList ns))
+ expect (assertEquals actual expected)
+
+test> data.Stream.take.tests.pullMinimal =
+ runs 100 do
+ Scope.run do
+ use Nat ==
+ n = gen.natIn 0 1000 ()
+ ref = Scope.ref 0
+ (Stream.repeat do Ref.modify ref Nat.increment) |> Stream.take n |> drain
+ |> ignore
+ expect (Ref.read ref == n)
+
+data.Stream.take! : Nat -> '{g, Stream a} r ->{g, Stream a} Optional r
+data.Stream.take! n s =
+ use Nat - >
+ h n = cases
+ { emit a -> k } ->
+ emit a
+ if n > 0 then handle k() with h (n - 1) else None
+ { r } -> Some r
+ if n > 0 then handle s() with h (n - 1) else None
+
+data.Stream.take!.doc : Doc
+data.Stream.take!.doc =
+ use Stream fromList take toList
+ {{
+ `Stream.take! n s` emits the first `n` elements of a stream `s`.
+
+ If the stream `s` has fewer than `n` elements, the result has exactly the
+ elements of `s`, and the result of the computation will be {Some} containing
+ the result of the computation `s`. Otherwise, the result is a stream of
+ length `n` with the first `n` elements of `s` and a result of {None}.
+
+ Note that the implementation tries to do as little work as possible in order
+ to not execute effects unnecessarily. For this reason, once `n` elements have
+ been emitted, the stream `s` is not consumed further and the result of the
+ computation is {None}. So even if the stream `s` has exactly 3 elements,
+ `Stream.take! 3 s` will emit all the elements and return {None}.
+
+ # Examples
+
+ ```
+ s = fromList [1, 2, 3, 4, 5]
+ toList (take 3 s)
+ ```
+
+ ```
+ s = take 3 (fromList [1, 2, 3])
+ (toList s, drain s)
+ ```
+
+ ```
+ s = take 4 (fromList [1, 2, 3])
+ (toList s, drain s)
+ ```
+ }}
+
+data.Stream.takeWhile :
+ (a ->{g} Boolean) -> '{g, Stream a} r -> '{g, Stream a} Optional r
+data.Stream.takeWhile p s = do takeWhile! p s
+
+data.Stream.takeWhile.doc : Doc
+data.Stream.takeWhile.doc =
+ use Nat <=
+ use Stream takeWhile
+ {{
+ `` takeWhile p s `` is a delayed computation of ``takeWhile! p s``.
+
+ # Example
+
+ ```
+ Stream.toList (takeWhile (x -> x <= 3) (Stream.from 0))
+ ```
+ }}
+
+test> data.Stream.takeWhile.tests.worksLikeList =
+ test.verify do
+ use Nat <
+ Each.repeat 100
+ vs = Random.listOf Random.nat do Random.natIn 0 100
+ s = Stream.fromList vs
+ s' = Stream.takeWhile (x -> x < 11529215046068469760) s
+ ensure
+ (Stream.toList s' === List.takeWhile (x -> x < 11529215046068469760) vs)
+
+data.Stream.takeWhile! :
+ (a ->{g} Boolean) -> '{g, Stream a} r ->{g, Stream a} Optional r
+data.Stream.takeWhile! p s =
+ h = cases
+ { emit a -> k } ->
+ if p a then
+ emit a
+ handle k() with h
+ else None
+ { r } -> Some r
+ handle s() with h
+
+data.Stream.takeWhile!.doc : Doc
+data.Stream.takeWhile!.doc =
+ use Nat <=
+ {{
+ `` takeWhile! p s `` emits elements from the {type Stream} `s` as long as `p`
+ returns `` true `` on those elements. That is, it emits elements until (but
+ not including) the first element `e` for which `p e` is false.
+
+ # Example
+
+ ```
+ Stream.toList do takeWhile! (x -> x <= 3) (Stream.from 0)
+ ```
+ }}
+
+data.Stream.tap : (x ->{g1} ()) -> '{g2, Stream x} r -> '{g1, g2, Stream x} r
+data.Stream.tap f s = do tap! f s
+
+data.Stream.tap.doc : Doc
+data.Stream.tap.doc =
+ {{ `Stream.tap f s` returns a delayed computation of `Stream.tap! f s`. }}
+
+data.Stream.tap! : (x ->{g1} ()) -> '{g2, Stream x} r ->{g1, g2, Stream x} r
+data.Stream.tap! f s =
+ handle s()
+ with cases
+ { emit x -> k } ->
+ f x
+ emit x
+ data.Stream.tap! f k
+ { r } -> r
+
+data.Stream.tap!.doc : Doc
+data.Stream.tap!.doc =
+ {{
+ `Stream.tap f s` returns a new stream that runs the function `f` for effects
+ on each element of the stream `s` before emitting it.
+ }}
+
+data.Stream.terminated : '{g, Stream a} r -> '{g, Stream (Optional a)} r
+data.Stream.terminated s _ = terminated! s
+
+data.Stream.terminated.doc : Doc
+data.Stream.terminated.doc =
+ {{
+ `Stream.terminated s` returns a delayed computation of `Stream.terminated!
+ s`.
+ }}
+
+test> data.Stream.terminated.test = runs 100 do
+ use List :+
+ ns = gen.listOf natInOrder ()
+ expected = List.map Some ns :+ None
+ s : '{Stream Nat} ()
+ s = Stream.fromList ns
+ s2 : '{Stream (Optional Nat)} ()
+ s2 = terminated s
+ actual = Stream.toList s2
+ expect (assertEquals actual expected)
+
+data.Stream.terminated! : '{g, Stream a} r ->{g, Stream (Optional a)} r
+data.Stream.terminated! s =
+ r = Stream.map! Some s
+ emit None
+ r
+
+data.Stream.terminated!.doc : Doc
+data.Stream.terminated!.doc =
+ {{
+ `Stream.terminated! s` emits all elements of a stream `s`, wrapped in a
+ `Some`, and then emits a final `None` to explicitly signal termination of the
+ stream.
+ }}
+
+test> data.Stream.tests.ex1 =
+ check ((Stream.toList do Stream.range! 0 5) === [0, 1, 2, 3, 4])
+
+data.Stream.to : Nat -> '{Stream Nat} ()
+data.Stream.to n _ = to! n
+
+data.Stream.to.doc : Doc
+data.Stream.to.doc =
+ {{ `Stream.to n` returns a delayed computation of `Stream.to! n`. }}
+
+data.Stream.to! : Nat ->{Stream Nat} ()
+data.Stream.to! n = rangeClosed! 0 n
+
+data.Stream.to!.doc : Doc
+data.Stream.to!.doc =
+ {{ `Stream.to! n` emits the natural numbers from 0 to `n` (inclusive). }}
+
+data.Stream.toDelayedList : '{g, Stream a} r -> '{g} [a]
+data.Stream.toDelayedList s _ = Stream.toList s
+
+data.Stream.toDelayedList.doc : Doc
+data.Stream.toDelayedList.doc =
+ {{ `Stream.toList s` returns a delayed computation of `Stream.toList! s`. }}
+
+data.Stream.toDelayedList.handler : Request {Stream a} () -> [a]
+data.Stream.toDelayedList.handler =
+ use List ++
+ h : [a] -> Request {Stream a} () -> [a]
+ h acc = cases
+ { emit e -> resume } -> handle resume() with h (acc ++ [e])
+ { u } -> acc
+ h []
+
+test> data.Stream.toDelayedList.test.bidirectional = runs 20 do
+ l = gen.listOf natInOrder ()
+ expect (assertEquals (Stream.toList (Stream.fromList l)) l)
+
+data.Stream.toDelayedListWithResult : '{g, Stream a} r -> '{g} ([a], r)
+data.Stream.toDelayedListWithResult s _ = toListWithResult s
+
+data.Stream.toDelayedListWithResult.doc : Doc
+data.Stream.toDelayedListWithResult.doc =
+ {{
+ `Stream.toListWithResult s` returns a delayed computation of
+ `Stream.toListWithResult! s`.
+ }}
+
+data.Stream.toList : '{g, Stream a} r ->{g} [a]
+data.Stream.toList s = at1 (toListWithResult s)
+
+data.Stream.toList.doc : Doc
+data.Stream.toList.doc =
+ {{
+ `Stream.toList! s` collects all values emitted by a stream `s` in a `List`.
+ }}
+
+data.Stream.toListWithResult : '{g, Stream a} r ->{g} ([a], r)
+data.Stream.toListWithResult =
+ use List :+
+ foldWithResult (:+) []
+
+data.Stream.toListWithResult.doc : Doc
+data.Stream.toListWithResult.doc =
+ {{
+ `Stream.toListWithResult! s` collects all values emitted by a stream `s` in a
+ `List`, returning a pair containing both the list of emitted values and the
+ value of `s` itself.
+ }}
+
+data.Stream.trace : Text -> '{g, Stream a} r ->{g} r
+data.Stream.trace msg s =
+ h = cases
+ { emit a -> k } ->
+ Debug.trace msg a
+ handle k() with h
+ { r } -> r
+ handle s() with h
+
+data.Stream.trace.doc : Doc
+data.Stream.trace.doc =
+ {{
+ `` Stream.trace msg s `` prints out the {type Text} `msg` on every element
+ emitted by the {type Stream} `s`, and also prints out the element.
+ }}
+
+data.Stream.uncons : '{g, Stream a} r ->{g} Either r (a, '{g, Stream a} r)
+data.Stream.uncons s =
+ handle s()
+ with cases
+ { r } -> Left r
+ { emit hd -> tl } -> Right (hd, (tl : '{g, Stream a} r))
+
+data.Stream.uncons.doc : Doc
+data.Stream.uncons.doc =
+ use Stream fromList uncons
+ {{
+ `` uncons s `` returns the first element of a {type Stream} `s` and the
+ remaining stream after that element. Returns {Left} if the {type Stream} is
+ empty.
+
+ ```
+ fromList [1, 2, 3] |> uncons |> Either.mapRight at1
+ ```
+
+ ```
+ fromList [] |> uncons
+ ```
+ }}
+
+test> data.Stream.uncons.tests = test.verify do
+ use List :+
+ n = Each.range 0 20
+ s = Stream.range 0 n
+ l = Stream.toList s
+ go acc s = match Stream.uncons s with
+ Left _ -> acc
+ Right (hd, tl) -> go (acc :+ hd) tl
+ ensure (go [] s === l)
+
+data.Stream.unfold : s -> (s ->{g} Optional (a, s)) -> '{g, Stream a} ()
+data.Stream.unfold s f = do
+ loop = cases
+ Some (a, s') ->
+ do
+ emit a
+ loop (f s') ()
+ None -> do ()
+ loop (f s) ()
+
+data.Stream.unfold.doc : Doc
+data.Stream.unfold.doc =
+ use Nat + <
+ use Stream unfold
+ {{
+ `` unfold s f `` builds a {type Stream} from a seed value `s` and a function
+ `f` that takes the current seed value and returns the next value in the
+ stream, if any.
+
+ # Example
+
+ ```
+ Stream.toList (unfold 0 (i -> (if i < 10 then Some (i, i + 1) else None)))
+ ```
+ }}
+
+data.Stream.until : Nat -> '{Stream Nat} ()
+data.Stream.until n _ = until! n
+
+data.Stream.until.doc : Doc
+data.Stream.until.doc =
+ {{ `Stream.until n` returns a delayed computation of `Stream.until n`. }}
+
+data.Stream.until! : Nat ->{Stream Nat} ()
+data.Stream.until! n = Stream.range! 0 n
+
+data.Stream.until!.doc : Doc
+data.Stream.until!.doc =
+ {{
+ `Stream.until! n` emits the natural numbers from 0 (inclusive) until `n`
+ (exclusive).
+ }}
+
+data.Stream.window : Nat -> '{g, Stream a} r -> '{g, Stream [a]} r
+data.Stream.window windowSize thunk = do window! windowSize thunk
+
+data.Stream.window.doc : Doc
+data.Stream.window.doc = {{ The delayed version of {window!}. }}
+
+data.Stream.window! : Nat -> '{g, Stream a} r ->{g, Stream [a]} r
+data.Stream.window! windowSize thunk =
+ use List :+
+ use Nat <= ==
+ when (windowSize == 0) do forever do emit []
+ go : [a] -> Nat -> Request {Stream a} r ->{Stream [a]} r
+ go acc need = cases
+ { r } -> r
+ { emit a -> k } ->
+ if need <= 1 then
+ newAcc = acc :+ a
+ emit newAcc
+ handle k() with go (List.drop 1 newAcc) 0
+ else handle k() with go (acc :+ a) (Nat.decrement need)
+ handle thunk() with go [] windowSize
+
+data.Stream.window!.doc : Doc
+data.Stream.window!.doc =
+ use Stream fromList toList
+ {{
+ Returns a {type Stream} of {type List}s, each containing `n` elements of the
+ input {type Stream} in a sliding window.
+
+ The first element is the first `n` elements of the input {type Stream}, the
+ second element has the `n` elements after the first, and so on.
+
+ If the input {type Stream} has fewer than `n` elements, then the output
+ {type Stream} is empty.
+
+ If `n` is zero, then the output {type Stream} is an infinite {type Stream} of
+ empty {type List}s.
+
+ # Examples
+
+ ```
+ toList <| window 3 (fromList [1, 2, 3, 4, 5])
+ ```
+
+ ```
+ toList <| window 3 (fromList [1, 2, 3])
+ ```
+
+ ```
+ toList <| window 3 (fromList [1, 2])
+ ```
+ }}
+
+data.Stream.zip : '{g, Stream a} r -> '{g, Stream b} r -> '{g, Stream (a, b)} r
+data.Stream.zip sa sb _ = zip! sa sb
+
+data.Stream.zip.doc : Doc
+data.Stream.zip.doc =
+ {{
+ `Stream.zip sa sb` returns a delayed computation of `Stream.zip! sa sb`.
+ }}
+
+data.Stream.zip! : '{g, Stream a} r -> '{g, Stream b} r ->{g, Stream (a, b)} r
+data.Stream.zip! sa sb = zipWith! Tuple.pair sa sb
+
+data.Stream.zip!.doc : Doc
+data.Stream.zip!.doc =
+ {{
+ `Stream.zip! sa sb` emits pairs of elements emitted by stream `sa` and stream
+ `sb`, respectively, in the order emitted. The computation terminates when
+ either `sa` or `sb` terminates, returning the value of the first stream to
+ terminate (or the value of `sa` if both streams terminate after emitting the
+ same number of elements).
+ }}
+
+data.Stream.zipWith :
+ (a ->{g} b ->{g} c)
+ -> '{g, Stream a} r
+ -> '{g, Stream b} r
+ -> '{g, Stream c} r
+data.Stream.zipWith f sa sb _ = zipWith! f sa sb
+
+data.Stream.zipWith.doc : Doc
+data.Stream.zipWith.doc =
+ {{
+ `Stream.zipWith f sa sb` returns a delayed computation of `Stream.zipWith! f
+ sa sb`
+ }}
+
+data.Stream.zipWith! :
+ (a ->{g} b ->{g} c) -> '{g, Stream a} r -> '{g, Stream b} r ->{g, Stream c} r
+data.Stream.zipWith! f sa sb =
+ readB : a -> '{g, Stream a} r -> Request (Stream b) r ->{g, Stream c} r
+ readB a sa = cases
+ { emit b -> sb } ->
+ emit (f a b)
+ handle sa() with readA sb
+ { r } -> r
+ readA : '{g, Stream b} r -> Request (Stream a) r ->{g, Stream c} r
+ readA sb = cases
+ { emit a -> sa } -> handle sb() with readB a sa
+ { r } -> r
+ handle sa() with readA sb
+
+data.Stream.zipWith!.doc : Doc
+data.Stream.zipWith!.doc =
+ {{
+ `Stream.zipWith! f sa sb` "zips" two streams `sa` and `sb` by combining
+ elements pairwise with a binary operation `f`, in the order emitted. The
+ computation terminates when either `sa` or `sb` terminates, returning the
+ value of the first stream to terminate (or the value of `sa` if both streams
+ terminate after emitting the same number of elements).
+ }}
+
+data.Trie.doc : Doc
+data.Trie.doc =
+ {{
+ A {type Trie} is a [prefix tree](https://en.wikipedia.org/wiki/Trie) that is
+ essentially a map from sequences of keys to values.
+
+ The {type Trie} type is parameterized by the key type and the value type.
+
+ # Constructing a trie
+
+ The empty trie:
+
+ @signature{Trie.empty}
+
+ A trie with a single value at a given sequence of keys:
+
+ @signature{Trie.singleton}
+
+ A trie can also be constructed from an optional value at the root and a
+ list of child tries:
+
+ @signature{Trie}
+
+ # Querying a trie
+
+ Get the value at a given sequence of keys:
+
+ @signature{Trie.lookup}
+
+ Get the child tries of a trie:
+
+ @signature{Trie.tail}
+
+ Get the optional value at the root of a trie:
+
+ @signature{Trie.head}
+
+ Get a {type List} of all the values in a trie:
+
+ @signature{Trie.values}
+
+ # Adding elements
+
+ Add a value at a given sequence of keys:
+
+ @signature{Trie.insert}
+
+ Combine two tries by adding all the values from the second trie to the
+ first trie. If a value is present in both tries, the value from the first
+ trie is used:
+
+ @signature{Trie.union}
+
+ Union two tries, but if a value is present in both tries, use the given
+ function to combine the values:
+
+ @signature{Trie.unionWith}
+
+ # Transforming a trie
+
+ Map a function over all the values in a trie:
+
+ @signature{Trie.map}
+
+ Map a function over the keys:
+
+ @signature{Trie.mapKeys}
+ }}
+
+data.Trie.empty : Trie k v
+data.Trie.empty = Trie None Map.empty
+
+data.Trie.empty.doc : Doc
+data.Trie.empty.doc = {{ The empty {type Trie}. }}
+
+data.Trie.fromList : [([k], v)] -> Trie k v
+data.Trie.fromList kvs =
+ go : [([k], v)] -> Trie k v -> Trie k v
+ go = cases
+ [], t -> t
+ (ks, v) +: kvs, t -> go kvs (Trie.insert ks v t)
+ go kvs Trie.empty
+
+data.Trie.gen : '{Gen} k -> '{Gen} v -> '{Gen} Trie k v
+data.Trie.gen k v = do
+ h = gen.optional v ()
+ g = data.Trie.gen k v
+ t = tests.mapOf k g ()
+ Trie h t
+
+data.Trie.gen.doc : Doc
+data.Trie.gen.doc =
+ {{
+ A generator of {type Trie} values, given a generator of keys and a generator
+ of values.
+
+ # Example
+
+ ```
+ deprecated.sample 10 (Trie.gen gen.int Char.ascii)
+ ```
+ }}
+
+data.Trie.head : Trie k v -> Optional v
+data.Trie.head = cases Trie head _ -> head
+
+data.Trie.head.doc : Doc
+data.Trie.head.doc =
+ use Trie fromList head
+ {{
+ Gets the element at the root of a {type Trie}. Returns {None} if the
+ {type Trie} has no element with an empty key.
+
+ # Examples
+
+ ```
+ head (fromList [([], 1), (["a"], 2)])
+ ```
+
+ ```
+ head (fromList [(["a"], 1), (["b"], 2)])
+ ```
+ }}
+
+data.Trie.head.modify :
+ (Optional v ->{𝕖} Optional v) -> Trie k v ->{𝕖} Trie k v
+data.Trie.head.modify f = cases Trie head tail -> Trie (f head) tail
+
+data.Trie.head.modify.doc : Doc
+data.Trie.head.modify.doc =
+ use Trie singleton
+ use head modify
+ {{
+ Modifies the {Trie.head} of the given {type Trie} using the given function.
+
+ # Example
+
+ ```
+ modify (Optional.map Nat.increment) (singleton [] 0)
+ ```
+
+ ```
+ modify (const None) (singleton [] 1)
+ ```
+ }}
+
+data.Trie.head.set : Optional v -> Trie k v -> Trie k v
+data.Trie.head.set head1 = cases Trie _ tail -> Trie head1 tail
+
+data.Trie.head.set.doc : Doc
+data.Trie.head.set.doc =
+ use head set
+ use text fromList toList
+ {{
+ Sets or deletes the value at the {Trie.head} of a {type Trie}. If the
+ provided value is {None}, the value at the head is deleted. Otherwise, the
+ value at the head is set to the provided value.
+
+ # Examples
+
+ ```
+ toList (set (Some 1) (fromList [("ab", 2)]))
+ ```
+
+ ```
+ toList (set None (fromList [("", 1), ("ab", 2)]))
+ ```
+ }}
+
+data.Trie.insert : [k] -> v -> Trie k v -> Trie k v
+data.Trie.insert path v t = Trie.unionWith const (Trie.singleton path v) t
+
+data.Trie.insert.doc : Doc
+data.Trie.insert.doc =
+ use Trie insert
+ {{
+ Inserts a value into a {type Trie} at a given key sequence.
+
+ # Example
+
+ ```
+ text.toList
+ (insert
+ (toCharList "foo")
+ 1
+ (insert (toCharList "bar") 2 (insert (toCharList "baz") 3 Trie.empty)))
+ ```
+ }}
+
+data.Trie.lookup : [k] -> Trie k v -> Optional v
+data.Trie.lookup path t = match path with
+ [] -> Trie.head t
+ p +: ps -> Optional.flatMap (data.Trie.lookup ps) (Map.get p (Trie.tail t))
+
+data.Trie.lookup.doc : Doc
+data.Trie.lookup.doc =
+ {{
+ Looks up a value in a {type Trie} at a given key sequence.
+
+ # Example
+
+ ```
+ Trie.lookup
+ (toCharList "foo") (text.fromList [("foo", 1), ("bar", 2), ("baz", 3)])
+ ```
+ }}
+
+data.Trie.map : (v1 ->{e} v2) ->{e} Trie k v1 ->{e} Trie k v2
+data.Trie.map f t =
+ Trie (Optional.map f (Trie.head t)) (Map.map (data.Trie.map f) (Trie.tail t))
+
+data.Trie.map.doc : Doc
+data.Trie.map.doc = {{ Apply a function to each value in a {type Trie}. }}
+
+data.Trie.mapKeys : (k1 ->{e} k2) ->{e} Trie k1 v ->{e} Trie k2 v
+data.Trie.mapKeys f t =
+ Trie
+ (Trie.head t) (Map.mapKeys f (Map.map (data.Trie.mapKeys f) (Trie.tail t)))
+
+data.Trie.mapKeys.doc : Doc
+data.Trie.mapKeys.doc =
+ {{
+ Transforms the keys of a {type Trie} using the given function.
+
+ # Example
+
+ ```
+ Trie.toList
+ (Trie.mapKeys Char.toUppercase (text.fromList [("bar", 1), ("baz", 2)]))
+ ```
+ }}
+
+data.Trie.singleton : [k] -> v -> Trie k v
+data.Trie.singleton path v = match path with
+ [] -> Trie (Some v) Map.empty
+ k +: ks -> Trie None (Map.fromList [(k, data.Trie.singleton ks v)])
+
+data.Trie.singleton.doc : Doc
+data.Trie.singleton.doc =
+ {{
+ Creates a {type Trie} with a single element.
+
+ # Example
+
+ ```
+ Trie.toList (Trie.singleton (toCharList "foo") 1)
+ ```
+ }}
+
+data.Trie.tail : Trie k v -> Map k (Trie k v)
+data.Trie.tail = cases Trie _ tail -> tail
+
+data.Trie.tail.doc : Doc
+data.Trie.tail.doc =
+ use Trie fromList tail
+ {{
+ Gets a {type Map} that maps the first element of each key in a {type Trie} to
+ the whole {type Trie} under that key element. Returns {None} if the
+ {type Trie} is empty or only has an empty key.
+
+ # Examples
+
+ ```
+ Map.toList (Map.map Trie.values (tail (fromList [([], 1), (["a"], 2)])))
+ ```
+
+ ```
+ Map.values (tail (fromList [(["a"], 1), (["b"], 2)]))
+ ```
+
+ ```
+ Map.values (tail (fromList [([], 1)]))
+ ```
+ }}
+
+data.Trie.tail.modify :
+ (Map k111 (Trie k111 v) ->{𝕖} Map k (Trie k v)) -> Trie k111 v ->{𝕖} Trie k v
+data.Trie.tail.modify f = cases Trie head tail -> Trie head (f tail)
+
+data.Trie.tail.modify.doc : Doc
+data.Trie.tail.modify.doc =
+ use tail modify
+ use text fromList toList
+ {{
+ Modifies the {Trie.tail} of the given {type Trie} using the given function.
+
+ # Example
+
+ ```
+ toList
+ (modify
+ (Map.map (Trie.map Nat.increment))
+ (fromList [("", 0), ("bar", 1), ("baz", 2)]))
+ ```
+
+ ```
+ toList
+ (modify (const Map.empty) (fromList [("", 0), ("bar", 1), ("baz", 2)]))
+ ```
+ }}
+
+data.Trie.tail.set : Map k (Trie k v) -> Trie k111 v -> Trie k v
+data.Trie.tail.set tail1 = cases Trie head _ -> Trie head tail1
+
+data.Trie.tail.set.doc : Doc
+data.Trie.tail.set.doc =
+ {{
+ Sets the {Trie.tail} of a {type Trie} to the provided {type Map}.
+
+ # Example
+
+ ```
+ text.toList
+ (tail.set
+ (Map.fromList [(?a, text.fromList [("b", 2)])])
+ (text.fromList [("", 0), ("foo", 1)]))
+ ```
+ }}
+
+data.Trie.text.fromList : [(Text, v)] -> Trie Char v
+data.Trie.text.fromList = List.map (first toCharList) >> Trie.fromList
+
+data.Trie.text.fromList.doc : Doc
+data.Trie.text.fromList.doc =
+ {{
+ Creates a {type Trie} from a list of {type Text} keys and values, where each
+ character of the {type Text} becomes a key in the {type Trie}.
+
+ # Example
+
+ ```
+ Trie.toList (text.fromList [("foo", 1), ("bar", 2), ("baz", 3)])
+ ```
+ }}
+
+data.Trie.text.insert : Text -> v -> Trie Char v -> Trie Char v
+data.Trie.text.insert k = Trie.insert (toCharList k)
+
+data.Trie.text.insert.doc : Doc
+data.Trie.text.insert.doc =
+ use text insert
+ {{
+ Inserts a value into a {type Trie} using a {type Text} key, where each
+ character of the {type Text} becomes a key in the {type Trie}
+
+ # Example
+
+ ```
+ Trie.toList (insert "foo" 1 (insert "bar" 2 (insert "baz" 3 Trie.empty)))
+ ```
+ }}
+
+data.Trie.text.lookup : Text -> Trie Char v -> Optional v
+data.Trie.text.lookup k = Trie.lookup (toCharList k)
+
+data.Trie.text.lookup.doc : Doc
+data.Trie.text.lookup.doc =
+ use text insert
+ {{
+ Looks up a value in a {type Trie} using a {type Text} key, where each
+ character of the {type Text} is a key in the {type Trie}.
+
+ # Example
+
+ ```
+ text.lookup
+ "foo" (insert "foo" 1 (insert "bar" 2 (insert "baz" 3 Trie.empty)))
+ ```
+ }}
+
+data.Trie.text.toList : Trie Char v -> [(Text, v)]
+data.Trie.text.toList = List.map (first fromCharList) << Trie.toList
+
+data.Trie.text.toList.doc : Doc
+data.Trie.text.toList.doc =
+ {{
+ Converts a {type Trie} to a list of {type Text} keys and values, such that
+ each character in the {type Trie}'s key sequence for a value becomes a
+ character in corresponding {type Text} in the output list.
+
+ # Example
+
+ ```
+ text.toList (text.fromList [("foo", 1), ("bar", 2), ("baz", 3)])
+ ```
+ }}
+
+data.Trie.toList : Trie k v -> [([k], v)]
+data.Trie.toList = Map.toList << Trie.toMap
+
+data.Trie.toList.doc : Doc
+data.Trie.toList.doc =
+ {{
+ Convert a {type Trie} to a list of key-value pairs.
+
+ # Example
+
+ ```
+ Trie.toList (Trie.fromList [([1, 2, 3], "a"), ([2, 3, 4], "b")])
+ ```
+ }}
+
+data.Trie.toMap : Trie k v -> Map [k] v
+data.Trie.toMap t =
+ use List :+
+ go : Trie k v -> [k] -> Map [k] v -> Map [k] v
+ go = cases
+ Trie None m, ks, acc -> go' m ks acc
+ Trie (Some v) m, ks, acc -> Map.insert ks v (go' m ks acc)
+ go' : Map k (Trie k v) -> [k] -> Map [k] v -> Map [k] v
+ go' = cases
+ internal.Tip, ks, m -> m
+ internal.Bin _ k v l r, ks, m ->
+ Map.unions [go' l ks m, go v (ks :+ k) m, go' r ks m]
+ go t [] Map.empty
+
+data.Trie.toMap.doc : Doc
+data.Trie.toMap.doc =
+ use Map toList
+ use Trie fromList toMap
+ {{
+ Converts a {type Trie} to a {type Map} from {type List}s to values.
+
+ # Examples
+
+ ```
+ toList (toMap (fromList [([], 1), (["a"], 2)]))
+ ```
+
+ ```
+ toList (toMap (fromList [(["a"], 1), (["b"], 2)]))
+ ```
+
+ ```
+ toList (toMap (fromList [([], 1)]))
+ ```
+ }}
+
+data.Trie.union : Trie k v -> Trie k v -> Trie k v
+data.Trie.union = Trie.unionWith const
+
+data.Trie.union.doc : Doc
+data.Trie.union.doc =
+ use Trie fromList
+ {{
+ Returns the union of two {type Trie}s. If a key is present in both
+ {type Trie}s, the value from the first {type Trie} is used.
+
+ # Examples
+
+ ```
+ Trie.toList
+ (Trie.union
+ (fromList [([1, 2, 3], "a"), ([2, 3, 4], "b")])
+ (fromList [([2, 3, 4], "c"), ([3, 4, 5], "d")]))
+ ```
+ }}
+
+test> data.Trie.union.tests.values = runs 100 do
+ use Trie gen values
+ t1 = gen natInOrder natInOrder ()
+ t2 = gen natInOrder natInOrder ()
+ t1vs = values t1
+ uvs = values (Trie.union t1 t2)
+ p = List.all (v -> List.contains v uvs) t1vs
+ expect p
+
+data.Trie.unionWith :
+ (v ->{e} v ->{e} v) ->{e} Trie k v ->{e} Trie k v ->{e} Trie k v
+data.Trie.unionWith f t1 t2 =
+ use Optional orElse
+ use Trie head tail
+ h1 = head t1
+ h2 = head t2
+ Trie
+ (orElse (orElse (Optional.map2 f h1 h2) h1) h2)
+ (Map.unionWith (data.Trie.unionWith f) (tail t1) (tail t2))
+
+data.Trie.unionWith.doc : Doc
+data.Trie.unionWith.doc =
+ use Nat +
+ use text fromList
+ {{
+ Takes two {type Trie}s and a function, and returns a new {type Trie} that
+ contains all the keys from both input {type Trie}s. If a key is present in
+ both input {type Trie}s the function is used to combine the values.
+
+ # Example
+
+ ```
+ text.toList
+ (Trie.unionWith
+ (+)
+ (fromList [("foo", 1), ("bar", 2)])
+ (fromList [("bar", 3), ("baz", 4)]))
+ ```
+ }}
+
+data.Trie.values : Trie k v -> [v]
+data.Trie.values = cases
+ Trie ov m ->
+ match ov with
+ None -> List.flatMap data.Trie.values (Map.values m)
+ Some v -> v List.+: List.flatMap data.Trie.values (Map.values m)
+
+data.Trie.values.doc : Doc
+data.Trie.values.doc =
+ {{
+ Returns a list of all values in a {type Trie}.
+
+ # Example
+
+ ```
+ Trie.values (text.fromList [("foo", 1), ("bar", 2), ("baz", 3)])
+ ```
+ }}
+
+data.Tuple.at1 : Tuple a b -> a
+data.Tuple.at1 = cases Cons a _ -> a
+
+data.Tuple.at1.doc : Doc
+data.Tuple.at1.doc =
+ {{
+ Get the first element of a tuple.
+
+ # Examples
+
+ ```
+ at1 ("Salt", "Pepper")
+ ```
+
+ ```
+ at1 ("Achivement", "Effort", "Reward")
+ ```
+
+ ```
+ at1 (Cons "one" ())
+ ```
+
+ See {Tuple.doc} for more information on tuples.
+ }}
+
+test> data.Tuple.at1.tests.ex1 = check (at1 ("A", "B") === "A")
+
+data.Tuple.at2 : Tuple a (Tuple b c) -> b
+data.Tuple.at2 = cases Cons _ (Cons b _) -> b
+
+data.Tuple.at2.doc : Doc
+data.Tuple.at2.doc =
+ {{
+ Get the second element of a tuple.
+
+ # Examples
+
+ ```
+ at2 ("Salt", "Pepper")
+ ```
+
+ ```
+ at2 ("Achivement", "Effort", "Reward")
+ ```
+
+ See {Tuple.doc} for more information on tuples.
+ }}
+
+test> data.Tuple.at2.tests.ex1 = check (at2 ("A", "B") === "B")
+
+data.Tuple.at3 : Tuple a (Tuple b (Tuple c d)) -> c
+data.Tuple.at3 = cases Cons _ (Cons _ (Cons c _)) -> c
+
+data.Tuple.at3.doc : Doc
+data.Tuple.at3.doc =
+ {{
+ Get the third element of a tuple.
+
+ # Examples
+
+ ```
+ at3 (?🔴, ?🟢, ?🔵)
+ ```
+
+ ```
+ at3 ("John", "Paul", "Ringo", "George")
+ ```
+
+ See {Tuple.doc} for more information on tuples.
+ }}
+
+test> data.Tuple.at3.tests.ex1 = check (at3 ("A", "B", "C") === "C")
+
+test> data.Tuple.at3.tests.ex2 = check (at3 ("A", "B", "C", "D") === "C")
+
+data.Tuple.at4 : Tuple a (Tuple b (Tuple c (Tuple d e))) -> d
+data.Tuple.at4 = cases Cons _ (Cons _ (Cons _ (Cons d _))) -> d
+
+data.Tuple.at4.doc : Doc
+data.Tuple.at4.doc =
+ {{
+ Get the fourth element of a tuple.
+
+ # Examples
+
+ ```
+ at4 ("Beyoncé", "Kelly", "LaTavia", "LeToya")
+ ```
+
+ ```
+ at4 ("Ginger", "Baby", "Scary", "Sporty", "Posh")
+ ```
+
+ See {Tuple.doc} for more information on tuples.
+ }}
+
+test> data.Tuple.at4.tests.ex1 = check (at4 ("A", "B", "C", "D") === "D")
+
+test> data.Tuple.at4.tests.ex2 =
+ check (at4 ("A", "B", "C", "D", "E", "F") === "D")
+
+data.Tuple.bimap : (a ->{g1} b) -> (a, a) ->{g1} (b, b)
+data.Tuple.bimap f = cases (a, b) -> (f a, f b)
+
+data.Tuple.bimap.doc : Doc
+data.Tuple.bimap.doc =
+ use Nat +
+ {{
+ Applies a function to both elements of a pair.
+
+ # Example
+
+ ```
+ Tuple.bimap (x -> x + 1) (1, 2)
+ ```
+ }}
+
+data.Tuple.Cons.doc : Doc
+data.Tuple.Cons.doc = {{ Prepends an element to a {type Tuple}. }}
+
+data.Tuple.doc : Doc
+data.Tuple.doc =
+ use Text reverse size
+ use Tuple bimap mapLeft mapRight second
+ {{
+ 📚 Language Reference:
+ [Tuple types](https://www.unison-lang.org/docs/language-reference/tuple-types/)
+
+ A {type Tuple} is a heterogeneous list of typed elements.
+
+ # Constructing tuples
+
+ Tuple literals are constructed with a built-in syntactic shorthand. For
+ example, `` (1, "Two") `` is a binary tuple (a pair), `` (-4, "Yes", false)
+ `` is a triple, and so on.
+
+ Behind the scenes, an n-tuple is represented by n applications of the
+ {Cons} constructor, associated to the right and terminated by ``()``:
+
+ ```
+ (Cons _ (Cons b (Cons _ _))) = (1, "Two", +3)
+ b
+ ```
+
+ You can use the same shorthand for pattern matching:
+
+ ```
+ swap = cases (x, y) -> (y, x)
+ swap (1, "2")
+ ```
+
+ There is no syntactic shorthand for a 1-element tuple:
+
+ ```
+ Cons "One" ()
+ ```
+
+ You can think of the `` () `` value as a zero-tuple.
+
+ There's no limit on the size of tuples.
+
+ # Indexing tuples
+
+ `` at1 t `` gets the first element of the {type Tuple} `t`. `` at2 t ``
+ gets the second element, and so on. These functions work on tuples of any
+ length.
+
+ The Base library provides indexing functions up to {at4}, but you can
+ access any element of a {type Tuple} using pattern matching:
+
+ ```
+ at5 = cases Cons _ (Cons _ (Cons _ (Cons _ (Cons x _)))) -> x
+ at5 (?a, ?b, ?c, ?d, ?e)
+ ```
+
+ # Mapping over tuples
+
+ {mapLeft} applies a function to the left element of a pair:
+
+ ```
+ mapLeft size ("Left", "Right")
+ ```
+
+ {mapRight} applies a function to the right element of a pair:
+
+ ```
+ mapRight size ("Left", "Right")
+ ```
+
+ {mapPair} applies one function on the left and another on the right:
+
+ ```
+ mapPair (size, reverse) ("Left", "Right")
+ ```
+
+ {bimap} applies a single function to both elements of a pair where both
+ have the same type:
+
+ ```
+ bimap size ("Left", "Right")
+ ```
+
+ {first} applies a function to the first element of any {type Tuple}:
+
+ ```
+ first reverse ("one", 2, +3)
+ ```
+
+ {second} applies a function to the second element of any {type Tuple}:
+
+ ```
+ second reverse (1, "two", +3)
+ ```
+ }}
+
+data.Tuple.first : (i ->{g} o) -> Tuple i b ->{g} Tuple o b
+data.Tuple.first f = cases Cons a b -> Cons (f a) b
+
+data.Tuple.first.doc : Doc
+data.Tuple.first.doc =
+ {{
+ The expression `` first f t `` constructs a new {type Tuple} the same as `t`
+ except the first element is modified by the function `f`.
+
+ # Example
+
+ ```
+ x = ("Soprano", "Alto", "Tenor", "Bass")
+ first Text.size x
+ ```
+ }}
+
+data.Tuple.mapLeft : (a ->{g} b) -> (a, c) ->{g} (b, c)
+data.Tuple.mapLeft = first
+
+data.Tuple.mapLeft.doc : Doc
+data.Tuple.mapLeft.doc =
+ use Tuple mapLeft
+ {{
+ {mapLeft} is {first} specialized to pairs. It modifies the first element of
+ the pair with the given function.
+
+ # Example
+
+ ```
+ x = ("Bread", "Butter")
+ mapLeft Text.size x
+ ```
+ }}
+
+data.Tuple.mapPair : ((a ->{g} b), (c ->{g} d)) -> (a, c) ->{g} (b, d)
+data.Tuple.mapPair = cases (f, g), (a, c) -> (f a, g c)
+
+data.Tuple.mapPair.doc : Doc
+data.Tuple.mapPair.doc =
+ {{
+ The expression `` mapPair (f, g) (a, b) `` constructs the new pair
+ `(f a, g b)`.
+
+ # Example
+
+ ```
+ x = ("Milk", "Cookies")
+ mapPair (Text.size, Text.reverse) x
+ ```
+ }}
+
+data.Tuple.mapRight : (a ->{g} b) -> (c, a) ->{g} (c, b)
+data.Tuple.mapRight = Tuple.second
+
+data.Tuple.mapRight.doc : Doc
+data.Tuple.mapRight.doc =
+ use Tuple mapRight
+ {{
+ {mapRight} is {Tuple.second} specialized to pairs. It modifies the second
+ element of the pair with the given function.
+
+ # Example
+
+ ```
+ x = ("Tea", "Biscuits")
+ mapRight Text.size x
+ ```
+ }}
+
+data.Tuple.pair : a -> b -> (a, b)
+data.Tuple.pair a b = (a, b)
+
+data.Tuple.pair.doc : Doc
+data.Tuple.pair.doc =
+ use List zipWith
+ use Tuple pair
+ {{
+ `` pair x y `` creates the {type Tuple} `(x,y)`.
+
+ # Example
+
+ When you want to create a tuple in a lambda:
+
+ ```
+ zipWith (x y -> (x, y)) [1, 2] [5, 10]
+ ```
+
+ This can be rewritten using {pair} to be easier to read:
+
+ ```
+ zipWith pair [1, 2] [5, 10]
+ ```
+ }}
+
+data.Tuple.second :
+ (i ->{g} o) -> Tuple a (Tuple i b) ->{g} Tuple a (Tuple o b)
+data.Tuple.second f = cases Cons a (Cons b c) -> Cons a (Cons (f b) c)
+
+data.Tuple.second.doc : Doc
+data.Tuple.second.doc =
+ use Tuple second
+ {{
+ The expression `` second f t `` constructs a new {type Tuple} the same as `t`
+ except the second element is modified by the function `f`.
+
+ ```
+ x = ("Spring", "Summer", "Autumn", "Winter")
+ second Text.size x
+ ```
+ }}
+
+data.Tuple.swap : (a, b) -> (b, a)
+data.Tuple.swap = cases (a, b) -> (b, a)
+
+data.Tuple.swap.doc : Doc
+data.Tuple.swap.doc =
+ {{
+ Swaps the elements of a 2-tuple.
+
+ # Example
+
+ ```
+ Tuple.swap (1, 2)
+ ```
+ }}
+
+test> data.Tuple.tests.ex1 =
+ use Text ++
+ check
+ let
+ tuple : (Text, Nat, Text)
+ tuple = ("Hello", 3, "Tuple")
+ actual = at1 tuple ++ Nat.toText (at2 tuple) ++ at3 tuple
+ expected = "Hello3Tuple"
+ actual === expected
+
+test> data.Tuple.tests.ex2 =
+ use Nat * +
+ check
+ let
+ pythagoras : (Nat, Nat, Nat)
+ pythagoras = (3, 4, 5)
+ square n = n * n
+ let
+ (a, b, c) = pythagoras
+ square a + square b === square c
+
+Debug.tap : Text -> a -> a
+Debug.tap = Function.tap << Debug.trace
+
+Debug.tap.doc : Doc
+Debug.tap.doc =
+ use Nat ==
+ {{
+ `` Debug.tap t v `` evaluates `` Debug.trace t v `` and then returns the
+ value `v`, allowing you to trace intermediate values in pipelines.
+
+ # Example
+
+ @typecheck ```
+ x =
+ [1, 2, 3] |> List.map Nat.increment |> Debug.tap "incremented" |> Nat.sum
+ check (x == 9)
+ ```
+
+ This will print the following to the console:
+
+ ``` raw
+ trace: incremented
+ [2, 3, 4]
+ ```
+
+ # See also
+
+ {Function.tap}
+ }}
+
+Debug.toDebugText : a -> Text
+Debug.toDebugText a =
+ match toDebugText.impl a with
+ None ->
+ "⚠️ (Debug.toText called, but Unison is not being run with tracing support)"
+ Some e -> Either.fold id id e
+
+Debug.toDebugText.doc : Doc
+Debug.toDebugText.doc =
+ {{
+ Makes a best effort attempt to convert an arbitrary value to {type Text}.
+
+ The representation might not be particularly pretty but it can still be
+ useful for debugging or if writing introspection tools in pure Unison.
+
+ Also see {Debug.trace}.
+ }}
+
+-- builtin Debug.toDebugText.impl : a -> Optional (Either Text Text)
+
+Debug.toDebugText.impl.doc : Doc
+Debug.toDebugText.impl.doc =
+ {{
+ This function converts any value to {type Text}, purely for debugging
+ purposes. It returns different things depending on the mode Unison is being
+ run in.
+
+ * {None} indicates Unison is being run with all tracing support switched off.
+ * {Left} indicates Unison is being run with only low-level tracing support.
+ The inside {type Text} may not be particularly pretty.
+ * A {Right} indicates Unison is being run with full tracing support. The
+ inside {type Text} will be a pretty-printed definition.
+ }}
+
+-- builtin Debug.trace : Text -> a -> ()
+
+Debug.trace.doc : Doc
+Debug.trace.doc =
+ use Debug trace
+ use Nat *
+ {{
+ {trace} prints out a {type Text} and any value and returns `()`.
+
+ For example, this returns `()` after printing `"Calling f"` followed by
+ `"2468"` to the console:
+
+ ```
+ f n = n * 2
+ trace "Calling f" (f 1234)
+ ```
+ }}
+
+-- builtin Debug.watch : Text -> a -> a
+
+Debug.watch.doc : Doc
+Debug.watch.doc =
+ use Nat *
+ {{
+ `` watch t e `` prints the text `t` to the console after the expression `e`
+ has been evaluated. Returns the value of `e`.
+
+ For example, this returns `2468` after printing "Calling f" to the console:
+
+ ```
+ f n = n * 2
+ watch "Calling f" (f 1234)
+ ```
+ }}
+
+(Doc.++) : Doc -> Doc -> Doc
+d1 Doc.++ d2 = match (d1, d2) with
+ (Doc.Join ds, Doc.Join ds2) -> Doc.Join (ds List.++ ds2)
+ (Doc.Join ds, _) -> Doc.Join (ds List.:+ d2)
+ (_, Doc.Join ds) -> Doc.Join (d1 List.+: ds)
+ _ -> Doc.Join [d1, d2]
+
+Doc.Anchor.doc : Doc
+Doc.Anchor.doc =
+ {{
+ {Anchor} decorates a {type Doc} with an anchor. Anchors are used to identify
+ a particular {type Doc} in a {type Doc} tree. Anchors are useful for linking
+ to a particular {type Doc} in a {type Doc} tree.
+ }}
+
+Doc.BulletedList.doc : Doc
+Doc.BulletedList.doc =
+ {{
+ A {BulletedList} is a {type Doc} that represents a bulleted list of items,
+ each of which is a {type Doc}.
+
+ You should use {docBulletedList} to construct a {BulletedList} instead of
+ using this constructor directly. Or use the
+ [documentation syntax](https://www.unison-lang.org/learn/usage-topics/documentation/).
+
+ # Example
+
+ ```
+ docBulletedList [{{ Winter }}, {{ Spring }}, {{ Summer }}, {{ Autumn }}]
+ ```
+ }}
+
+Doc.Callout.doc : Doc
+Doc.Callout.doc =
+ {{
+ {Callout} wraps a {type Doc} in a callout block. Callout blocks are used to
+ highlight a particular {type Doc} for the reader, visually separating it from
+ the surrounding {type Doc} with an optional title or symbol.
+
+ You should not use {Callout} directly. Instead use {docCallout}.
+ }}
+
+Doc.codeBlock : Text -> Text -> Doc
+Doc.codeBlock typ code = CodeBlock typ (Word code)
+
+Doc.codeBlock.doc : Doc
+Doc.codeBlock.doc =
+ {{
+ Returns a {type Doc} code block. The first argument is the language, and the
+ second argument is the code.
+
+ # Example
+
+ ```
+ codeBlock
+ "unison" "fib x =\n if x < 2 then x else fib (x - 1) + fib (x - 2)"
+ ```
+ }}
+
+Doc.CodeBlock.doc : Doc
+Doc.CodeBlock.doc =
+ {{
+ Embeds a code block in a {type Doc} value. The first argument is the language
+ of the code block, and the second argument is the code itself.
+
+ You should not use this function directly. Instead, use the {docCodeBlock}
+ function, or use the documentation syntax as detailed in
+ [Documenting Unison Code](https://unisonweb.org/docs/documentation).
+ }}
+
+Doc.Column.doc : Doc
+Doc.Column.doc =
+ {{
+ Takes a {type List} of {type Doc}s and returns a {type Doc} that displays the
+ {type List} of {type Doc}s in a column with their left edges aligned.
+
+ You should not call this constructor directly. Instead use {docColumn}.
+ }}
+
+(Doc.Deprecated.++) : Deprecated -> Deprecated -> Deprecated
+d1 Doc.Deprecated.++ d2 = match (d1, d2) with
+ (Deprecated.Join ds, Deprecated.Join ds2) -> Deprecated.Join (ds List.++ ds2)
+ (Deprecated.Join ds, _) -> Deprecated.Join (ds List.:+ d2)
+ (_, Deprecated.Join ds) -> Deprecated.Join (d1 List.+: ds)
+ _ -> Deprecated.Join [d1, d2]
+
+Doc.Deprecated.example : Link.Term -> Deprecated
+Doc.Deprecated.example e =
+ source = Deprecated.Source (Link.Term e)
+ value = Evaluate e
+ Deprecated.Join [Blob "\n", source, Blob "\n\nOutput: ", value, Blob ""]
+
+Doc.Deprecated.example.doc : Deprecated
+Doc.Deprecated.example.doc =
+ [: Given a `termRef` reference, constructs an example that shows the source
+ of that term as well as the result of evaluating it.:]
+
+Doc.doc : Doc
+Doc.doc =
+ {{
+ # Unison Documentation
+
+ A {type Doc} is computable first-class documentation. You are reading a
+ {type Doc} at this very moment.
+
+ 📚 Tutorial:
+ [Documenting Unison code](https://www.unison-lang.org/docs/usage-topics/documentation/)
+ }}
+
+Doc.EmbedSvg.doc : Doc
+Doc.EmbedSvg.doc =
+ {{
+ Used by @inlineSignature{embedSvg} for embedding SVG images into a
+ {type Doc}.
+ }}
+
+Doc.EmbedSvg.embedSvg : Text -> Doc
+Doc.EmbedSvg.embedSvg txt = Special (Embed (Any (EmbedSvg txt)))
+
+Doc.EmbedSvg.embedSvg.doc : Doc
+Doc.EmbedSvg.embedSvg.doc =
+ use embedSvg example
+ {{
+ Embeds an SVG image into a {type Doc}.
+
+ For example, given this code:
+
+ @source{example}
+
+ Here it is rendered:
+
+ {{ example }}
+ }}
+
+Doc.EmbedSvg.embedSvg.example : Doc
+Doc.EmbedSvg.embedSvg.example =
+ txt =
+ """
+
+ """
+ embedSvg txt
+
+Doc.Folded.doc : Doc
+Doc.Folded.doc =
+ {{
+ {Folded} decorates a {type Doc} with a folded state represented by a
+ {type Boolean}. If the {type Boolean} is `` true `` the {type Doc} is folded.
+ Otherwise the {type Doc} is unfolded.
+ }}
+
+Doc.FrontMatter.doc : Doc
+Doc.FrontMatter.doc =
+ {{
+ Unison {type Doc} type for embedding FrontMatter in Unison Doc.
+
+ ex:
+
+ ``` unison
+ myDoc = {{
+
+ {{ Special (Embed (Any (FrontMatter [("title", "FrontMatter Example"), ("author", "Simon")]))) }}
+
+ # FrontMatter Example
+
+ }}
+ ```
+
+ This will result in the following YAML and HTML when used with the
+ `docs.to-html` ucm command:
+
+ ``` html
+ ---
+ title: FrontMatter Example
+ author: Simon
+ ---
+ FrontMatter Example
+
FrontMatter Example
+
Video Example
+
+
+
Video Example
+
+
+