How do I create syntactic sugar for Julia functions?

For example, if I have a function “f(x, y)” and want to abbreviate it with “x S y”, how can it be done?

1 Like
2 Likes

If you really want this enough that wrapping your code in a macro is worthwhile you can get it…

@withSop ...mycode   a S b...

you’d need to look for the S symbol, and convert it to S(a,b) manually.

No, that won’t work. A macro can only be applied to syntactically correct code.

1 Like

True, you’d have to do :S

From reading this thread, I conclude that some symbols are parsed as infix, but some aren’t, for example

julia> function ↑(x, y)
           return x - y + 3
       end
↑ (generic function with 1 method)

julia> 9 ↑ 2
10

julia> function f(x, y)
           return x - y + 3
       end
f (generic function with 1 method)

julia> 9 f 2
ERROR: syntax: extra token "f" after end of expression
Stacktrace:
 [1] top-level scope at REPL[10]:0

Julia parses as an infix, but not f.

I don’t know if it is possible to make Julia parse f as an infix.

It’s not possible… but using the macro approach above, if you did :f it would be parsed as “the symbol f” and then you could write your macro to convert

9 :f 2 into f(9,2)

I would need an introduction to Julia macros, because I am not very familiar with them.

That’s still not valid syntax.

hmm… my lisp background is showing through. (9 :f 2) is valid lisp, and results in a list of three items… I guess in Julia there’s no equivalent where you just have a list of values? The closes equivalent of that is a tuple (9,:f,2) and that’s no help.

is there any hope for something equivalent to the Lisp:

(my-special-rpn-calc 2 3 + 5 - 33 f )

and have it convert to f(33,-(5,+(2,3)))

You can create a string macro and then do anything at all with the contents of that string…but it’s really not worth the effort. I’d recommend just living within the set of syntax supported by the Julia parser.

3 Likes

9 f 2 is three expressions, which if you have sufficient patience perhaps you could make a macro understand, in some contexts:

julia> macro pr(exs...)
       @show exs; nothing
       end;

julia> @pr x = 9 f 2
exs = (:(x = 9), :f, 2)

julia> @pr x = (9 f 2)
ERROR: syntax: missing comma or ) in argument list

Notice that x=9 is a completely separate expression, f is not part of its right hand side.

Yes, it doesn’t appear it will be worth the effort of creating a new macro.

It looks like if you’re committed to getting a sequence of values… you could do:

@myrpncalc [ 1 2 + 3 - 55 f ]

and you’d have to fix stuff but it’d parse as an array of things

I just forked and rewrote the old InfixFunctions.jl trick to work on 1.0: https://github.com/MasonProtter/InfixFunctions.jl

julia> using InfixFunctions

julia> foo(x::Int, y::Int) = 2(x - y)//(x + y)
foo (generic function with 1 method)

julia> @infix foo
[ Info: foo has been infixified
foo (generic function with 1 method)

julia> 1 |foo| 2
-2//3

It’s a hack, but it works.

5 Likes

That’s a very nice trick, it solves my problem.

I got myself curious about this trick. I am reading the source code but I don’t seem to be understanding because I lack familiarity with macros. But here’s what I understood: “@infix foo” creates two new distinct methods for the function “Base.:|”, the first creates an InfixFunction, and the second applies an operator to the argument. So that when “1 | foo | 2” is written in the REPL, first Julia evaluates “1 | foo”, yielding an InfixFunction as output, and then applies the InfixFunction1 | foo” to “2”. Did I get it right?

macro infix(operator::Symbol)
    return quote
        $operator::Function

        function Base.:|(arg₁, infix::typeof($operator))
            return $InfixFunction(arg₂ -> infix(arg₁, arg₂))
        end

        Base.:|(infix::typeof($operator), arg₂) = infix(arg₂)

        @info "$($operator) has been infixified"

        $operator
    end |> esc
end

Fortunately Julia already supports infixing for a large subset of Unicode characters. All I need to do is suffix those characters with modifying marks and I’m done! Here’s the quote from the documentation

“Most of the Unicode infix operators (in category Sm), such as ⊕, are parsed as infix operators and are available for user-defined methods (e.g. you can use const ⊗ = kron to define ⊗ as an infix Kronecker product). Operators can also be suffixed with modifying marks, primes, and sub/superscripts, e.g. +̂ₐ″ is parsed as an infix operator with the same precedence as +.”

By the way, why is it possible to subscript some characters but not others? For example, why does typing “+\_x-tab” in the REPL work but “+\_z-tab” doesn’t?

Yep, you got it.

Yeah, that’s the best way to go about this in general.

That’s just because Unicode doesn’t cover the whole alphabet for subscripts and superscipts, despite having every emoji you could imagine. Here’s a wiki page on what’s available: https://en.wikipedia.org/wiki/Unicode_subscripts_and_superscripts

It’s pretty disappointing and not really something we can change on our own, but there has been some thought before about submitting a proposal to the Unicode consortium to fix this: https://github.com/stevengj/subsuper-proposal but that proposal is currently laying dormant as far as I’m aware.

2 Likes