Mapping a 'getter'

Hi

Is there a way to use a “getter” as a function? But without defining a “getter”.
Example

struct Person
    name
    age
end

This gets me a type. Now suppose I have

ps = [Person("John", 12), Person("Alice", 14)]

I would like to map the age over ps and get something like

map(*age*, ps)

I.e., is there some “function” that allows me to get to the slot, without having to define a “getter”?

Now you know where I am coming from :slight_smile:

Cheers

Marco

The easiest thing here is probably an anonymous function. It’s only slightly longer than what you’d hoped for:

map(p -> p.age, ps)
6 Likes

also nice sometimes is broadcasting getfield:

getfield.(ps,:age)
5 Likes

Thank you both.

Now, what about automagically creating a function (a… reader) when you create a struct?

We know it can be done :wink:

messing around with “automagic” (macros) is not neccesary for beginners or even most advanced users. Consider you are free to write the mundane and powerful thing

reader(vec,symb) = (getfield(p,symb) for p in vec)

then you can do things like

total_ages = sum(reader(ps,:age))

but using a bit more characters to write sum(p.age for p in ps) is probably preferred to readability.

If you are attached to the fuzzy feeling of vectorized code (note there’s really no need, the for loops in julia are fast, but I get it!) there are some cool packages like Transducers.jl that are especially powerful.

2 Likes

Another option is to use the Broadcasted Field Fetcher:

julia> bff(w) = Base.Fix1(broadcast, Base.Fix2(getfield, w))
bff (generic function with 1 method)

julia> ps |> bff(:age)
2-element Vector{Int64}:
 12
 14

bff can be your best friend forever (or until some future breaking Julia version).

3 Likes

What would this function do exactly? It’s not clear to me from the context and that isn’t valid Julia code.

Hi

you can do things like map(age, persons).

Of course you can write

age(p :: Person) = p.age

I guess I am asking why this was not considered, given that is has been done before.

Yes. Thanks.

… and yet, having a (automagically created) function named like the field would, IMHO (and not only mine), make life easier. There are non-Julia precedents.

I guess I will live with the x -> x.field functions.

All the best

MA

There’s no good reason to do that when the dot syntax and the underlying getfield and getproperty already exist, and there’s a good reason to avoid doing that: although Julia lacks formal access modifiers, something similar is done by getter/setter methods being public API while the fields remain internal details that can change in minor revisions. Typically the getters and some of the types’ fields will diverge in names and structure. That’s not done for every type; fields or derived properties may be exposed as public API and idiomatically accessed by dot syntax.

3 Likes

But what’s wrong with getfield.(persons, :age)? It seems to what you are looking for, except better, because it doesn’t pollute the namespace with a function.

Field names are supposed to be internal and ‘hidden’, producing functions that ‘steal’ those names seems really bad to me.

1 Like

Pardon me. But there’s plenty of very good people who thought that that (having functions as field/slot readers) was a good idea.

And it still is.

Now. It’s not there in Julia? Ok. No biggie. But it is still a good idea. :smirk:

Cheers

MA

Is it idiomatic in any language to automatically generate getter and setter methods for all fields like you’re suggesting? From what I’ve seen, getters and setters are manually implemented to do more than just expose fields ie validation or derivation, and automatically exposing all fields goes against encapsulation principles. Dot syntax is used for simple access of fields, and only the public fields in languages with access modifiers.

2 Likes

But what if you really don’t want such a function to be automagically generated. Then it would be a bad idea, don’t you agree? I want internal names to be hidden. Isn’t it more reasonable that you opt in to this behavior when you need it, by defining a function?

2 Likes

Sure. I am all for choice.

Oh yes. It is idiomatic in at least another language. The one with even better macros than Julia (look ma, no ‘@’), many implementations and some very good compilers. :grinning:

In any case, I can live with x -> x.field.

Cheers
MA

Have only seen that in Haskell, but some other functional languages might also do that. Clojure has a clever compromise in that symbols are callable and look themselves up in a struct, i.e., (:fieldname obj) instead of obj.fieldname or fieldname(obj).

1 Like

I won’t make claims about which language has better macros, but I can let you know that the @ naming convention was deliberately chosen to visually distinguish macros. They’re not a necessary evil, but a design choice. So presumably, the devs thought this was, actually, better.

(Amusingly, and incidentally, biking without your hands is actually a bad idea.)

1 Like

Haskell is good. Clojure, as nice as it is with its concurrency, a little less so. OTOH we know where Clojure come from. :blush:

Well, I had a “I am missing something with Julia macros” moment a couple of weeks ago, but maybe I just need to grok them better.

As per the devs, I have horror stories (well, maybe just comedy ones) about devs being quite wrong. Again, the ‘@’ is also no biggie: another thing I can live with it. I wouldn’t be here otherwise.

Cheers
MA