Convenience macro for type type-stable re-assignment / increment?


#1

I find that expressions like

a = f(a, ...)
a += f(...)

can be a frequent source of nasty type instabilities. Nasty especially because they can lie dormant until generic code is suddenly used with a different numerical type, etc. So I typically
guard against that with something like

a = oftype(a, f(a, ...)) 

But it’s tedious to write and makes the code harder to read. It might be nice to have a macro like

@typestable a = f(a, ...)
@typestable a += f(...)

that inserts the necessary oftype in assignment-like expressions. Do we have something like this around, already?


#2

Agree. Writing n += UInt16(1), n += int_T(1), x *= Float32(2.0) all over the place is very annoying. Unfortunately, I don’t know a less annoying way.


#3

If there’s no macro like this around yet, can someone suggest a fitting package to contribute this to? Or maybe we might even want something like this in Base?


#4

You should be able to do:

function f(...)
  A = typeof(a)
  a::A = f(a, ...)
  # now no more ::A is needed
  a = g(a, ...)
  ....
end

See https://docs.julialang.org/en/stable/manual/types/#Type-Declarations-1


#5

True - it doesn’t seem very common, though, for some reason (only a few instance in the whole of Base, also not frequent in common packages). But I agree, it’s probably the safest way. Is there some drawback to it? I just wonder why one doesn’t see it more often.


#6

I guess the intersection where the programmer can determine the type easily, but the compiler can’t, is pretty rare.


#7

I’m not quite sure I understand - whether I do

a = ...
...
a += typeof(a)(...)

or

x = ...
a::typeof(x) = x
...
a += ...

it’s type inference by the compiler in both cases, right?

The situation mostly occurs with variables that need to be modified within a loop (e.g. sums), of course.


#8

Does this help?

 macro keeptype(x)
    y = x.args[1]
    z = x.args[2]
        f = z.args[1]
        a = z.args[2]
        b = z.args[3]
       return esc(:($y = $f($a, typeof($a)($b))))
end
@keeptype a = 1 + 2.0

This only works for two valued functions like +, but you could use splatting and mapping to make it more general.


#9

In the second case, there is a type assertion too.

I was referring to the case where the type is stable, it is just that the compiler can’t prove it. In my experience, this is getting quite rare in v0.7, but I tend to write short, small functions so it is possible that my style just meshes well with the compiler.