# Unsure about properties

**URL:** <https://discourse.julialang.org/t/unsure-about-properties/110756>\
**Category:** General Usage\
**Tags:** properties\
**Created:** [February 26, 2024, 4:10am UTC](https://discourse.julialang.org/t/unsure-about-properties/110756 "2024-02-26T04:10:46Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![sadish-d](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sadish-d/32/48058_2.png) [@sadish-d](https://discourse.julialang.org/u/sadish-d)\
**Post date:** [February 26, 2024, 4:10am UTC](https://discourse.julialang.org/t/unsure-about-properties/110756/1 "2024-02-26T04:10:46Z")

</div>

I like the syntax of properties, like that they’re not the same as fields, and that I don’t have to export them. I’m considering switching from using accessor functions to properties in a large chunk of code, but I’m not sure it’s a good idea.

Suppose I have `struct S end`.

1. Is it okay to use underscores as property names? `S()._` is currently valid syntax.

2. Should we be allowed to define properties for `Type{S}`? This would be useful in, for example, accessing parameters in parametric types (like `Array{Int64, 2}.dims`). Currently, I can define `S._` by overwriting `Base.getproperty`. The issue is, types already come with these property names `(:name, :super, :parameters, :types, :instance, :layout, :hash, :flags)` and I don’t know that it’s gaurenteed that they will not change in the future. I don’t think I can redefine `Base.getproperty` from inside another funtion, so I would need a macro? I’d need to make sure that the existing names don’t conflict the user-defined names. But then also make sure that I’m able to update the definition of properties that I myself defined.

3. What if I redefine `S.name`?

4. If I should be allowed to define properties of types (`S._`), what’s a good way to update `Base.propertynames`?

5. What happens if I don’t update `propertynames`? I figure it’s a good way to keep track of what properties a type or its subtypes is supposed to implement. Which brings me to:

6. What’s a good way to verify that a list of properties have been implemented for a type? This is my current approach:

```julia
Base.getproperty(s::S, name::Symbol) = getproperty(s, Val(name)) # dispatch on Val{name}
Base.getproperty(s, ::Val{name}) where {name} = Base.getfield(s, name) # default: pass to getfield
Base.getproperty(s, ::Val{:_}) = "underscore" # define property
hasmethod(getproperty, Tuple{S, Val{:_}}) # verify: do this for all properties

```

1. What are other things to consider when choosing between (accessor) functions and properties?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [February 26, 2024, 7:45am UTC](https://discourse.julialang.org/t/unsure-about-properties/110756/2 "2024-02-26T07:45:32Z")

</div>

> [@sadish-d](#):
>
> I’d need to make sure that the existing names don’t conflict the user-defined names

I don’t think they’ll change anytime soon, but technically those are internals, and custom field names that don’t conflict with them cannot be public because they can be broken arbitrarily.

> [@sadish-d](#):
>
> What if I redefine `S.name`?

You’d break a lot of Base code, like in reflection.jl.

> [@sadish-d](#):
>
> If I should be allowed to define properties of types (`S._`), what’s a good way to update `Base.propertynames`?

Not sure what’s better than manually mirroring `getproperty` and `propertynames` methods, maybe some slick metaprogramming to make one method’s definition redefine the other. It’s not just a matter of checking `name` literals in the method, properties are technically an [instance-wise characteristic](https://github.com/JuliaLang/julia/issues/34788#issuecomment-587138355), not one of the type. Note how `propertynames` works on instances and is documented so, while `fieldnames` works on types.

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [February 26, 2024, 9:06am UTC](https://discourse.julialang.org/t/unsure-about-properties/110756/3 "2024-02-26T09:06:43Z")

</div>

- 
  1. Sure, you can use `_` as a field name so I don’t see why you couldn’t use it as a property name. Either case is kind of user unfriendly though, since property destructuring, `(; _) = S()`, puts your field into a write-only variable.

- 2-4. Don’t mess with properties of types you don’t own. (`S` is yours, but not `typeof(S)`).
- 
  1. The most obvious consequence is that you can’t tab-complete your properties in the REPL.

- 
  1. You need to be very careful with only using `getfield` to access your real fields inside your `getproperty` and `setproperty!` implementations, to avoid performance traps.

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [February 26, 2024, 9:11am UTC](https://discourse.julialang.org/t/unsure-about-properties/110756/4 "2024-02-26T09:11:08Z")

</div>

> [@sadish-d](#):
>
> Is it okay to use underscores as property names? `S()._` is currently valid syntax.

Probably a bad idea. I’d swear there’s an issue in the context of [#24990](https://github.com/JuliaLang/julia/pull/24990) where some argue that `S()._` being allowed is a bug, an oversight from when `_` variables became forbidden before 1.0, and it might be “fixed” if #24990 ever gets merged.

> [@sadish-d](#):
>
> Should we be allowed to define properties for `Type{S}`?

I think the short answer is: don’t do it.

The more technical answer: as I understand, even if you own `S` you don’t own `Type{S}` and the usual piracy rules apply: you’re free to define `getproperty(::Type{S}, ::T)` where `T` is a type you own, but that’s largely useless, what you typically want is the case `T == String` or `T == Symbol` and that would be type piracy and it will cause problems.

---

<div class="post-metadata">

**Author:** ![sadish-d](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sadish-d/32/48058_2.png) [@sadish-d](https://discourse.julialang.org/u/sadish-d)\
**Post date:** [February 26, 2024, 2:39pm UTC](https://discourse.julialang.org/t/unsure-about-properties/110756/5 "2024-02-26T14:39:38Z")

</div>

> [@sijo](#):
>
> getproperty(::S, ::T)

Did you mean `getproperty(::Type{S}, ::T)` here? `getproperty(::S, ::Symbol)` would not be type piracy, right?

I think it’s starting to sink in that I don’t own `Type{S}` because I don’t own `Type`.

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [February 26, 2024, 2:47pm UTC](https://discourse.julialang.org/t/unsure-about-properties/110756/6 "2024-02-26T14:47:21Z")

</div>

Thanks, fixed.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [February 26, 2024, 2:54pm UTC](https://discourse.julialang.org/t/unsure-about-properties/110756/7 "2024-02-26T14:54:00Z")

</div>

You could consider a wrapper around `Type{S}` that implements the properties you are interested in.

---

<div class="post-metadata">

**Author:** ![sadish-d](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sadish-d/32/48058_2.png) [@sadish-d](https://discourse.julialang.org/u/sadish-d)\
**Post date:** [February 26, 2024, 3:06pm UTC](https://discourse.julialang.org/t/unsure-about-properties/110756/8 "2024-02-26T15:06:01Z")

</div>

Wrapper as in a wrapper function, right? (still learning programming lingo). I think that’s what I have right now, mostly to get parameters from parametric types. So if S is parametric like `S{D}`, I have things like `dims_of(::Type{S{D}}) where {D} = D`.

---

<div class="post-metadata">

**Author:** ![sadish-d](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sadish-d/32/48058_2.png) [@sadish-d](https://discourse.julialang.org/u/sadish-d)\
**Post date:** [February 26, 2024, 3:08pm UTC](https://discourse.julialang.org/t/unsure-about-properties/110756/9 "2024-02-26T15:08:44Z")

</div>

> [@GunnarFarneback](#):
>
> You need to be very careful with only using `getfield` to access your real fields inside your `getproperty` and `setproperty!` implementations, to avoid performance traps.

Could you tell me more about these performance traps? Is there even a way to access fields (not properties) except for `getfield`? I thought I could do arbitrarily large computations inside getproperty and it gets complied just like any other function.

I know that things can get recursive if I use the dot syntax inside the definition of getproperty, but I haven’t come across talk of any performance issues. Maybe you’re refering to cases where there are lots of properties with lots of `if/else` branching inside the definition of `getproperty`. I’m planning to avoid if/else altogether using dispatch as in my example above. So I would be using `getproperty` inside the definition of `getproperty` without it being recursive, and without all the branching. I’ll expand on the example here:

Say I have coordinates `x` and `y` as fields of my struct `S`.

```julia
struct S
x
y
end

```

Then, I would define

```julia
Base.getproperty(s::S, name::Symbol) = getproperty(s, Val(name)) # dispatch on Val{name}
Base.getproperty(s::S, ::Val{name}) where {name} = Base.getfield(s, name) # default: pass to getfield, works for existing fields unless explicitly overwritten. So s.x, s.y works.
Base.getproperty(s::S, ::Val{:dist}) = hypot(s.x, s.y) # define new property that's not a field
hasmethod(getproperty, Tuple{S, Val{:dist}}) # verify: do this for all properties

```

The only other issue I’ve seen mentioned namespace management, and I don’t know if I need to worry about that too much. I can’t quite see how properties would be problematic.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [February 26, 2024, 3:18pm UTC](https://discourse.julialang.org/t/unsure-about-properties/110756/10 "2024-02-26T15:18:29Z")

</div>

```julia
struct SType
    S::Type{S}
end
function Base.getproperty(ST::SType, s::Symbol)
    if s in propertynames(S)
        return getproperty(S, s)
    end
    return getproperty(ST, Val(s))
end

```

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [February 26, 2024, 3:20pm UTC](https://discourse.julialang.org/t/unsure-about-properties/110756/11 "2024-02-26T15:20:37Z")

</div>

> [@mkitti](#):
>
> ```julia
> struct SType
> S::Type{S}
> end
> 
> ```

This throws `UndefVarError: S not defined`, field annotations unknowns must be from the type definition’s `where` clause.

---

<div class="post-metadata">

**Author:** ![sadish-d](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sadish-d/32/48058_2.png) [@sadish-d](https://discourse.julialang.org/u/sadish-d)\
**Post date:** [February 26, 2024, 3:34pm UTC](https://discourse.julialang.org/t/unsure-about-properties/110756/12 "2024-02-26T15:34:10Z")

</div>

I think @mkitti assumed here that `S` and `getproperty(ST, Val(s))` are already defined. And it makes sense:

```julia
struct S
end
struct SType
    S::Type{S}
    SType() = new(S)
end
Base.getproperty(::SType, ::Val{:proper_tea}) = nothing
function Base.getproperty(ST::SType, s::Symbol)
    if s in propertynames(S)
        return getproperty(S, s)
    end
    return getproperty(ST, Val(s))
end

SType().name # pre-defind
SType().proper_tea # user-defined
SType().non_existent # error

```

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [February 26, 2024, 5:18pm UTC](https://discourse.julialang.org/t/unsure-about-properties/110756/13 "2024-02-26T17:18:57Z")

</div>

10 posts were merged into an existing topic: [Is dispatch on another type’s parameter (::NotMyType{MyType}) piracy?](https://discourse.julialang.org/t/is-dispatch-on-another-types-parameter-notmytype-mytype-piracy/110797/2)
