# Method of struct

**URL:** https://discourse.julialang.org/t/method-of-struct/89458
**Category:** General Usage
**Tags:** proposal
**Created:** [October 28, 2022, 5:34pm UTC](https://discourse.julialang.org/t/method-of-struct/89458 "2022-10-28T17:34:39Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![duan](https://avatars.discourse-cdn.com/v4/letter/d/a9adbd/32.png) [@duan](https://discourse.julialang.org/u/duan)
#### Post date: [October 28, 2022, 5:34pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/1 "2022-10-28T17:34:40Z")

</div>

I’ve being using Julia for less than a year, and I appreciate how multi-dispatch makes codes efficient. Meanwhile, I also miss the methods that come with Python or C++ classes. I know that a method `obj.func(...)` in Python is really `func(obj, ...)`, and I understand the argument of not treating the first argument as something special. However, the first argument can be special sometimes. For example, the first argument of `map()` is hidden in the following example:

```julia
map(1:10) do x 
    2x 
end

```

In other situations, e.g. `dict.haskey(key)` is slightly easier to write and read than `haskey(dict, key)`. Object methods also allow the construction of long but readable expressions like those in JavaScript.

I’m sure there’re many implementation details I’m ignorant of. But I just can’t help wondering if it’s possible to have Julia compile `obj.func(...)` to `func(obj, ...)` automatically if the latter definition exists?

---

<div class="post-metadata">

### Author: ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)
#### Post date: [October 28, 2022, 5:39pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/2 "2022-10-28T17:39:22Z")

</div>

TLDR no.

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [October 28, 2022, 5:46pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/3 "2022-10-28T17:46:05Z")

</div>

You can read this long, and still active, thread on the subject:

> [@Allowing the object.method(args...) syntax as an alias for method(object, args ...)](https://discourse.julialang.org/t/allowing-the-object-method-args-syntax-as-an-alias-for-method-object-args/62051):
>
> I started exploring Julia very recently and I like it. I’ve been chatting about it with colleagues. Many people coming from OOP languages (Python…) like the syntax object.method(a,b,c…). Since you can get a preview of the available methods acting on object with tab press. Would it be possible to have that in Julia (I’m assuming it’s not already there) to improve usability? Let say I define a IIR filter struct mutable struct IIRfilter state; alpha; IIRfilter(state,alpha) = 0\<=alpha\<=1 ? …

But the short answer is that the exact thing you suggest will not happen. Perhaps some improved piping syntax is possible.

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [October 28, 2022, 5:49pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/4 "2022-10-28T17:49:07Z")

</div>

🤣 I thougt Oscar was providing a very comprehensive answer, so I decided to keep quiet…

Since he didn´t:

There are actually implementations of OO-style programming in Julia: [GitHub - Suzhou-Tongyuan/ObjectOriented.jl: Conventional object-oriented programming in Julia without breaking Julia's core design ideas](https://github.com/Suzhou-Tongyuan/ObjectOriented.jl)

And there are some specific situations in which it is convenient to make the objects contain the functions, such that the syntax `object.f(...)` works for that function.

However, probably the simplest answer is that if you stick to Julia you will sooner or later get used to it. Sometimes the OO style seems nicer, sometimes is the contrary. What one misses from the OO style is the autocompletion, but not the clarity of the syntax itself.

---

<div class="post-metadata">

### Author: ![dfdx](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dfdx/32/120_2.png) [@dfdx](https://discourse.julialang.org/u/dfdx)
#### Post date: [October 29, 2022, 12:48pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/5 "2022-10-29T12:48:28Z")

</div>

As others suggested, new users typically get used to the functional style of Julia pretty quickly. In some cases, e.g. when experimenting in REPL, I also use `|>` operator to chain the calls, e.g.:

```julia
x = 2.0
x |> sin |> cos |> tanh

```

It’s convenient because you don’t need to move the cursor to the beginning of the line and count opening and closing parentheses. Yet, for multiarg functions it’s pretty verbose:

```julia
x |> sin |> cos |> x -> x + 1

```

This syntax is also less readable, so I rarely use it in a package code, preferring normal call notation:

```julia
x = sin(x)
x = cos(x)
x = x + 1

```

So in 99% of cases it makes code better and more idiomatic. But in some rare cases you really, really want the OO-style dot syntax for function chaining. I faced such a case during work on [Spark.jl](https://github.com/dfdx/Spark.jl/), which mimics PySpark API. PySpark uses function chaining a lot! For example, creating a Spark session looks like this:

```python
spark = SparkSession.builder.appName("Main").master("local").getOrCreate()

```

Splitting it into 4 lines of code (+1 line for every custom config) would look cumbersome! Also, since we mimic a Python library, we want an easy way to translate thousands of Python examples into Julia.

To support this (again, pretty specific) case, I wrote a simple macro [`@chainable`](https://github.com/dfdx/Spark.jl/blob/main/src/chainable.jl) which overloads struct’s `getproperty()` method in such a way that calling `obj.f(args...)` is translated into `f(obj, args...)`. For example:

```julia
julia> struct Point x; y end

julia> plus_x(p1::Point, p2::Point) = Point(p1.x + p2.x, p1.y)
plus_x (generic function with 1 method)

julia> mul_y(p::Point, val::Real) = Point(p.x, p.y * val)
mul_y (generic function with 1 method)

julia> p1 = Point(1.0, 2.0)
Point(1.0, 2.0)

julia> p2 = Point(3.0, 4.0)
Point(3.0, 4.0)

julia> p1.plus_x(p2).mul_y(5)
Point(4.0, 10.0)

```

* * *

> **\`@chainable\` implementation**
>
> ```julia
> """
> DotChainer{O, Fn}
> 
> See `@chainable` for details.
> """
> struct DotChainer{O, Fn}
> obj::O
> fn::Fn
> end
> 
> # DotChainer(obj, fn) = DotChainer{typeof(obj), typeof(fn)}(obj, fn)
> 
> (c::DotChainer)(args...) = c.fn(c.obj, args...)
> 
> """
> @chainable T
> 
> Adds dot chaining syntax to the type, i.e. automatically translate:
> 
> foo.bar(a)
> 
> into
> 
> bar(foo, a)
> 
> For single-argument functions also support implicit calls, e.g:
> 
> foo.bar.baz(a, b)
> 
> is treated the same as:
> 
> foo.bar().baz(a, b)
> 
> Note that `@chainable` works by overloading `Base.getproperty()`,
> making it impossible to customize it for `T`. To have more control,
> one may use the underlying wrapper type - `DotCaller`.
> """
> macro chainable(T)
> return quote
> function Base.getproperty(obj::$(esc(T)), prop::Symbol)
> if hasfield(typeof(obj), prop)
> return getfield(obj, prop)
> elseif isdefined(@ __MODULE__ , prop)
> fn = getfield(@ __MODULE__ , prop)
> return DotChainer(obj, fn)
> else
> error("type $(typeof(obj)) has no field $prop")
> end
> end
> end
> end
> 
> function Base.getproperty(dc::DotChainer, prop::Symbol)
> if hasfield(typeof(dc), prop)
> return getfield(dc, prop)
> else
> # implicitely call function without arguments
> # and propagate getproperty to the returned object
> return getproperty(dc(), prop)
> end
> end
> 
> ```

---

<div class="post-metadata">

### Author: ![duan](https://avatars.discourse-cdn.com/v4/letter/d/a9adbd/32.png) [@duan](https://discourse.julialang.org/u/duan)
#### Post date: [October 29, 2022, 2:23pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/6 "2022-10-29T14:23:35Z")

</div>

Thank you @DNF @lmiq @dfdx for the explanations and helpful suggestions! I’m glad that it’s not me being stupid or ignorant, but there’re serious discussions and even implementations of similar ideas.

Again, I can understand why Julia developers may not want to implement this idea. But many people like (syntax) sugar for a reason, and the same question will keep popping up as Julia attract more people 😀

---

<div class="post-metadata">

### Author: ![dfdx](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dfdx/32/120_2.png) [@dfdx](https://discourse.julialang.org/u/dfdx)
#### Post date: [October 29, 2022, 4:31pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/7 "2022-10-29T16:31:34Z")

</div>

Please note that although object-oriented syntax may be convenient in some specific cases, the default **functional style is still better suited for most situations**. A notable example is high-order functions that usually take _another function_ as its first argument:

```julia
broadcast(sin, x)
# or simply
sin.(x)

```

or functions with no single “main” object:

```julia
zip(xs, ys)

```

or symbolic operators:

```julia
1 - x

```

etc.

In general, functional syntax is much more flexible then the dot notation, and it’s worth to get used to it before searching for alternatives.

---

<div class="post-metadata">

### Author: ![duan](https://avatars.discourse-cdn.com/v4/letter/d/a9adbd/32.png) [@duan](https://discourse.julialang.org/u/duan)
#### Post date: [October 29, 2022, 5:32pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/8 "2022-10-29T17:32:46Z")

</div>

Your point is well taken. But at the same time, it’s a reality that the first argument is special in many methods, and I hope Julia will eventually do something about it.

As far as I’m concerned, I don’t plan to use any third-party implemented syntax sugar because I came to Julia for performance, or I’d use Python.

---

<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: [October 29, 2022, 5:51pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/9 "2022-10-29T17:51:17Z")

</div>

It’s worth at least checking out some of the piping packages that are in the ecosystem. I would recommend one of the following.

> **[Alexander Plavin / DataPipes.jl · GitLab](https://gitlab.com/aplavin/DataPipes.jl)**
>
> GitLab.com

> **[GitHub - jkrumbiegel/Chain.jl: A Julia package for piping a value through a...](https://github.com/jkrumbiegel/Chain.jl)**
>
> A Julia package for piping a value through a series of transformation expressions using a more convenient syntax than Julia's native piping functionality. - GitHub - jkrumbiegel/Chain.jl: A Jul...

---

<div class="post-metadata">

### Author: ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)
#### Post date: [October 29, 2022, 8:00pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/10 "2022-10-29T20:00:04Z")

</div>

It’s funny how the dot seems to be hardwired/required for that syntax. With function chaining via `|>` and some overloads of partially applied ones, almost the same effect can be achieved:

```julia
plus_x(p2) = p1 -> plus_x(p1, p2)
mul_y(val) = p -> mul_y(p, val)

```

and then assuming the above definitions together with the original example

```julia
julia> p1 |> plus_x(p2) |> mul_y(5)
Point(4.0, 10.0)

```

Fun fact: In Haskell the dot denotes function composition and together with its automatic currying, we get a rather similar looking syntax

```haskell
ghci> data Point = Point { x::Int, y::Int } deriving Show
ghci> plus_x p2 p1 = Point (x p1 + x p2) (y p1)
ghci> mul_y val p = Point (x p) (val * y p)
ghci> let p1 = Point 1 2; p2 = Point 3 4 in mul_y 5 . plus_x p2 $ p1
Point {x = 4, y = 10}

```

which reads from right-to-left though.

---

<div class="post-metadata">

### Author: ![duan](https://avatars.discourse-cdn.com/v4/letter/d/a9adbd/32.png) [@duan](https://discourse.julialang.org/u/duan)
#### Post date: [October 29, 2022, 9:44pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/11 "2022-10-29T21:44:53Z")

</div>

It’s enlightening to see how OOP chaining can be implemented in Julia in many ways. However, this is not my point. The fact that so many people are glued on to the dot syntax shows how natural this syntax is. Indeed, there’re a large class of functions that just seem naturally belong to their first argument. For example, `dict.haskey(key)` just reads and writes better than `haskey(dict, key)` for most people because Julia is a language that mostly reads from left to right. Accidentally, the dot syntax also provides a natural way to chain such methods together. This feature, of course, can be abused. But one can only teach not force people to code nicely.

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [October 30, 2022, 12:00am UTC](https://discourse.julialang.org/t/method-of-struct/89458/12 "2022-10-30T00:00:01Z")

</div>

> [@duan](#):
>
> functions that just seem naturally belong to their first argument. For example, `dict.haskey(key)` just reads and writes better than `haskey(dict, key)`

I don’t think that’s a good example. I don’t find  
the OO better and don’t think that `haskey` belongs to the dictionary.

There are other examples in which I do feel that the function belongs to the object, like

```julia
person.eat("apple")

```

because the function is an action of the object and modifies it somehow.

In the dict example, the functional form extends naturally to asking if two dicts have the same key (`haskey.(dict_collection, key)`). Asking two people to eat the same apple is less natural.

Thus, I do think sometimes one style is more natural than the other. But people being used to one of those makes a great deal for what feels natural to begin with.

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [October 30, 2022, 12:14am UTC](https://discourse.julialang.org/t/method-of-struct/89458/13 "2022-10-30T00:14:47Z")

</div>

Correct me if I’m wrong, but I don’t think this works in Python either.

`obj.foo(x)` isn’t syntactic sugar for `foo(obj, x)` but for something like `class_of(obj).foo(obj, x)`. If you try `obj.bar(x)` for an ‘ordinary’ function `bar`, it won’t work. You could ask yourself ‘why not’, but I think it’s that it would not make sense. `obj.foo` is meant for accessing member fields, properties, functions of `obj`, not for the kind of transformation you are thinking of.

Julia objects can also keep functions in their fields, but mostly functions are of the _other_ type, and, just like in Python, require the `bar(obj, x)` call syntax.

`obj.foo` is for accessing something that _belongs_ to an object, I think the proposed syntax is incompatible with that view.

---

<div class="post-metadata">

### Author: ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)
#### Post date: [October 30, 2022, 1:11pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/14 "2022-10-30T13:11:33Z")

</div>

> [@duan](#):
>
> As far as I’m concerned, I don’t plan to use any third-party implemented syntax sugar because I came to Julia for performance, or I’d use Python.

“Extended piping” syntax implemented in packages doesn’t bring any performance regressions - it amounts to executing the same code as you’d manually write. For example:

```julia
julia> using DataPipes

julia> @macroexpand @p [1, 2, 3] |> filter(_ > 1) |> __.+ 1
# here I just manually removed comments and added readable variable names
let
    A = [1, 2, 3]
    B = filter(((a,)->begin
                    a > 1
                end), A)
    B .+ 1
end

```

---

<div class="post-metadata">

### Author: ![duan](https://avatars.discourse-cdn.com/v4/letter/d/a9adbd/32.png) [@duan](https://discourse.julialang.org/u/duan)
#### Post date: [October 30, 2022, 1:58pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/15 "2022-10-30T13:58:38Z")

</div>

> “Extended piping” syntax implemented in packages doesn’t bring any performance regressions - it amounts to executing the same code as you’d manually write.

What you said may be true. But I’m really not here for data piping or method chaining which is an accidental feature of the dot syntax 🙂

---

<div class="post-metadata">

### Author: ![duan](https://avatars.discourse-cdn.com/v4/letter/d/a9adbd/32.png) [@duan](https://discourse.julialang.org/u/duan)
#### Post date: [October 30, 2022, 2:11pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/16 "2022-10-30T14:11:46Z")

</div>

> Correct me if I’m wrong, but I don’t think this works in Python either.

I can’t claim to be a Python expert. But as you said, `obj.foo(x)` can be seen as a syntax sugar for `cls.foo(obj, x)`, where `foo()` is a function defined in the class name space `cls` of `obj`.

Obviously, I’m not a Julia expert either. But I like Julia’s approach of not defining `foo()` in the class / struct name space so that `foo()` can be overloaded / multi-dispatched for different types. Maybe this is an obstacle to the dot syntax? I don’t know. All I’m saying is that the syntax `obj.foo(args…)` seems natural for a large class of functions.

---

<div class="post-metadata">

### Author: ![duan](https://avatars.discourse-cdn.com/v4/letter/d/a9adbd/32.png) [@duan](https://discourse.julialang.org/u/duan)
#### Post date: [October 30, 2022, 2:31pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/17 "2022-10-30T14:31:36Z")

</div>

> In the dict example, the functional form extends naturally to asking if two dicts have the same key (`haskey.(dict_collection, key)` ).

How about `dict_collecgtion..haskey(key)` as the sugar syntax for `haskey.(dict_collection, key)` 🙃

> Thus, I do think sometimes one style is more natural than the other. But people being used to one of those makes a great deal for what feels natural to begin with.

I agree. Maybe one would have to write `mul(x, y)` in Julia had we not all learned `x * y` in elementary school 😀

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [October 30, 2022, 2:56pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/18 "2022-10-30T14:56:18Z")

</div>

> [@duan](#):
>
> I agree. Maybe one would have to write `mul(x, y)` in Julia had we not all learned `x * y` in elementary school 😀

Sure we would. And it would be more natural than `a.mul(b)` .

---

<div class="post-metadata">

### Author: ![duan](https://avatars.discourse-cdn.com/v4/letter/d/a9adbd/32.png) [@duan](https://discourse.julialang.org/u/duan)
#### Post date: [October 30, 2022, 3:31pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/19 "2022-10-30T15:31:42Z")

</div>

> Sure we would. And it would be more natural than `a.mul(b)` .

In this case, `a.mul(b)` is less natural than `mul(a, b)` because `a` isn’t more special to `mul()` than `b` (although it could be useful in chaining / piping). But once we’ve learned `a * b`, most people would prefer it over `mul(a, b)`. Similarly, many people are glued on to the dot syntax once they’ve learned it because many functions do treat the first argument differently.

---

<div class="post-metadata">

### Author: ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)
#### Post date: [October 30, 2022, 3:37pm UTC](https://discourse.julialang.org/t/method-of-struct/89458/20 "2022-10-30T15:37:57Z")

</div>

The situation is julia is much more consistent than eg in python. In python, some functions are methods and called with dots, some are freestanding functions such as `len`/`map`/… or a lot of `numpy.xxx`/`scipy.xxx` functions - without much (if any) intuition for any individual case.  
Just try using instruments that julia gives you for some time, and probably you won’t miss the dot notation anymore. Note that autocomplete is independent to the dot vs piping syntax and is a matter of tooling - some improvements are happening there right now.

[Next page](https://discourse.julialang.org/t/method-of-struct/89458.md?page=2)
