Roger-luo / Configurations.jl

Options & Configurations made easy.
https://configurations.rogerluo.dev/stable
MIT License
80 stars 12 forks source link

Syntax for empty tables? #11

Closed singularitti closed 3 years ago

singularitti commented 3 years ago

In TOML syntax, empty tables are allowed.

image

I am not sure how to write that. Consider I have the following code:

@option "pressures" struct Pressures
    values::AbstractVector
    unit::String = "GPa"
end

@option "volumes" struct Volumes
    values::AbstractVector
    unit::String = "bohr^3"
end

@option "trial_eos" struct TrialEos
    name::String
    parameters::AbstractVector
end

@option "fit" struct EosFittingConfig
    fixed::Union{Pressures,Volumes}
    trial_eos::Union{TrialEos,Nothing}
end

Here, fixed in EosFittingConfig is a normal usage, which means you want to either put Pressures or Volumes there. But for trial_eos you can either provide it or do nothing. In the current implementation, I can neither create one EosFittingConfig with nothing as trial_eos:

julia> EosFittingConfig(Pressures(1:10, "GPa"), nothing) |> toml 
ERROR: TOML syntax function for type `Nothing` did not return a valid TOML type but a `Nothing`
Stacktrace:
  [1] error(s::String)
    @ Base ./error.jl:33
  [2] printvalue(f::Configurations.var"#1#2"{EosFittingConfig}, io::IOBuffer, value::Nothing; sorted::Bool)
    @ TOML.Internals.Printer /Users/julia/src/julia/usr/share/julia/stdlib/v1.6/TOML/src/print.jl:42
  [3] _print(f::Configurations.var"#1#2"{EosFittingConfig}, io::IOBuffer, a::OrderedCollections.OrderedDict{String, Any}, ks::Vector{String}; indent::Int64, first_block::Bool, sorted::Bool, by::Function)
    @ TOML.Internals.Printer /Users/julia/src/julia/usr/share/julia/stdlib/v1.6/TOML/src/print.jl:88
  [4] #print#12
    @ /Users/julia/src/julia/usr/share/julia/stdlib/v1.6/TOML/src/print.jl:129 [inlined]
  [5] #toml#9
    @ ~/.julia/packages/Configurations/4UV72/src/Configurations.jl:151 [inlined]
  [6] #toml#4
    @ ~/.julia/packages/Configurations/4UV72/src/Configurations.jl:127 [inlined]
  [7] toml(io::IOBuffer, x::EosFittingConfig)
    @ Configurations ~/.julia/packages/Configurations/4UV72/src/Configurations.jl:127
  [8] sprint(f::Function, args::EosFittingConfig; context::Nothing, sizehint::Int64)
    @ Base ./strings/io.jl:105
  [9] sprint
    @ ./strings/io.jl:101 [inlined]
 [10] #toml#3
    @ ~/.julia/packages/Configurations/4UV72/src/Configurations.jl:123 [inlined]
 [11] toml
    @ ~/.julia/packages/Configurations/4UV72/src/Configurations.jl:123 [inlined]
 [12] |>(x::EosFittingConfig, f::typeof(toml))
    @ Base ./operators.jl:839
 [13] top-level scope
    @ REPL[14]:1

nor load from a TOML file

[fixed.pressures]
values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
unit = "GPa"

[trial_eos]
julia> from_toml(EosFittingConfig, file)
ERROR: alias name for multi-option Union{Nothing, TrialEos} is required
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:33
 [2] from_dict_inner(#unused#::Type{EosFittingConfig}, d::Dict{String, Any})
   @ Configurations ~/.julia/packages/Configurations/4UV72/src/Configurations.jl:306
 [3] from_dict_validate(#unused#::Type{EosFittingConfig}, d::Dict{String, Any})
   @ Configurations ~/.julia/packages/Configurations/4UV72/src/Configurations.jl:240
 [4] from_dict(::Type{EosFittingConfig}, d::Dict{String, Any}; kw::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ Configurations ~/.julia/packages/Configurations/4UV72/src/Configurations.jl:220
 [5] from_dict
   @ ~/.julia/packages/Configurations/4UV72/src/Configurations.jl:218 [inlined]
 [6] #from_toml#11
   @ ~/.julia/packages/Configurations/4UV72/src/Configurations.jl:354 [inlined]
 [7] from_toml(::Type{EosFittingConfig}, filename::String)
   @ Configurations ~/.julia/packages/Configurations/4UV72/src/Configurations.jl:351
 [8] top-level scope
   @ REPL[15]:1
[fixed.pressures]
values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
unit = "GPa"

[trial_eos]
name = "bm3"
parameters = ["1m^3", "20GPa", 2.0, "3eV"]
julia> from_toml(EosFittingConfig, file)
EosFittingConfig(;
    fixed = Pressures(;
        values = [
1, 
2, 
3, 
4, 
5, 
6, 
7, 
8, 
9, 
10, 
        ],
        unit = "GPa",
    ),
    trial_eos = TrialEos(;
        name = "bm3",
        parameters = Union{Float64, String}["1m^3", "20GPa", 2.0, "3eV"],
    ),
)
Roger-luo commented 3 years ago

I think this is because the toml converter doesn't treat Nothing as an empty field. In TOML, Nothing is equivalent to empty collection.

Roger-luo commented 3 years ago

I don't think I have time to fix it this week, so feel free to submit a PR, if you want to use it now.

Roger-luo commented 3 years ago

OK after looking into this I believe the implementation is OK, but your definition is slightly wrong

to define empty table you need to let the default value be nothing, TOML does not support null type, thus the parser is not able to convert nothing to a valid TOML type.

@option "fit" struct EosFittingConfig
    fixed::Union{Pressures,Volumes}
    trial_eos::Union{TrialEos,Nothing} = nothing
end

for from_toml you misunderstood the meaning of a type alias, the correct TOML file should be

[fixed.pressures]
values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
unit = "GPa"

[trial_eos.trial_eos]
name = "bm3"
parameters = ["1m^3", "20GPa", 2.0, "3eV"]
singularitti commented 3 years ago

Yeah, that's the strategy I'm using now. Thanks!