LaVOZs

The World’s Largest Online Community for Developers

'; julia - Functions as infix operators without pipe? - LavOzs.Com

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)?

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.

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.

Related
How to make user defined function descriptions (“docstrings”) available to julia REPL?
How do I pass an enumerated function argument in Julia?
Is it possible to create types in Julia at runtime?
How can I add a method to an existing function in Julia?
map, reduce with `|>` in julia
How to pass functions as arguments to other functions in Julia without sacrificing performance?
Calling a C function from Julia and passing a 2D array as a pointer of pointers as argument
Creating an analogue of Haskell's Either type in Julia
How to easily check the implementation of embeded functions in Julia language?
Julia scoping specifics: defining closure within loop