Skip to content

Commit 69e3924

Browse files
phipsgablersunxd3
andcommitted
Allow name and property interpolation in varnames (#54)
Implements #46. Most of this is dependent on my [PR in Setfield.jl](jw3126/Setfield.jl#168) to allow interpolation of properties (so it works only if you `dev` that branch). It's a bit of a hack though, since `parse_object_lens` behaves a bit weirdly when the innermost value of a lens chain is an interpolation: ```julia julia> Setfield.parse_obj_lens(@q $name.a) (:($(Expr(:escape, :_))), :((Setfield.compose)($(Expr(:escape, :name)), (Setfield.PropertyLens){:a}()))) julia> Setfield.parse_obj_lens(@q x.a) (:($(Expr(:escape, :x))), :((Setfield.compose)((Setfield.PropertyLens){:a}()))) ``` I'd be really nice to always get the latter form. (`@q` is just an ad-hoc macro to construct expressions preserving the `:$`.) @jw3126, could you maybe look at this and suggest any better alternatives? Either how to implement this nicely in AbstractPPL.jl, or improvements I can add to my PR. Co-authored-by: Xianda <[email protected]>
1 parent e57ebca commit 69e3924

File tree

2 files changed

+40
-3
lines changed

2 files changed

+40
-3
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1515
[compat]
1616
AbstractMCMC = "2, 3, 4"
1717
DensityInterface = "0.4"
18-
Setfield = "0.8.1, 1"
18+
Setfield = "0.8.2, 1"
1919
julia = "~1.6.6, 1.7.3"

src/varname.jl

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ concretized as `VarName` only supports non-dynamic indexing as determined by
515515
[`is_static_index`](@ref). See examples below.
516516
517517
## Examples
518+
518519
### Dynamic indexing
519520
```jldoctest
520521
julia> x = (a = [1.0 2.0; 3.0 4.0; 5.0 6.0], );
@@ -573,6 +574,28 @@ julia> x = (a = [(b = rand(2), )], ); getlens(@varname(x.a[1].b[end], true))
573574
(@lens _.a[1].b[2])
574575
```
575576
577+
Interpolation can be used for names (the base name as well as property names). Variables within
578+
indices are always evaluated in the calling scope, in the same manner as `Setfield` does:
579+
580+
```jldoctest
581+
julia> name, i = :a, 10;
582+
583+
julia> @varname(x.\$name[i, i+1])
584+
x.a[10,11]
585+
586+
julia> @varname(\$name)
587+
a
588+
589+
julia> @varname(\$name[1])
590+
a[1]
591+
592+
julia> @varname(\$name.x[1])
593+
a.x[1]
594+
595+
julia> @varname(b.\$name.x[1])
596+
b.a.x[1]
597+
```
598+
576599
!!! compat "Julia 1.5"
577600
Using `begin` in an indexing expression to refer to the first index requires at least
578601
Julia 1.5.
@@ -591,17 +614,31 @@ function varname(expr::Expr, concretize=Setfield.need_dynamic_lens(expr))
591614
# to call `QuoteNode` on it.
592615
sym = drop_escape(sym_escaped)
593616

617+
# This is to handle interpolated heads -- Setfield treats them differently:
618+
# julia> Setfield.parse_obj_lens(@q $name.a)
619+
# (:($(Expr(:escape, :_))), :((Setfield.compose)($(Expr(:escape, :name)), (Setfield.PropertyLens){:a}())))
620+
# julia> Setfield.parse_obj_lens(@q x.a)
621+
# (:($(Expr(:escape, :x))), :((Setfield.compose)((Setfield.PropertyLens){:a}())))
622+
if sym != :_
623+
sym = QuoteNode(sym)
624+
else
625+
sym = lens.args[2]
626+
lens = Expr(:call, lens.args[1], lens.args[3:end]...)
627+
end
628+
594629
if concretize
595630
return :(
596-
$(AbstractPPL.VarName){$(QuoteNode(sym))}(
631+
$(AbstractPPL.VarName){$sym}(
597632
$(AbstractPPL.concretize)($lens, $sym_escaped)
598633
)
599634
)
600635
elseif Setfield.need_dynamic_lens(expr)
601636
error("Variable name `$(expr)` is dynamic and requires concretization!")
602637
else
603-
:($(AbstractPPL.VarName){$(QuoteNode(sym))}($lens))
638+
:($(AbstractPPL.VarName){$sym}($lens))
604639
end
640+
elseif Meta.isexpr(expr, :$, 1)
641+
return :($(AbstractPPL.VarName){$(esc(expr.args[1]))}())
605642
else
606643
error("Malformed variable name `$(expr)`!")
607644
end

0 commit comments

Comments
 (0)