diff --git a/.travis.yml b/.travis.yml index d40604c..162d36c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,8 @@ os: - linux - osx julia: - - 0.6 + - 0.7 + - 1.0 - nightly notifications: email: false @@ -25,13 +26,10 @@ git: #before_script: # homebrew for mac # - if [ $TRAVIS_OS_NAME = osx ]; then brew install gcc; fi -# uncomment the following lines to override the default test script -script: - - julia -e 'Pkg.clone(pwd()); Pkg.build("Setfield"); VERSION < v"0.7-" && Pkg.add("StaticArrays"); Pkg.test("Setfield"; coverage=true)' after_success: # push coverage results to Coveralls - - julia -e 'cd(Pkg.dir("Setfield")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())' + - julia -e 'using Pkg; cd(Pkg.dir("Setfield")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())' # push coverage results to Codecov - - julia -e 'cd(Pkg.dir("Setfield")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' - - julia -e 'Pkg.add("Documenter")' - - julia -e 'cd(Pkg.dir("Setfield")); include(joinpath("docs", "make.jl"))' + - julia -e 'using Pkg; cd(Pkg.dir("Setfield")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' + - julia -e 'using Pkg; Pkg.add("Documenter")' + - julia -e 'using Pkg; cd(Pkg.dir("Setfield")); include(joinpath("docs", "make.jl"))' diff --git a/REQUIRE b/REQUIRE index a612f60..157645b 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,2 +1,2 @@ -julia 0.6 -MacroTools +julia 0.7- +MacroTools 0.4.4 diff --git a/src/Setfield.jl b/src/Setfield.jl index 62bc9bb..cbab666 100644 --- a/src/Setfield.jl +++ b/src/Setfield.jl @@ -1,22 +1,9 @@ __precompile__(true) module Setfield - -if isdefined(Base, :getproperty) - using Base: getproperty -else - const getproperty = getfield - # the following breaks type stability: - # @inline getproperty(obj, name) = getfield(obj, name) -end -if isdefined(Base, :setproperty!) - using Base: setproperty! -else - const setproperty! = setfield! - # @inline setproperty!(obj, name, val) = setfield!(obj, name, val) -end +using MacroTools +using MacroTools: isstructdef, splitstructdef include("lens.jl") include("sugar.jl") -include("macrotools.jl") include("settable.jl") end diff --git a/src/lens.jl b/src/lens.jl index e48b2ff..eb15367 100644 --- a/src/lens.jl +++ b/src/lens.jl @@ -2,11 +2,8 @@ export Lens, set, get, modify export @lens export set, get, modify -import Base: get, setindex - -abstract type MutationPolicy end -struct EncourageMutation <: MutationPolicy end -struct ForbidMutation <: MutationPolicy end +import Base: get +using Base: setindex, getproperty """ Lens @@ -74,17 +71,15 @@ Replace a deeply nested part of `obj` by `val`. See also [`Lens`](@ref). """ function set end -set(l::Lens, obj, val) = set(l,obj,val,ForbidMutation()) -modify(f,l::Lens, obj) = modify(f, l,obj,ForbidMutation()) -@inline function modify(f, l::Lens, obj, m::MutationPolicy) +@inline function modify(f, l::Lens, obj) old_val = get(l, obj) new_val = f(old_val) - set(l, obj, new_val, m) + set(l, obj, new_val) end struct IdentityLens <: Lens end get(::IdentityLens, obj) = obj -set(::IdentityLens, obj, val,::MutationPolicy) = val +set(::IdentityLens, obj, val) = val struct PropertyLens{fieldname} <: Lens end @@ -99,14 +94,8 @@ function assert_hasfield(T, field) end end -@generated function set(l::PropertyLens{field}, obj, val, m::MutationPolicy) where {field} - T = obj - M = m - if T.mutable && (M == EncourageMutation) - :(setproperty!(obj, field, val); obj) - else - :(setproperty(obj, Val{field}(), val)) - end +@generated function set(l::PropertyLens{field}, obj, val) where {field} + :(setproperty(obj, Val{field}(), val)) end @generated constructor_of(::Type{T}) where T = @@ -146,10 +135,10 @@ function get(l::ComposedLens, obj) get(l.lens1, inner_obj) end -function set(l::ComposedLens, obj, val, m::MutationPolicy) +function set(l::ComposedLens, obj, val) inner_obj = get(l.lens2, obj) - inner_val = set(l.lens1, inner_obj, val, m) - set(l.lens2, obj, inner_val, m) + inner_val = set(l.lens1, inner_obj, val) + set(l.lens2, obj, inner_val) end struct IndexLens{I <: Tuple} <: Lens @@ -157,11 +146,4 @@ struct IndexLens{I <: Tuple} <: Lens end get(l::IndexLens, obj) = getindex(obj, l.indices...) -set(l::IndexLens, obj, val, ::ForbidMutation) = Base.setindex(obj, val, l.indices...) -function set(l::IndexLens, obj, val, ::EncourageMutation) - if applicable(setindex!, obj, val, l.indices) - setindex!(obj, val, l.indices...) - else - set(l, obj, val, ForbidMutation()) - end -end +set(l::IndexLens, obj, val) = setindex(obj, val, l.indices...) diff --git a/src/macrotools.jl b/src/macrotools.jl deleted file mode 100644 index b2e4df0..0000000 --- a/src/macrotools.jl +++ /dev/null @@ -1,69 +0,0 @@ -using MacroTools - -const STRUCTSYMBOL = VERSION < v"0.7-" ? :type : :struct -isstructdef(ex) = Meta.isexpr(ex, STRUCTSYMBOL) - -function splittypedef(ex) - ex = MacroTools.striplines(ex) - ex = MacroTools.flatten(ex) - d = Dict{Symbol, Any}() - if @capture(ex, struct header_ body__ end) - d[:mutable] = false - elseif @capture(ex, mutable struct header_ body__ end) - d[:mutable] = true - else - parse_error(ex) - end - - if @capture header nameparam_ <: super_ - nothing - elseif @capture header nameparam_ - super = :Any - else - parse_error(ex) - end - d[:supertype] = super - if @capture nameparam name_{param__} - nothing - elseif @capture nameparam name_ - param = [] - else - parse_error(ex) - end - d[:name] = name - d[:params] = param - d[:fields] = [] - d[:constructors] = [] - for item in body - if @capture item field_::T_ - push!(d[:fields], (field, T)) - elseif item isa Symbol - push!(d[:fields], (item, Any)) - else - push!(d[:constructors], item) - end - end - d -end - -function combinetypedef(d)::Expr - name = d[:name] - parameters = d[:params] - nameparam = isempty(parameters) ? name : :($name{$(parameters...)}) - header = :($nameparam <: $(d[:supertype])) - fields = map(d[:fields]) do field - fieldname, typ = field - :($fieldname::$typ) - end - body = quote - $(fields...) - $(d[:constructors]...) - end - - Expr(STRUCTSYMBOL, d[:mutable], header, body) -end - -function combinefield(x) - fieldname, T = x - :($fieldname::$T) -end diff --git a/src/settable.jl b/src/settable.jl index 1368d53..32ddd0a 100644 --- a/src/settable.jl +++ b/src/settable.jl @@ -1,10 +1,9 @@ export @settable -using MacroTools: prewalk, splitdef, combinedef +using MacroTools: prewalk +using MacroTools: splitdef, combinedef +using MacroTools: splitstructdef, isstructdef, combinestructdef macro settable(ex) - if VERSION < v"0.7-" - __module__ = current_module() - end esc(settable(__module__, ex)) end @@ -87,7 +86,7 @@ function posonly_constructor(dtype::Dict)::Expr end function add_posonly_constructor(ex::Expr)::Expr - dtype = splittypedef(ex) + dtype = splitstructdef(ex) if isempty(dtype[:constructors]) ex elseif has_posonly_constructor(dtype) @@ -95,7 +94,7 @@ function add_posonly_constructor(ex::Expr)::Expr else push!(dtype[:constructors], posonly_constructor(dtype)) @assert has_posonly_constructor(dtype) - combinetypedef(dtype) + combinestructdef(dtype) end end diff --git a/src/sugar.jl b/src/sugar.jl index 716cccc..b4ea674 100644 --- a/src/sugar.jl +++ b/src/sugar.jl @@ -1,4 +1,4 @@ -export @set, @set!, @lens +export @set, @lens using MacroTools """ @@ -27,48 +27,11 @@ T(T(2, 2), 2) julia> @set t.a.b = 3 T(T(2, 3), 2) ``` -See also [`@set!`](@ref). """ macro set(ex) atset_impl(ex) end -""" - @set! assignment - -Update deeply nested parts of an object. In contrast to `@set`, `@set!` -overwrites the variable binding and mutates the original object -if possible. -```jldoctest -julia> using Setfield - -julia> struct T;a;b end - -julia> t = T(1,2) -T(1, 2) - -julia> @set! t.a = 5 -T(5, 2) - -julia> t -T(5, 2) - -julia> @set t.a = 10 -T(10, 2) - -julia> t -T(5, 2) -``` -### Warning -Since `@set!` rebinds the variable, it will cause type instabilites -for updates that change the type. - -See also [`@set`](@ref). -""" -macro set!(ex) - atset_impl(ex, :(EncourageMutation()), true) -end - function parse_obj_lenses(ex) if @capture(ex, front_[indices__]) obj, frontlens = parse_obj_lenses(front) @@ -103,7 +66,7 @@ struct _UpdateOp{OP,V} end (u::_UpdateOp)(x) = u.op(x, u.val) -function atset_impl(ex::Expr, mut=:(ForbidMutation()), rebind=false) +function atset_impl(ex::Expr) @assert ex.head isa Symbol @assert length(ex.args) == 2 ref, val = ex.args @@ -112,18 +75,15 @@ function atset_impl(ex::Expr, mut=:(ForbidMutation()), rebind=false) ret = if ex.head == :(=) quote lens = $lens - set(lens, $obj, $val, $mut) + set(lens, $obj, $val) end else op = UPDATE_OPERATOR_TABLE[ex.head] f = :(_UpdateOp($op,$val)) quote - modify($f, $lens, $obj, $mut) + modify($f, $lens, $obj) end end - if rebind - ret = :($obj = $ret) - end ret end diff --git a/test/runtests.jl b/test/runtests.jl index ba6813a..9711fee 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -20,9 +20,6 @@ using Setfield include("test_core.jl") end -@testset "macrotools" begin - include("test_macrotools.jl") -end @testset "settable" begin include("test_settable.jl") end @@ -35,10 +32,8 @@ end include("test_kwonly.jl") end -@static if VERSION < v"0.7-" @testset "QuickTypes.jl" begin include("test_quicktypes.jl") end -end end # module diff --git a/test/test_core.jl b/test/test_core.jl index e4a40c2..beea620 100644 --- a/test/test_core.jl +++ b/test/test_core.jl @@ -178,68 +178,6 @@ end @test compose(la, id) === la end -@testset "Mutability" begin - - @testset "array" begin - v_init = [1,2,3] - v = v_init - @set! v[1] = 2 - @test v_init[1] == 2 - @test v === v_init - - # Julia 0.7 with --depwarn=error: - is_deperror07 = VERSION >= v"0.7-" && Base.JLOptions().depwarn == 2 - - v = randn(3) - # @set! v[:] .= 0 # dot-call not supported - @test_broken v == [0,0,0.] - if is_deperror07 - v[:] .= 1 - else - v = @test_deprecated07 (@set! v[:] = 1; v) - end - @test v == [1,1,1.] - if is_deperror07 - v[2:3] .= 4 - else - v = @test_deprecated07 (@set! v[2:3] = 4; v) - end - @test v == [1,4,4] - # @set! v[1:2] .= 5 # dot-call not supported - @test_broken v == [5,5,4] - end - - @testset "@set vs @set!" begin - m1 = M(1,2) - m2 = @set m1.a = 10 - @test !(m2 === m1) - @test m1.a === 1 - @test m2.a === 10 - m3 = @set! m1.a = 100 - @test m3 === m1 - @test m1.a === 100 - end - - @testset "composition only mutates the innermost" begin - m_init = M(1,2) - m = m_init - @set! m.a = 10 - @test m_init.a == 10 - @test m === m_init - m_inner_init = M(1,2) - m_init = M(m_inner_init, 2) - m = m_init - @set! m.a.a = 2 - @test m.a.a == 2 - @test m === m_init - @test m.a === m_inner_init - end - - obj = (1,) - @set! obj[1] = 2 - @test obj === (2,) -end - struct A{X, Y} x::X y::Y diff --git a/test/test_macrotools.jl b/test/test_macrotools.jl deleted file mode 100644 index 2915191..0000000 --- a/test/test_macrotools.jl +++ /dev/null @@ -1,46 +0,0 @@ -using MacroTools -using Setfield: splittypedef, combinetypedef - -@testset "combinetypedef, splittypedef" begin - ex = :(struct S end) - @test ex |> splittypedef |> combinetypedef |> Base.remove_linenums! == - :(struct S <: Any end) - - @test splittypedef(ex) == Dict( - :constructors => Any[], - :mutable => false, - :params => Any[], - :name => :S, - :fields => Any[], - :supertype => :Any) - - ex = :(mutable struct T end) - @test splittypedef(ex)[:mutable] === true - @test ex |> splittypedef |> combinetypedef |> Base.remove_linenums! == - :(mutable struct T <: Any end) - - ex = :(struct S{A,B} <: AbstractS{B} - a::A - end) - @test splittypedef(ex) == Dict( - :constructors => Any[], - :mutable => false, - :params => Any[:A, :B], - :name => :S, - :fields => Any[(:a, :A)], - :supertype => :(AbstractS{B}),) - - @test ex |> splittypedef |> combinetypedef |> Base.remove_linenums! == - ex |> Base.remove_linenums! - - ex = :(struct S{A} <: Foo; S(a::A) where {A} = new{A}() end) - @test ex |> splittypedef |> combinetypedef |> - Base.remove_linenums! |> MacroTools.flatten == - ex |> Base.remove_linenums! |> MacroTools.flatten - - constructors = splittypedef(ex)[:constructors] - @test length(constructors) == 1 - @test first(constructors) == - :((S(a::A) where A) = new{A}()) |> MacroTools.flatten - -end diff --git a/test/test_quicktypes.jl b/test/test_quicktypes.jl index cdae5a8..099e8a9 100644 --- a/test/test_quicktypes.jl +++ b/test/test_quicktypes.jl @@ -1,10 +1,5 @@ module TestQuicktypes - -@static if VERSION < v"0.7-" - using Base.Test -else - using Test -end +using Test import Base: == import MacroTools @@ -64,13 +59,13 @@ end end -@qstruct Pack{T}(animals::Vector{T}) +@qstruct Pack{T, N}(animals::NTuple{N, T}) @testset "Pack" begin - x = Pack([Cat(:Tama, 1), Cat(:Pochi, 2)]) + x = Pack((Cat(:Tama, 1), Cat(:Pochi, 2))) - @set! x.animals[2].nlegs = 5 - @test x.animals == [Cat(:Tama, 1), Cat(:Pochi, 2, 5)] + x = @set x.animals[2].nlegs = 5 + @test x.animals == (Cat(:Tama, 1), Cat(:Pochi, 2, 5)) end @@ -116,10 +111,9 @@ end @qstruct Group{x}(members::x; _concise_show=true) @testset "Group" begin - x = Group([0, 1]) - - x = @set! x.members[2] = 111 - @test x.members == [0, 111] + x = Group((0, 1)) + x = @set x.members[2] = 111 + @test x.members == (0, 111) end @settable @qstruct_fp Plane1(nwheels, weight::Number; brand=:zoomba)