@@ -32,8 +32,28 @@ x[:,1][2]
32
32
struct VarName{sym,T<: Lens }
33
33
indexing:: T
34
34
35
- VarName {sym} (indexing = IdentityLens ()) where {sym} =
36
- new {sym,typeof(indexing)} (indexing)
35
+ function VarName {sym} (indexing= IdentityLens ()) where {sym}
36
+ # TODO : Should we completely disallow or just `@warn`?
37
+ # TODO : Does this affect performance?
38
+ if ! is_static_lens (indexing)
39
+ error (" attempted to construct `VarName` with dynamic lens of type $(nameof (typeof (indexing))) " )
40
+ end
41
+ return new {sym,typeof(indexing)} (indexing)
42
+ end
43
+ end
44
+
45
+ """
46
+ is_static_lens(l::Lens)
47
+
48
+ Return `true` if `l` does not require runtime information to be resolved.
49
+
50
+ In particular it returns `false` for `Setfield.DynamicLens` and `Setfield.FunctionLens`.
51
+ """
52
+ is_static_lens (l:: Lens ) = is_static_lens (typeof (l))
53
+ is_static_lens (:: Type{<:Lens} ) = false
54
+ is_static_lens (:: Type{<:Union{PropertyLens, IndexLens, IdentityLens}} ) = true
55
+ function is_static_lens (:: Type{ComposedLens{LO, LI}} ) where {LO, LI}
56
+ return is_static_lens (LO) && is_static_lens (LI)
37
57
end
38
58
39
59
# A bit of backwards compatibility.
@@ -408,39 +428,52 @@ end
408
428
Return `vn` instantiated on `x`, i.e. any runtime information evaluated using `x`.
409
429
410
430
# Examples
411
- ```jldoctest
412
- julia> x = (a = [1.0 2.0;], );
413
-
414
- julia> vn = @varname(x.a[1, :])
415
- x.a[1,:]
416
-
417
- julia> AbstractPPL.concretize(vn, x)
418
- x.a[1,:]
431
+ ```jldoctest; setup=:(using Setfield)
419
432
420
- julia> vn = @varname(x.a[1, end][:] );
433
+ julia> x = (a = [1.0 2.0;], );
421
434
422
- julia> AbstractPPL.concretize(vn , x)
435
+ julia> AbstractPPL.concretize(@lens(_.a[1, end][:]) , x)
423
436
x.a[1,2][:]
424
437
```
425
438
"""
426
439
concretize (vn:: VarName , x) = VarName (vn, concretize (vn. indexing, x))
427
440
428
441
"""
429
- @varname(expr[, concretize] )
442
+ @varname(expr)
430
443
431
444
A macro that returns an instance of [`VarName`](@ref) given a symbol or indexing expression `expr`.
432
445
433
446
If `concretize` is `true`, the resulting expression will be wrapped in a [`concretize`](@ref) call.
434
- This is useful if you for example want to ensure that no `Setfield.DynamicLens` is used.
435
447
436
- The `sym` value is taken from the actual variable name, and the index values are put appropriately
437
- into the constructor (and resolved at runtime).
448
+ Note that expressions involving dynamic indexing, i.e. `begin` and/or `end`, will need to be
449
+ resolved as `VarName` only supports non-dynamic indexing as determined by
450
+ [`is_static_index`](@ref). See examples below.
438
451
439
452
## Examples
453
+ ### Dynamic indexing
454
+ ```jldoctest
455
+ julia> # Dynamic indexing is not allowed in `VarName`
456
+ @varname(x[end])
457
+ ERROR: UndefVarError: x not defined
458
+ [...]
459
+
460
+ julia> # To be able to resolve `end` we need `x` to be available.
461
+ x = randn(2); @varname(x[end])
462
+ x[2]
463
+
464
+ julia> # Note that "dynamic" here refers to usage of `begin` and/or `end`,
465
+ # _not_ "information only available at runtime", i.e. the following works.
466
+ [@varname(x[i]) for i = 1:length(x)][end]
467
+ x[2]
468
+ ```
469
+
470
+ ### General indexing
471
+
472
+ Under the hood Setfield.jl's `Lens` are used for the indexing:
440
473
441
474
```jldoctest
442
475
julia> @varname(x).indexing
443
- ()
476
+ (@lens _ )
444
477
445
478
julia> @varname(x[1]).indexing
446
479
(@lens _[1])
@@ -455,29 +488,42 @@ julia> @varname(x[1,2][1+5][45][3]).indexing
455
488
(@lens _[1, 2][6][45][3])
456
489
```
457
490
491
+ This also means that we support property access:
492
+
493
+ ```jldoctest
494
+ julia> @varname(x.a).indexing
495
+ (@lens _.a)
496
+
497
+ julia> @varname(x.a[1]).indexing
498
+ (@lens _.a[1])
499
+
500
+ julia> x = (a = [(b = rand(2), )], ); @varname(x.a[1].b[end]).indexing
501
+ (@lens _.a[1].b[2])
502
+ ```
503
+
458
504
!!! compat "Julia 1.5"
459
505
Using `begin` in an indexing expression to refer to the first index requires at least
460
506
Julia 1.5.
461
507
"""
462
- macro varname (expr:: Union{Expr,Symbol} , concretize :: Bool = false )
463
- return varname (expr, concretize )
508
+ macro varname (expr:: Union{Expr,Symbol} )
509
+ return varname (expr)
464
510
end
465
511
466
- varname (sym:: Symbol , concretize:: Bool = false ) =
467
- :($ (AbstractPPL. VarName){$ (QuoteNode (sym))}())
468
- function varname (expr:: Expr , concretize:: Bool = false )
512
+ varname (sym:: Symbol ) = :($ (AbstractPPL. VarName){$ (QuoteNode (sym))}())
513
+ function varname (expr:: Expr )
469
514
if Meta. isexpr (expr, :ref ) || Meta. isexpr (expr, :.)
470
515
# Split into object/base symbol and lens.
471
516
sym_escaped, lens = Setfield. parse_obj_lens (expr)
472
517
# Setfield.jl escapes the return symbol, so we need to unescape
473
518
# to call `QuoteNode` on it.
474
519
sym = drop_escape (sym_escaped)
475
520
476
- return if concretize && Setfield. need_dynamic_lens (expr)
477
- :($ (AbstractPPL. concretize)(
478
- $ (AbstractPPL. VarName){$ (QuoteNode (sym))}($ lens),
479
- $ sym_escaped,
480
- ))
521
+ return if Setfield. need_dynamic_lens (expr)
522
+ :(
523
+ $ (AbstractPPL. VarName){$ (QuoteNode (sym))}(
524
+ $ (AbstractPPL. concretize)($ lens, $ sym_escaped)
525
+ )
526
+ )
481
527
else
482
528
:($ (AbstractPPL. VarName){$ (QuoteNode (sym))}($ lens))
483
529
end
0 commit comments