3

I'm working on a project where it would be very useful to be able to initialize anonymous structs (aka don't care what type they are) that have an arbitrary number of fields and values of type Any, all in one line.

Essentially I need a Dict, but with values that can be accessed using dot notation (I realize I could just use a Dict here, but I'm not the end user here).

Is there a way I could do this in Julia as it is or could I define some magical type (apparently now called a mutable struct) that has a crazy getfield() overload? I saw the PR on Github about overloading . which would also be cool, but I saw that it was likely not coming soon.

Thanks!

Joe Murphy
  • 75
  • 6

2 Answers2

3

If you're happy with your 'anonymous struct' being immutable (which I assume is the case since you use the word 'literals'), then you can simulate this with a closure, which can be constructed very straightforwardly via the let keyword:

julia> D = let a=1, b=2; () -> Any[a,b]; end
(::#1) (generic function with 1 method)

julia> fieldnames(D)
2-element Array{Symbol,1}:
 :b
 :a

julia> D()
2-element Array{Any,1}:
 1
 2

julia> D.a
1

julia> D.b
2

julia> (let a=1, b=2; () -> Any[a,b]; end).a
1

julia> (let a=1, b=2; () -> Any[a,b]; end).b
2

This works because closures capture their 'closed' variables as fields.


PS. More specifically, closures are implemented under the hood as callable types (or so I'm told :p)

PPS. For some reason, the fields in the closure are introduced in reverse order. If order matters to you (such that elements in fieldnames(D) and D() are in equivalent order), introduce the 'fields' in reverse order within the let declaration, e.g.: D = let c=3, b=2, a=1; () -> Any[a,b,c]; end.

PPPS. Alternatively, if you find this syntax ugly, you could always implement a "createAnonymousStruct" function that takes a Dictionary or something and returns such a closure

Tasos Papastylianou
  • 21,371
  • 2
  • 28
  • 57
  • Is it possible to hack it such that the variables are declared with 'const' instead of 'let' to allow mutability on the variables (even if the object itself is immutable)? I tried the "copy-paste-edit" method and it appears not to work, but I don't know if there is any work around. – Joe Murphy Jun 29 '17 at 16:12
  • Hm, I feel we're rapidly headed down [XY Problem](http://xyproblem.info/) territory here ... you started with a vague question and are now trying to steer my answer towards what you're really after, but I still don't have a clear idea what that is! :p This is why I asked if you could clarify what you envisage your end-result might look like by way of an example! There are literally dozens of things I could suggest, most of which would be irrelevant to you and we'd waste time getting to what you're after by some tedious process of elimination ... – Tasos Papastylianou Jun 29 '17 at 20:26
  • but in any case, I don't get your comment about `const` instead of `let`, they are two entirely different things. Have a look at the [let block section](https://docs.julialang.org/en/stable/manual/variables-and-scoping/#Let-Blocks-1) in the docs to see what's going on here. Also, given you plan to use these as 'anonymous' structures, I would advise against mutability in the first place (if you plan to assign to a named variable, you might as well use a named type). If you want to convert to-and-from Dicts to manipulate the fields, this can be done easily: e.g. `Dict(zip(fieldnames(D), D()))` – Tasos Papastylianou Jun 29 '17 at 20:30
  • I'd invite you to a chatroom here to figure out what your use case is and what you're after exactly and get you to re-phrase your question accordingly, but unfortunately you need rep > 20 to participate in a chatroom here. You're welcome to join `https://gitter.im/JuliaLang/julia` and chat there though. (my handle is @tpapastylianou) – Tasos Papastylianou Jun 29 '17 at 20:33
  • The context to this question is that I need to make function calls in a one-liner, and those function calls may include things that would ordinarily be called 'structs'. Unfortunately, I don't have the luxury of polluting the parent scope with whatever random type needs to be constructed to fit the structure specified. Moreover, the code running in the function call is outside of my control. Therefore, I needed something that generated a value directly (aka a literal), did not require a new type declaration for changing field names, and lastly generated an object with mutable values. – Joe Murphy Jul 01 '17 at 02:35
  • Thanks, this helps. The reason XY Problem is important is because, for instance, you mention "mutable", but as I'm confirming via the accepted answer, this wasn't a requirement (NamedTuples are not mutable). All you needed was to have a setter function, which NamedTuples do via a special `setindex` function (as opposed to, say, `D.a = 1` syntax), which returns a new object (i.e. exactly what I was suggesting you do with the above implementation by converting to a Dict and then back again). In fact I wouldn't be surprised to find that the NT macro is implemented exactly as above under the hood. – Tasos Papastylianou Jul 01 '17 at 09:07
  • See, the difference between your answer and the one marked correct is that the one marked correct will actually work in the near future ( before asking this question, I was reading the PR in the works to merge the NamedTuples functionality into base Julia). At the present, neither works perfectly, and one of the two is well documented. To be honest, I'm not super appreciating the meta discussion here of whether or not I ( the asker ) actually know what I am asking for. The answer to that is yes, I do know what I'm asking. – Joe Murphy Jul 03 '17 at 08:15
  • Does my use of "mutable" need work? Maybe, but really to me if a new object is created in memory, I don't really care, as long as I can change the values associated with a certain variable (the "appearance of mutability"). For your solution the default ways of doing that don't work, but for NamedTuples at least the method of working around that is documented and standardized. – Joe Murphy Jul 03 '17 at 08:19
  • Woah. Sorry, I wasn't trying to offend! Nor did I imply you didn't know what you were asking (what I _did_ imply was you didn't give enough info to get the answer you needed, and hence possibly wasting people's time until they "bump" on the right answer). I agree using a package is better since it is likely to be maintained, that's not the point. Neither answer supports `D.a = 1` syntax without true mutability, so if your end-user expects to do that they still can't. Your question implied this was the functionality you were after, the accepted answer shows that it wasn't. That's my point here. – Tasos Papastylianou Jul 03 '17 at 10:37
  • Sorry if that came off brusque, I appreciate the help here, it's just that sometimes (a lot of times in fact) S.O. tends to try to answer what someone thinks is "the actual problem", but in fact was not the problem which is why I didn't ask it in the first place. You definitely weren't the worst offender, by far, and your first answer was legitimately in the right direction, but the NamedTuples is getting language-level hacks so that in the relatively near future it works exactly like `D.a = 1`, if I was reading the GH discussion correctly. – Joe Murphy Jul 03 '17 at 13:10
  • Also, I just wanted to to re-mention that I came off much more brusque than I meant to and sorry about that. Every time I am sorting through saved questions I notice this one and say to myself "shoot that was a meaner than I intended". But in the spirit of the open Internet, I'll keep those comments as they are. – Joe Murphy Jul 26 '17 at 05:16
  • Hahah, don't worry about it man. It's just the magic of the internet; no offense taken. Didn't mean to rub your fur the wrong way either, sorry about that. – Tasos Papastylianou Jul 26 '17 at 12:05
2

Try the NamedTuples package for this: https://github.com/blackrock/NamedTuples.jl We are also working on adding this to the language, so it can have nicer syntax and so we can be sure it will work in every case.

Jeff Bezanson
  • 3,147
  • 23
  • 26