Didn’t get applicable
much more robust over the time? Because that tells whether there is last
, after all.
Types seem to be inferred, but comparing these two outputs:
mylast(a) = applicable(lastindex, a) ? a[end] : 0;
@code_native last([1,2,3])
@code_native mylast([1,2,3])
the second case adds the following operations:
pushq %rbx
subq $24, %rsp
movq %rdi, %rbx
movabsq $jl_system_image_data, %rax
movq %rax, -24(%rbp)
movq %rdi, -16(%rbp)
movabsq $jl_f_applicable, %rax
leaq -24(%rbp), %rsi
xorl %edi, %edi
movl $2, %edx
callq *%rax
cmpb $0, (%rax)
je L99
It seems too much for something that can often find itself in hot loops…
You could make the decision at compile time
lastarg(_,x) = x
@generated mylast(a) = hasmethod(lastindex, (a,)) ? :(last(a)) : :(foldl(lastarg, a));
That’s problematic, because mylast
will not be updated if lastindex
is redefined later. GitHub - oxinabox/Tricks.jl: Cunning tricks though the julia compiler internals can do this with backedges, but this should generally be avoided, since it can cause excessive invalidations. In this particular case, you can probably just use dispatch for this.
Indeed the documentation says explicitely:
Generated functions must not mutate or observe any non-constant global state (including, for example, IO, locks, non-local dictionaries, or using
hasmethod
).
@simeonschaub how would you use dispatch here? Currently last
is defined as
last(a) = a[end]
I could define this method only for AbstractArray
, but that would not be backward compatible: the method would no longer be called for types that implement getindex
and lastindex
without deriving from AbstractArray
…
Incidentally I actually used last
for some kind of fixed point iteration here and defining it without type constraint seemingly broke my Atom IDE