Purity indicator for functions


#1

What does the community think about purity indicator in functions like “!” in ruby.
For example we may use:

 println!("salut")

instead of

 println("salut")

This have two major benefits:
The first one is that developpers will have a better readability of the code.
For example we can force the operator plus() to be pure and accept that the operator plusAssign!() to be unpure.

As the code

val list = listOf(1, 2) + listOf(3) // [1, 2].plus([3])
val mList = mutableList(1, 2) // mList = [1, 2]
mList += 3 // mList.plusAssign!([3])

Here we can see that mList should be modified by the plusAssign! but we can also see that plus() do not authorized to modify [1, 2].

The second benefit for this kind of keyword is to improve the performance of the code when optimization.

A function call is used only if the function is not pure or the returned value of the value is used.

fun square(x: Int): Int = x * x
val x = 10  // x is used by square(x)
val sqrtX = square(x) // here square is a pure function and his result is used later
println!(sqrtX) // println should have a side effect so we cannot determined if sqrtX is used for real or not

Here the compiler execute all the program

fun square(x: Int): Int = x * x
val x = 10
val sqrtX = square(x)
foo(sqrtX)

In the second example, we see that foo is pure so we can replace the call foo(sqrtX) with his result Unit

fun square(x: Int): Int = x * x
val x = 10
val sqrtX = square(x)
// replaced by Unit

here we can see that sqrtX is not used anymore so we can suppress the call to square(x), etc. At the end we see that the program execute nothing.


#2

This is nice from a theoretical standpoint, but I personally would dislike having to put an ! every time a call a impure function.
Also how do you suggest to implement this. I mean you can trust the developer to only mark functions as pure if they are, but with reflection I guess it is really difficult to determine whether or not a function is really pure.

Also your optimization is not really working. In general I would say most of the time you just would not call a function if it has no side effects and you don’t use the result. I guess this could be a nice way to improve the speed of more generic code but I am not sure if there is really that much speed gain and Kotlin is not build to be the best performing language out there (there is no way to beat native, optimized code using code on a VM).

Last I guess this is similar to the kotlin internal contracts which I would love to see becoming publicly usable.


#3

I don’t think the implementation is really difficult. We only need to determine when a function can strictly considered a pure.

A function is pured when she as no side effects and this call can be replaced by this returned result.

For example

class Foo(
    val id: Int,
    var name: String
)

fun getIdOfFoo(foo: Foo): Int = foo.id
// here getIdOfFoo is pure because it's only get the value of id
// and foo.id equivalent to foo.getId()

fun setNameToFoo!(foo: Foo, name: Name) = foo.name = name
// Here the function is unpure because it's set the name to foo
// as foo.name = name is equivalent to foo.setName!(name)
// The function is unpure because it has impact out side his context
// and we cannot replace the line setNameToFoo!(fooA, "some name") by his returned value Unit.

A pure function can only call other pure function, the problem is when we have an encapsulated internal unpure state as:

fun zeros(length: Int): List<Int> {
    val res = mutableList<Int>()
    for(i in 0 until length) {
        res += 0 // equivalent to res.plusAssign!(0)
    }
    return res
}

#4

You still did not solve the problem with reflection. You could get a reference to a function using reflection. How would you know whether this function is pure? Or would you mark all code using reflection as impure?

Also you would need to go through the entirety of the Java standard library and mark their functions as either pure or impure. Static code analysis is not that simple. I guess you would not plan to mark every function calling into Java as impure.


#5

Yes we need to define in all the basic instructions if the are pure or not, then with the recursiveness we can determined if a function is pure or not.