Effects Parentheses convention

I’m starting to have serious doubts about the effects-parentheses convention. I’ve put this question in contributors, because I don’t believe compiler and standard library design can be separated from communally agreed use patterns.

it seems to me that parentheses are so syntactically overloaded in Scala, that this convention confuses more than it clarifies. When I look at a method name followed by empty parentheses, my brain naturally interprets that as 0 repeated parameters, or as a method call using all default parameters.

1 Like

Conversely, when I see a method name not followed by (), I interpret it as a property access, not as a method call. And I expect property access not to have (observable) side effects.

7 Likes

The first call of a val or a lazy val does not guarantee that there are no effects in Scala. I am particularly concerned with what helps people who are new to Scala, who may also be new to the importance of tracking effects. The optionality of parenthesis for parameterless methods, but the need to use parenthesis on the apply method, implicit parenthesis lists, repeat and default parameters already can make for a steep learning curve.

Telling people that the absence of parentheses indicates that it very probably doesn’t have effects and that the presence of an empty parentheses list might indicate the use of effects, but in high probability means something else, does not strike me as helpful.

I’m all for finding a way or ways to indicate and increase awareness of effects, but the more I think about it the more I feel the presence / absence of empty parentheses is not a good way to advance this cause.

It would be interesting to force using exclamation points to discard Unit values.

{
  val _ = println("foo") // <-- ok, no discard
  val x = println("bar") // <-- ok, no discard
  println("baz") // <-- illegal unit discard
  println("baz")! // <-- ok, explicit discard
}

I sometimes use the convention of postfixing _! to all the effectful operation names. It makes it quite clear and easy to discern what is not referentially transparent. For instance:

val v = freshVar_!
println_!("fresh: " + v)
v.registerConstraint_!(t)
1 Like

println(“baz”)Σ( ° △ °|||)︴

1 Like