def foo { println(1) } warning: Procedure syntax is deprecated. Use `: Unit =` instead.

598 views
Skip to first unread message

Simon Ochsenreither

unread,
May 28, 2013, 8:35:19 AM5/28/13
to scala-...@googlegroups.com
— Or: Can we decide on one-true-procedure-style instead of having three?

I have been focusing on beginner-level issues for the last few weeks and one large chunk of popular problems which comes up a lot is the different declaration style for procedures.

Scala has
  • its own special syntax for procedures, def foo { println(1) }
  • Unit-returning methods with inference, def foo = { println(1) }
  • has explicit Unit-returning methods, def foo: Unit = { println(1) }

Imho, that's at least one option too many.

The first option is especially troubling, because we reward people who use side-effects with special, less verbose syntax, while we claim that the language nudges people toward a more value-driven style of programming.
Additionally, we make it extremely easy for beginners to mistakenly use procedures instead of methods (by forgetting the =). Because they are still learning the language and often write some methods down without necessarily wiring them up in the same turn the compiler doesn't create a type error. They assume that they wrote a method, but in reality they wrote a procedure instead. Then they run it and keep wondering why they don't get any values back. Incredibly frustrating. For some, their Scala journey ends right there.

The question is, why are we causing this huge confusion for basically no tangible benefit? Imho, it would be a lot easier to explain and more consistent if one could say "every member implementation consists of an =, separating the signature to the left and the body to the right" instead of having an additional syntax for procedures.

I spent a lot of time, sat down with those learning the language and asked them _explicitly_ "from your limited experience, what part of Scala sucks?" and the procedure-vs.-method thing is one of the main points where everyone says "yes, one can learn it, but there seems to be no apparent reason why it is that way, apart from being a pointless nuisance".

The second option is pretty bad from a different perspective: Those who actually want to return a value, but have forgotten the equals sign fall into this trap. This comes up a lot. (We have at least a warning for that now.)

The third option is explicit and the style I think should be preferred. (Additionally, I recommend that this style is enforced in traits, too. I think it's mind-boggling and invites errors that we even allow writing trait Foo { def foo } instead of trait Foo { def foo: Unit }.

What about

  • deprecating and removing option number one,
  • deprecating and removing procedure declarations in traits without signature (as described in option number three)
  • adding a warning if a method's result type is inferred to be Unit for option number two and
  • guiding users towards specifying the return type explicitly, like in option number three?

This would improve consistency of the language, remove confusing variations in style, reduce the amount of special-cases users have to learn and prevent a lot of beginner mistakes. I think we have a lot of places were we could spend the complexity budget of our language much more effectively than on building error-prone syntactic special-cases.

I think the idea about recommending : Unit = is quite similar to the reason why we started to emit a warning for catching exceptions with case _: we don't prevent people to do potentially dangerous/error-prone stuff, but we tell them to be explicit about their intention, so that future readers don't have to guess whether something is intentional or a bug.

To guarantee a smooth transition, I additionally propose to

  • adapt the documentation
  • look if some automatic refactoring can be provided so that users don't need to change things manually

Better ideas, opinions, criticism?

Simon

Haoyi Li

unread,
May 28, 2013, 9:43:33 AM5/28/13
to Simon Ochsenreither, scala-...@googlegroups.com
> deprecating and removing option number one,

+1, as I think that 9 ways to define a method is too many, and cutting that number down as much as possible is a good thing. Yeah, you could argue that it's actually multiple super generic concepts interacting to produces these syntaxes, but as a newbie I didn't know any of this, I just see 9 ways of doing the same thing. "The = is optional? Sometimes?" was probably second on my own personal syntax-confusion-rankings, just below "def f(a: A) is called via f(a), def f via f, but def f() can be called via f or f()? What?". 

I've been writing Scala for one and a half years now, from the most banal website to compiler plugins and metacircular JVMs, and bugs caused by this still bites me at least once a month. It doesn't take long to find and fix (i.e. <1 minute), and maybe I'm just thick, but I'd be glad to see this syntax gone. Maybe you can kill those damn optional-parens while you're at it too!

> deprecating and removing procedure declarations in traits without signature (as described in option number three)

+1; Mainly because I've never used it, but some regularity would be nice. Everyone else needs to declare types on abstract properties, Unit should too.

> adding a warning if a method's result type is inferred to be Unit for option number two and
> guiding users towards specifying the return type explicitly, like in option number three?

-1; I'm all for regularity, and that means that if pure functions get to have their return types inferred, functions which return Unit should too. Replacing the special case favoring side effects with a (weaker) special case favoring purity is fine, but I think just removing these damn special cases altogether would be the best outcome. I don't think side effecting procedures are anywhere near catch-all-exceptions in terms being dangerous. Like var and val, pure functions and side-effecting procedures should be of equal standing.

-Haoyi


Simon

--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Rex Kerr

unread,
May 28, 2013, 3:25:50 PM5/28/13
to Simon Ochsenreither, scala-debate
To reiterate my largely opposing opinion:

I think that most of this change is counterproductive.

Arguments in favor of special syntax

(1) Functions and methods are not the same.  Methods that return values are not the same as those that do not.  The def f {} syntax reflects this.

(2) Side-effecting methods need more care than functional methods.  Procedural methods (i.e. those with no return value, i.e. those which convert to functions returning Unit) are useful entirely for their side-effects.  Having syntax that highlights them as distinct is therefore advantageous.

(3) Whether or not you return something alters syntax with for also (the other language construct that can operate in functional or side-effecting mode).  For instance
  for (i <- 1 to 10) { println(i) }
  for (i <- 1 to 10) yield { println(i) }

(4) The natural generalization of arity on return values is not to Unit as a return type, but to (), that is, an empty return-value list, or to , that is, simply nothing at all.  I think the current system is actually better for an eventual move to arbitrary arity returns than more vehemently insisting that zero return values is actually a return of Unit.  (Unit isn't it: it isn't a product, and (() => "fish").tupled doesn't work.  Maybe it should, but this is a unification that is yet to happen.)

Arguments against confusion mattering

(1) Scala is full of special syntax that exists for some reason but provides a barrier to learning: why is there a difference between f(a,b) and f((a,b)) (and why only sometimes do you notice?); why doesn't def f = "fish" fill in for a by-name parameter the way that def f() = "fish" fills in for () => String? and why can you call the latter with f but not the former with f(); what is up with f(i)(j) vs. g(i,j) and why can't you have f(i: Int, j: Int) and f(i: Int)(j: Int) if they're actually the same?  In these and the procedures-are-different case, there is a reason why things are the way they are (not always a good reason, but some reason), and it's no big deal for people to learn them.

(2) The problem caused by this is caught quickly by the compiler due to strong typing.  It doesn't impact one's ability to write robust code.  (In fact, by reducing the distinction of necessarily side-effecting methods, it may even decrease one's ability to write robust code.)

(3) Your IDE can fix it for you, in principle, if you like to see an extra : Unit =

(4) There's already lots of code written using the old syntax, and changing it would either require a bunch of work (easy to do with an automatic converters now, perhaps, but for how long do we want to maintain a converter?) or would cause abandonment of older code that otherwise basically works.

Summary

I am in favor of at least having a -X level option that warns about def f = println and perhaps def f(i: Int) { i + 1 } but everything else is a change for the worse, not better.

  --Rex



On Tue, May 28, 2013 at 8:35 AM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
Simon

--

Simon Ochsenreither

unread,
May 28, 2013, 3:42:56 PM5/28/13
to scala-...@googlegroups.com, Simon Ochsenreither
I think the points are not very strong and I think I have addressed most of them already in the other thread, so no need to repeat them here.

The point about special syntax sounds a lot like arguing along the line of the broken-window-theory: "hey, there is already so much mess, a bit more or less doesn't matter"

Kevin Wright

unread,
May 29, 2013, 6:53:58 PM5/29/13
to Rex Kerr, Simon Ochsenreither, scala-debate
I agree 100% with your underlying statements, but somehow manage to come to the exact opposite conclusion:


(1) Functions and methods are not the same.  Methods that return values are not the same as those that do not.

It's therefore frustrating that the only difference is the absence of a single character in defining a procedure.  An absence that doesn't even trigger a syntax error if left out by mistake.  An explicit `: Unit' would be far harder to overlook and near impossible to produce with a mere typo.


(2) Side-effecting methods need more care than functional methods.  Procedural methods (i.e. those with no return value, i.e. those which convert to functions returning Unit) are useful entirely for their side-effects.  Having syntax that highlights them as distinct is therefore advantageous.

I'm in total agreement with every word here, we just have different opinions as to a "syntax that highlights".


(3) Whether or not you return something alters syntax with for also (the other language construct that can operate in functional or side-effecting mode).  For instance
  for (i <- 1 to 10) { println(i) }
  for (i <- 1 to 10) yield { println(i) }

I'd love to see a dedicated syntax for the side-effecting case, such as using `do` as a keyword.
  for (i <- 1 to 10) do { println(i) }
This would nicely mirror the val/var duality.

Again… dropping the idea of highlighting through omission.


(4) The natural generalization of arity on return values is not to Unit as a return type, but to (), that is, an empty return-value list, or to , that is, simply nothing at all.  I think the current system is actually better for an eventual move to arbitrary arity returns than more vehemently insisting that zero return values is actually a return of Unit.  (Unit isn't it: it isn't a product, and (() => "fish").tupled doesn't work.  Maybe it should, but this is a unification that is yet to happen.)

Yet () IS Unit, just as "hello, world" IS String.  IIRC () is the only value that can ever inhabit the Unit type.
I also completely agree that () should be a product.  Consistency is a Good Thing™

Arguments against confusion mattering

(1) Scala is full of special syntax that exists for some reason but provides a barrier to learning: why is there a difference between f(a,b) and f((a,b)) (and why only sometimes do you notice?); why doesn't def f = "fish" fill in for a by-name parameter the way that def f() = "fish" fills in for () => String? and why can you call the latter with f but not the former with f(); what is up with f(i)(j) vs. g(i,j) and why can't you have f(i: Int, j: Int) and f(i: Int)(j: Int) if they're actually the same?  In these and the procedures-are-different case, there is a reason why things are the way they are (not always a good reason, but some reason), and it's no big deal for people to learn them.

All fair game :)

The whole area of interaction between currying, tuples, parameter lists, named/default params, by-names, etc. is worthy of exploring.  I'd definitely love to a cleanup here in some future major-version Scala release, perhaps when we get dependent object types?


(2) The problem caused by this is caught quickly by the compiler due to strong typing.  It doesn't impact one's ability to write robust code.  (In fact, by reducing the distinction of necessarily side-effecting methods, it may even decrease one's ability to write robust code.)

Missing a character is an easy typo.  Typos should be reported as syntax errors if possible, not as a type error which could well be reported far away from the code where the mistake was made.  Pinpointing errors quickly will never harm your ability to write robust code.


(3) Your IDE can fix it for you, in principle, if you like to see an extra : Unit = 

That's a slippery slope. "It's okay because the IDE can fix it" is a mantra common in discussing Java's syntax, yet it neatly sidesteps the fact that there's something which needs to be fixed in the first place!


(4) There's already lots of code written using the old syntax, and changing it would either require a bunch of work (easy to do with an automatic converters now, perhaps, but for how long do we want to maintain a converter?) or would cause abandonment of older code that otherwise basically works.

Possibly the strongest argument against this (or any other change), and a very valid one.  Yet it's also an argument against introducing the new collections framework in 2.8.  I'll always favour language improvements over stagnation, even if it does risk some loss of stability.
--
Kevin Wright
mail: kevin....@scalatechnology.com
gtalk / msn : kev.lee...@gmail.com
vibe / skype: kev.lee.wright
steam: kev_lee_wright

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra

Rex Kerr

unread,
May 29, 2013, 8:19:37 PM5/29/13
to Kevin Wright, Simon Ochsenreither, scala-debate
On Wed, May 29, 2013 at 6:53 PM, Kevin Wright <kev.lee...@gmail.com> wrote:
I agree 100% with your underlying statements, but somehow manage to come to the exact opposite conclusion:


(1) Functions and methods are not the same.  Methods that return values are not the same as those that do not.

It's therefore frustrating that the only difference is the absence of a single character in defining a procedure.  An absence that doesn't even trigger a syntax error if left out by mistake.  An explicit `: Unit' would be far harder to overlook and near impossible to produce with a mere typo.

Weird.  I wonder if your visual system just parses code differently from mine?  When I glance at code I see

  def #(#: ####): #### = {
    #####
    ########
  }

  def #(#: ###) = {
    ############
    #######
  }

  def #(#: #####) {
    ##########
    ################
    ####
  }


and the loss of that = is really clear, way more clear than that one #### happens to be Long and another Unit and another Char and another Node.

Rex Kerr

unread,
May 29, 2013, 8:38:49 PM5/29/13
to Kevin Wright, Simon Ochsenreither, scala-debate
Hit send by accident...there were two other points I wanted to make.  Sorry about the extra email.


On Wed, May 29, 2013 at 6:53 PM, Kevin Wright <kev.lee...@gmail.com> wrote:

(2) The problem caused by this is caught quickly by the compiler due to strong typing.  It doesn't impact one's ability to write robust code.  (In fact, by reducing the distinction of necessarily side-effecting methods, it may even decrease one's ability to write robust code.)

Missing a character is an easy typo.  Typos should be reported as syntax errors if possible, not as a type error which could well be reported far away from the code where the mistake was made.  Pinpointing errors quickly will never harm your ability to write robust code.

I agree, but I am very wary of introducing boilerplate to save people from trivial ills.  Strong typing makes drastic type errors (like Unit instead of Array[Option[String]]) nearly trivial to detect.  The type error might be reported far away, but Unit hardly ever goes through many transformations so that it's hard to track back to where the problem was.
 

(3) Your IDE can fix it for you, in principle, if you like to see an extra : Unit = 

That's a slippery slope. "It's okay because the IDE can fix it" is a mantra common in discussing Java's syntax, yet it neatly sidesteps the fact that there's something which needs to be fixed in the first place!

Except I don't agree that the change is better.  I think it's worse.  It's more boilerplate to save me from a problem that I don't have.  I recognize that some people might have it.  If most people have it, my IDE should save me.  If most don't, the ones who need saving should rely on their IDEs.  This is exactly the kind of thing that an IDE can provide.

  --Rex


Simon Ochsenreither

unread,
Jun 7, 2013, 4:45:48 PM6/7/13
to scala-...@googlegroups.com, Rex Kerr, Simon Ochsenreither

(1) Functions and methods are not the same.  Methods that return values are not the same as those that do not.

It's therefore frustrating that the only difference is the absence of a single character in defining a procedure.  An absence that doesn't even trigger a syntax error if left out by mistake.  An explicit `: Unit' would be far harder to overlook and near impossible to produce with a mere typo.

If anybody still needs any data points why the procedure syntax is such an horrible idea:

I helped at a Scala workshop today, which lasted ~ 3 hours.
Of the 15 attendees, 5 managed to run into the procedure trap.

That's a 33% failure rate for defining a method.

Artem Khodyush

unread,
Jun 7, 2013, 5:39:25 PM6/7/13
to Simon Ochsenreither, scala-debate, Rex Kerr
Was it the only problem encountered by participants? I'm just curious what was overall failure rate, that is how many attendees managed to produce some working scala code at all at the workshop?

Also, what was the 'failure mode'? Did they just forget to write = ? Then banning procedure syntax won't help at all - people will still forget about '=' and experience the same failure to define a method.





--

Simon Ochsenreither

unread,
Jun 7, 2013, 5:53:05 PM6/7/13
to scala-...@googlegroups.com, Simon Ochsenreither, Rex Kerr

Was it the only problem encountered by participants?

One or two people had an issue with implementing equals, because they came from C++ and didn't know the Java convention (argument type is Any).
During the introduction of the instructor, the usual suspect map + (x, y) vs. someMap + ((x, y)) and one weirdness I didn't know before, List(1,2,3).toSet(), came up.
 
I'm just curious what was overall failure rate, that is how many attendees managed to produce some working scala code at all at the workshop?

All of them produced working code after intervening and explaining how they can fix their code.
 
Also, what was the 'failure mode'? Did they just forget to write = ?

Yes.

Then banning procedure syntax won't help at all - people will still forget about '=' and experience the same failure to define a method.

That's incorrect. The failure is not “failing to define a method”, it is “accidentally defining a procedure” . If we remove the procedure syntax, forgetting the = will become a syntax error and will prevent the unintended definition of procedures.

ramn.se ‘¸

unread,
Jun 29, 2013, 5:20:55 PM6/29/13
to Simon Ochsenreither, scala-...@googlegroups.com, Rex Kerr
On Fri, Jun 7, 2013 at 11:53 PM, Simon Ochsenreither
<simon.och...@gmail.com> wrote:
>
>> Was it the only problem encountered by participants?
>
>
> One or two people had an issue with implementing equals, because they came
> from C++ and didn't know the Java convention (argument type is Any).
> During the introduction of the instructor, the usual suspect map + (x, y)
> vs. someMap + ((x, y)) and one weirdness I didn't know before,
> List(1,2,3).toSet(), came up.

scala> List().toSet()
res0: Boolean = false

scala> List(()).toSet()
res1: Boolean = true


>
>>
>> I'm just curious what was overall failure rate, that is how many attendees
>> managed to produce some working scala code at all at the workshop?
>
>
> All of them produced working code after intervening and explaining how they
> can fix their code.
>
>>
>> Also, what was the 'failure mode'? Did they just forget to write = ?
>
>
> Yes.
>
>> Then banning procedure syntax won't help at all - people will still forget
>> about '=' and experience the same failure to define a method.
>
>
> That's incorrect. The failure is not “failing to define a method”, it is
> “accidentally defining a procedure” . If we remove the procedure syntax,
> forgetting the = will become a syntax error and will prevent the unintended
> definition of procedures.
>

Simon Ochsenreither

unread,
Jun 30, 2013, 8:00:59 AM6/30/13
to scala-...@googlegroups.com, Simon Ochsenreither, Rex Kerr

scala> List().toSet()
res0: Boolean = false

scala> List(()).toSet()
res1: Boolean = true

Simon Ochsenreither

unread,
Jul 21, 2013, 4:12:32 PM7/21/13
to scala-...@googlegroups.com, Simon Ochsenreither, Rex Kerr

Som Snytt

unread,
Jul 21, 2013, 6:47:02 PM7/21/13
to Simon Ochsenreither, scala-debate, Rex Kerr
Of course, people reading "Scala for the Impatient" are self-selecting as "impatient."

Perhaps this calls for an extension to the Odersky levels:

import scala.language.level.{ A0 => noob }

Oh, I seem to recall that was already proposed in "SIP-00006".  But that proposal was shot down like it was some kind of joke.

https://groups.google.com/forum/embed/#!topic/scala-sips/niNW-lIUOVU

I wish had that feature this morning when I wasted a few more minutes on that other toSet curveball, which somehow looks different from inside a for comprehension.  I was low on caffeine, it was in the middle of a large file, and I was thinking, did I misspell left-arrow?  I checked that all the parens were balanced in the previous fifty lines.  Then I had the d'oh moment.


scala> def checkNames(names: Seq[String]*) = for (p <- names.flatten.toSet) println(p)
<console>:7: error: missing parameter type
       def checkNames(names: Seq[String]*) = for (p <- names.flatten.toSet) println(p)
                                                  ^

scala> def checkNames(names: Seq[String]*) = for (p <- names.flatten.to[Set]) println(p)
checkNames: (names: Seq[String]*)Unit

scala> def checkNames(names: Seq[String]*) = for (p: String <- names.flatten.toSet) println(p)
checkNames: (names: Seq[String]*)Unit

scala> ss.flatten.toSet foreach { case p => println(p) }
<console>:9: error: missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: ? => ?
              ss.flatten.toSet foreach { case p => println(p) }
                                       ^






On Sun, Jul 21, 2013 at 1:12 PM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
https://gist.github.com/noahlz/6048606

Francois Armand

unread,
Aug 19, 2013, 2:24:35 PM8/19/13
to Rex Kerr, scala-debate, Simon Ochsenreither, Kevin Wright

Rex, I can confirm that my visual parsing system is different from yours. Even knowing what I was looking for, I didn't see the missing equals before the third pass :/. And I get bitten by that syntax even after 7 years of Scala, because well, I just don't see that missing equal.

I did notice the different length in # at first glance, thought, not sure what it tells.

Dennis Haupt

unread,
Aug 19, 2013, 3:05:23 PM8/19/13
to Francois Armand, Rex Kerr, scala-debate, Simon Ochsenreither, Kevin Wright
i immediately saw the missing =
 
Gesendet: Montag, 19. August 2013 um 20:24 Uhr
Von: "Francois Armand" <fan...@gmail.com>
An: "Rex Kerr" <ich...@gmail.com>
Cc: scala-debate <scala-...@googlegroups.com>, "Simon Ochsenreither" <simon.och...@gmail.com>, "Kevin Wright" <kev.lee...@gmail.com>
Betreff: Re: [scala-debate] def foo { println(1) } warning: Procedure syntax is deprecated. Use `: Unit =` instead.

Simon Ochsenreither

unread,
Aug 19, 2013, 5:10:46 PM8/19/13
to scala-...@googlegroups.com, Rex Kerr, Simon Ochsenreither, Kevin Wright
Just for the record: at a recent FP meetup I'm organizing, I mentioned in a half-sentence that I'm looking into making the langauge easier for beginners. In pretty much the same second two different people brought up the procedure syntax as a negative example.

Rex Kerr

unread,
Aug 19, 2013, 5:45:18 PM8/19/13
to Simon Ochsenreither, scala-debate, Kevin Wright
I'm thinking (given reactions like Francois' and Dennis') that this is a problem that IDEs should solve.  It's looking more and more to me like there are actually major differences in how interpretable the syntaxes are to different people.  You can't solve this problem by fixing the language, though you can shift the burden/difficulty from one group to another.  But an IDE can display it the way you like it, so everyone wins (who uses an IDE).

Since the simplicity and conformity arguments work in opposite directions, I wish I were convinced by the uniformity argument.  There is already so much nonuniformity surrounding unit and argument lists and so on that I can't get very excited about "fixing" one.

  --Rex

Russ Paielli

unread,
Aug 19, 2013, 8:01:46 PM8/19/13
to scala-debate
Not that I expect anyone to care what I think, but I still say that Scala should have a new keyword, "proc," for "functions" that do not return anything. That would solve the problem nicely -- certainly more elegantly than "Unit =".


--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Hanns Holger Rutz

unread,
Aug 20, 2013, 5:03:01 AM8/20/13
to Russ Paielli, scala-debate
What happens with

def foo[A](fun: Any => A): A

when A is Unit? Are you having an illegal procedure?

I think the fact that Unit is just an ordinary type indicates that "procedures" are just a way we talk about certain things.

best, .h.h.
---
Ich wähle am 22.09. Merkel, Friedrich und Rösler ab. #Supergrundrecht



Russ P.

unread,
Aug 20, 2013, 1:53:05 PM8/20/13
to scala-...@googlegroups.com, Russ Paielli
I'm not sure I understand your point, but I think Scala could still allow a function to return Unit even if it had my suggested "proc" keyword. So a procedure could still be defined using "def," just as a value that never changes can be called a "var".

Rex Kerr

unread,
Aug 20, 2013, 4:06:21 PM8/20/13
to Russ P., scala-debate
I think there are two ways to view functions.  One is that all functions take a natural number of arguments (maybe zero) and return a natural number of arguments (maybe zero), and then you just have

  f : A -> B

(in mathematical notation) which covers every possibility.  Alternatively, there may be procedures which do not return values, functions which return one, and multi-return-valued functions which return more than one:

  f00:
  f10: A ->
  f01: -> A
  f11: A ->B
  f20: (A,B) ->
  f02: -> (A,B)
  f21: (A,B) -> C
  f12: A -> (B,C)
  f30: (A,B,C) ->
  f03: -> (A,B,C)
  ...

which means there are now an infinite number of types of functions.

Scala has an uneasy compromise between these two views.  In particular, many programming languages have the idea of either a single return or no return value, which gives the f_0 and f_1 set from the infinite list above.

The problem is that Java is one of these languages, and the JVM is written accordingly; but Scala also tries to be functional which is a methodology that works much better if you adopt the first perspective.

I don't think the solution is to break apart the f_0 cases (procedures) from the f_1 cases (single-return functions), which is what introducing a proc keyword would do.

Having syntactic sugar to acknowledge the difference between _0 and _1 is nice, just as it is nice to have syntactic sugar that papers over the difference between def length() and def length and def length()()(), even though as functions they would be
  => ()
  =>
  => () => () => ()
respectively (in some sense).

But having : Unit is particularly jarring when contrasted with the syntax for returning more than two arguments:

Intelligence test:
  def f: (A,B,C) = // I return 3
  def f: (A,B) =   // I return 2
  def f: (A) =     // I return 1
  def f_____       // I return 0
  def f: A =       // Acceptable variant for 1
  def f: A,B =     // NOT acceptable for 2
What should go in place of _____ in the above progression?
A. Functions cannot return zero arguments--use proc f instead.
B. : () =
C. : Unit =
D. : =
E.
F. {...}

I don't know about you, but I would say B is by far the best answer, followed by D.  Either of these are things you might actually be able to intuit.  Everything else is arbitrary (albeit historically grounded) complexity.  You might be able to figure out E (just leave out everything that's unnecessary).  It's yet another step to get to F (i.e. too many for mortals to manage reliably) which is what we actually have, and how you could imagine C or A is beyond me.  And we're proposing C as the solution?

Okay, now, outputs should look like inputs, right?  Let's see.

Intelligence test:
  val a: Unit => Int  // Takes one parameter (of type Unit)
  val b: () => Int    // Takes no parameters
Which of the following returns no parameters?
A. val z: Int => Unit
B. val z: Int => ()

Um, B?  Really, isn't it B?  Please?

More than anything else, this is why the shift to Unit bothers me.  There's are perfectly sensible patterns there, and they are being broken so that f_0 can be collapsed into f_1 by use of a magic sentinel value.  I've nothing against sentinel values in particular--they're very handy for a variety of algorithms--but we've already got something better for f_2 and higher.  Not to mention that it exacerbates the contrast with the input side.  You don't make me handle sentinels at the leaves of trees or end of lists--why make me handle it here?  If that's how you want to implement it behind the scenes, go ahead, but if you're going to go to the trouble to let me use (Int, String) instead of Tuple2[Int, String], why not extend the same courtesy here?

  --Rex


Russ Paielli

unread,
Aug 20, 2013, 6:14:09 PM8/20/13
to Rex Kerr, scala-debate
I like your thinking, Rex. Returning "Unit" bothers me because, as you said, it's just a sentinel. One could just as well declare a convention of returning zero when no return value is intended. What you want is nothing, but zero is something, not nothing.

Aside from aesthetics and syntactic consistency, the important issue in my view to not allow the return value to be assigned. As a bug-prevention safeguard, the compiler should reject any attempt to assign or use a non-existent return value. I can go along with your suggestion of using () for a non-existent return value as long as it cannot be assigned. But if it's an empty tuple it can be assigned, can't it? If it can, then maybe your alternative D is the right choice.

Rex Kerr

unread,
Aug 21, 2013, 12:08:42 AM8/21/13
to Russ Paielli, scala-debate
Well, what you want is firmly in the "I have infinitely many different kinds of incompatible functions", which I think is a less useful view than the "I have one kind of function" view.

It's very important to allow the empty return to be assigned because otherwise you have to write all your generic code twice: once for handling empty returns, and once for non-empty returns.  Ugh!  Shall we write it all again if we want to return two things, and yet again for three?  No thanks.

Use type information to save you instead.  (That is, you should not be happy with Seq[Unit] if you are expecting Seq[Option[Int]].)

  --Rex

Haoyi Li

unread,
Aug 21, 2013, 1:26:36 AM8/21/13
to Rex Kerr, Russ Paielli, scala-debate
+1 for having () be a type alias for Unit. The number of times I've (incorrectly, by current syntax) tried typing  T => () to represent a return-nothing thunk must be well over a hundred by now.

Having a Tuple1 and Unit being an alias for Tuple0 would be nice too, just for consistency.

Russ Paielli

unread,
Aug 21, 2013, 1:46:45 AM8/21/13
to Rex Kerr, scala-debate
On Tue, Aug 20, 2013 at 9:08 PM, Rex Kerr <ich...@gmail.com> wrote:
Well, what you want is firmly in the "I have infinitely many different kinds of incompatible functions", which I think is a less useful view than the "I have one kind of function" view.

You lost me there. Why infinitely many? I count two: (1) functions that return something and (2) functions that don't return anything (other than a place holder for nothing). Actually, the latter are not even "functions" in the mathematical sense. They are procedures, which is why I am suggesting that they be called what they are.
 

It's very important to allow the empty return to be assigned because otherwise you have to write all your generic code twice: once for handling empty returns, and once for non-empty returns.  Ugh!  Shall we write it all again if we want to return two things, and yet again for three?  No thanks.

I don't understand what you mean. Can you provide a simple example?

Ken Scambler

unread,
Aug 21, 2013, 2:00:42 AM8/21/13
to Russ Paielli, Rex Kerr, scala-debate
This "proc" idea is a total non-starter.  I can't think of a single language that uses a different syntactic form depending on whether methods/functions/subroutines return something or not, not even Haskell.  

Scala is all about unifying concepts where applicable, and only has four class level concepts; val, var, def and type.   Why on earth would we add another one that is perfectly well addressed by the def form?  As Hans pointed out, you want to be able to generalise over functions even if their return type is useless.  It's the same concept.   Even if you paper over it in the way you suggested, this is more confusing and less simple than the status quo.

Cheers,
Ken

Tony Sloane

unread,
Aug 21, 2013, 2:25:59 AM8/21/13
to Ken Scambler, scala-debate
On 21/08/2013, at 4:00 PM, Ken Scambler <ken.sc...@gmail.com> wrote:

This "proc" idea is a total non-starter.  I can't think of a single language that uses a different syntactic form depending on whether methods/functions/subroutines return something or not, not even Haskell.   

Actually, there is a very famous example: Pascal. It uses the "function" keyword for subroutines that return a value, and "procedure" for ones that don't. Pascal is not used much now of course, but it's been quite influential on the design of many other languages.

(Note: I'm not arguing for two separate concepts in Scala...)

regards,
Tony

Ken Scambler

unread,
Aug 21, 2013, 2:42:08 AM8/21/13
to Tony Sloane, scala-debate
Thanks Tony, that's interesting!  I don't know any Pascal at all.   The funny thing is of course that Odersky studied under Niklaus Wirth, so I'm sure he'd be very familiar with the details!

Russ Paielli

unread,
Aug 21, 2013, 2:43:10 AM8/21/13
to Ken Scambler, Rex Kerr, scala-debate
On Tue, Aug 20, 2013 at 11:00 PM, Ken Scambler <ken.sc...@gmail.com> wrote:
This "proc" idea is a total non-starter.  I can't think of a single language that uses a different syntactic form depending on whether methods/functions/subroutines return something or not, not even Haskell.

Ever heard of Ada?
 

Scala is all about unifying concepts where applicable, and only has four class level concepts; val, var, def and type.   Why on earth would we add another one that is perfectly well addressed by the def form?  As Hans pointed out, you want to be able to generalise over functions even if their return type is useless.  It's the same concept.   Even if you paper over it in the way you suggested, this is more confusing and less simple than the status quo.

According to that line of thinking, we don't need both val and var. var can always be used in place of val.

Ken Scambler

unread,
Aug 21, 2013, 2:57:04 AM8/21/13
to Russ Paielli, Rex Kerr, scala-debate
On 21 August 2013 16:43, Russ Paielli <russ.p...@gmail.com> wrote:
On Tue, Aug 20, 2013 at 11:00 PM, Ken Scambler <ken.sc...@gmail.com> wrote:
This "proc" idea is a total non-starter.  I can't think of a single language that uses a different syntactic form depending on whether methods/functions/subroutines return something or not, not even Haskell.

Ever heard of Ada?
 
Yes, but I don't know anything about it, like Pascal and many others.   Hence the qualified language.  I could perhaps have phrased it better; I think I can safely state that it's not a very commonly chosen distinction in PLs.
 

Scala is all about unifying concepts where applicable, and only has four class level concepts; val, var, def and type.   Why on earth would we add another one that is perfectly well addressed by the def form?  As Hans pointed out, you want to be able to generalise over functions even if their return type is useless.  It's the same concept.   Even if you paper over it in the way you suggested, this is more confusing and less simple than the status quo.

According to that line of thinking, we don't need both val and var. var can always be used in place of val.
 
- Syntactically, vals are fixed values, and can be imported from, and can use the .type singleton type syntax.  Vars cannot. 
- Physically, vars generate two methods, foo and foo_$eq, whereas vals generate only one.  
- Semantically, the difference is enormous; a value that cannot change is vastly easier to reason about than one that is externally modifiable.

I don't think any of these apply to a distiction between Unit/non-unit methods.

Russ Paielli

unread,
Aug 21, 2013, 3:31:13 AM8/21/13
to Ken Scambler, Rex Kerr, scala-debate
Well, you might be interested to know that Ada is the standard higher-order language for avionics. The next time you fly, your life may depend on it.

What I meant is that you can take any working Scala code, replace every occurrence of "val" with "var" and it will work exactly the same if I am not mistaken. So why do we need both again?

No, I'm not suggesting that we get rid of "val." I'm just suggesting that "proc" serves a similar purpose by clearly distinguishing between a function and a procedure. If that distinction is not fundamental, then functional programming is not fundamental.

Also, after thinking about Rex's earlier post, I realized that the number of values returned by a function is not nearly as fundamental as whether it returns any at all. Any number of returned values can be trivially bundled into single tuple after all (which looks a lot like an argument list). So the distinction between one and any other number of returned values is less fundamental than the distinction between zero and more than zero. The important thing, as I said earlier, is that the compiler should not allow a procedure to appear on the right side of an assignment.

Rich Oliver

unread,
Aug 21, 2013, 5:05:56 AM8/21/13
to scala-...@googlegroups.com
  • its own special syntax for procedures, def foo { println(1) }
  • Unit-returning methods with inference, def foo = { println(1) }
  • has explicit Unit-returning methods, def foo: Unit = { println(1) }
I'd like to see option 1 removed.

As for option 2, has consideration been given to disallowing type inference of Unit, Any, AnyRef and AnyVal?

Ross Rose

unread,
Aug 21, 2013, 9:21:55 AM8/21/13
to Russ Paielli, Rex Kerr, scala-debate
I've created a ReturnType class modeled after Some that has a Void object, and an Error case class.

Billy

Marc Siegel

unread,
Aug 21, 2013, 9:25:13 AM8/21/13
to scala-...@googlegroups.com
Having worked to convince most of 26 developers here to learn and adopt Scala, I'd like to express a strong agreement with the unification suggested up-thread by Rex Kerr:

  1.  Replace usage of Unit by () -- as an alias of Tuple0 and adding Tuple1 probably
  2.  Remove procedure syntax -- just use ()

That would leave us, finally, with a unified syntax that is easy to explain:

  def f_0_0(): () = { ... }
  def f_0_1(): (Int) = { ... }
  def f_0_2(): (Int, String) = { ... }

  def f_1_0(Int): () = { ... }
  def f_1_1(Int): (Int) = { ... }
  def f_1_2(Int): (Int, String) = { ... }

I can't understand, from any of the above discussion, why this seemingly obvious simplification and unification is not adopted. What is gained by calling () Unit in some circumstances, and having separate syntax for returning it?

Thanks,
-Marc

--
Marc Siegel
Team Lead
TIM Group

Hanns Holger Rutz

unread,
Aug 21, 2013, 9:45:26 AM8/21/13
to Ken Scambler, Russ Paielli, Rex Kerr, scala-debate
well said.

+100

Hanns Holger Rutz

unread,
Aug 21, 2013, 9:48:47 AM8/21/13
to Tony Sloane, Ken Scambler, scala-debate

On 21 Aug 2013, at 08:25, Tony Sloane wrote:

> On 21/08/2013, at 4:00 PM, Ken Scambler <ken.sc...@gmail.com> wrote:
>
>> This "proc" idea is a total non-starter. I can't think of a single language that uses a different syntactic form depending on whether methods/functions/subroutines return something or not, not even Haskell.
>
> Actually, there is a very famous example: Pascal. It uses the "function" keyword for subroutines that return a value, and "procedure" for ones that don't. Pascal is not used much now of course, but it's been quite influential on the design of many other languages.

I am sure, Ceylon will pay tribute to that historical line and have 3 1/2 different keywords to distinguish these "cases". They will probably be called `effectual`, `situation` and `transformation`

x

Hanns Holger Rutz

unread,
Aug 21, 2013, 9:50:28 AM8/21/13
to Russ Paielli, Ken Scambler, Rex Kerr, scala-debate
you can also change every `private` to `public` and it will "work", so by that logic we could remove visibility altogether.
> pergrundrecht



Simon Ochsenreither

unread,
Aug 21, 2013, 9:59:54 AM8/21/13
to scala-...@googlegroups.com, Tony Sloane, Ken Scambler

I am sure, Ceylon will pay tribute to that historical line and have 3 1/2 different keywords to distinguish these "cases". They will probably be called `effectual`, `situation` and `transformation`

:-P

Just a reminder to those interested: The plan was to get rid of stuff. This means ending up with less than before, not more.

Hanns Holger Rutz

unread,
Aug 21, 2013, 10:02:14 AM8/21/13
to Haoyi Li, Rex Kerr, Russ Paielli, scala-debate
consistency is that values != types. there is no other case in scala were you would mix the names of the two up. that would be a very horrible change IMO.

the problem of course is that you use double right arrow in both cases

val f: Any => Unit = any => ()

where the following is clearer

val f: Function1[Any, Unit] = any => ()

But you can write

val Unit = ()

val f: Any => Unit = Any => Unit

is that any better or easier to understand?

Ken Scambler

unread,
Aug 21, 2013, 10:44:36 AM8/21/13
to Russ Paielli, Rex Kerr, scala-debate
On 21 August 2013 17:31, Russ Paielli <russ.p...@gmail.com> wrote:
Well, you might be interested to know that Ada is the standard higher-order language for avionics. The next time you fly, your life may depend on it.
 
That's interesting, thanks Russ.
 

What I meant is that you can take any working Scala code, replace every occurrence of "val" with "var" and it will work exactly the same if I am not mistaken. So why do we need both again?

The restriction imposed by using "val" over "var" is immensely useful, because it massively reduces the number of possible incorrect programs we can write.   

What does the restriction imposed by "proc" buy us?   It doesn't matter if we accidentally return an Int or something, because the caller would ignore the value anyway.  If the proc is on the right side of an assignment, it doesn't matter, because the type system stops us from doing anything silly with ().  

Additionally, it's useful to treat Unit as just another type; the IO monad in Haskell/Scalaz is something that comes to mind.

No, I'm not suggesting that we get rid of "val." I'm just suggesting that "proc" serves a similar purpose by clearly distinguishing between a function and a procedure. If that distinction is not fundamental, then functional programming is not fundamental.
 
I don't think it's fundamental; or at least, it's not useful to consider it so.  Being able to abstract over the two cases is a good thing; they both represent blobs of code that take arguments, and are represented in the same way on the underlying platform.

Cheers,
Ken

Ken Scambler

unread,
Aug 21, 2013, 10:57:36 AM8/21/13
to Hanns Holger Rutz, Haoyi Li, Rex Kerr, Russ Paielli, scala-debate
I like val Unit = ().   We certainly call the value "unit" when we are reading the code.  The () symbol is noisy syntactically, because parentheses already represent method calls, nesting and tupling.  In a signature with lots of ([<: :])(:[[]]): () it becomes hard to stomach.


Jason Zaugg

unread,
Aug 21, 2013, 11:01:34 AM8/21/13
to Ken Scambler, Hanns Holger Rutz, Haoyi Li, Rex Kerr, Russ Paielli, scala-debate
On Wed, Aug 21, 2013 at 4:57 PM, Ken Scambler <ken.sc...@gmail.com> wrote:
I like val Unit = ().   We certainly call the value "unit" when we are reading the code.  The () symbol is noisy syntactically, because parentheses already represent method calls, nesting and tupling.  In a signature with lots of ([<: :])(:[[]]): () it becomes hard to stomach.

It's taken, somewhat confusingly, by the companion object of the type Unit:

scala> Unit
res0: Unit.type = object scala.Unit

 -jason

Hanns Holger Rutz

unread,
Aug 21, 2013, 11:17:14 AM8/21/13
to Ken Scambler, Haoyi Li, Rex Kerr, Russ Paielli, scala-debate
But when I look at where I actually use () -- which only happens in very specific context, like the one below -- I like the status quo:

trait Foo {
def dispose(): Unit
}

object Bar extends Foo {
def dispose() = () // no resources allocated
}

This makes sense for me, as I could also write `= {}`. Whereas

object Bar extends Foo {
def dispose() = Unit // no resources allocated
}

hmmm, that doesn't look right to me. Like if I was returning a type or a singleton object, something of "substance".

best, .h.h.

Rex Kerr

unread,
Aug 21, 2013, 2:33:37 PM8/21/13
to Hanns Holger Rutz, Haoyi Li, Russ Paielli, scala-debate
I don't want () to be an alias for Unit.  It's more fundamental than that.  The model is inherently exposing you to implementation details that create special cases and confuse things.

I want Unit to go away, and for a consistent Tuple0 to replace it.

  --Rex



Lars Hupel

unread,
Aug 21, 2013, 4:01:35 PM8/21/13
to scala-...@googlegroups.com
> What I meant is that you can take any working Scala code, replace every
> occurrence of "val" with "var" and it will work exactly the same if I am
> not mistaken. So why do we need both again?

Not sure if already mentioned in this thread: No. It won't, because of
stable references.

scala> class T { object O }
defined class T

scala> val t = new T
t: T = T@4884e6bc

scala> t.O
res0: t.O.type = T$O$@134bfb2f

scala> var t = new T
t: T = T@300283d9

scala> t.O
res1: T#O.type = T$O$@5a8c6475

scala> t.O: t.O.type
<console>:10: error: stable identifier required, but t.O found.
t.O: t.O.type
^


Matthew Pocock

unread,
Aug 22, 2013, 8:31:39 AM8/22/13
to Marc Siegel, scala-debate
+1


--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--
Dr Matthew Pocock
Turing ate my hamster LTD

Integrative Bioinformatics Group, School of Computing Science, Newcastle University

skype: matthew.pocock
tel: (0191) 2566550
Reply all
Reply to author
Forward
0 new messages