:> basic containers |% :: ++ first |* a=^ -.a :: ++ second |* a=^ +.a :: ++ either |*([a=mold b=mold] $%({%& p/a} {%| p/b})) :: either :: ++ thr |% ++ apply :> applies {b} {a} is first, or {b} to {a} is second. |* [a=(either) b=$-(* *) c=$-(* *)] ?- -.a %& (b p.a) %| (c p.a) == :: ++ firsts :> returns a list of all first elements in {a}. |* a=(list (either)) => .(a (homo a)) |- ?~ a ~ ?- -.i.a %& [p.i.a $(a t.a)] %| $(a t.a) == :: ++ seconds :> returns a list of all second elements in {a}. |* a=(list (either)) => .(a (homo a)) |- ?~ a ~ ?- -.i.a %& $(a t.a) %| [p.i.a $(a t.a)] == :: ++ partition :> splits the list of eithers into two lists based on first or second. |* a=(list (either)) => .(a (homo a)) |- ^- {(list _?>(?=({{%& *} *} a) p.i.a)) (list _?>(?=({{%| *} *} a) p.i.a))} ?~ a [~ ~] =+ ret=$(a t.a) ?- -.i.a %& [[p.i.a -.ret] +.ret] %| [-.ret [p.i.a +.ret]] == -- ++ maybe |*(a=mold $@(~ {~ u/a})) :: maybe ++ myb |% ++ is-null :> returns %.y if maybe is null. :> :> corresponds to {isJust} in haskell. |* a=(maybe) :> whether {a} is null. ?~ a %.y %.n :: ++ exists :> returns %.y if maybe contains a real value. :> :> corresponds to {isNothing} in haskell. |* a=(maybe) :> whether {a} is not null. ?~ a %.n %.y :: ++ need :> returns the value or crashes. :> :> corresponds to {fromJust} in haskell. |* a=(maybe) ?~ a ~>(%mean.[%leaf "need"] !!) :> the value from the maybe. u.a :: ++ default :> returns the value in the maybe, or a default value on null. :> :> corresponds to {fromMaybe} in haskell. |* [a=(maybe) b=*] ?~(a b u.a) :: ++ from-list :> returns the first value of the list, or null on empty list. :> :> corresponds to {listToMaybe} in haskell. |* a=(list) ^- (maybe _i.a) ?~ a ~ [~ i.a] :: ++ to-list :> converts the maybe to a list. :> :> corresponds to {maybeToList} in haskell. |* a=(maybe) ^- (list _u.a) ?~ a ~ [u.a ~] :: ++ concat :> converts a list of maybes to a list of non-null values. :> :> corresponds to {catMaybes} in haskell. |* a=(list (maybe)) => .(a (homo a)) |- ^- (list _u.+.i.-.a) ?~ a ~ ?~ i.a $(a t.a) [u.i.a $(a t.a)] :: ++ map :> a version of map that can throw out items. :> :> takes a list of items and a function of the type :> :> todo: while this was in Data.Maybe in haskell, this might better :> logically be put in our list class? murn is. :> :> corresponds to {mapMaybes} in haskell. |* [a=(list) b=$-(* (maybe))] => .(a (homo a)) |- ^- (list _,.+:*b) ?~ a ~ =+ c=(b i.a) ?~ c $(a t.a) :: todo: the span of c does not have the faces of a maybe. how do i either :: force a resurface or act safely on the incoming? [+.c $(a t.a)] :: ++ apply :> applies {b} to {a}. |* [a=(maybe) b=$-(* (maybe))] ?~ a ~ (b u.a) :: :: todo: bind, bond, both, flit, hunt, lift, mate, :: :: used in other files: bond, drop (but only once) :: unusued: clap -- ++ ls :: we are back to a basic problem here: when we try to pass lists without :: {i} and {t} faces, we have to use {-} and {+} to access the structure of :: the list. but we then can't deal with incoming lists that do have faces, :: as `+:[i="one" t=~]` is `t=~`, not `~`. :: :: what i really want is that the sapn outside a |* is `{"" 2 "" ~}`, but :: inside, it is `(list $?(@ud tape))`. all of a sudden, you don't need :: ++limo or ++homo, because you have the right span from the beginning! :: those two functions really feel like they're working around the type :: system instead of cooperating with it. :: :> list utilities |% :> # %basic :> basic list manipulation +| :: ++ head :> returns the first item in the list, which must be non-empty. |* a=(list) => .(a (homo a)) :> the first item in the list. ?~ a ~>(%mean.[%leaf "head"] !!) i.a :: ++ last :> returns the final item in the list, which must be non-empty. |* a=(list) :> the last item in a list. ?~ a ~>(%mean.[%leaf "last"] !!) ?~ t.a i.a $(a t.a) :: ++ tail :> returns all items after the head of the list, which must be non-empty. |* a=(list) ^+ a ?~ a ~>(%mean.[%leaf "tail"] !!) t.a :: ++ init :> returns all items in the list except the last one. must be non-empty. |* a=(list) => .(a (homo a)) |- ^+ a ?~ a ~>(%mean.[%leaf "init"] !!) |- ?~ t.a ~ [i.a $(a t.a)] :: :: :: :: ommitted: uncons, null :: :: ++ size :> returns the number of items in {a}. :> :> corresponds to {length} in haskell. |= a=(list) =| b=@u ^- @u |- ?~ a b $(a t.a, b +(b)) :: :> # %transformations :> functions which change a list into another list +| :: ++ map :> applies a gate to each item in the list. |* [a=(list) b=$-(* *)] ^- (list _*b) ?~ a ~ [(b i.a) $(a t.a)] :: ++ reverse :> reverses the order of the items in the list. |* a=(list) => .(a (homo a)) ^+ a =+ b=`_a`~ |- ?~ a b $(a t.a, b [i.a b]) :: ++ intersperse :> places {a} between each element in {b}. |* [a=* b=(list)] => .(b (homo b)) |- ^+ (homo [a b]) ?~ b ~ =+ c=$(b t.b) ?~ c [i.b ~] [i.b a c] :: ++ intercalate :> places {a} between each list in {b}, and flatten to a single list. |* [a=(list) b=(list (list))] => .(a ^.(homo a), b ^.(homo b)) |- ^+ (concat [a b]) ?~ b ~ =+ c=$(b t.b) ?~ c i.b :(weld i.b a c) :: ++ transpose :> transposes rows and columns of a 2d list structure. |* input=(list (list)) :: todo: this should homogenize with each sublist. ^- (list (list)) =/ items %^ foldl input `{(list) (list (list))}`[~ ~] |= :> current: the list of first items under construction. :> remaining: the remaining item lists. :> next: the next list in {input}. {state/{current/(list) remaining/(list (list))} next/(list)} ?~ next state ?~ t.next [[i.next current.state] remaining.state] [[i.next current.state] [t.next remaining.state]] ?~ +.items `(list (list))`[(reverse -.items) ~] [(reverse -.items) $(input (reverse +.items))] :: :: :: ++ subsequences :: :: |= a=(list) :: :: ?~ a :: :: ~ :: :: :- -.a :: :: %^ foldr :: :: $(a +.a) :: :: `(list)`~ :: :: |= [ys=(list) r=(list)] :: :: ~ ::[ys [-.a ys] r ~] :: :: TODO: :: :: ++subsequences :: :: ++permutations :: :> # %folds :> functions which reduce a list to a value +| :: ++ foldl :> left associative fold :> :> this follows haskell giving an explicit starting value instead of {roll}. |* [a=(list) b=* c=$-({* *} *)] ^+ b ?~ a b $(a t.a, b (c b i.a)) :: ++ foldr :> right associative fold |* [a=(list) b=* c=$-({* *} *)] ^+ b ?~ a b (c $(a t.a) i.a) :: ++ concat :> concatenate a list of lists into a single level. |* a=(list (list)) => .(a ^.(homo a)) |- ^+ (homo i:-.a) ?~ a ~ (weld (homo i.a) $(a t.a)) :: ++ weld :> combine two lists, possibly of different types. |* [a=(list) b=(list)] => .(a ^.(homo a), b ^.(homo b)) |- ^- (list $?(_i.-.a _i.-.b)) ?~ a b [i.a $(a t.a)] :: ++ any :> returns yes if any element satisfies the predicate |* [a=(list) b=$-(* ?)] ?~ a %.n ?|((b i.a) $(a t.a)) :: ++ all :> returns yes if all elements satisfy the predicate |* [a=(list) b=$-(* ?)] ?~ a %.y ?&((b i.a) $(a t.a)) :: :: haskell has a bunch of methods like sum or maximum which leverage type :: classes, but I don't think they can be written generically in hoon. :: :: :> # %building :> functions which build lists +| ++ scanl :> returns a list of successive reduced values from the left. |* [a=(list) b=* c=$-({* *} *)] => .(a (homo a)) |- ?~ a [b ~] [b $(a t.a, b (c b i.a))] :: ++ scanl1 :> a variant of ++scanl that has no starting value. |* [a=(list) c=$-({* *} *)] => .(a (homo a)) |- ?~ a ~ ?~ t.a ~ (scanl t.a i.a c) :: ++ scanr :> the right-to-left version of scanl. |* [a=(list) b=* c=$-({* *} *)] => .(a (homo a)) |- ^- (list _b) ?~ a [b ~] =+ rest=$(a t.a) ?> ?=(^ rest) [(c i.a i.rest) rest] :: ++ scanr1 :> a variant of ++scanr that has no starting value. |* [a=(list) c=$-({* *} *)] => .(a (homo a)) |- ^+ a ?~ a ~ ?~ t.a [i.a ~] =+ rest=$(a t.a) ?> ?=(^ rest) [(c i.a i.rest) rest] :: ++ map-foldl :> performs both a ++map and a ++foldl in one pass. :> :> corresponds to {mapAccumL} in haskell. |* [a=(list) b=* c=$-({* *} {* *})] ^- {_b (list _+:*c)} ?~ a [b ~] =+ d=(c b i.a) =+ recurse=$(a t.a, b -.d) [-.recurse [+.d +.recurse]] :: ++ map-foldr :> performs both a ++map and a ++foldr in one pass. :> :> corresponds to {mapAccumR} in haskell. |* [a=(list) b=* c=$-({* *} {* *})] ^- {_b (list _+:*c)} ?~ a [b ~] =+ recurse=$(a t.a) =+ d=(c -.recurse i.a) [-.d [+.d +.recurse]] :: ++ unfoldr :> generates a list from a seed value and a function. |* [b=* c=$-(* (maybe {* *}))] |- ^- (list _b) =+ current=(c b) ?~ current ~ :: todo: the span of {c} is resurfaced to have a u. this might do funky :: things with faces. [-.+.current $(b +.+.current)] :: :> # %sublists :> functions which return a portion of the list +| :: ++ take :> returns the first {a} elements of {b}. |* [a=@ b=(list)] => .(b (homo b)) |- ^+ b ?: =(0 a) ~ ?~ b ~ [i.b $(a (dec a), b +.b)] :: ++ drop :> returns {b} without the first {a} elements. |* [a=@ b=(list)] ?: =(0 a) b ?~ b b $(a (dec a), b +.b) :: ++ split-at :> returns {b} split into two lists at the {a}th element. |* [a=@ b=(list)] => .(b (homo b)) |- ^+ [b b] ?: =(0 a) [~ b] ?~ b [~ b] =+ d=$(a (dec a), b t.b) [[i.b -.d] +.d] :: ++ take-while :> returns elements from {a} until {b} returns %.no. |* [a=(list) b=$-(* ?)] => .(a (homo a)) |- ^+ a ?~ a ~ ?. (b -.a) ~ [i.a $(a t.a)] :: ++ drop-while :> returns elements form {a} once {b} returns %.no. |* [a=(list) b=$-(* ?)] => .(a (homo a)) |- ?~ a ~ ?. (b i.a) a $(a t.a) :: ++ drop-while-end :> drops the largest suffix of {a} which matches {b}. |* [a=(list) b=$-(* ?)] => .(a (homo a)) |- ?~ a ~ =+ r=$(a t.a) ?: ?&(=(r ~) (b i.a)) ~ [i.a r] :: ++ split-on :> returns [the longest prefix of {b}, the rest of the list]. :> :> corresponds to {span} in haskell. renamed to not conflict with hoon. |* [a=(list) b=$-(* ?)] => .(a (homo a)) |- ^+ [a a] ?~ a [~ ~] ?. (b i.a) [~ a] =+ d=$(a +.a) [[i.a -.d] +.d] :: ++ break :> like {split-on}, but reverses the return code of {b}. |* [a=(list) b=$-(* ?)] => .(a (homo a)) |- ^+ [a a] ?~ a [~ ~] ?: (b i.a) [~ a] =+ d=$(a t.a) [[i.a -.d] +.d] :: ++ strip-prefix :> returns a {maybe} of {b} with the prefix {a} removed, or ~ if no match. |* [a=(list) b=(list)] ^- (maybe _b) ?~ a `b ?~ b ~ $(a +.a, b +.b) :: :: todo: ++group :: ++ inits :> returns all initial segments in reverse order. :> :> unlike haskell, this does not return the empty list as the first :> element, as hoon uses null as the list terminator. |* a=(list) => .(a (homo a)) %- flop |- ?~ a ~ [a $(a (init a))] :: ++ tails :> returns all final segments, longest first. |* a=(list) => .(a (homo a)) |- ?~ a ~ [a $(a t.a)] :: :> # %predicates :> functions which compare lists +| :: ++ is-prefix-of :> returns %.y if the first list is a prefix of the second. |* [a=(list) b=(list)] => .(a (homo a), b (homo b)) |- ^- ? ?~ a %.y ?~ b %.n ?. =(i.a i.b) %.n $(a t.a, b t.b) :: ++ is-suffix-of :> returns %.y if the first list is the suffix of the second. |* [a=(list) b=(list)] => .(a (homo a), b (homo b)) ^- ? :: todo: this is performant in haskell because of laziness but may not be :: adequate in hoon. (is-prefix-of (reverse a) (reverse b)) :: ++ is-infix-of :> returns %.y if the first list appears anywhere in the second. |* [a=(list) b=(list)] => .(a (homo a), b (homo b)) |- ^- ? ?~ a %.y ?~ b %.n ?: (is-prefix-of a b) %.y $(b t.b) :: :: todo: ++is-subsequence-of :: :> # %searching :> finding items in lists :: ++ elem :> does {a} occur in list {b}? |* [a=* b=(list)] ?~ b %.n ?: =(a i.b) %.y $(b t.b) :: ++ lookup :> looks up the key {a} in the association list {b} |* [a=* b=(list (pair))] ^- (maybe _+.-.b) ?~ b ~ ?: =(a p.i.b) [~ q.i.b] $(b t.b) :: ++ find :> returns the first element of {a} which matches predicate {b}. |* [a=(list) b=$-(* ?)] ^- (maybe _-.a) ?~ a ~ ?: (b i.a) [~ i.a] $(a t.a) :: ++ filter :> filter all items in {a} which match predicate {b}. |* [a=(list) b=$-(* ?)] => .(a (homo a)) |- ^+ a ?~ a ~ ?. (b i.a) [i.a $(a t.a)] $(a t.a) :: ++ partition :> returns two lists, one whose elements match {b}, the other which doesn't. |* [a=(list) b=$-(* ?)] => .(a (homo a)) |- ^+ [a a] ?~ a [~ ~] =+ rest=$(a t.a) ?: (b i.a) [[i.a -.rest] +.rest] [-.rest [i.a +.rest]] :: :> # %indexing :> finding indices in lists +| :: ++ elem-index :> returns {maybe} the first occurrence of {a} occur in list {b}. =| i=@u |= [a=* b=(list)] ^- (maybe @ud) ?~ b ~ ?: =(a i.b) `i $(b t.b, i +(i)) :: ++ elem-indices :> returns a list of indices of all occurrences of {a} in {b}. =| i/@u |= [a=* b=(list)] ^- (list @ud) ?~ b ~ ?: =(a i.b) [i $(b t.b, i +(i))] $(b t.b, i +(i)) :: ++ find-index :> returns {maybe} the first occurrence which matches {b} in {a}. =| i=@u |* [a=(list) b=$-(* ?)] ^- (maybe @ud) ?~ a ~ ?: (b i.a) `i $(a t.a, i +(i)) :: ++ find-indices :> returns a list of indices of all items in {a} which match {b}. =| i=@u |* [a=(list) b=$-(* ?)] ^- (list @ud) ?~ a ~ ?: (b i.a) [i $(a t.a, i +(i))] $(a t.a, i +(i)) :: ++ zip :> takes a list of lists, returning a list of each first items. |* a=(list (list)) => .(a (multi-homo a)) |^ ^+ a ?~ a ~ ?. valid ~ =+ h=heads ?~ h ~ [heads $(a tails)] :: ++ valid %+ all a |= next=(list) ?~ a %.n %.y :: ++ heads ^+ (homo i:-.a) |- ?~ a ~ ?~ i.a ~ [i.i.a $(a t.a)] :: ++ tails ^+ a |- ?~ a ~ ?~ i.a ~ [t.i.a $(a t.a)] -- ++ multi-homo |* a=(list (list)) ^+ =< $ |% +- $ ?:(*? ~ [i=(homo (snag 0 a)) t=$]) -- a :: :> # %set :> set operations on lists +| ++ unique :> removes duplicates elements from {a} :> :> corresponds to {nub} in haskell. |* a=(list) => .(a (homo a)) =| seen/(list) ^+ a |- ?~ a ~ ?: (elem i.a seen) $(a t.a) [i.a $(seen [i.a seen], a t.a)] :: ++ delete :> removes the first occurrence of {a} in {b} |* [a=* b=(list)] => .(b (homo b)) ^+ b |- ?~ b ~ ?: =(a i.b) t.b [i.b $(b t.b)] :: ++ delete-firsts :> deletes the first occurrence of each element in {b} from {a}. |* [a=(list) b=(list)] => .(a (homo a), b (homo b)) |- ^+ a ?~ a ~ ?~ b a ?: (elem i.a b) $(a t.a, b (delete i.a b)) [i.a $(a t.a)] :: ++ union :> the list union of {a} and {b}. |* [a=(list) b=(list)] => .(a (homo a), b (homo b)) |- ^+ (weld a b) ?~ a b ?~ b ~ [i.a $(a t.a, b (delete i.a b))] :: ++ intersect :> the intersection of {a} and {b}. |* [a=(list) b=(list)] => .(a (homo a), b (homo b)) |- ^+ a ?~ a ~ ?: (elem i.a b) [i.a $(a t.a)] $(a t.a) :: :: todo: everything about ++sort and ++sort-on needs more thought. the :: haskell implementation uses the Ord typeclass to sort things by :: default. ++sort as is is probably the correct thing to do. :: -- :: ++ dict :> a dictionary mapping keys of {a} to values of {b}. :> :> a dictionary is treap ordered; it builds a treap out of the hashed key :> values. |* [a=mold b=mold] %+ cork (tree (pair a b)) |= c/(tree (pair a b)) ^+ c ?.((valid:dct c) ~ c) :: ++ dct |% :> # %query :> looks up values in the dict. +| ++ empty :> is the dict empty? |* a=(dict) ?~ a %.y %.n :: ++ size :> returns the number of elements in {a}. |= a=(dict) ^- @u ?~ a 0 :(add 1 $(a l.a) $(a r.a)) :: ++ member :> returns %.y if {b} is a key in {a}. |= [a=(dict) key=*] ^- ? ?~ a %.n ?|(=(key p.n.a) $(a l.a) $(a r.a)) :: ++ get :> grab value by key. |* [a=(dict) key=*] ^- (maybe _?>(?=(^ a) q.n.a)) :: ^- {$@(~ {~ u/_?>(?=(^ a) q.n.a)})} ?~ a ~ ?: =(key p.n.a) `q.n.a ?: (gor key p.n.a) $(a l.a) $(a r.a) :: :: :: todo: is ++got the correct interface to have? Haskell has lookup which :: :: returns a Maybe and a findWithDefault which passes in a default value. :: ++ got :: :> todo: move impl here. :: :> todo: is there a way to make b/_<><>.a ? :: |* [a=(dict) key=*] :: (~(got by a) key) :: :: todo: skipping several methods which rely on the the Ord typeclass, like :: lookupLT. :: :> # %insertion +| ++ put :> inserts a new key/value pair, replacing the current value if it exists. :> :> corresponds to {insert} in haskell. |* [a=(dict) key=* value=*] |- ^+ a ?~ a [[key value] ~ ~] ?: =(key p.n.a) ?: =(value q.n.a) a [[key value] l.a r.a] ?: (gor key p.n.a) =+ d=$(a l.a) ?> ?=(^ d) ?: (vor p.n.a p.n.d) [n.a d r.a] [n.d l.d [n.a r.d r.a]] =+ d=$(a r.a) ?> ?=(^ d) ?: (vor p.n.a p.n.d) [n.a l.a d] [n.d [n.a l.a l.d] r.d] :: ++ put-with :> inserts {key}/{value}, applying {fun} if {key} already exists. :> :> corresponds to {insertWith} in haskell. |* [a=(dict) key=* value=* fun=$-({* *} *)] |- ^+ a ?~ a [[key value] ~ ~] ?: =(key p.n.a) :: key already exists; use {fun} to resolve. [[key (fun q.n.a value)] l.a r.a] ?: (gor key p.n.a) =+ d=$(a l.a) ?> ?=(^ d) ?: (vor p.n.a p.n.d) [n.a d r.a] [n.d l.d [n.a r.d r.a]] =+ d=$(a r.a) ?> ?=(^ d) ?: (vor p.n.a p.n.d) [n.a l.a d] [n.d [n.a l.a l.d] r.d] :: ++ put-with-key :> inserts {key}/{value}, applying {fun} if {key} already exists. :> :> corresponds to {insertWithKey} in haskell. |* [a=(dict) key=* value=* fun=$-({* * *} *)] |- ^+ a ?~ a [[key value] ~ ~] ?: =(key p.n.a) :: key already exists; use {fun} to resolve. [[key (fun p.n.a q.n.a value)] l.a r.a] ?: (gor key p.n.a) =+ d=$(a l.a) ?> ?=(^ d) ?: (vor p.n.a p.n.d) [n.a d r.a] [n.d l.d [n.a r.d r.a]] =+ d=$(a r.a) ?> ?=(^ d) ?: (vor p.n.a p.n.d) [n.a l.a d] [n.d [n.a l.a l.d] r.d] :: ++ put-lookup-with-key :> combines insertion with lookup in one pass. :> :> corresponds to {insertLookupWithKey} in haskell. |* [a=(dict) key=* value=* fun=$-({* * *} *)] |- ^- {(maybe _value) _a} ?~ a [~ [[key value] ~ ~]] ?: =(key p.n.a) :: key already exists; use {fun} to resolve. [`q.n.a [[key (fun p.n.a q.n.a value)] l.a r.a]] ?: (gor key p.n.a) =+ rec=$(a l.a) =+ d=+.rec ?> ?=(^ d) ?: (vor p.n.a p.n.d) [-.rec [n.a d r.a]] [-.rec [n.d l.d [n.a r.d r.a]]] =+ rec=$(a r.a) =+ d=+.rec ?> ?=(^ d) ?: (vor p.n.a p.n.d) [-.rec [n.a l.a d]] [-.rec [n.d [n.a l.a l.d] r.d]] :: :> # %delete-update +| :: ++ delete :> deletes entry at {key}. |* [a=(dict) key=*] |- ^+ a ?~ a ~ ?. =(key p.n.a) ?: (gor key p.n.a) [n.a $(a l.a) r.a] [n.a l.a $(a r.a)] (pop-top a) :: ++ adjust :> updates a value at {key} by passing the value to {fun}. |* [a=(dict) key=* fun=$-(* *)] %^ alter-with-key a key |= [key=_p.-.n.-.a value=(maybe _q.+.n.-.a)] ^- (maybe _q.+.n.-.a) ?~ value ~ [~ (fun u.value)] :: ++ adjust-with-key :> updates a value at {key} by passing the key/value pair to {fun}. |* [a=(dict) key=* fun=$-({* *} *)] %^ alter-with-key a key |= [key=_p.-.n.-.a value=(maybe _q.+.n.-.a)] ^- (maybe _q.+.n.-.a) ?~ value ~ [~ (fun key u.value)] :: ++ update :> adjusts or deletes the value at {key} by {fun}. |* [a=(dict) key=* fun=$-(* (maybe *))] %^ alter-with-key a key |= [key=_p.-.n.-.a value=(maybe _q.+.n.-.a)] ^- (maybe _q.+.n.-.a) ?~ value ~ (fun u.value) :: ++ update-with-key :> adjusts or deletes the value at {key} by {fun}. |* [a=(dict) key=* fun=$-({* *} (maybe *))] %^ alter-with-key a key |= [key=_p.-.n.-.a value=(maybe _q.+.n.-.a)] ^- (maybe _q.+.n.-.a) ?~ value ~ (fun key u.value) :: :: todo: :: ++update-lookup-with-key :: ++ alter :> inserts, deletes, or updates a value by {fun}. |* [a=(dict) key=* fun=$-((maybe *) (maybe *))] %^ alter-with-key a key |= [key=_p.-.n.-.a value=(maybe _q.+.n.-.a)] (fun value) :: ++ alter-with-key :> inserts, deletes, or updates a value by {fun}. |* [a=(dict) key=* fun=$-({* (maybe *)} (maybe *))] |- ^+ a ?~ a =+ ret=(fun key ~) ?~ ret ~ [[key u.ret] ~ ~] ?: =(key p.n.a) =+ ret=(fun key `q.n.a) ?~ ret (pop-top a) ?: =(u.ret q.n.a) a [[key u.ret] l.a r.a] ?: (gor key p.n.a) =+ d=$(a l.a) ?~ d [n.a ~ r.a] ?: (vor p.n.a p.n.d) [n.a d r.a] [n.d l.d [n.a r.d r.a]] =+ d=$(a r.a) ?~ d [n.a l.a ~] ?: (vor p.n.a p.n.d) [n.a l.a d] [n.d [n.a l.a l.d] r.d] :: :> # %combine +| :: ++ union :> returns the union of {a} and {b}, preferring the value from {a} if dupe |* [a=(dict) b=(dict)] |- ^+ a ?~ b a ?~ a b ?: (vor p.n.a p.n.b) ?: =(p.n.b p.n.a) [n.a $(a l.a, b l.b) $(a r.a, b r.b)] ?: (gor p.n.b p.n.a) $(a [n.a $(a l.a, b [n.b l.b ~]) r.a], b r.b) $(a [n.a l.a $(a r.a, b [n.b ~ r.b])], b l.b) ?: =(p.n.a p.n.b) [n.b $(b l.b, a l.a) $(b r.b, a r.a)] ?: (gor p.n.a p.n.b) $(b [n.b $(b l.b, a [n.a l.a ~]) r.b], a r.a) $(b [n.b l.b $(b r.b, a [n.a ~ r.a])], a l.a) :: ++ union-with :> returns the union of {a} and {b}, running {fun} to resolve duplicates. |* [a=(dict) b=(dict) fun=$-({* *} *)] |- ^+ a ?~ b a ?~ a b ?: (vor p.n.a p.n.b) ?: =(p.n.b p.n.a) [[p.n.a (fun q.n.a q.n.b)] $(a l.a, b l.b) $(a r.a, b r.b)] ?: (gor p.n.b p.n.a) $(a [n.a $(a l.a, b [n.b l.b ~]) r.a], b r.b) $(a [n.a l.a $(a r.a, b [n.b ~ r.b])], b l.b) ?: =(p.n.a p.n.b) [n.b $(b l.b, a l.a) $(b r.b, a r.a)] ?: (gor p.n.a p.n.b) $(b [n.b $(b l.b, a [n.a l.a ~]) r.b], a r.a) $(b [n.b l.b $(b r.b, a [n.a ~ r.a])], a l.a) :: ++ union-with-key :> returns the union of {a} and {b}, running {fun} to resolve duplicates. |* [a=(dict) b=(dict) fun=$-({* * *} *)] |- ^+ a ?~ b a ?~ a b ?: (vor p.n.a p.n.b) ?: =(p.n.b p.n.a) [[p.n.a (fun p.n.a q.n.a q.n.b)] $(a l.a, b l.b) $(a r.a, b r.b)] ?: (gor p.n.b p.n.a) $(a [n.a $(a l.a, b [n.b l.b ~]) r.a], b r.b) $(a [n.a l.a $(a r.a, b [n.b ~ r.b])], b l.b) ?: =(p.n.a p.n.b) [n.b $(b l.b, a l.a) $(b r.b, a r.a)] ?: (gor p.n.a p.n.b) $(b [n.b $(b l.b, a [n.a l.a ~]) r.b], a r.a) $(b [n.b l.b $(b r.b, a [n.a ~ r.a])], a l.a) :: :: TODO: this is untested; move it. :: :: :: ++ difference :: :: todo: move real implementation here. :: :> returns elements in {a} that don't exist in {b}. :: |* [a=(dict) b=(dict)] :: (~(dif by a) b) :: :: :: :: todo: :: :: ++difference-with :: :: ++difference-with-key :: :: :: ++ intersection :: :: todo: move real implementation here. :: :> returns elements in {a} that exist in {b}. :: |* [a=(dict) b=(dict)] :: (~(int by a) b) :: :: :: :: todo: :: :: ++intersection-with :: :: ++intersection-with-key :: :> # %traversal +| :: ++ map :> applies {fun} to each value in {a}. |* [a=(dict) fun=$-(* *)] ^- (dict _p.-.n.-.a fun) ?~ a ~ [[p.n.a (fun q.n.a)] $(a l.a) $(a r.a)] :: ++ map-with-key :> applies {fun} to each value in {a}. |* [a=(dict) fun=$-({* *} *)] ^- (dict _p.-.n.-.a _*fun) ?~ a ~ [[p.n.a (fun p.n.a q.n.a)] $(a l.a) $(a r.a)] :: ++ map-fold :> performs a fold on all the values in {a}. :> :> lists have an order, but dicts are treaps. this means there isn't a :> horizontal ordering, and thus the distinction between left and right :> folding isn't relevant. your accumulator function will be called in :> treap order. :> :> corresponds to {mapAccum} in haskell. |* [a=(dict) b=* fun=$-({* *} {* *})] ^- {_b (dict _p.-.n.-.a _+:*fun)} ?~ a [b ~] =+ d=(fun b q.n.a) =. q.n.a +.d =+ e=$(a l.a, b -.d) =+ f=$(a r.a, b -.e) [-.f [n.a +.e +.f]] :: ++ map-keys :> applies {fun} to all keys. :: todo: the haskell version specifies that the "greatest" original key :: wins in case of duplicates. this is currently unhandled. maybe i just :: shouldn't have this gate. |* [a=(dict) fun=$-(* *)] %- from-list %+ map:ls (to-list a) |= item/_n.-.a [(fun p.item) q.item] :: ++ map-keys-with :> applies {fun} to all keys, creating a new value with {combine} on dupes. |* [a=(dict) fun=$-(* *) combine=$-({* *} *)] ^- (dict _*fun _q.+.n.-.a) =/ new-list %+ map:ls (to-list a) |= item/_n.-.a [(fun p.item) q.item] %^ foldl:ls new-list `(dict _*fun _q.+.n.-.a)`~ |= [m=(dict _*fun _q.+.n.-.a) p=_i.-.new-list] (put-with m -.p +.p combine) :: ++ fold :> performs a fold on all the values in {a}. :> :> lists have an order, but dicts are treaps. this means there isn't a :> horizontal ordering, and thus the distinction between left and right :> folding isn't relevant. your accumulator function will be called in :> treap order. |* [a=(dict) b=* fun=$-({* *} *)] ^- _b ?~ a b =+ d=(fun b q.n.a) =+ e=$(a l.a, b d) $(a r.a, b e) :: ++ fold-with-keys :> performs a fold on all the values in {a}, passing keys too. |* [a=(dict) b=* fun=$-({* * *} *)] ^+ b ?~ a b =+ d=(fun b p.n.a q.n.a) =+ e=$(a l.a, b d) $(a r.a, b e) :: ++ any :> returns yes if any element satisfies the predicate |* [a=(dict) b=$-(* ?)] ^- ? ?~ a %.n ?|((b q.n.a) $(a l.a) $(a r.a)) :: ++ any-with-key :> returns yes if any element satisfies the predicate |* [a=(dict) b=$-({* *} ?)] ^- ? ?~ a %.n ?|((b p.n.a q.n.a) $(a l.a) $(a r.a)) :: ++ all :> returns yes if all elements satisfy the predicate |* [a=(dict) b=$-(* ?)] ^- ? ?~ a %.y ?&((b q.n.a) $(a l.a) $(a r.a)) :: ++ all-with-key :> returns yes if all elements satisfy the predicate |* [a=(dict) b=$-({* *} ?)] ^- ? ?~ a %.y ?&((b p.n.a q.n.a) $(a l.a) $(a r.a)) :: :> # %conversion +| ++ elems :> return all values in the dict. |* a=(dict) %+ turn (to-list a) second :: ++ keys :> returns all keys in the dict. |* a=(dict) %+ turn (to-list a) first :: :: todo: ++assocs probably doesn't make sense when we have ++to-list and :: when there's no general noun ordering. :: ++ keys-set :> returns all keys as a set. |* a=(dict) (si:nl (keys a)) :: ++ from-set :> computes a dict by running {fun} on every value in a set. |* [a=(set) fun=$-(* *)] ^- (dict _n.-.a _*fun) ?~ a ~ [[n.a (fun n.a)] $(a l.a) $(a r.a)] :: :> # %lists +| :: ++ to-list :> creates a list of pairs from the tree. |* a=(dict) =| b=(list _n.-.a) |- ^+ b ?~ a b $(a r.a, b [n.a $(a l.a)]) :: ++ from-list :> creates a tree from a list. |* a=(list (pair)) |- %^ foldl:ls a `(dict _p.-.i.-.a _q.+.i.-.a)`~ |= [m=(dict _p.-.i.-.a _q.+.i.-.a) p=_i.-.a] (put m p) :: ++ from-list-with :> creates a dict from a list, with {fun} resolving duplicates. |* [a=(list (pair)) fun=$-(* *)] %^ foldl:ls a `(dict _*fun _q.+.i.-.a)`~ |= [m=(dict _*fun _q.+.i.-.a) p=_i.-.a] (put-with m -.p +.p fun) :: :: todo: without a natural ordering, association lists and gates to operate :: on them probably don't make sense. i'm skipping them for now. :: :> # %filters +| ++ filter :> filters a dict of all values that satisfy {fun}. |* [a=(dict) fun=$-(* ?)] %+ filter-with-key a |= [key=* value=_q.+.n.-.a] (fun value) :: ++ filter-with-key :> filters a dict of all values that satisfy {fun}. |* [a=(dict) fun=$-({* *} ?)] |- ^+ a ?~ a ~ ?: (fun n.a) =. l.a $(a l.a) =. r.a $(a r.a) (pop-top a) [n.a $(a l.a) $(a r.a)] :: ++ restrict-keys :> returns a dict where the only allowable keys are {keys}. |* [a=(dict) keys=(set)] %+ filter-with-key a |= [key=_p.-.n.-.a value=*] :: todo: replace this with a call to our set library when we advance that :: far. !(~(has in keys) key) :: ++ without-keys :> returns a dict where the only allowable keys are not in {keys}. |* [a=(dict) keys=(set)] %+ filter-with-key a |= [key=_p.-.n.-.a value=*] :: todo: replace this with a call to our set library when we advance that :: far. (~(has in keys) key) :: ++ partition :> returns two lists, one whose elements match {fun}, the other doesn't. |* [a=(dict) fun=$-(* ?)] :: todo: is the runtime on this is bogus? =/ data %+ partition:ls (to-list a) |= p/_n.-.a (fun q.p) [(from-list -.data) (from-list +.data)] :: :: todo: ++partition-with-key once ++partition works. :: :: i'm going to ignore all the Antitone functions; they don't seem to be :: useful without ordering on the dict. :: ++ map-maybe :> a version of map that can throw out items. |* [a=(dict) fun=$-(* (maybe))] %+ map-maybe-with-key a |= [key=* value=_q.+.n.-.a] (fun value) :: ++ map-maybe-with-key :> a version of map that can throw out items. |* [a=(dict) fun=$-({* *} (maybe))] ^- (dict _p.-.n.-.a _+:*fun) ?~ a ~ =+ res=(fun n.a) ?~ res =. l.a $(a l.a) =. r.a $(a r.a) (pop-top a) [[p.n.a +.res] $(a l.a) $(a r.a)] :: ++ map-either :> splits the dict in two on a gate that returns an either. |* [a=(dict) fun=$-(* (either))] %+ map-either-with-key a |= [key=* value=_q.+.n.-.a] (fun value) :: ++ map-either-with-key :> splits the dict in two on a gate that returns an either. |* [a=(dict) fun=$-({* *} (either))] |- ^- $: (dict _p.-.n.-.a _?>(?=({{%& *} *} *fun) +:*fun)) (dict _p.-.n.-.a _?>(?=({{%| *} *} *fun) +:*fun)) == ?~ a [~ ~] :: todo: runtime wise, can I do better than recursive unions? =+ lr=$(a l.a) =+ rr=$(a r.a) =+ x=(fun n.a) ~! x ?- -.x %& [(put (union -.lr -.rr) p.n.a +.x) (union +.lr +.rr)] %| [(union -.lr -.rr) (put (union +.lr +.rr) p.n.a +.x)] == :: :: ++split, ++split-lookup and ++split-root do not make sense without :: ordinal keys. :: ++ is-subdict :> returns %.y if every element in {a} exists in {b} with the same value. |* [a=(dict) b=(dict)] ^- ? (is-subdict-by a b |=([a=* b=*] =(a b))) :: ++ is-subdict-by :> returns %.y if every element in {a} exists in {b} with the same value. |* [a=(dict) b=(dict) fun=$-({* *} ?)] |- ^- ? ?~ a %.y ?~ b %.n ~! b ~! p.n.a =+ x=(get b p.n.a) ?~ x %.n |((fun q.n.a u.x) $(a l.a) $(a r.a)) :: :> # %impl :> implementation details +| ++ pop-top :> removes the head of the tree and rebalances the tree below. |* a=(dict) ^- {$?(~ _a)} ?~ a ~ |- ?~ l.a r.a ?~ r.a l.a ?: (vor p.n.l.a p.n.r.a) [n.l.a l.l.a $(l.a r.l.a)] [n.r.a $(r.a l.r.a) r.r.a] :: ++ valid :> returns %.y if {a} if this tree is a valid treap dict. |* a=(tree (pair * *)) =| [l=(maybe) r=(maybe)] |- ^- ? ?~ a & ?& ?~(l & (gor p.n.a u.l)) ?~(r & (gor u.r p.n.a)) ?~(l.a & ?&((vor p.n.a p.n.l.a) $(a l.a, l `p.n.a))) ?~(r.a & ?&((vor p.n.a p.n.r.a) $(a r.a, r `p.n.a))) == -- ++ random :> produces a core which produces random numbers. :> :> random numbers are generated through repeated sha-256 operations. :> :> this design forces implementation details to be hidden, forces users to :> go through =^. this should be less error prone for pulling out multiple :> random numbers, at the cost of making getting a single random number :> slightly more cumbersome. :> :> =+ gen=(random eny) :> =^ first gen (range:gen 0 10) :> =^ second gen (range:gen 0 10) |= a=@ => |% ++ raw :: random bits |= b=@ ^- @ %+ can 0 =+ c=(shas %og-a (mix b a)) |- ^- (list {@ @}) ?: =(0 b) ~ =+ d=(shas %og-b (mix b (mix a c))) ?: (lth b 256) [[b (end 0 b d)] ~] [[256 d] $(c d, b (sub b 256))] :: ++ rad :: random in range |= b=@ ^- @ =+ c=(raw (met 0 b)) ?:((lth c b) c $(a +(a))) -- ^? |% ++ range :> returns a random number in the range [start, end], and generator. |= [start=@ end=@] ?: (gte start end) ~_(leaf+"invalid range" !!) =+ offset=(sub end start) =+ r=(rad offset) [(add start r) +>.$(a (shas %og-s (mix a r)))] :: ++ bits :> returns {b} bits in the range, and generator. |= b=@ =+ r=(raw b) [r +>.$(a (shas %og-s (mix a r)))] -- --