Macro challenge: Factory for creating named tuples

I can understand the motivation, having faced the exact same issue before, but I would have a slight preference for biting the bullet and teaching about structs. I would do this because if I rely on the Julia ecosystem, students will need to interact with code that uses structs anyway—it is more or less inevitable. I agree that parametric types can be confusing in the first pass. So I would omit them initially, then later demo how it speeds up calculations.

I taught R for almost a decade to econ grad students. Since S3/S4 classes are really cumbersome, the solution for organizing values that belong together was the list(...) (or c(...)) construct, basically equivalent to a NamedTuple when all values are named. It worked in courses, then when I was supervising thesis projects, I would observe that students continue to use it to build baroque, error-prone monstrosities.

In constrast, Julia’s structures are really lightweight, with a lot of convenience macros built around them, eg Parameters.jl and similar, and Base.@kwdef. I think the trade-off boils down to making it easier to teach vs encouraging good habits that serve the subset of students who will continue to use Julia later on.

Why is the struct type unstable?

They may be referring to abstract field types.

Thinking about this, it would be great to have a

@typed struct Foo{T} <: SuperType
   abstract1::Any
   abstract2::AbstractString
   abstract3 # unspecified
   concrete::Float64
   ... # possible inner constructor
end

that expands to

struct Foo{T, A1 <: Any, A2 <: AbstractString, A3} <: SuperType
   abstract1::A1
   abstract2::A2
   abstract3::A3
   concrete::Float64
   ... # possible inner constructor
end

which

  1. works for mutable, too,

  2. with optional supertype,

  3. and generates A1, … not to clash with existing parameter or field names,

  4. handles docstrings :smile:.

sort of automagically addressing the performance hint above.

MacroTools.jl makes implementation considerably easier, but it is still a great festival of special cases :wink:

1 Like

First of all, I that is a great idea for a macro, and I think it belongs in Parameters.jl with the kw_args built into it… I see no reason to separate it. So

@with_kw_typed struct Foo{T} <: SuperType
   abstract1::Any = 2.0
   abstract2::AbstractString = "TEST!"
   abstract3 # unspecified
   concrete::Float64
   ... # possible inner constructor
end

Second, I would still have intro users focus on named tuples, and use it throughout most of the code. I know I would use it. My goal is to avoid having to teach them about defining there own types (and the mechanics of abtract types) until later in the course. This is a longer discussion, but I want people to be able to have code with the aboslute minimum of syntax beyond the mathematics… and code that matlab users can say “wow, that is shorter and even more clear than matlab”.

2 Likes

I agree that this is perfectly justifiable, especially in a course where the material is mostly self-contained and you want to minimize time devoted to CS concepts (who doesn’t? :wink:). I can’t wait for v0.7 myself, named tuples will simplify a lot of code.

Huh that’s really bizarre I have no idea why that’s happening…

Yes, I think Tamas’ idea is good; I had it too! To make it work with @with_kw something like this syntax is needed:

@with_kw struct A
       a::{<:AbstractArray}                                                                                                                                                            
       b::Int
end

I think I’d rather only allow:
@with_kw (a=1, b="test", w = w)
i.e. @with_kw only taking one argument.

It took me a while to mentally parse your suggestion correctly… Forgetting a space between the two would be a pretty common problem.

Could we allow multiple arguments and flatten one level to avoid that issue?

How about throwing a helpful error message? I think I would prefer that.

1 Like

Sure. If the error message explicitly tells people to try putting a space (as opposed to just failing to find the macro) that would be fine. Though I don’t entirely understand why we couldn’t have a forwarding version of the macro that takes that takes the variable arguments, throws them in a tuple, and calls the single argument version.

Two things on this:

  • I tried this with the NamedTuples library on v0.7 master, and I couldn’t replicate the issue. Seems to be v0.62 only?
  • I tried to modify your macro to use the built-in named tuples on v0.7, and couldn’t figure it out. Simply getting rid of the $NamedTuples.@NT did not do it, as I thought it would.