-
Notifications
You must be signed in to change notification settings - Fork 10
Using Setfield.jl's lenses to handle indexing #26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
1e267f8
286e211
eb65b76
e8058a4
b72243a
122092d
29e051b
6092b7c
2259b30
298e1fc
9dd6cf7
b94bcce
9af3694
e26307f
fff6f1f
e47abbb
5328a6b
924f45d
79118eb
71af2e4
e2ffdad
638bafc
b4db7a4
8299329
33d6777
38ac60a
e14ce7c
854bbe1
f010a1b
253dffe
d31461e
b596c11
c72faab
fc53f29
111f805
84badce
8c1d193
3a854e2
2019180
a397bfe
5905206
3f287f5
b616902
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,6 @@ | ||
| using Setfield | ||
| import Setfield: PropertyLens, ComposedLens, IdentityLens, IndexLens, DynamicIndexLens | ||
|
|
||
| """ | ||
| VarName{sym}(indexing::Tuple=()) | ||
|
|
||
|
|
@@ -26,10 +29,10 @@ julia> @varname x[:, 1][1+1] | |
| x[:,1][2] | ||
| ``` | ||
| """ | ||
| struct VarName{sym, T<:Tuple} | ||
| struct VarName{sym, T} | ||
| indexing::T | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we rename
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to suggest that already, but expected someone to complain about incompatibility! I'm all for it. |
||
|
|
||
| VarName{sym}(indexing::Tuple=()) where {sym} = new{sym,typeof(indexing)}(indexing) | ||
| VarName{sym}(indexing=()) where {sym} = new{sym,typeof(indexing)}(indexing) | ||
phipsgabler marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| end | ||
|
|
||
| """ | ||
|
|
@@ -45,7 +48,7 @@ julia> VarName(@varname(x[1][2:3])) | |
| x | ||
| ``` | ||
| """ | ||
| function VarName(vn::VarName, indexing::Tuple = ()) | ||
| function VarName(vn::VarName, indexing = ()) | ||
| return VarName{getsym(vn)}(indexing) | ||
| end | ||
|
|
||
|
|
@@ -89,7 +92,7 @@ getindexing(vn::VarName) = vn.indexing | |
| Base.hash(vn::VarName, h::UInt) = hash((getsym(vn), getindexing(vn)), h) | ||
| Base.:(==)(x::VarName, y::VarName) = getsym(x) == getsym(y) && getindexing(x) == getindexing(y) | ||
|
||
|
|
||
| function Base.show(io::IO, vn::VarName) | ||
| function Base.show(io::IO, vn::VarName{<:Any, <:Tuple}) | ||
| print(io, getsym(vn)) | ||
| for indices in getindexing(vn) | ||
| print(io, "[") | ||
|
|
@@ -98,6 +101,15 @@ function Base.show(io::IO, vn::VarName) | |
| end | ||
| end | ||
|
|
||
| function Base.show(io::IO, vn::VarName{<:Any, <:Lens}) | ||
|
||
| print(io, getsym(vn)) | ||
| Setfield.print_application(io, vn.indexing) | ||
| end | ||
|
|
||
| # TODO: Should this really go here? | ||
|
||
| Setfield.print_application(io::IO, l::IndexLens) = print(io, "[", join(map(prettify_index, l.indices), ", "), "]") | ||
| Setfield.print_application(io::IO, l::DynamicIndexLens) = print(io, l, "(_)") | ||
|
|
||
| prettify_index(x) = string(x) | ||
| prettify_index(::Colon) = ":" | ||
|
|
||
|
|
@@ -214,7 +226,65 @@ _issubrange(i::ConcreteIndex, j::ConcreteIndex) = issubset(i, j) | |
| _issubrange(i::Union{ConcreteIndex, Colon}, j::Colon) = true | ||
| _issubrange(i::Colon, j::ConcreteIndex) = true | ||
|
|
||
| # Idea behind `subsumes` for `Lens` is that we traverse the two lenses in parallel, | ||
| # checking `subsumes` for every level. This for example means that if we are comparing | ||
| # `PropertyLens{:a}` and `PropertyLens{:b}` we immediately know that they do not subsume | ||
| # each other since at the same level/depth they access different properties. | ||
| subsumes(t::ComposedLens, u::ComposedLens) = subsumes(t.outer, u.outer) && subsumes(t.inner, u.inner) | ||
|
|
||
| # If `t` is still a composed lens, then there is no way it can subsume `u` since `u` is a | ||
| # leaf of the "lens-tree". | ||
| subsumes(t::ComposedLens, u::PropertyLens) = false | ||
| # Here we need to check if `u.outer` (i.e. the next lens to be applied from `u`) is | ||
| # subsumed by `t`, since this would mean that the rest of the composition is also subsumed | ||
| # by `t`. | ||
| subsumes(t::PropertyLens, u::ComposedLens) = subsumes(t, u.outer) | ||
|
|
||
| # For `PropertyLens` either they have the same `name` and thus they are indeed the same. | ||
| subsumes(t::PropertyLens{name}, u::PropertyLens{name}) where {name} = true | ||
| # Otherwise they represent different properties, and thus are not the same. | ||
| subsumes(t::PropertyLens, u::PropertyLens) = false | ||
|
|
||
| # Indices subsumes if they are subindices, i.e. we just call `_issubindex`. | ||
| # FIXME: Does not support `DynamicIndexLens`. | ||
| # FIXME: Does not correctly handle cases such as `subsumes(x, x[:])` | ||
| # (but neither did old implementation). | ||
| subsumes(t::IndexLens, u::IndexLens) = _issubindex(t.indices, u.indices) | ||
|
|
||
| """ | ||
| concretize(l::Lens, x) | ||
|
|
||
| Return `l` instantiated on `x`, i.e. any runtime information evaluated using `x`. | ||
| """ | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This empty line seems wrong, I don't think the docstring will show up anywhere. |
||
| concretize(I::Lens, x) = I | ||
| concretize(I::DynamicIndexLens, x) = IndexLens(I.f(x)) | ||
| function concretize(I::ComposedLens, x) | ||
| x_inner = get(x, I.outer) | ||
| return ComposedLens(concretize(I.outer, x), concretize(I.inner, x_inner)) | ||
| end | ||
| """ | ||
| concretize(vn::VarName, x) | ||
|
|
||
| Return `vn` instantiated on `x`, i.e. any runtime information evaluated using `x`. | ||
|
|
||
| # Examples | ||
| ```jldoctest | ||
| julia> x = (a = [1.0 2.0;], ); | ||
|
|
||
| julia> vn = @varname(x.a[1, :]) | ||
| x.a[1, :] | ||
|
|
||
| julia> AbstractPPL.concretize(vn, x) | ||
| x.a[1, :] | ||
|
|
||
| julia> vn = @varname(x.a[1, end][:]); | ||
|
|
||
| julia> AbstractPPL.concretize(vn, x) | ||
| x.a[1, 2][:] | ||
| ``` | ||
| """ | ||
| concretize(vn::VarName, x) = VarName(vn, concretize(vn.indexing, x)) | ||
|
|
||
| """ | ||
| @varname(expr) | ||
|
|
@@ -231,30 +301,43 @@ julia> @varname(x).indexing | |
| () | ||
|
|
||
| julia> @varname(x[1]).indexing | ||
| ((1,),) | ||
| (@lens _[1]) | ||
|
|
||
| julia> @varname(x[:, 1]).indexing | ||
| ((Colon(), 1),) | ||
| (@lens _[:, 1]) | ||
|
|
||
| julia> @varname(x[:, 1][2]).indexing | ||
| ((Colon(), 1), (2,)) | ||
| (@lens _[:, 1][2]) | ||
|
|
||
| julia> @varname(x[1,2][1+5][45][3]).indexing | ||
| ((1, 2), (6,), (45,), (3,)) | ||
| (@lens _[1, 2][6][45][3]) | ||
| ``` | ||
|
|
||
| !!! compat "Julia 1.5" | ||
| Using `begin` in an indexing expression to refer to the first index requires at least | ||
| Julia 1.5. | ||
| """ | ||
| macro varname(expr::Union{Expr, Symbol}) | ||
| return esc(varname(expr)) | ||
| return varname(expr) | ||
| end | ||
|
|
||
| varname(sym::Symbol) = :($(AbstractPPL.VarName){$(QuoteNode(sym))}()) | ||
| function varname(expr::Expr) | ||
| if Meta.isexpr(expr, :ref) | ||
| sym, inds = vsym(expr), vinds(expr) | ||
| if Meta.isexpr(expr, :ref) || Meta.isexpr(expr, :.) | ||
| sym = vsym(expr) | ||
|
|
||
| # Need to recursively unwrap until we reach the outer-most variable. | ||
| # TODO: implement as recursion? | ||
| curexpr = expr | ||
| while !(curexpr.args[1] isa Symbol) | ||
| curexpr = curexpr.args[1] | ||
| end | ||
|
|
||
| # Then we replace the variable with `_`, to get an expression we can | ||
| # use `lensmacro` on. | ||
| curexpr.args[1] = :_ | ||
| inds = Setfield.lensmacro(identity, expr) | ||
|
|
||
| return :($(AbstractPPL.VarName){$(QuoteNode(sym))}($inds)) | ||
| else | ||
| error("Malformed variable name $(expr)!") | ||
|
|
@@ -294,7 +377,7 @@ function vsym end | |
|
|
||
| vsym(expr::Symbol) = expr | ||
| function vsym(expr::Expr) | ||
| if Meta.isexpr(expr, :ref) | ||
| if Meta.isexpr(expr, :ref) || Meta.isexpr(expr, :.) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might also need to handle
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe the more stable option is to use
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with the usage of Regarding |
||
| return vsym(expr.args[1]) | ||
| else | ||
| error("Malformed variable name $(expr)!") | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.