How do you address side effects for callable types?


#1

Say there is a callable type implemented with some side effect, but the variable name can be something without !, which looks like

count_foo = 0

struct Foo
    a::Int
end

(::Foo)(x::Int) = (global count_foo; count_foo += 1; count_foo)

foo = Foo(1.0)
foo(1) # this actually has some side effect

for functions, I can just name them with ! as postfix, but what for callables? Is there any way to address this?


#2

There’s a few spots you can still put the !

struct Foo!;
  a::Int;
  end;

(::Foo!)(x) = ...

foo! = Foo!(1);
foo!(1)

#3

If you are asking about a style convention, I am not aware of any.

You could use variable names with ! to emphasize that they change the argument (eg foo! =), but in your example (::Foo)(::Int) changes a global state and ! is usually used for mutating the (first) argument. Perhaps use a different API, with a descriptive function name like increment_global_count.


#4

Thanks. Err… maybe this is a bad example, e.g

struct MulByTwo
end

(::MulByTwo)(x::AbstractArray) = # this will multiply x with 2 and assign it back, e.g x *= 2

Then is it possible to (or force my users) mark their variable name with ! or something?


#5

No. Not more than giving your singleton object a name you want by assigning it to a (constant) global variable. Note that such enforcement doesn’t even exist for “normal functions” either. Everyone is free to do f = push! and use the f that doesn’t have the ! in the name.


#6

I see. thanks! and just curious, will it be possible to ask the compiler warn people in the future when they try f = push!?


#7

The compiler will almost certainly never warn that, there’s really nothing wrong with it. An external linter could.

Note that it can also appear when you pass function as an argument to another function and there’s little you can do about naming in that case.