Make function paramaters immutable by default

If you have passed an object to the C land, you anyway can’t guarantee that it will not modify your data as discussed earlier. If it’s all Julia code, your friend can just use an immutable type, and you can make an auto conversion rule from your mutable type to immutable.

1 Like

I think these kinds of safety features have been discussed before as good to have, just not a priority. Scientific computing seems to care a lot more about speed and features first, while this kind of safety is moreso for software development. But yes, this sounds reasonable. You should make a PR :slight_smile:. I think that this along with “private” fields is a long requested but low priority item.

This option will usually do the trick.

This can affect speed as well. Lets say you have code that looks like this

@paralellize begin
  f!(a, b)
  g!(a, c)
end

If julia had a way to annotate which inputs might be modified by a function call, then a hypothetical @parallellize macro would be able to determine if f! will modify a or if it is safe to run g! in parallell to f!.

Which was exactly my point. If it can be done for one parameter to one function, then it’s only incrementally harder to have julia do it for every parameter to every function, except where the user opts out. (Making it fast is a different issue :wink: )

I probably would, if I had a few months to familiarize myself with julia internals… :slight_smile:

I could probably write a PR for a dummy feature that lets you declare method parameters as “modifiable” but does nothing to enforce the “constantness” of the remaining parameters. (The only way to tell that a parameter is “modifiable” would be to call parameter_is_modifiable(f, types) which would return a tuple of booleans.) In my opinion this would already be useful, because it would allow the above-mentioned @parallellize macro, and any function that modifies a parameter that is not declared “modifiable” would simply be considered buggy.

An easy second step would be to make the `@test´ macro make copies of non-“modifiable” mutable inputs and throw an error if it detects modification. This would break a lot of existing code, and require modification to every method declaration with an exclamation point in the function name, paving the way for using truly “constant” types.

However, as I’m not familiar with julia internals, the PR that I would write would probably cause too big a performance hit to have any chance of being accepted…

Still, in my opinion, the most important aspects of julia to get “right” are those that affect parallelization. Single core clock frequencies are not going to go much further. In the future, “fast” programming languages will be those that make it easy to run code in parallel.

One of the classic ways to contribute to Julia is to make a PR which implements most of a feature, but isn’t good code, so then someone like Jeff is motivated to make the good version of the PR using your code as a starting point :slight_smile: . Moral of the story is, a PR is always helpful.

Yeah but, some scientific computing routines (differential equations…) have many many algorithms which do not parallelize well. Massive parallelism AND single-core performance are very important in this domain.

Even if f! doesn’t modify a, it may modify the global state (say, set environment variable) that g! reads.

2 Likes

Good point, so the @parallellize macro should not be applied blindly to any block of code. But I can think of lots of situations where I know that none of the functions I use have side effects (or I don’t care about the order of side effects).

Also, (some internal field of) b might be aliasing (some internal field of) a or c, so just knowing that f! does not directly modify (some internal field of) a does not let you establish anything about parallelizability here.

Good point. So again, the hypothetical @parallellize macro cannot be applied blindly. By analogy, there are many of the current “exclamation point” functions that simply declare it to be the fault of the programmer (“the result is undefined”) if the modified parameter aliases another one.

Yes. In many cases this would be a bizarre usage of a mutating function.

Why then you need a special attribute to mark modifiable parameters?

Note, you can already do what you described like this, for example:

f!(a, b) = println("a=$a b=$b")
g!(a, c) = println("a=$a c=$c")

@parallel for func in [() -> f!(1, 2), () -> g!(1, 3)] 
    func() 
end

You can also wrap it into more convenient form with another macro if you wish.

Because that would allow me to write an @parallelize macro that would work on 99.9 % of the code that I would want it to work on. (And I wouldn’t use it on the remaining 0.1 %.) I deal with a lot of code that has no side effect, no global variables, and no parameter aliasing. (Much of it auto-generated. None of it in julia at the moment, but I’m hoping for that to change.) I can’t just run everything in @parallel. I need a macro that figures out which functions must wait for other functions to finish, given the above-mentioned properties.

But the hypothetical @parallelize macro is really not the issue here. The point is to have a programmatic way of telling which, if any, of its parameters that a function is intended to modify. This can be used for anything from syntax highlighting and data flow visualizations to unit testing. How many of the julia functions that are not intended to modify their mutable inputs currently have unit tests that verify that they really don’t?

I think I can get most of the way with a package rather than a PR (even though the syntax will be slightly clumsier). I’ll put it on my to-do list.