diff --git a/dynamics.md b/dynamics.md index e1cc889f..6930c2df 100644 --- a/dynamics.md +++ b/dynamics.md @@ -17,7 +17,7 @@ Affordances: - [x] DSL for writing down functions, composing, etc. - - [ ] A function `tcompose(t::Trie{AlgebraicFunction})::AlgebraicFunction`, implementing the Trie-algebra structure on morphisms + - [ ] A function `tcompose(t::Dtry{AlgebraicFunction})::AlgebraicFunction`, implementing the Dtry-algebra structure on morphisms - [ ] Interpret/compile a symbolic function into a real function - [ ] Serialize symbolic functions - [ ] Compilation @@ -35,12 +35,12 @@ ``` Affordances: - - A function `tcompose(arena::Trie{Arena})::Arena`, implementing the Trie-algebra structure on objects + - A function `tcompose(arena::Dtry{Arena})::Arena`, implementing the Dtry-algebra structure on objects - [ ] Multilenses Sketch: ```julia struct MultiLens - inner_boxes::Trie{Arena} + inner_boxes::Dtry{Arena} outer_box::Arena # used for namespacing `params` in composition, must not overlap with `inner_boxes` name::Symbol @@ -53,7 +53,7 @@ ``` Affordances: - - A function `ocompose(l::MultiLens, args::Trie{MultiLens})::MultiLens` implementing the Trie-multicategory structure + - A function `ocompose(l::MultiLens, args::Dtry{MultiLens})::MultiLens` implementing the Dtry-multicategory structure - [ ] Systems Sketch: ```julia @@ -69,7 +69,7 @@ ``` Affordances: - - A function `oapply(l::MultiLens, args::Trie{System})::System` implementing the action of the Trie-multicategory of multilenses on systems. + - A function `oapply(l::MultiLens, args::Dtry{System})::System` implementing the action of the Dtry-multicategory of multilenses on systems. ## Resource sharers @@ -82,20 +82,20 @@ end struct Rhizome - boxes::Trie{Interface} - junctions::Trie{VariableType} - mapping::Dict{TrieVar, TrieVar} + boxes::Dtry{Interface} + junctions::Dtry{VariableType} + mapping::Dict{DtryVar, DtryVar} end ``` Affordances: - - `ocompose(r::Rhizome, rs::Trie{Rhizome})::Rhizome` + - `ocompose(r::Rhizome, rs::Dtry{Rhizome})::Rhizome` In `ocompose`, the names of the junctions in the top-level rhizome dominate. - [ ] Systems ```julia struct ResourceSharer - variables::Trie{VariableType} + variables::Dtry{VariableType} params::AlgType output::AlgType # (params, state) -> state @@ -106,6 +106,6 @@ ``` Affordances: - - `oapply(r::Rhizome, sharers::Trie{ResourceSharer})::ResourceSharer` + - `oapply(r::Rhizome, sharers::Dtry{ResourceSharer})::ResourceSharer` In `oapply`, variables get renamed to the junctions that they are attached to. diff --git a/src/nonstdlib/dynamics/ResourceSharers.jl b/src/nonstdlib/dynamics/ResourceSharers.jl index 429adb67..9a3e82f2 100644 --- a/src/nonstdlib/dynamics/ResourceSharers.jl +++ b/src/nonstdlib/dynamics/ResourceSharers.jl @@ -2,8 +2,8 @@ module ResourceSharers export Rhizome, @rhizome, ResourceSharer, @resource_sharer, Variable using ...Syntax -using ...Util.Tries -using ...Util.Tries: flatten, node +using ...Util.Dtrys +using ...Util.Dtrys: flatten, node using ...Syntax.GATs: tcompose using ...Util using MLStyle @@ -33,10 +33,10 @@ Fields: """ struct PortVariable type::AlgType - junction::TrieVar + junction::DtryVar end -const Interface = Trie{AlgType} +const Interface = Dtry{AlgType} """ A rhizome is a variant of a UWD where the underlying cospan is epi-monic. @@ -49,12 +49,12 @@ This also differs from the ACSet-based UWDs in the following ways 1. We use tries instead of numbering things sequentially. 2. All of the "functions out" of the tries are handled by just storing data in the leaves of the tries, rather than having external "column vectors". -So e.g. we would use `Trie{Int}` rather than a pair of `Trie{Nothing}`, -`Dict{TrieVar, Int}`. +So e.g. we would use `Dtry{Int}` rather than a pair of `Dtry{Nothing}`, +`Dict{DtryVar, Int}`. 3. We use an indexed rather than fibered representation for inner ports on boxes. That is, we have a function Boxes -> Set rather than a function InnerPorts -> Boxes. Following 2., this is just stored in the leaf nodes -of the trie of boxes, hence `Trie{Trie{PortVariable}}`. +of the trie of boxes, hence `Dtry{Dtry{PortVariable}}`. 4. We don't have separate sets for outer ports and junctions: rather each junction is marked as "exposed" or not. This simplifies naming. Also see [`Variable`](@ref). @@ -66,11 +66,11 @@ of enforcing this would be to do something like ``` struct Box - ports::Trie{PortVariable} + ports::Dtry{PortVariable} end struct Rhizome - stuff::Trie{Union{Variable, Box}} + stuff::Dtry{Union{Variable, Box}} end ``` @@ -81,36 +81,36 @@ struct Rhizome theory::GAT mcompose::AlgClosure mzero::AlgClosure - boxes::Trie{Trie{PortVariable}} - junctions::Trie{Variable} + boxes::Dtry{Dtry{PortVariable}} + junctions::Dtry{Variable} end """ - ocompose(r::Rhizome, rs::Trie{Rhizome}) + ocompose(r::Rhizome, rs::Dtry{Rhizome}) -This implements the composition operation for the Trie-multicategory of +This implements the composition operation for the Dtry-multicategory of rhizomes. This is the better way of doing the [operad of undirected wiring][1]. -TODO: add link to arXiv paper on Trie-multicategories once it goes up. +TODO: add link to arXiv paper on Dtry-multicategories once it goes up. See [2] for a reference on general T-multicategories, then trust me for now -that Trie is a cartesian monoid. +that Dtry is a cartesian monoid. [1]: [The operad of wiring diagrams: formalizing a graphical language for databases, recursion, and plug-and-play circuits](https://arxiv.org/abs/1305.0297) [2]: [Higher Operads, Higher Categories](https://arxiv.org/abs/math/0305049) """ -function ocompose(r::Rhizome, rs::Trie{Rhizome}) - # paired :: Trie{Tuple{Trie{PortVariable}, Rhizome}} +function ocompose(r::Rhizome, rs::Dtry{Rhizome}) + # paired :: Dtry{Tuple{Dtry{PortVariable}, Rhizome}} paired = zip(r.boxes, rs) - boxes = flatten(mapwithkey(Trie{Trie{PortVariable}}, paired) do k, (interface, r′) - # k :: TrieVar - # interface :: Trie{PortVariable} + boxes = flatten(mapwithkey(Dtry{Dtry{PortVariable}}, paired) do k, (interface, r′) + # k :: DtryVar + # interface :: Dtry{PortVariable} # r′ :: Rhizome # We want to create the new collection of boxes - map(Trie{PortVariable}, r′.boxes) do b - # b :: Trie{PortVariable} + map(Dtry{PortVariable}, r′.boxes) do b + # b :: Dtry{PortVariable} map(PortVariable, b) do p # p :: PortVariable # p.junction :: namespace(b.junctions) @@ -129,10 +129,10 @@ function ocompose(r::Rhizome, rs::Trie{Rhizome}) end) # Add all unexposed junctions newjunctions = flatten( - map(Trie{Variable}, rs) do r′ + map(Dtry{Variable}, rs) do r′ internal_junctions = filter(j -> !j.exposed, r′.junctions) if isnothing(internal_junctions) - Tries.node(OrderedDict{Symbol, Trie{Variable}}()) + Dtrys.node(OrderedDict{Symbol, Dtry{Variable}}()) else internal_junctions end @@ -215,7 +215,7 @@ macro rhizome(theorymod, head, body) # macroexpand `theory` to get the actual theory theory = macroexpand(__module__, :($theorymod.Meta.@theory)) # parse the name and junctions out of `head` - junctions = OrderedDict{TrieVar, Variable}() + junctions = OrderedDict{DtryVar, Variable}() (name, args) = @match head begin Expr(:call, name::Symbol, args...) => (name, args) end @@ -229,16 +229,16 @@ macro rhizome(theorymod, head, body) type = fromexpr(theory, typeexpr, AlgType) junctions[v] = Variable(true, type) end - junctions = Trie(junctions) + junctions = Dtry(junctions) - boxes = OrderedDict{TrieVar, Trie{PortVariable}}() + boxes = OrderedDict{DtryVar, Dtry{PortVariable}}() # for each line in body, add a box to boxes for line in body.args (box, args) = @match line begin _::LineNumberNode => continue Expr(:call, name, args...) => (parse_var(name), args) end - interface = OrderedDict{TrieVar, PortVariable}() + interface = OrderedDict{DtryVar, PortVariable}() for arg in args (pname, junction) = @match arg begin pname::Symbol => (pname, pname) @@ -252,14 +252,14 @@ macro rhizome(theorymod, head, body) j = junctions[jvar] interface[v] = PortVariable(j.type, jvar) end - boxes[box] = Trie(interface) + boxes[box] = Dtry(interface) end :( $name = $Rhizome( $theory, $theorymod.Meta.Constructors.:(+), $theorymod.Meta.Constructors.zero, - $(Trie(boxes)), + $(Dtry(boxes)), $(junctions), ) ) |> esc @@ -273,7 +273,7 @@ TODO: there should be a smart constructor that takes an AlgClosure and finds the right method of it for the variables and params. """ struct ResourceSharer - variables::Trie{Variable} + variables::Dtry{Variable} params::AlgType # (tcompose(variables[..].type), params) -> tcompose(variables) update::AlgMethod @@ -309,7 +309,7 @@ macro resource_sharer(theory, name, body) end function parse_namespace(theory::GAT, expr::Expr0) - vs = OrderedDict{TrieVar, AlgType}() + vs = OrderedDict{DtryVar, AlgType}() argexprs = @match expr begin Expr(:tuple, args...) => args _ => [expr] @@ -324,7 +324,7 @@ function parse_namespace(theory::GAT, expr::Expr0) type = fromexpr(theory, typeexpr, AlgType) vs[v] = type end - Trie(vs) + Dtry(vs) end function ResourceSharer(theory::GAT; variables::Expr0, params::Expr0, update::Expr0) @@ -371,7 +371,7 @@ Arguments Produces an AlgMethod going from tcompose(t1) to tcompose(first.(t2)) """ -function pullback(t1::Trie{AlgType}, t2::Trie{Tuple{AlgType, TrieVar}}; argname=:x) +function pullback(t1::Dtry{AlgType}, t2::Dtry{Tuple{AlgType, DtryVar}}; argname=:x) ty1 = tcompose(t1) ty2 = tcompose(map(first, t2)) ctx = TypeScope(argname => ty1) @@ -399,13 +399,13 @@ Arguments Produces an AlgMethod going from tcompose(first.(t2)) to tcompose(t1) """ function pushforward( - t1::Trie{AlgType}, - t2::Trie{Tuple{AlgType, TrieVar}}, + t1::Dtry{AlgType}, + t2::Dtry{Tuple{AlgType, DtryVar}}, mcompose::AlgClosure, mzero::AlgClosure; argname=:x ) - preimages = Dict{TrieVar, Vector{TrieVar}}() + preimages = Dict{DtryVar, Vector{DtryVar}}() traversewithkey(t2) do k, (_, v) if haskey(preimages, v) push!(preimages[v], k) @@ -429,7 +429,7 @@ function pushforward( AlgMethod(ctx, body, "", [LID(1)], ty1) end -function oapply(r::Rhizome, sharers::Trie{ResourceSharer}) +function oapply(r::Rhizome, sharers::Dtry{ResourceSharer}) new_variables = filter(v -> !v.exposed, flatten( map(sharers) do sharer sharer.variables @@ -443,13 +443,13 @@ function oapply(r::Rhizome, sharers::Trie{ResourceSharer}) state = map(v -> v.type, variables) state_type = tcompose(state) - # full_state : Trie{Tuple{AlgType, TrieVar}} + # full_state : Dtry{Tuple{AlgType, DtryVar}} # This is a trie mapping all the variables in all of the systems # to their type, and to the variable in the reduced system that they # map to full_state = flatten( - mapwithkey(Trie{Tuple{AlgType, TrieVar}}, zip(r.boxes, sharers)) do b, (interface, sharer) - mapwithkey(Tuple{AlgType, TrieVar}, sharer.variables) do k, v + mapwithkey(Dtry{Tuple{AlgType, DtryVar}}, zip(r.boxes, sharers)) do b, (interface, sharer) + mapwithkey(Tuple{AlgType, DtryVar}, sharer.variables) do k, v if v.exposed (v.type, interface[k].junction) else diff --git a/src/syntax/GATs.jl b/src/syntax/GATs.jl index efe2a61a..cfc74bae 100644 --- a/src/syntax/GATs.jl +++ b/src/syntax/GATs.jl @@ -15,7 +15,7 @@ using ..Scopes import ..ExprInterop: fromexpr, toexpr import ..Scopes: retag, rename, reident -using ...Util.Tries +using ...Util.Dtrys using AbstractTrees import AlgebraicInterfaces: equations diff --git a/src/syntax/gats/ast.jl b/src/syntax/gats/ast.jl index 288cb061..fcad7449 100644 --- a/src/syntax/gats/ast.jl +++ b/src/syntax/gats/ast.jl @@ -163,14 +163,14 @@ retag(reps::Dict{ScopeTag, ScopeTag}, t::AlgTerm) = AlgTerm(retag(reps, t.body)) reident(reps::Dict{Ident}, t::AlgTerm) = AlgTerm(reident(reps, t.body)) -function tcompose(t::AbstractTrie{AlgTerm}) +function tcompose(t::AbstractDtry{AlgTerm}) @match t begin - Tries.Leaf(v) => v - Tries.Node(bs) => + Dtrys.Leaf(v) => v + Dtrys.Node(bs) => AlgTerm(AlgNamedTuple{AlgTerm}(OrderedDict{Symbol, AlgTerm}( (n, tcompose(v)) for (n, v) in bs ))) - Tries.Empty() => + Dtrys.Empty() => AlgTerm(AlgNamedTuple{AlgTerm}(OrderedDict{Symbol, AlgTerm}())) end end @@ -270,12 +270,12 @@ function AlgSort(t::AlgType) end end -function tcompose(t::AbstractTrie{AlgType}) +function tcompose(t::AbstractDtry{AlgType}) @match t begin - Tries.Node(bs) => + Dtrys.Node(bs) => AlgType(AlgNamedTuple(OrderedDict(k => tcompose(v) for (k,v) in AbstractTrees.children(t)))) - Tries.Leaf(v) => v - Tries.Empty() => AlgType(AlgNamedTuple(OrderedDict{Symbol, AlgType}())) + Dtrys.Leaf(v) => v + Dtrys.Empty() => AlgType(AlgNamedTuple(OrderedDict{Symbol, AlgType}())) end end @@ -327,10 +327,10 @@ end headof(a::AlgDot) = a.head bodyof(a::AlgDot) = a.body -function Base.getindex(a::AlgTerm, v::TrieVar) +function Base.getindex(a::AlgTerm, v::DtryVar) @match v begin - Tries.Root() => a - Tries.Nested((n, v′)) => getindex(AlgTerm(AlgDot(n, a)), v′) + Dtrys.Root() => a + Dtrys.Nested((n, v′)) => getindex(AlgTerm(AlgDot(n, a)), v′) end end diff --git a/src/syntax/gats/closures.jl b/src/syntax/gats/closures.jl index a23ccf17..366cc40b 100644 --- a/src/syntax/gats/closures.jl +++ b/src/syntax/gats/closures.jl @@ -38,10 +38,10 @@ function (m::AlgMethod)(argvals::Any...) end """ -This implements the Trie-algebra structure on the multicategory of types and +This implements the Dtry-algebra structure on the multicategory of types and AlgMethods. """ -function tcompose(ms::Trie{AlgMethod}, argnames::Vector{Symbol}) +function tcompose(ms::Dtry{AlgMethod}, argnames::Vector{Symbol}) # First check that all methods have argument contexts of the same # length/variable names # argnames = ... @@ -62,7 +62,7 @@ function tcompose(ms::Trie{AlgMethod}, argnames::Vector{Symbol}) # Finally, compose all of the bodies into an expression creating an # AlgNamedTuple - body = Tries.fold( + body = Dtrys.fold( AlgTerm(AlgNamedTuple(OrderedDict{Symbol, AlgTerm}())), x -> x, d -> AlgTerm(AlgNamedTuple{AlgTerm}(d)), @@ -106,7 +106,7 @@ function add_method!(f::AlgClosure, m::AlgMethod) f.methods[sorts] = m end -function tcompose(fs::Trie{AlgClosure}, argnames::Vector{Symbol}) +function tcompose(fs::Dtry{AlgClosure}, argnames::Vector{Symbol}) ms = map(f -> only(values(f.methods)), fs) m = tcompose(ms, argnames) f = AlgClosure(first(fs).theory) diff --git a/src/util/Tries.jl b/src/util/Dtrys.jl similarity index 63% rename from src/util/Tries.jl rename to src/util/Dtrys.jl index 9e39f08c..02375371 100644 --- a/src/util/Tries.jl +++ b/src/util/Dtrys.jl @@ -1,5 +1,6 @@ -module Tries -export Trie, NonEmptyTrie, AbstractTrie, PACKAGE_ROOT, ■, TrieVar, filtermap, mapwithkey, traversewithkey +module Dtrys +export Dtry, NonEmptyDtry, AbstractDtry, PACKAGE_ROOT, ■, DtryVar, + filtermap, mapwithkey, traversewithkey using AbstractTrees using OrderedCollections @@ -7,7 +8,7 @@ using MLStyle using StructEquality """ -An internal node of a [`Trie`](@ref). Should not be used outside of this module. +An internal node of a [`Dtry`](@ref). Should not be used outside of this module. Cannot be empty. """ @@ -30,36 +31,36 @@ Cannot be empty. end """ -A leaf node of a [`Trie`](@ref). Should not be used outside of this module. +A leaf node of a [`Dtry`](@ref). Should not be used outside of this module. """ @struct_hash_equal struct Leaf_{A} value::A end -abstract type AbstractTrie{A} end +abstract type AbstractDtry{A} end -function Base.:(==)(t1::AbstractTrie, t2::AbstractTrie) +function Base.:(==)(t1::AbstractDtry, t2::AbstractDtry) content(t1) == content(t2) end """ A non-empty trie. -See the docs for [`Trie`](@ref) for general information about tries; these +See the docs for [`Dtry`](@ref) for general information about tries; these docs are specific to the reasoning behind having a non-empty variant. We use non-empty tries because we don't want to worry about the difference between the empty tuple `()` and a named tuple `(a::())`. In either one, the set of valid paths is the same. Thus, we only allow a trie to be empty at the toplevel, and subtries must be non-empty. So the self-similar recursion -happens for non-empty tries, while [`Trie`](@ref) is just a wrapper around -`Union{NonEmptyTrie{A}, Nothing}`. +happens for non-empty tries, while [`Dtry`](@ref) is just a wrapper around +`Union{NonEmptyDtry{A}, Nothing}`. """ -struct NonEmptyTrie{A} <: AbstractTrie{A} - content::Union{Leaf_{A}, Node_{NonEmptyTrie{A}}} +struct NonEmptyDtry{A} <: AbstractDtry{A} + content::Union{Leaf_{A}, Node_{NonEmptyDtry{A}}} end -content(t::NonEmptyTrie) = getfield(t, :content) +content(t::NonEmptyDtry) = getfield(t, :content) @active Node(t) begin inner = content(t) @@ -83,20 +84,20 @@ We use `trie` in a slightly idiosyncratic way here. 1. Branches are indexed by `Symbol`s rather than `Char`s 2. We only store values at leaf nodes rather than internal nodes. -One way of slickly defining NonEmptyTrie is that it is the free monad on the +One way of slickly defining NonEmptyDtry is that it is the free monad on the polynomial p = ∑_{U ⋐ Symbol, U ≠ ∅} y^U -Then Trie = NonEmptyTrie + 1. +Then Dtry = NonEmptyDtry + 1. -[1]: https://en.wikipedia.org/wiki/Trie +[1]: https://en.wikipedia.org/wiki/Dtry """ -struct Trie{A} <: AbstractTrie{A} - content::Union{Nothing, NonEmptyTrie{A}} +struct Dtry{A} <: AbstractDtry{A} + content::Union{Nothing, NonEmptyDtry{A}} end -function content(p::Trie) +function content(p::Dtry) unwrapped = getfield(p, :content) if !isnothing(unwrapped) content(unwrapped) @@ -108,9 +109,9 @@ end end @active NonEmpty(t) begin - if t isa NonEmptyTrie + if t isa NonEmptyDtry Some(t) - elseif t isa Trie + elseif t isa Dtry c = getfield(t, :content) if !isnothing(c) Some(c) @@ -119,10 +120,10 @@ end end """ -Construct a new Trie node. +Construct a new Dtry node. """ -function node(d::OrderedDict{Symbol, <:AbstractTrie{A}}) where {A} - nonempties = OrderedDict{Symbol, NonEmptyTrie{A}}() +function node(d::OrderedDict{Symbol, <:AbstractDtry{A}}) where {A} + nonempties = OrderedDict{Symbol, NonEmptyDtry{A}}() for (k, t) in pairs(d) @match t begin NonEmpty(net) => begin @@ -132,46 +133,46 @@ function node(d::OrderedDict{Symbol, <:AbstractTrie{A}}) where {A} end end if length(nonempties) > 0 - Trie{A}(NonEmptyTrie{A}(Node_{NonEmptyTrie{A}}(nonempties))) + Dtry{A}(NonEmptyDtry{A}(Node_{NonEmptyDtry{A}}(nonempties))) else - Trie{A}() + Dtry{A}() end end -node(ps::Pair{Symbol, T}...) where {A, T<:AbstractTrie{A}} = node(OrderedDict{Symbol, T}(ps...)) +node(ps::Pair{Symbol, T}...) where {A, T<:AbstractDtry{A}} = node(OrderedDict{Symbol, T}(ps...)) node(g::Base.Generator) = node(OrderedDict(g)) -leaf(x::A) where {A} = Trie{A}(NonEmptyTrie{A}(Leaf_{A}(x))) +leaf(x::A) where {A} = Dtry{A}(NonEmptyDtry{A}(Leaf_{A}(x))) -Trie{A}() where {A} = Trie{A}(nothing) +Dtry{A}() where {A} = Dtry{A}(nothing) -struct TrieIndexError <: Exception - trie::Trie +struct DtryIndexError <: Exception + trie::Dtry key::Symbol end -struct TrieDerefError <: Exception - trie::Trie +struct DtryDerefError <: Exception + trie::Dtry end -function Base.getproperty(t::AbstractTrie{A}, n::Symbol) where {A} +function Base.getproperty(t::AbstractDtry{A}, n::Symbol) where {A} @match t begin Node(bs) => if haskey(bs, n) bs[n] else - throw(TrieIndexError(t, n)) + throw(DtryIndexError(t, n)) end - _ => throw(TrieIndexError(t, n)) + _ => throw(DtryIndexError(t, n)) end end -function Base.filter(f, t::AbstractTrie{A}) where {A} +function Base.filter(f, t::AbstractDtry{A}) where {A} @match t begin - Leaf(v) => f(v) ? t : Trie{A}() + Leaf(v) => f(v) ? t : Dtry{A}() Node(bs) => begin - bs′ = OrderedDict{Symbol, NonEmptyTrie{A}}() + bs′ = OrderedDict{Symbol, NonEmptyDtry{A}}() for (n, s) in bs @match filter(f, s) begin NonEmpty(net) => (bs′[n] = net) @@ -185,23 +186,23 @@ function Base.filter(f, t::AbstractTrie{A}) where {A} end """ - filtermap(f, return_type::Type, t::AbstractTrie) + filtermap(f, return_type::Type, t::AbstractDtry) Map the function `f : eltype(t) -> Union{Some{return_type}, Nothing}` over the trie `t` to produce a trie of type `return_type`, filtering out the elements of `t` on which `f` returns `nothing`. We pass `return_type` explicitly so that in -the case `t` is the empty trie this doesn't return `Trie{Any}`. +the case `t` is the empty trie this doesn't return `Dtry{Any}`. """ -function filtermap(f, return_type::Type, t::AbstractTrie) +function filtermap(f, return_type::Type, t::AbstractDtry) @match t begin Leaf(v) => begin @match f(v) begin Some(v′) => leaf(v′) - nothing => Trie{return_type}() + nothing => Dtry{return_type}() end end Node(bs) => begin - bs′ = OrderedDict{Symbol, NonEmptyTrie{return_type}}() + bs′ = OrderedDict{Symbol, NonEmptyDtry{return_type}}() for (n, s) in bs @match filtermap(f, return_type, s) begin NonEmpty(net) => (bs′[n] = net) @@ -215,21 +216,21 @@ function filtermap(f, return_type::Type, t::AbstractTrie) end """ - zipwith(f, t1::AbstractTrie, t2::AbstractTrie) + zipwith(f, t1::AbstractDtry, t2::AbstractDtry) Produces a new trie whose leaf node at a path `p` is given by `f(t1[p], t2[p])`. Throws an error if `t1` and `t2` are not of the same shape: i.e. they don't have the exact same set of paths. """ -function zipwith(f, t1::AbstractTrie{A1}, t2::AbstractTrie{A2}) where {A1, A2} +function zipwith(f, t1::AbstractDtry{A1}, t2::AbstractDtry{A2}) where {A1, A2} @match (t1, t2) begin (Leaf(v1), Leaf(v2)) => leaf(f(v1, v2)) (Node(bs1), Node(bs2)) => begin keys(bs1) == keys(bs2) || error("cannot zip two tries not of the same shape") node(OrderedDict(n => zipwith(f, s1, s2) for ((n, s1), (_, s2)) in zip(bs1, bs2))) end - (Empty(), Empty()) => Trie{Core.Compiler.return_type(f, Tuple{A1, A2})}() + (Empty(), Empty()) => Dtry{Core.Compiler.return_type(f, Tuple{A1, A2})}() _ => error("cannot zip two tries not of the same shape") end end @@ -242,18 +243,18 @@ Produces a new trie whose leaf node at a path `p` is given by `(t1[p], t2[p])`. Throws an error if `t1` and `t2` are not of the same shape: i.e. they don't have the exact same set of paths. """ -Base.zip(t1::AbstractTrie, t2::AbstractTrie) = zipwith((a,b) -> (a,b), t1, t2) +Base.zip(t1::AbstractDtry, t2::AbstractDtry) = zipwith((a,b) -> (a,b), t1, t2) -Base.getindex(p::Trie, n::Symbol) = getproperty(p, n) +Base.getindex(p::Dtry, n::Symbol) = getproperty(p, n) -function Base.getindex(t::AbstractTrie{A})::A where {A} +function Base.getindex(t::AbstractDtry{A})::A where {A} @match t begin Leaf(v) => v - _ => throw(TrieDerefError(t)) + _ => throw(DtryDerefError(t)) end end -function Base.first(t::AbstractTrie) +function Base.first(t::AbstractDtry) @match t begin Leaf(v) => v Node(bs) => first(first(values(bs))) @@ -261,14 +262,14 @@ function Base.first(t::AbstractTrie) end end -function Base.hasproperty(t::AbstractTrie, n::Symbol) +function Base.hasproperty(t::AbstractDtry, n::Symbol) @match t begin Node(bs) => haskey(bs, n) _ => false end end -function Base.propertynames(t::AbstractTrie) +function Base.propertynames(t::AbstractDtry) @match t begin Node(bs) => keys(bs) _ => Symbol[] @@ -276,68 +277,68 @@ function Base.propertynames(t::AbstractTrie) end Base.keys(t) = Base.propertynames(t) -Base.valtype(t::AbstractTrie{A}) where {A} = A -Base.valtype(::Type{<:AbstractTrie{A}}) where {A} = A -Base.eltype(t::AbstractTrie{A}) where {A} = A -Base.eltype(::Type{<:AbstractTrie{A}}) where {A} = A +Base.valtype(t::AbstractDtry{A}) where {A} = A +Base.valtype(::Type{<:AbstractDtry{A}}) where {A} = A +Base.eltype(t::AbstractDtry{A}) where {A} = A +Base.eltype(::Type{<:AbstractDtry{A}}) where {A} = A """ - map(f, return_type::Type, t::AbstractTrie) + map(f, return_type::Type, t::AbstractDtry) Produce a new trie of the same shape as `t` where the value at a path `p` is given by `f(t[p])`. We pass in the return type explicitly so that in the case -that `t` is empty we don't get `Trie{Any}`. +that `t` is empty we don't get `Dtry{Any}`. There is a variant defined later where `return_type` is not passed in, and it tries to use type inference from the Julia compiler to infer `return_type`: use of this should be discouraged. """ -function Base.map(f, return_type::Type, t::AbstractTrie) +function Base.map(f, return_type::Type, t::AbstractDtry) @match t begin Leaf(v) => leaf(f(v)) Node(bs) => node( - OrderedDict{Symbol, Trie{return_type}}( + OrderedDict{Symbol, Dtry{return_type}}( (n => map(f, return_type, t′)) for (n, t′) in bs ) ) - Empty() => Trie{return_type}() + Empty() => Dtry{return_type}() end end """ - map(f, t::AbstractTrie) + map(f, t::AbstractDtry) -Variant of `map(f, return_type, t::AbstractTrie)` which attempts to infer the +Variant of `map(f, return_type, t::AbstractDtry)` which attempts to infer the return type of `f`. """ -function Base.map(f, t::AbstractTrie{A}) where {A} +function Base.map(f, t::AbstractDtry{A}) where {A} B = Core.Compiler.return_type(f, Tuple{A}) map(f, B, t) end """ - flatten(t::AbstractTrie{Trie{A}}) + flatten(t::AbstractDtry{Dtry{A}}) -The monad operation for Tries. Works on NonEmptyTries and Tries. +The monad operation for Dtrys. Works on NonEmptyDtrys and Dtrys. """ -function flatten(t::AbstractTrie{Trie{A}}) where {A} +function flatten(t::AbstractDtry{Dtry{A}}) where {A} @match t begin Leaf(v) => v # Note that if flatten(v) is empty, the `node` constructor will # automatically remove it from the built trie. Node(bs) => node(n => flatten(v) for (n, v) in bs) - Empty() => Trie{A}() + Empty() => Dtry{A}() end end -struct TrieVar - content::Union{Nothing, Tuple{Symbol, TrieVar}} +struct DtryVar + content::Union{Nothing, Tuple{Symbol, DtryVar}} end -PACKAGE_ROOT = TrieVar(nothing) +PACKAGE_ROOT = DtryVar(nothing) ■ = PACKAGE_ROOT -content(v::TrieVar) = getfield(v, :content) +content(v::DtryVar) = getfield(v, :content) @active Root(v) begin isnothing(content(v)) @@ -350,42 +351,42 @@ end end end -function Base.getproperty(v::TrieVar, n::Symbol) +function Base.getproperty(v::DtryVar, n::Symbol) @match v begin - Root() => TrieVar((n, v)) - Nested((n′, v′)) => TrieVar((n′, getproperty(v′, n))) + Root() => DtryVar((n, v)) + Nested((n′, v′)) => DtryVar((n′, getproperty(v′, n))) end end -function Base.:(*)(v1::TrieVar, v2::TrieVar) +function Base.:(*)(v1::DtryVar, v2::DtryVar) @match v1 begin Root() => v2 - Nested((n, v1′)) => TrieVar((n, v1′ * v2)) + Nested((n, v1′)) => DtryVar((n, v1′ * v2)) end end """ - mapwithkey(f, return_type::Type, t::AbstractTrie) + mapwithkey(f, return_type::Type, t::AbstractDtry) Constructs a new trie with the same shape as `t` where the value at the path `p` is `f(p, t[p])`. """ -function mapwithkey(f, return_type::Type, t::AbstractTrie; prefix=PACKAGE_ROOT) +function mapwithkey(f, return_type::Type, t::AbstractDtry; prefix=PACKAGE_ROOT) @match t begin Leaf(v) => leaf(f(prefix, v)) Node(bs) => node([k => mapwithkey(f, return_type, v; prefix=getproperty(prefix, k)) for (k, v) in bs]...) - Empty() => Trie{return_type}() + Empty() => Dtry{return_type}() end end """ - traversewithkey(f, t::AbstractTrie; prefix=PACKAGE_ROOT) + traversewithkey(f, t::AbstractDtry; prefix=PACKAGE_ROOT) Similar to [`mapwithkey`](@ref) but just evaluates `f` for its side effects instead of constructing a new trie. """ -function traversewithkey(f, t::AbstractTrie; prefix=PACKAGE_ROOT) +function traversewithkey(f, t::AbstractDtry; prefix=PACKAGE_ROOT) @match t begin Leaf(v) => (f(prefix, v); nothing) Node(bs) => begin @@ -398,7 +399,7 @@ function traversewithkey(f, t::AbstractTrie; prefix=PACKAGE_ROOT) end """ - fold(emptycase::A, leafcase, nodecase, t::AbstractTrie)::A + fold(emptycase::A, leafcase, nodecase, t::AbstractDtry)::A Fold over `t` to produce a single value. @@ -407,7 +408,7 @@ Args: - `leafcase::eltype(t) -> A` - `nodecase::OrderedDict{Symbol, A} -> A` """ -function fold(emptycase::A, leafcase, nodecase, t::AbstractTrie)::A where {A} +function fold(emptycase::A, leafcase, nodecase, t::AbstractDtry)::A where {A} @match t begin Empty() => emptycase Leaf(v) => leafcase(v) @@ -416,26 +417,26 @@ function fold(emptycase::A, leafcase, nodecase, t::AbstractTrie)::A where {A} end """ - all(f, t::AbstractTrie) + all(f, t::AbstractDtry) Checks if `f` returns `true` when applied to all of the elements of `t`. Args: - `f::eltype(t) -> Bool` """ -function Base.all(f, t::AbstractTrie) +function Base.all(f, t::AbstractDtry) fold(true, f, d -> all(values(d)), t) end # precondition: the union of the keys in t1 and t2 is prefix-free -function Base.merge(t1::AbstractTrie{A}, t2::AbstractTrie{A}) where {A} +function Base.merge(t1::AbstractDtry{A}, t2::AbstractDtry{A}) where {A} @match (t1, t2) begin (Leaf(_), _) || (_, Leaf(_)) => error("cannot merge tries with overlapping keys") (Empty(), _) => t2 (_, Empty()) => t1 (Node(b1), Node(b2)) => begin - b = OrderedDict{Symbol, NonEmptyTrie{A}}() + b = OrderedDict{Symbol, NonEmptyDtry{A}}() for (n, t) in b1 if haskey(b2, n) @match merge(t, b2[n]) begin @@ -461,8 +462,8 @@ Make a trie out of a dict from trie keys to values. Fails if the keys in the dict are not prefix-free. """ -function Trie(d::OrderedDict{TrieVar, A}) where {A} - branches = OrderedDict{Symbol, OrderedDict{TrieVar, A}}() +function Dtry(d::OrderedDict{DtryVar, A}) where {A} + branches = OrderedDict{Symbol, OrderedDict{DtryVar, A}}() for (v, x) in d @match v begin Root() => @@ -479,12 +480,12 @@ function Trie(d::OrderedDict{TrieVar, A}) where {A} end end end - node(n => Trie(d′) for (n, d′) in branches) + node(n => Dtry(d′) for (n, d′) in branches) end -Trie(pairs::Pair{TrieVar, A}...) where {A} = Trie(OrderedDict{TrieVar, A}(pairs...)) +Dtry(pairs::Pair{DtryVar, A}...) where {A} = Dtry(OrderedDict{DtryVar, A}(pairs...)) -function Base.show(io::IO, v::TrieVar) +function Base.show(io::IO, v::DtryVar) print(io, "■") while true @match v begin @@ -497,7 +498,7 @@ function Base.show(io::IO, v::TrieVar) end end -function Base.haskey(t::AbstractTrie, v::TrieVar) +function Base.haskey(t::AbstractDtry, v::DtryVar) @match (t, v) begin (Leaf(_), Root) => true (Node(_), Nested((n, v))) => hasproperty(t, n) && haskey(getproperty(t, n), v) @@ -505,32 +506,32 @@ function Base.haskey(t::AbstractTrie, v::TrieVar) end end -struct TrieVarNotFound <: Exception - p::Trie - v::TrieVar +struct DtryVarNotFound <: Exception + p::Dtry + v::DtryVar end -function Base.getindex(t::AbstractTrie, v::TrieVar) +function Base.getindex(t::AbstractDtry, v::DtryVar) @match (t, v) begin (Leaf(v), Root) => v (Node(_), Nested((n, v′))) => if hasproperty(t, n) getproperty(t, n)[v′] else - throw(TrieVarNotFound(t, v)) + throw(DtryVarNotFound(t, v)) end - _ => throw(TrieVarNotFound(t, v)) + _ => throw(DtryVarNotFound(t, v)) end end -function AbstractTrees.children(t::AbstractTrie) +function AbstractTrees.children(t::AbstractDtry) @match t begin Leaf(_) => () Node(bs) => bs end end -function AbstractTrees.printnode(io::IO, t::AbstractTrie{A}; kw...) where {A} +function AbstractTrees.printnode(io::IO, t::AbstractDtry{A}; kw...) where {A} @match t begin Leaf(v) => print(io, v) Node(bs) => print(io, nameof(typeof(t)), "{$A}") @@ -538,11 +539,11 @@ function AbstractTrees.printnode(io::IO, t::AbstractTrie{A}; kw...) where {A} end end -function Base.show(io::IO, t::AbstractTrie{A}) where {A} +function Base.show(io::IO, t::AbstractDtry{A}) where {A} @match t begin Leaf(v) => print(io, "leaf(", v, ")::$(nameof(typeof(t))){$A}") Node(_) => print_tree(io, t) - Empty() => print(io, "Trie{$A}()") + Empty() => print(io, "Dtry{$A}()") end end diff --git a/src/util/module.jl b/src/util/module.jl index 2ed32ab9..92bd76dc 100644 --- a/src/util/module.jl +++ b/src/util/module.jl @@ -2,12 +2,12 @@ module Util using Reexport -include("Tries.jl") +include("Dtrys.jl") include("MetaUtils.jl") include("HashColor.jl") include("Eithers.jl") -@reexport using .Tries +@reexport using .Dtrys @reexport using .MetaUtils @reexport using .HashColor @reexport using .Eithers diff --git a/test/nonstdlib/ResourceSharers.jl b/test/nonstdlib/ResourceSharers.jl index 2fa777f6..d5a6fcb8 100644 --- a/test/nonstdlib/ResourceSharers.jl +++ b/test/nonstdlib/ResourceSharers.jl @@ -2,8 +2,8 @@ module TestResourceSharers using Test -using GATlab.Util.Tries -using GATlab.Util.Tries: node, leaf +using GATlab.Util.Dtrys +using GATlab.Util.Dtrys: node, leaf using GATlab.NonStdlib.ResourceSharers using GATlab.NonStdlib.ResourceSharers: ocompose, oapply using GATlab @@ -37,7 +37,7 @@ end @test sprint(show, rtop) isa String -r = ocompose(rtop, Trie(■.X => rX, ■.Y => rY)) +r = ocompose(rtop, Dtry(■.X => rX, ■.Y => rY)) @resource_sharer ThRing Spring begin variables = x, v @@ -56,9 +56,9 @@ end; gravity(v) end -@test sprint(show, Gravity) isa String +@test sprint((io, r) -> show(io, r; theory=ThRing.Meta.theory), Gravity) isa String -s = oapply(SpringGravity, Trie(■.spring => Spring, ■.gravity => Gravity)); +s = oapply(SpringGravity, Dtry(■.spring => Spring, ■.gravity => Gravity)); body = toexpr(GATContext(ThRing.Meta.theory, s.update.context), s.update.body) diff --git a/test/syntax/GATs.jl b/test/syntax/GATs.jl index 8cdc907d..f204efee 100644 --- a/test/syntax/GATs.jl +++ b/test/syntax/GATs.jl @@ -142,7 +142,7 @@ end id_span(x) := Span(x, id(x),id(x)) ⊣ [x::Ob] end -# Tries +# Dtrys tuplescope = fromexpr(ThMonoid.Meta.theory, :([x::(a::(s,t),b)]), TypeScope) diff --git a/test/util/Tries.jl b/test/util/Dtrys.jl similarity index 58% rename from test/util/Tries.jl rename to test/util/Dtrys.jl index 3af72d21..984028da 100644 --- a/test/util/Tries.jl +++ b/test/util/Dtrys.jl @@ -1,32 +1,32 @@ -module TestTries +module TestDtrys -using GATlab.Util.Tries -import .Tries: node, leaf, Node, Leaf, Empty, NonEmpty, zipwith, flatten, fold +using GATlab.Util.Dtrys +import .Dtrys: node, leaf, Node, Leaf, Empty, NonEmpty, zipwith, flatten, fold using Test using OrderedCollections using MLStyle -@test_throws ErrorException Tries.Node_(OrderedDict{Symbol, Int}()) +@test_throws ErrorException Dtrys.Node_(OrderedDict{Symbol, Int}()) t1 = node(:a => leaf(1), :b => node(:a => leaf(2), :c => leaf(3))) -@test t1.a isa AbstractTrie +@test t1.a isa AbstractDtry @test t1.a == t1[:a] -@test_throws Tries.TrieDerefError t1[] -@test_throws Tries.TrieIndexError t1.z +@test_throws Dtrys.DtryDerefError t1[] +@test_throws Dtrys.DtryIndexError t1.z @test t1.a[] == 1 -@test t1.b.a isa NonEmptyTrie +@test t1.b.a isa NonEmptyDtry @test t1.b.a[] == 2 -@test sprint(show, t1.a) == "leaf(1)::NonEmptyTrie{Int64}" -@test sprint(show, t1.b) == "NonEmptyTrie{Int64}\n├─ :a ⇒ 2\n└─ :c ⇒ 3\n" -@test sprint(show, Trie{Int}()) == "Trie{Int64}()" +@test sprint(show, t1.a) == "leaf(1)::NonEmptyDtry{Int64}" +@test sprint(show, t1.b) == "NonEmptyDtry{Int64}\n├─ :a ⇒ 2\n└─ :c ⇒ 3\n" +@test sprint(show, Dtry{Int}()) == "Dtry{Int64}()" @test ■ == PACKAGE_ROOT -@test ■.a isa TrieVar -@test ■.a.b isa TrieVar -@test_throws Tries.TrieVarNotFound t1[■] +@test ■.a isa DtryVar +@test ■.a.b isa DtryVar +@test_throws Dtrys.DtryVarNotFound t1[■] @test haskey(t1, ■.a) @test t1[■.a] == 1 @@ -36,10 +36,10 @@ t1 = node(:a => leaf(1), :b => node(:a => leaf(2), :c => leaf(3))) @test sprint(show, ■.a) == "■.a" @test map(x -> x + 1, leaf(2)) == leaf(3) -@test map(x -> x + 1, Trie{Int}()) == Trie{Int}() +@test map(x -> x + 1, Dtry{Int}()) == Dtry{Int}() @test filter(x -> x % 2 == 0, t1) == node(:b => node(:a => leaf(2))) -@test filter(_ -> false, Trie{Int}()) == Trie{Int}() +@test filter(_ -> false, Dtry{Int}()) == Dtry{Int}() function int_sqrt(x) try @@ -50,7 +50,7 @@ function int_sqrt(x) end @test filtermap(int_sqrt, Int, t1) == node(:a => leaf(1)) -@test filtermap(int_sqrt, Int, Trie{Int}()) == Trie{Int}() +@test filtermap(int_sqrt, Int, Dtry{Int}()) == Dtry{Int}() @test t1 == @match t1 begin NonEmpty(net1) => net1 @@ -58,12 +58,12 @@ end @test zipwith(+, t1, t1) == node(:a => leaf(2), :b => node(:a => leaf(4), :c => leaf(6))) # TODO: fix this, zipwith should take an argument for the return type -@test zipwith(+, Trie{Int}(), Trie{Int}()) == Trie{Int}() -@test_throws ErrorException zipwith(+, Trie{Int}(), t1) +@test zipwith(+, Dtry{Int}(), Dtry{Int}()) == Dtry{Int}() +@test_throws ErrorException zipwith(+, Dtry{Int}(), t1) @test zip(t1, t1) == node(:a => leaf((1,1)), :b => node(:a => leaf((2,2)), :c => leaf((3,3)))) @test first(t1) == 1 -@test_throws ErrorException first(Trie{Int}()) +@test_throws ErrorException first(Dtry{Int}()) @test hasproperty(t1, :a) @test !hasproperty(t1, :z) @@ -75,7 +75,7 @@ end @test valtype(typeof(t1)) == Int @test eltype(t1) == Int -@test flatten(Trie{Trie{Int}}()) == Trie{Int}() +@test flatten(Dtry{Dtry{Int}}()) == Dtry{Int}() @test flatten(leaf(t1)) == t1 @test flatten(leaf(leaf(1))) == leaf(1) @test flatten(node(:f => leaf(leaf(1)), :g => leaf(t1))) == @@ -90,10 +90,10 @@ end ) ) -@test mapwithkey((k, _) -> k, TrieVar, t1) == node(:a => leaf(■.a), :b => node(:a => leaf(■.b.a), :c => leaf(■.b.c))) -@test mapwithkey((k, _) -> k, TrieVar, Trie{Int}()) == Trie{TrieVar}() +@test mapwithkey((k, _) -> k, DtryVar, t1) == node(:a => leaf(■.a), :b => node(:a => leaf(■.b.a), :c => leaf(■.b.c))) +@test mapwithkey((k, _) -> k, DtryVar, Dtry{Int}()) == Dtry{DtryVar}() -t1_keys = TrieVar[] +t1_keys = DtryVar[] traversewithkey((k, _) -> push!(t1_keys, k), t1) @@ -101,23 +101,23 @@ traversewithkey((k, _) -> push!(t1_keys, k), t1) b = Ref(true) -traversewithkey((_, _) -> b[] = false, Trie{Int}()) +traversewithkey((_, _) -> b[] = false, Dtry{Int}()) @test b[] @test fold(0, identity, d -> sum(values(d)), t1) == 6 -@test fold(0, identity, d -> sum(values(d)), Trie{Int}()) == 0 +@test fold(0, identity, d -> sum(values(d)), Dtry{Int}()) == 0 @test all(iseven, t1) == false @test all(iseven, filter(iseven, t1)) @test merge(t1, node(:b => node(:z => leaf(4)))) == node(:a => leaf(1), :b => node(:a => leaf(2), :c => leaf(3), :z => leaf(4))) -@test Trie(■.a => 1, ■.b.a => 2, ■.b.c => 3) == t1 -@test_throws ErrorException Trie(■.a => 1, ■.a.b => 2) +@test Dtry(■.a => 1, ■.b.a => 2, ■.b.c => 3) == t1 +@test_throws ErrorException Dtry(■.a => 1, ■.a.b => 2) -t2 = node(:a => Trie{Int}()) +t2 = node(:a => Dtry{Int}()) -@test t2 == Trie{Int}() +@test t2 == Dtry{Int}() end diff --git a/test/util/tests.jl b/test/util/tests.jl index b2c8fa90..ef12c46b 100644 --- a/test/util/tests.jl +++ b/test/util/tests.jl @@ -6,8 +6,8 @@ using Test include("MetaUtils.jl") end -@testset "Tries" begin - include("Tries.jl") +@testset "Dtrys" begin + include("Dtrys.jl") end end