What's your favorite syntactical sugar in Julia

Primes.jl looks very suspicious. :thinking:

(primes(999).^2).% 24

that’s correct, it is a fact for all primes >= 5

Squared Primes Modulo 24
Roger B. Nelsen
Mathematics Magazine, 93 (2020) pg 228
DOI: 10.1080/0025570X.2020.1736877

Julia and Primes.jl turned many problems on https://projecteuler.net/ into jokes :joy:

Since Julia 1.6 I think

a = 1
b = 2
c = 3
d = 4

f(a, b; c, d) = a+b+c+d

f(a, b; c = c, d = d)

# no need to name them with `=` if variable name is same as argument name
f(a, b; c, d) 

That’s what I do with Emacs

When reduction operators take generators:

f(k) = sum(x^2 for x in 1:k)

even better than math notation!

seems much slower than

f(k) = sum((1:k).^2)

Unfortunately, this doesn’t work with sum, but if we define our own:

julia> function mysum(f, r)
           s = zero(f(first(r)))
           for i ∈ r
               s += f(i)
           end
           s
       end
mysum (generic function with 1 method)

julia> @btime mysum(abs2, 1:$(Ref(10))[])
  1.585 ns (0 allocations: 0 bytes)
385

julia> @btime mysum(abs2, 1:$(Ref(100))[])
  1.586 ns (0 allocations: 0 bytes)
338350

julia> @btime mysum(abs2, 1:$(Ref(1000))[])
  1.588 ns (0 allocations: 0 bytes)
333833500

julia> @btime mysum(abs2, 1:$(Ref(10000))[])
  1.586 ns (0 allocations: 0 bytes)
333383335000

LLVM can optimize away the loop, so performance is independent of k.

How so?

using BenchmarkTools, BenchmarkPlots, StatsPlots
K = 10_000
bg = BenchmarkGroup()
bg[:a] = @benchmark sum((1:$K).^2)
bg[:b] = @benchmark sum(x^2 for x in 1:$K)
bg[:c] = @benchmark sum(x -> x^2, 1:$K)
plot(bg; yscale=:log10)

image

The generator method is clearly much faster (but not as fast as the two argument version).

But still about 100x slower than mysum would be at that size.

It is indeed a lot faster even than sum(f, x). I wonder what it is about sum that can’t be optimized like mysum can.

I think it’s probably harder to constant fold pairwise summation.

What about sum is pairwise? I would expect sum to do more or less the same thing as mysum

It’s a technique to increase floating-point accuracy : Pairwise summation - Wikipedia

 pkg> add BenchmarkPlots
    Updating registry at `C:\Users\Hermesr\.julia\registries\General`
    Updating git-repo `https://github.com/JuliaRegistries/General.git`
ERROR: The following package names could not be resolved:
 * BenchmarkPlots (not found in project, manifest or registry)

My bad, that’s an unmerged pr. You can clone it and add by directory if you want.

Another nice thing to do with end was given by @giordano in a Slack thread:

image

Alexander Palvin:
I wish it was also possible to store the indexing expression in a variable.

ix = min(i, end)
x[ix]  # same effect as x[min(i, end)]

@giordano , Using the package EndpointRanges.jl solved it using:

julia> using EndpointRanges

julia> using EndpointRanges: Endpoint, IndexFunction

julia> Base.min(x::Endpoint, y::Endpoint) = IndexFunction(r->Base.min(x(r), y(r)))

julia> Base.min(x::Endpoint, y::Number) = IndexFunction(r->Base.min(x(r), y))

julia> Base.min(x::Number, y::Endpoint) = IndexFunction(r->Base.min(x, y(r)))

julia> m = min(3, iend)
IndexFunction{var"#5#6"{Int64, EndpointRanges.IEnd}}(var"#5#6"{Int64, EndpointRanges.IEnd}(3, EndpointRanges.IEnd()))

julia> (1:10)[m]
3

julia> (1:2)[m]
2

@RoyiAvital that’s more an example of missing sugar though… It’s nice that it can be implemented but it would be nice to have something like this in Base.

@times it’s amazing

What package is that from?