Pass by ref for inline functions?

Pass by reference parameters can be represented with the mutable property references:

var x: Double = 3.0
var y: Double = 4.0

fun main(args: Array<String>) {
    normalize(::x, ::y)

    println("x=$x, y=$y")
}

inline fun normalize(xRef: KMutableProperty0<Double>, yRef: KMutableProperty0<Double>) {
    val invLen = 1.0 / Math.hypot(xRef.get(), yRef.get())

    xRef.set(xRef.get() * invLen)
    yRef.set(yRef.get() * invLen)
}

However currently there are a couple of issues with that approach:

  • property references are not supported for local variables, though this limitation may be lifted later;
  • generated bytecode includes a lot of calls to the mutable property reference objects, though the inliner could optimize these references away and work directly with the properties themselves.
2 Likes

You’re still projecting, my friend…

This uncle of yours is full of certainty and big words, but then presents an example, which is not functional at all, with those three ugly vars and ten perverted assignments in there. It’s definitely cleaner than the other one, and I like clean code as much as anyone, but I find calling that “semi-functional” to be quite funny.

That can’t happen, because it’s not a practice. It’s a language feature, like a tool in a toolbox. If you don’t think it’s appropriate for the work you’re doing, then by all means don’t use it.

How come so many other languages support pass-by-ref, if it’s so abhorrent? I guess we were lucky their designers didn’t ask for your infinite votes :slight_smile:

1 Like

Thanks for your feedback.

At the call site, it’s pretty good looking, but the function definition is way more syntactically noisy compared to the ref version…

It also seems to me that implementing ref would only require a small tweak to the renaming algorithm part of the inliner, whereas those two issues seem significantly more complex to solve…

1 Like

Ignoring the juvenile ad hominem attack.

You don’t know who Uncle Bob is and you think you have any credibility on good design?

As I said, it is “Not the best talk on the subject” primarily due to his poor example. But on the conceptual level he was spot on. And it is possible to have functions that mutate internal state (e.g. their own mutable variables and for the function to still be a pure function.

And a language feature that should not exist. To be perfectly precise it is not pass by reference that is the problem but mutating the state of the reference. For example, some functional languages have pass by reference, but do not allow state mutation so it doesn’t really matter.

And unfortunately, it is not as simple as if you don’t like it don’t use it. If it is there it will be used, encouraging the bad practices of the past.

C had raw pointers, why did they include them if they are so abhorent? Should they be included in modern languages just because some other language designer made the bad choice? No, we learn from the mistakes of the past and don’t keep repeating those mistakes just because some one else did it.

In the case of C#, probably mostly to support similar calls to the C/C++ code.

Yup, and it’s also possible for those functions to be fairly long, and having pass-by-ref would offer a very handy tool for breaking them into pieces… I imagine that in many cases the main function could then be much shorter and a sort of an overview of what’s going on, and its pieces would sit next to it, each doing its own thing…

Or, in other cases, it’d be much easier to avoid both useless boilerplate code and copy/pasting certain chunks of code, where now you need to pick one or the other…

Now please explain how having the option to increase readability and/or improve DRYness will encourage bad practices, to me it seems quite the opposite.

You keep on repeating this, but offer no argument except for supposed proliferation of bad practices somehow vaguely related to mutable state. But what I’m proposing would introduce absolutely no new mutable state even when used - the vars passed by reference would have to already be vars…

Again, you just make the claim that they were a mistake, yet provide no argument for it. I think they’re a pretty good feature, considering all the useful software that has been written in C, including the JVM itself, as far as I know… Without pointers, C would be totally crippled (probably to the point of being useless), and you and I probably couldn’t be having this fine discussion.

Interesting, so we can actually write a generic swap function in Kotlin which works with scalar properties:

import kotlin.reflect.KMutableProperty0

var x = 3.0
var y = 4.0

var s = "abc"
var t = "def"

inline fun <T> swap(xRef: KMutableProperty0<T>, yRef: KMutableProperty0<T>) {
    val tmp = xRef.get()
    xRef.set(yRef.get())
    yRef.set(tmp)
}

fun main(args: Array<String>) {
    swap(::x, ::y)
    println("x = $x, y = $y")
    swap(::s, ::t)
    println("s = $s, t = $t")
} 

/*
    x = 4.0, y = 3.0
    s = def, t = abc
*/

It’s a pity this isn’t currently available for local variables as well though, if it’s reflection based, it’s presumably quite slow compared to the ‘time honoured’ way.

On the general discussion, I really can’t see the harm in ‘ref’ parameters as long as you realize that your variables could be mutated! Having to use ‘ref’ (or whatever) at the call-site as well as in the function signature certainly helps you to remember this.

In all the years I’ve used C#, I can’t remember hearing anyone complain about the language having ‘ref’ and ‘out’ parameters which are implemented in the CLR using ‘managed pointers’. Heck, in C# 7.0 they’ve even added ‘ref’ locals and ‘ref’ returns!

These certainly aren’t there just for C/C++ interop as the standard library makes use of them in (for example) its various TryParse methods and also the Array.Resize method.

1 Like

I didn’t understand your point: the body of inline function will be inserted instead of the call to subroutine. In this case there is no really call by anything (value or reference).

Pass by value in OOP means deep copy of all parameters, which is very inefficient.

Marking parameters as in/out/inout only means that parameter in the body will be modifiable or not. In Kotlin it is controlled by var/val.

Actually in Kotlin parameters are always read only.

1 Like

Coming from a C# background myself, I’d really like to be able to use ref as well.

At the moment, whenever I need this, I simply create an array of one size and parse in the array, then change the value inside the function and then it can be used. I’m sure this could be implemented inside Kotlin so that the ref keyword creates all the boilerplate and returns the value back. The only issue I can see is that ref types would have to be var not val.

A Ref class should be helpful

data class Ref<T>(var v: T)

Probably not data though.

  1. It will generate additional code which is not needed in this case.
  2. In my opinion data classes are meant to be a kind of value-types substitution, so data class with mutable properties does not make any sense.

I am not sure what exactly you mean by value-type, data is Kotlin way to do Java Bean (no business logic, only data and code to get/set values). However setting values make sense in OOP.

Why mutable value objects does not makes sense? AFAIK, value type just means that their values passed by value rather than by reference.

I wasn’t the original poster, but with language support for destructuring, the invocation wouldn’t look like that. Rather, it’d be something like this:

(x, y) = normalize(x, y)

The proposal only suggests having the ability to pass by reference built into the language for inline functions.
This means that, if the functions are all inlined, the compiled code would never directly use a ref variable, but only access already-existing local variables (or properties, which I will touch on below).

I don’t think there are any objects that can be mutated additionally as a result of implementing the requested feature unless property references are supported. If that is true, no additional concurrency issues arise if it’s implemented. Please correct me if I’m wrong.

In other words, you can only mutate objects if property references are supported by the implementation. Any property references can only be modified if they are var, in which case they are intended to be mutable anyway. As such, one could argue that it’s the existence of val or final properties that is there to limit mutability, and exactly that would affect this feature in the same way if property references are supported.

I understand existing arguments against mutability. I’m (obviously) saying that mutability is not a concern with this proposal.

Maybe the poster can clarify if the proposal is intended for property references in addition to local variable references.