Skip to content
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

support explicit specification of the optic target #55

Merged
merged 2 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions src/sugar.jl
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,20 @@ function lower_index(collection::Symbol, index, dim)
end

function parse_obj_optics(ex)
dollar_exprs = foldtree([], ex) do exs, x
x isa Expr && x.head == :$ ?
push!(exs, only(x.args)) :
exs
end
if !isempty(dollar_exprs)
length(dollar_exprs) == 1 || error("Only a single dollar-expression is supported")
# obj is the only dollar-expression:
obj = esc(only(dollar_exprs))
# parse expr with an underscore instead of the dollar-expression:
_, optics = parse_obj_optics(postwalk(x -> x isa Expr && x.head == :$ ? :_ : x, ex))
return obj, optics
end

if @capture(ex, (front_ |> back_))
obj, frontoptic = parse_obj_optics(front)
backoptic = try
Expand Down Expand Up @@ -289,7 +303,7 @@ function setmacro(optictransform, ex::Expr; overwrite::Bool=false)
f = :($_UpdateOp($op,$val))
:($modify($f, $obj, ($optictransform)($optic)))
end
return overwrite ? :($obj = $ret) : ret
return _macro_expression_result(obj, ret; overwrite=overwrite)
end

"""
Expand Down Expand Up @@ -322,9 +336,18 @@ function insertmacro(optictransform, ex::Expr; overwrite::Bool=false)
obj, optic = parse_obj_optic(ref)
val = esc(val)
ret = :($insert($obj, ($optictransform)($optic), $val))
return overwrite ? :($obj = $ret) : ret
return _macro_expression_result(obj, ret; overwrite=overwrite)
end

_macro_expression_result(obj, ret; overwrite) =
if overwrite
@assert Meta.isexpr(obj, :escape)
only(obj.args) isa Symbol || throw(ArgumentError("Rebinding macros can only be used with plain variables as targets. Got expression: $obj"))
return :($obj = $ret)
else
return ret
end

"""
@optic

Expand Down
19 changes: 19 additions & 0 deletions test/test_core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ end
nt = (a=1,)
@reset nt.a = 5
@test nt === (a=5,)

@test_throws Exception eval(:(@reset func(x, y) = 100))
end

@testset "@set" begin
Expand Down Expand Up @@ -137,6 +139,23 @@ end
@test @set(p[2] = 1.23) === (1 => 1.23)
end

@testset "explicit target" begin
x = [1, 2, 3]
x_orig = x
@test (@set $(x)[2] = 100) == [1, 100, 3]
@test (@set $(x[2]) = 100) == 100
# these are impossible with @set without $:
@test (@set $(x)[2] + 2 = 100) == [1, 98, 3]
@test (@set $(x[2]) + 2 = 100) == 98
@test (@set first($x, 2) = [10, 20]) == [10, 20, 3]

@test_throws Exception eval(:(@reset $(x[2]) = 100))
@test_throws Exception eval(:(@reset $(x)[2] = 200))

# ensure the object itself didn't change:
@test x_orig === x == [1, 2, 3]
end


struct UserDefinedLens end

Expand Down
Loading