Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Functions as infix operators without pipe?

Tags:

julia

Reading a book on Julia I found the following code example:

The author creates a type

type Student
    name::String
end

creates an instance of this type

antony = Student("Antony Miller")

and then tests the type of antony in two different ways

isa(antony, Student)
true

antony isa Student
true

This is an awesome way of using syntax to make code readable. However, how is the function isa() enabled to be used as an infix operator, here? I would have guessed that the following is possible...

antony |> isa(Student)
true

...as the pipe (|>) uses the object to the left as the first argument in the function to the right. But why is it possible to omit the pipe in the example further up? And can I use the same behavior in my own functions as well to make the code more readable (I would really like to)?

like image 502
Georgery Avatar asked Jan 26 '23 06:01

Georgery


2 Answers

As explained in this answer: User-defined infix operator, Julia has a fixed set of infix operators. You can overload the the operators, but you are not allowed to define new infix operators.

isa is in this predefined set of infix operators.

You can, however, simulate infix operators with macros (also pointed out in the linked thread). You can see an example in the DiffEq docs.

like image 119
David Varela Avatar answered Feb 07 '23 18:02

David Varela


It's possible to make the piping semantics you asked for work. Suppose we have some function of two arguments

rel_diff(x, y) = (x - y)/(x + y) 

We can define

rel_diff(y) = x -> rel_diff(x, y)

so that

julia> 1 |> rel_diff(2)
-0.3333333333333333

I don't think this is very aesthetically pleasing, but you might.


Another alternative would be this trick:

struct Infixed{X, F <: Function}
    x::X
    f::F
end

(|)(args...) = Base.:(|)(args...)
(|)(x, f::Function) = Infixed(x, f)
(|)(xf::Infixed, y) = xf.f(xf.x, y)

and now we can do

julia> 1 |rel_diff| 2
-0.3333333333333333

Note that this relies on shadowing the base definition of | so that we don't commit type piracy. This won't work in the global scope REPL if you've already used |, but it'll work if you make a new local scope like with let or inside a function body.

like image 39
Mason Avatar answered Feb 07 '23 16:02

Mason