# Is it ok to write interfaces that require field access on subtypes?

**URL:** <https://discourse.julialang.org/t/is-it-ok-to-write-interfaces-that-require-field-access-on-subtypes/98429>\
**Category:** New to Julia\
**Tags:** question, type, design, inheritance\
**Created:** [May 7, 2023, 4:04am UTC](https://discourse.julialang.org/t/is-it-ok-to-write-interfaces-that-require-field-access-on-subtypes/98429 "2023-05-07T04:04:06Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![aleferna12](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aleferna12/32/49549_2.png) [@aleferna12](https://discourse.julialang.org/u/aleferna12)\
**Post date:** [May 7, 2023, 4:04am UTC](https://discourse.julialang.org/t/is-it-ok-to-write-interfaces-that-require-field-access-on-subtypes/98429/1 "2023-05-07T04:04:06Z")

</div>

Hello!

Since we can’t define fields on abstract types (at least yet, see [this very long discussion](https://github.com/JuliaLang/julia/issues/4935)), I was wondering whether the following design pattern is considered good practice in Julia:

```julia
abstract type A end

struct B <: A
    x::Int
end

getx(a::A) = a.x

```

I’m asking because if the following struct is later defined:

```julia
struct C <: A end

```

then the call to `getx(C())` will not work, thus breaking the [Liskov substitution principle](https://blog.knoldus.com/what-is-liskov-substitution-principle-lsp-with-real-world-examples/).

Is this not a concern?

The alternative of course would be to write individual getters/setters for each struct, which quickly becomes a lot of boiler-plate code.

How one should go about this?

Cheers

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [May 7, 2023, 4:58am UTC](https://discourse.julialang.org/t/is-it-ok-to-write-interfaces-that-require-field-access-on-subtypes/98429/2 "2023-05-07T04:58:18Z")

</div>

Hi, welcome to the forum 🙂

One aspect to keep in mind is that Julia uses a functional approach with multiple dispatch, rather than object oriented programming. Therefore, some of the principles from OOP don’t translate well into Julia.

A common approach in Julia is to work with so-called interfaces. E.g. informally defined sets of methods each subtype is expected to have. You are right that this can lead to a bit more code here and there, but it brings other advantages (well explained in the video I linked below.)

It is not so common to write getter/setter methods in Julia. If you do so, often `getproperty/setproperty` are your friends. In your case, that could be that you (for yourself) make the rule that each subtype of `A` should have a well-defined behaviour for `getproperty(a, :x)`.

Notice that multiple dispatch gives you more freedom (which has advantages and disadvantages; it’s just a different approach 😉 ). Here is a quick example:

```julia
abstract type A end
two_times_x(a::A) = 2*a.x
x_squared(a::A) = a.x^2

mutable struct B <: A
    x::Int
end

mutable struct C <: A 
    x²::Int 
end

Base.getproperty(c::C, s::Symbol) = s == :x ? Int(sqrt(c.x²)) : getfield(c, s)
Base.setproperty!(c::C, s::Symbol, v) = s == :x ? (c.x² = v^2) : setfield!(c, s, v)
x_squared(c::C) = c.x²

c = C(64)
@show c.x # == 8
c.x = 9 # uses setproperty
@show c.x # == 9

b = B(8)
@show b.x # == 8

two_times_x(b), two_times_x(c) # == (16, 18)
x_squared(b), x_squared(c) # == (64, 81)

```

As you can see, you have the freedom the implement subtypes which don’t even have the field `x` but maybe just an abstract representation of the data. But externally they still behave compatible.

* * *

Some other links:

- People often recommend this talk regarding the differences between OOP and multiple dispatch: [(205) JuliaCon 2019 | The Unreasonable Effectiveness of Multiple Dispatch | Stefan Karpinski - YouTube](https://www.youtube.com/watch?v=kc9HwsxE1OY)
- There are several packages which bring OOP into Julia (e.g. [Suzhou-Tongyuan/ObjectOriented.jl: Conventional object-oriented programming in Julia without breaking Julia’s core design ideas (github.com)](https://github.com/Suzhou-Tongyuan/ObjectOriented.jl)) . However, if you are new to Julia it is recommeded to first learn what multiple dispatch does, so that you can decided well when OOP is really needed (mostly it is not needed).

---

<div class="post-metadata">

**Author:** ![aleferna12](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aleferna12/32/49549_2.png) [@aleferna12](https://discourse.julialang.org/u/aleferna12)\
**Post date:** [May 7, 2023, 5:48am UTC](https://discourse.julialang.org/t/is-it-ok-to-write-interfaces-that-require-field-access-on-subtypes/98429/3 "2023-05-07T05:48:35Z")

</div>

Hey Steffen!

Thanks for the answer, that was really helpful.

Just to wrap things up and make sure I understood what you said so I can mark your answer as a solution:

Sometimes part of the interface of a type involves getting access to one of its properties.

For example, if I have an abstract type `Vehicle` I might want to include in my interface a method `model(v::Vehicle)` that, for most concrete `Vehicles`, will just access a field in the struct. Not necessarily all concretes of `Vehicle` have this field, however.

Therefore I could define my interface as such:

```julia
abstract type Vehicle end
model(v::Vehicle) = v.model

struct Car <: Vehicle
    age::Int
    model::String
end

struct Bus <: Vehicle
    age::Int
    model::String
end

# Not worth saving the model as a property, I know its the same for every instance of the struct
struct ToyotaCorolla <: Vehicle
    age:Int
end
model(tc::ToyotaCorolla) = "Corolla"

```

Is this reasonable or should I not “rely” of the field access and instead go for:

```julia
abstract type Vehicle end

struct Car <: Vehicle
    age::Int
    model::String
end
model(c::Car) = c.model

struct Bus <: Vehicle
    age::Int
    model::String
end
model(b::Bus) = b.model

struct ToyotaCorolla <: Vehicle
    age:Int
end
model(tc::ToyotaCorolla) = "Corolla"

```

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [May 7, 2023, 5:59am UTC](https://discourse.julialang.org/t/is-it-ok-to-write-interfaces-that-require-field-access-on-subtypes/98429/4 "2023-05-07T05:59:02Z")

</div>

The first variant looks best 👍 Define a generic method that works in most cases and let special cases specialize that function.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [May 7, 2023, 2:28pm UTC](https://discourse.julialang.org/t/is-it-ok-to-write-interfaces-that-require-field-access-on-subtypes/98429/5 "2023-05-07T14:28:26Z")

</div>

This pattern,

```julia
abstract type Vehicle end
model(v::Vehicle) = v.model

```

is ok if you are the author of all the subtypes of `Vehicle`, but if other people will be extending the `Vehicle` interface with their own subtypes of `Vehicle`, then it is better to use your second option above. Julia interfaces typically do not require subtypes to have specific fields.

For instance, suppose I define my own `Vehicle` subtype like this:

```julia
struct HondaCivic <: Vehicle
    age:Int
end

```

If I forget to extend `model` for `HondaCivic`, I will get an `ERROR: type HondaCivic has no field model` when `model` is called on a `HondaCivic`. But it is more idiomatic to get a `MethodError` if a method is not implemented for a type. In this case, `model` is in fact implemented for `HondaCivic` via the `model(v::Vehicle)` default method—it just happens to be incorrectly implemented for `HondaCivic`.

Methods on abstract types make more sense if they only call generic functions on their arguments. Here’s an example:

```julia
abstract type AbstractRectangle end

area(r::AbstractRectangle) = len(r) * wid(r)

struct Rectangle <: AbstractRectangle
    length::Float64
    width::Float64
end

len(r::Rectangle) = r.length
wid(r::Rectangle) = r.width

struct Square <: AbstractRectangle
    width::Float64
end

len(s::Square) = s.width
wid(s::Square) = s.width

```

Note that I have to give concrete definitions of `len` and `wid` for each of the concrete subtypes of `AbstractRectangle`.

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [May 8, 2023, 6:29am UTC](https://discourse.julialang.org/t/is-it-ok-to-write-interfaces-that-require-field-access-on-subtypes/98429/6 "2023-05-08T06:29:01Z")

</div>

There is at least one instance in `Base` where field access is used: `OrdinalRange`

> <https://github.com/JuliaLang/julia/blob/master/base/range.jl#L835>

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 8, 2023, 9:12am UTC](https://discourse.julialang.org/t/is-it-ok-to-write-interfaces-that-require-field-access-on-subtypes/98429/7 "2023-05-08T09:12:04Z")

</div>

> [@aleferna12](#):
>
> will just access a field in the struct

Note that you are almost never doing that, unless you use `getfield` explicitly. The `.` syntax is lowered to `getproperty`, which happens to fall back to `getfield` by default. But it does not have to.

The only slightly cumbersome thing about `getproperty` is that it takes a value, so you cannot add “methods” to it. For simple cases, people use an `if` (see the manual), but if you insist you can do

```julia
Base.getproperty(x::MyType, key) = my_getproperty(x, Val(key))

my_getproperty(x::MyType, ::Val{:some_field}) = ...

```

and it will work fine.

That said,

> [@aleferna12](#):
>
> The alternative of course would be to write individual getters/setters for each struct, which quickly becomes a lot of boiler-plate code.

may just signal a broader issue with your API. If most structs are “containers”, then accessing their fields directly is fine IMO, and working around the occasional exception with `getproperty` etc is OK. But if a nontrivial type hierarchy has the same pattern and you need a lot of extras (calculating fields, checking valid values, etc), then exposing field access with a function could be an idiomatic choice. YMMV.
