"Nullable" (really "Nothingable") Types

I’m curious if there’s a proper Julian way to implement optional struct fields through an effective Union{T, Nothing}.

I’ve written the macro below and it seems to work for my very simple test case. Is there a better way to achieve this? I suppose there could also be a macro for specific fields instead of the whole struct.

macro optional(block::Expr)
    struct_name = block.args[2]
    fields_expr = block.args[3].args
    fields = Expr[]
    for field ∈ fields_expr
        if typeof(field) !== LineNumberNode
            s = field.args[1]
            t = field.args[2]
            new_field = :($(s)::Union{$(t), Nothing})
            push!(fields, new_field)
        end
    end
    esc(quote
            struct $struct_name
                $(fields...)
            end
    end)
end

@optional struct A
    x::Float64
    y::Float64
end

a = A(1.0, nothing)
println(a)
1 Like

One other option would be something like

julia> macro var"?"(T)
           esc(:(Union{$T, Nothing}))
       end
@? (macro with 1 method)

julia> struct A
           x:: @? Float64
           y:: Float64
       end

julia> f(x::@? A) = x
f (generic function with 1 method)

julia> f(nothing)

julia> f(A(nothing, 1))
A(nothing, 1.0)
4 Likes

Welp, I’m sold. That does sound like the best route! I didn’t even know one can use strings to define methods like you just did… Crazy stuff. Thanks!

2 Likes

One thing to note is that the var stringmacro was introduced in version 1.3, but it’s a oneliner to define your own version:

julia> macro myvar_str(s) esc(Symbol(s)) end;

julia> myvar"a multiword variable name" = 1
1

julia> myvar"a multiword variable name"
1
2 Likes