Skip to content

Commit

Permalink
Add @kw_only macro
Browse files Browse the repository at this point in the history
`@kw_only` behaves the same as `@with_kw` but does not declare a default
constructor when no inner constructor is found.
  • Loading branch information
frankier committed Aug 25, 2022
1 parent 029dad2 commit de85ef3
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 7 deletions.
31 changes: 24 additions & 7 deletions src/Parameters.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import Base: @__doc__
import OrderedCollections: OrderedDict
using UnPack: @unpack, @pack!

export @with_kw, @with_kw_noshow, type2dict, reconstruct, @unpack, @pack!, @pack, @consts
export @with_kw, @kw_only, @with_kw_noshow, type2dict, reconstruct, @unpack, @pack!, @pack, @consts

## Parser helpers
#################
Expand Down Expand Up @@ -326,7 +326,7 @@ macro pack_MM(varname)
end
```
"""
function with_kw(typedef, mod::Module, withshow=true)
function with_kw(typedef, mod::Module, withshow=true, allow_default=true)
if typedef.head==:tuple # named-tuple
withshow==false && error("`@with_kw_noshow` not supported for named tuples")
return with_kw_nt(typedef, mod)
Expand Down Expand Up @@ -486,18 +486,27 @@ function with_kw(typedef, mod::Module, withshow=true)
push!(args, k)
push!(kwargs.args, Expr(:kw,k,w))
end
if length(typparas)>0
tps = stripsubtypes(typparas)
innerc = :( $tn{$(tps...)}($kwargs) where {$(tps...)} = $tn{$(tps...)}($(args...)))
if allow_default
if length(typparas)>0
tps = stripsubtypes(typparas)
innerc = :( $tn{$(tps...)}($kwargs) where {$(tps...)} = $tn{$(tps...)}($(args...)))
else
innerc = :($tn($kwargs) = $tn($(args...)) )
end
else
innerc = :($tn($kwargs) = $tn($(args...)) )
if length(typparas)>0
tps = stripsubtypes(typparas)
innerc = :( $tn{$(tps...)}($kwargs) where {$(tps...)} = new{$(tps...)}($(args...)))
else
innerc = :($tn($kwargs) = new($(args...)) )
end
end
push!(typ.args[3].args, innerc)

# Inner positional constructor: only make it if no inner
# constructors are user-defined. If one or several are defined,
# assume that one has the standard positional signature.
if length(inner_constructors)==0
if length(inner_constructors)==0 && allow_default
if length(typparas)>0
tps = stripsubtypes(typparas)
innerc2 = :( $tn{$(tps...)}($(args...)) where {$(tps...)} = new{$(tps...)}($(args...)) )
Expand Down Expand Up @@ -679,6 +688,14 @@ macro with_kw(args...)
""")
end

"""
As `@with_kw` but does not declare a default constructor when no inner
constructor is found.
"""
macro kw_only(typedef)
return esc(with_kw(typedef, __module__, true, false))
end

"""
As `@with_kw` but does not define a `show` method to avoid annoying
redefinition warnings.
Expand Down
28 changes: 28 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,34 @@ const TMT1_3 = MT1_3{Int} # Julia bug https://github.com/JuliaLang/julia/issues/
@test "Field r Default: 4\n" == Markdown.plain(REPL.fielddoc(TMT1_3, :r))
@test "A field Default: sdaf\n" == Markdown.plain(REPL.fielddoc(TMT1_3, :c))

# no positional inner constructor
@kw_only struct KWO
foo
bar
end

function KWO(args...)
global outer_constructor_called = true
KWO(; foo="foo", bar="bar")
end

outer_constructor_called = false
kwo = KWO(42)
@test outer_constructor_called == true
@test kwo.foo == "foo"
@test kwo.bar == "bar"

outer_constructor_called = false
kwo2 = KWO(42, 43)
@test outer_constructor_called == true
@test kwo2.foo == "foo"
@test kwo2.bar == "bar"

outer_constructor_called = false
kwo3 = KWO(; foo=42, bar=43)
@test outer_constructor_called == false
@test kwo3.foo == 42
@test kwo3.bar == 43

# parameter-less
@with_kw_noshow mutable struct MT2
Expand Down

0 comments on commit de85ef3

Please sign in to comment.