JuliaData / StructTypes.jl

Abstract definitions and convenience methods for describing, processing, and constructing Julia objects
https://juliadata.github.io/StructTypes.jl/stable/
MIT License
79 stars 22 forks source link

Use different constructors #73

Closed robertdj closed 2 years ago

robertdj commented 2 years ago

Hi

Thanks for this package -- I really enjoy it in conjunction with JSON3.

Consider a simple example of setting up a struct and a mapping.

using JSON3
using StructTypes

struct Foo
    a::Int64
    b::Int64
end

StructTypes.StructType(::Type{Foo}) = StructTypes.Struct()

This provides a way to map from JSON into Foo:

JSON3.read("{\"a\": 1, \"b\": 2}", Foo)

I would like a constructor that sets some fields based on others, like

function Foo(a)
    Foo(a, 2a)
end

Now Foo(1) returns Foo(1, 2) as expected. But I cannot figure out how to map JSON with only an "a". That is, this fails:

JSON3.read("{\"a\": 1}", Foo)
cordarei commented 2 years ago

I haven't used it, but I would guess that you could define a StructTypes.construct method for your type and have it dispatch to Foo(a) if b is nothing.

quinnj commented 2 years ago

@cordarei is correct; you'll want to overload StructTypes.construct or just provide an additional Foo(a::Int) = ... constructor.

Alternatively, you could go with the StructTypes.Mutable approach, in which case it would work like:

mutable struct Foo
    a::Int64
    b::Int64
end
Foo() = Foo(0, 0)

StructTypes.StructType(::Type{Foo}) = StructTypes.Mutable()
robertdj commented 2 years ago

Thanks for your answers @cordarei and @quinnj .

Reading about StructTypes.construct my understanding is that it is a new way to provide a constructor? That is, these two lines should do the same:

StructTypes.construct(::Type{Foo}, a::Int) = Foo(a, 2a)
Foo(a) = Foo(a, 2a)

But neither approach is working for me. The suggestion with b as nothing doesn't seem to help:

StructTypes.construct(::Type{Foo}, a::Int, b::Nothing) = Foo(a)

Can you spare a bit more time to provide an example of how to do this?

Providing multiple inner constructors and using StructTypes.Mutable stops Julia from erroring, but I don't get the expected result:

mutable struct Foo2
    a::Int64
    b::Int64

    Foo() = new()
    Foo(a) = new(a, 2a)
end

With this approach JSON3.read("{\"a\": 1}", Foo) returns Foo(1, 0).