Proposal to remove procedure syntax

Can you clarify how it’s safe? What “safe” are you trying to preserve?

To quote the Scala Style Guide:

All public methods should have explicit type annotations. Type inference may break encapsulation in these cases, because it depends on internal method and class details. Without an explicit type, a change to the internals of a method or val could alter the public API of the class without warning, potentially breaking client code. Explicit type annotations can also help to improve compile times.

Without an explicit return type, methods can accidentally return more than they intended to. For a somewhat stupid example, if someone blindly changed

def addAuthCode(code: String) {
  codes += code
}

to

def addAuthCode(code: String) = {
  codes += code
}

they would unintentionally be returning all authorization codes from a function that was supposed to return nothing.

In other words def foo() = {...} is never the same thing as def foo() {}. If this proposal is accepted, one must always use def foo(): Unit = {...} for a function which returns nothing. I personally feel like this is a lot of song and dance for these kinds of methods.

Of course, not specifying a return type is an issue not just limited to Unit returning functions, but at least in this case procedure syntax provides a safe, concise way to do this.

1 Like

Here’s why I think this is not a big deal:

  1. There will be a rewrite for it that will do the right thing for users.
  2. IntelliJ warns against this code.
  3. People that don’t like the rewrite can get a nice compilation error explaining how : Unit = { not = { don’t mean the same (to be implemented if we remove procedure sytnax).
  4. We could enable -Ywarn-nullary-unit by default to warn the user when his Unit-non-returning method actually returns Unit.

If I understand @jdolson’s concern correctly (and that of many others who have commented on this thread), they’re not so much concerned about the migration process as about the final state of their codebase. It seems many people would not be happy about having their codebase full of : Unit = {.

No amount of migration rules, warnings, or compilation errors can alleviate that concern.

1 Like

This is the way I’ve read most of the feedback, but the way I read @jdolson’s concerns I think he means something different:

We could enforce that “must always” with the rewrites plus the extra solutions I mentioned.

I think my preferred solution would be to have: “proc”, “def” and “fun” instead of just “def”. proc could then use procedure syntax. fun would be for pure functions. Calling a fun where it had no programmatic effect, could then give an automatic error.

2 Likes

Great idea :+1:. I don’t think they will accept it, since it doesn’t “simplify language” (if you think about it so doesn’t for), but I like my languages expressive and powerful (like Haskell), not simple and toothless (like Go).

First you would need a good effect tracking system though. Otherwise it’s just a promise or best guess by some programmer. But apparently they are thinking about something like this in dotty. -> for pure and => for side-effecting functions. I suppose you could/should extend that to methods.

Virtually everything relies on the promise of some programmer. A simple system where funs can not call defs or procs would go a long way. Beginners could be told if they’re not sure whether its a fun or a def call it a def.

Note we special case foreach, which is a Unit returning map. I think that’s a good thing. I also think it would be a good thing if we clearly distinguished between if-procedures, if-else-procedures and if-else functions.

In my view there’s nothing like keyword-anorexia for complicating a language. But anyway there seems to have been a complete change of heart in dotty. Its been accepted for example that not having special syntax for extension methods, was complicating not simplifying the language.

1 Like

Adding 3 distinct keywords solely for expressing programmer intention without actual compile time checks seems a bit overkill to me, to say the least. But it’s probably best not go too far off topic.

We have Iterable,foreach, because, not being obliged to create a new collection, it can be implemented more efficiently than Iterable.map.

1 Like

No one commented on my previous comment, so here’s a PR for “omitted Unit” syntax.

See the test for examples, def f() := println().

Please don’t. Since users can’t overload = it is common to use :=, which can be mutating and returning Unit. I really don’t want to see def := (that : Any) := println("this is weird") in anyone’s code.

In addition to that, := is often used in math (and some other programming languages) to signify a definition or assignment, so that’s maybe the worst possible symbol to use for a thing that returns no value.

It’s the user’s prerogative to do weird things if they choose, so I don’t see that as a counterargument.

I did think of your example, but I was saving it for an important occasion.

This is the shortest edit distance to a syntax that satisfies the constraints, including that it’s hard for a beginner to use accidentally, but not onerous to type or read.

This signifies a definition.

You’d be hard-pressed to quantify “worse possible”, so that is a disqualifying qualification.

The difference between having a = or not is too subtle. Also, procedures or purely side-effecting functions are rare these days… if I need one, I am happy to have : Unit = so I am in favor of removing procedure syntax.

2 Likes

Yeah, so rare… Not every codebase is same.

I maintain a minecraft plugin. In this, I ALWAYS use def foo(): Unit =() syntax, and it’s MOST of the methods (as most methods are overrides/mutational helpers).

This is also true for most code I write where things are highly mutational.

3 Likes

This topic was automatically closed after 30 days. New replies are no longer allowed.