Removing the pain from the partial function syntax

405 views
Skip to first unread message

Simon Ochsenreither

unread,
Feb 1, 2014, 6:07:23 PM2/1/14
to scala-i...@googlegroups.com
I managed my deep annoyance of partial functions by just not touching them, but I had to use them when I did the last Coursera course. What a pain!

It gets additionally painful if one doesn't want to declare the partial functions inline, but wants to extract them to values for readability: Scala punishes this with requiring even more noise than before!
It's mind-boggling to me that we let people use this stuff.

So I sat down, hacked the compiler.

Instead of

    val root: PartialFunction[Double,Double] = {
      case d if (d >= 0) => math.sqrt(d)
    }


one can write

  val root: PartialFunction[Double,Double] =
    case d if (d >= 0) => math.sqrt(d)


now, just like with functions.

I'm planning to do the same for

    future.onSuccess({case _ => println("Success!")})

so that one can write

    future.onSuccess(case _ => println("Success!"))

instead.

Overall, this is an increase in consistency, because users can now treat curly braces as "originating" from the outer scope everywhere, instead of having this special case where the curly braces are hard-coded into to the partial function syntax.

Whether it might be possible to cut down on the PartialFunction[Double,Double] boilerplate remains to be seen, I guess.


Opinions?

Erik Osheim

unread,
Feb 1, 2014, 6:12:27 PM2/1/14
to scala-i...@googlegroups.com
On Sat, Feb 01, 2014 at 03:07:23PM -0800, Simon Ochsenreither wrote:
> So I sat down, hacked the compiler.
>
> Instead of
>
> val root: PartialFunction[Double,Double] = {
> case d if (d >= 0) => math.sqrt(d)
> }
>
> one can write
>
> val root: PartialFunction[Double,Double] =
> case d if (d >= 0) => math.sqrt(d)
>
> now, just like with functions.

+1

This looks nice. I really like destructuring pattern matches when
writing Scala, but the slight syntactic overhead is sometimes a bit
annoying. IMO, this is a step in the right direction, if it doesn't
create ambiguities elsewhere (I certainly don't see any).

-- Erik

Konrad Malawski

unread,
Feb 1, 2014, 6:20:29 PM2/1/14
to scala-i...@googlegroups.com
Hi Simon,
Completely +1, especially the `onSuccess({ case _ => })` syntax always trips of and confuses newcomers (and is ugly).

Even after explaining this to new-to-scala devs they’re more often than not left with an wtf expression on their faces.
And for those who know that gimmick... Yay, less noise! :-)

Would love to see that change introduced.

-- 
Konrad

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

Simon Ochsenreither

unread,
Feb 1, 2014, 6:22:14 PM2/1/14
to scala-i...@googlegroups.com, er...@plastic-idolatry.com
Also:

  type =?>[-A, +B] = PartialFunction[A, B]
  def foo(pf1: String =?> Int, pf2: String =?> Int) = pf1 orElse pf2

Instead of

  foo({case "" => 0}, {case _ => 42})

use

  foo(case "" => 0, case _ => 42)

Tony Sloane

unread,
Feb 1, 2014, 6:38:26 PM2/1/14
to scala-i...@googlegroups.com
On Sat, Feb 01, 2014 at 03:07:23PM -0800, Simon Ochsenreither wrote:
> So I sat down, hacked the compiler.
>
> Instead of
>
> val root: PartialFunction[Double,Double] = {
> case d if (d >= 0) => math.sqrt(d)
> }
>
> one can write
>
> val root: PartialFunction[Double,Double] =
> case d if (d >= 0) => math.sqrt(d)
>
> now, just like with functions.

Seems like a lot of trouble to just save a couple of braces…

Also, does your change support multiple case clauses in a single partial function?

regards,
Tony

Som Snytt

unread,
Feb 1, 2014, 8:18:16 PM2/1/14
to scala-internals
this special case where the curly braces are hard-coded into to the partial function syntax

The braces are a case block.  It's not PF syntax.  As a pattern matching anon fun, it can be either a Function or a PF, as required.  It can also be a function arg.

You're changing

future.onSuccess { case _ => println("Success!") } // no parens, thx

to enable

future.onSuccess ( case _ => println("Success!") )

Can I also do

f onSuccess case _ => Console println "Yeah, baby!"

Do you only get an Expr on the RHS of the arrow?  That would make sense as a compromise, the way you get shortcuts for x => ??? with mild restrictions.

or maybe multiple cases with one result expr each is easy too

f onComplete case Success(_) => Console println "Yeah, baby!" case Failure(_) => Console println "Aargh."

Some people put semis between one-liner cases.

I think the semicolon was chosen as the statement separator because it's the emoticon for a poke in the eye. ;(






--

Simon Ochsenreither

unread,
Feb 1, 2014, 8:42:17 PM2/1/14
to scala-i...@googlegroups.com

Do you only get an Expr on the RHS of the arrow?  That would make sense as a compromise, the way you get shortcuts for x => ??? with mild restrictions.

I plan to make it work exactly like function literals.

Rex Kerr

unread,
Feb 1, 2014, 9:20:49 PM2/1/14
to scala-i...@googlegroups.com
On Sat, Feb 1, 2014 at 3:07 PM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
I managed my deep annoyance of partial functions by just not touching them, but I had to use them when I did the last Coursera course. What a pain!

It gets additionally painful if one doesn't want to declare the partial functions inline, but wants to extract them to values for readability: Scala punishes this with requiring even more noise than before!
It's mind-boggling to me that we let people use this stuff.

So I sat down, hacked the compiler.

Instead of

    val root: PartialFunction[Double,Double] = {
      case d if (d >= 0) => math.sqrt(d)
    }


one can write

  val root: PartialFunction[Double,Double] =
    case d if (d >= 0) => math.sqrt(d)



That's better.  Not super-exciting.
 
now, just like with functions.

I'm planning to do the same for

    future.onSuccess({case _ => println("Success!")})

so that one can write

    future.onSuccess(case _ => println("Success!"))

Meh.  Even less exciting, because you can already

    future.onSuccess{ case _ => println("Success!") }

(and indeed, this is what I do--I never, ever have a superfluous set of parens).  It's nice for when you're comma-separated, though.

Instead,

    future.onSuccess(_ => println("Success!"))

would be the big win.  Why do you have to specify a case when there isn't one?

I like the =?> as a boilerplate-reducer.  I hope it's not too nonintuitive of a symbol.  I also worry that people will try to write

    future.onSuccess(_ =?> println("Success!"))

and be all confused by the error message.

<console>:10: error: missing parameter type for expanded function ((x$1) => x$1.$eq$qmark$greater(println("Success")))

On the other hand,

x match {
  Some(y) =?> println(y)
  None =?> println("sad")
}

would not be hard to get used to.  That would be more serious parser-fiddling, though.  (And I still worry about scaring people with the question mark.  Personally I would like it, but I have a suspicion that my enjoyment might not be worth others' displeasure.)

  --Rex


Simon Ochsenreither

unread,
Feb 1, 2014, 9:27:24 PM2/1/14
to scala-i...@googlegroups.com
Thanks for you comments.

Regarding the =?>: That's currently not part of the plan, I just defined it because signatures tend to become verbose and unreadable PartialFunction and I don't wanted to distract people from the rest.

Chris Hodapp

unread,
Feb 1, 2014, 10:18:20 PM2/1/14
to scala-i...@googlegroups.com
Any idea if there's any real basis for case blocks not being PartialFunction literals? It would seem to me that a set of implicits from partial functions on n-ary Tuples to corresponding-arity functions would do the same job and allow a simpler desugaring of case blocks. Additionally, a set of implicits from n-ary functions to corresponding-arity (always-defined) partial functions could address Rex's "big win" case from his email sent about an hour ago.

Haoyi Li

unread,
Feb 1, 2014, 10:21:13 PM2/1/14
to scala-i...@googlegroups.com
> Seems like a lot of trouble to just save a couple of braces… 

In the status quo there's an incentive to curry functions that take PFs even if you never expect to partially apply them, e.g. in ScalaTest's inside matcher. So people are already putting in a lot of trouble to save a couple of braces, whether you think it's crazy or not.

> It's nice for when you're comma-separated, though.

Yeah, passing in partial functions in a multi-argument function is ugly as sin:

foo(1, {case Seq(x, y) => x + y}, "haha")

> Not super-exciting.

Baby steps...

> and indeed, this is what I do--I never, ever have a superfluous set of parens

...until you want to pass a partial function to a constructor of some sort: new Thing({...}) extends Cow({...}). While this change won't cover the multi-case-block use case (since that'll still be ambiguous with anonymous classes) at least it'll cover the single-case-block use case.

> Some(y) =?> println(y)
 
Looks beautiful, but it seems much harder to do in a backwards compatible way, and the difficulty of doing big/amazing things shouldn't make us not do the small/nice things.

Simon Ochsenreither

unread,
Feb 1, 2014, 10:39:27 PM2/1/14
to scala-i...@googlegroups.com
Hey Haoyi,

do you want to have a look at the changes? You have probably more experience with the parser and currently it feels like I'm just walking in the dark and working in an trial&error kind of way.

Bye,

Simon

Naftoli Gugenheim

unread,
Feb 3, 2014, 11:39:13 PM2/3/14
to scala-internals

It shouldn't. Multiple statements always need curly braces. I don't see a difference between regular statements and case statements in that.

On Feb 1, 2014 6:38 PM, "Tony Sloane" <inky...@gmail.com> wrote:
On Sat, Feb 01, 2014 at 03:07:23PM -0800, Simon Ochsenreither wrote:
> So I sat down, hacked the compiler.
>
> Instead of
>
>    val root: PartialFunction[Double,Double] = {
>      case d if (d >= 0) => math.sqrt(d)
>    }
>
> one can write
>
>  val root: PartialFunction[Double,Double] =
>    case d if (d >= 0) => math.sqrt(d)
>
> now, just like with functions.

Seems like a lot of trouble to just save a couple of braces...


Also, does your change support multiple case clauses in a single partial function?

regards,
Tony

Naftoli Gugenheim

unread,
Feb 3, 2014, 11:39:13 PM2/3/14
to scala-internals

It would be nice. In fact I think at one point it was the plan to introduce the partial function alias arrow. I think it was aborted because function arrows need to associate to the right.

On Feb 1, 2014 9:27 PM, "Simon Ochsenreither" <simon.och...@gmail.com> wrote:
Thanks for you comments.

Regarding the =?>: That's currently not part of the plan, I just defined it because signatures tend to become verbose and unreadable PartialFunction and I don't wanted to distract people from the rest.

--

Tony Sloane

unread,
Feb 3, 2014, 11:46:00 PM2/3/14
to scala-i...@googlegroups.com
Perhaps, but I don’t think a “case statement” is actually a statement at all (or an expression for that matter), it’s a different syntactic category (case clause per the specification).

In fact, a match expression requires the braces around the case clauses, so this argues that partial function syntax should also require them for consistency as is currently the case. If I had a vote, this would be my preference.

Or is the proposal to also allow syntax such as

e match case d => f

??

regards,
Tony

Simon Ochsenreither

unread,
Feb 3, 2014, 11:59:13 PM2/3/14
to scala-i...@googlegroups.com

In fact, a match expression requires the braces around the case clauses, so this argues that partial function syntax should also require them for consistency as is currently the case. If I had a vote, this would be my preference.

The idea is to change the current approach of braces-are-hardcoded-syntax of case-clauses to let the surrounding context decide whether they are necessary.

From a user POV, this is the current situation:
  • "match" says that curly braces are required (which makes sense, because having a match expression with just one statement is kind of odd).
  • "catch" already kind of says "well, if there is only one, why not?":
    val handleEx: Throwable =?> Unit = {case ex => ex.printStackTrace}
    try { } catch handleEx
  • The rule for member declarations, argument lists etc. is "if it is a single expression, leaving out curly braces is even suggested ... EXCEPT for partial functions!"
I'd like to make the last rule more consistent, by removing the special case.

Tony Sloane

unread,
Feb 4, 2014, 12:41:18 AM2/4/14
to scala-i...@googlegroups.com
It’s interesting how a single language feature can develop quite different connections in different people’s brains.

I don’t connect the braces that help to define a partial function with the grouping required for member declarations, argument lists etc. I connect them with the braces from match expressions, since to me an anonymous partial function is just a match expression where the selector expression is implicit. Hence why I prefer to keep the braces for partial functions.

BTW, I’m not sure where your last rule is articulated. For class and trait bodies the braces around member declarations are not optional. I’m missing how the brace question applies to argument lists too.

cheers,
Tony

Simon Ochsenreither

unread,
Feb 4, 2014, 12:49:55 AM2/4/14
to scala-i...@googlegroups.com
Classes and match share the commonality that the common usage scenario has more than one definition.
Members, argument lists haven't. I think that's a reasonable distinction: Don't create possibilities of syntactic variation where it is not necessary, but allow it where it is useful.

Tony Sloane

unread,
Feb 4, 2014, 12:54:59 AM2/4/14
to scala-i...@googlegroups.com
[Probably we should take this to scala-debate…]

On 4 Feb 2014, at 4:49 pm, Simon Ochsenreither <simon.och...@gmail.com> wrote:

> Classes and match share the commonality that the common usage scenario has more than one definition.

Most of the partial functions I write have more than one case clause…

> Members, argument lists haven't. I think that’s a reasonable distinction: Don't create possibilities of syntactic variation where it is not necessary, but allow it where it is useful.

I’m still missing what you mean by these ones. E.g., aren’t argument lists surrounded by parentheses, not braces? Can you give examples?

Tony

Simon Ochsenreither

unread,
Feb 4, 2014, 12:58:51 AM2/4/14
to scala-i...@googlegroups.com
 Another point:

Isn't it pretty inconsistent having to write stuff like

coll.filter(...).map(...).collect{...}.sum

? The issue is that it stops working for multiple arguments:

coll.filter(...).map(...).takesMultiplePFs( {...}, {...} ).sum

Of, course one could make it more consistent by always writing it like this even for calls with a single argument.
But the amount of code out there which has chosen this option is pretty low, and I think if people vote with their feet, they have probably a point here.

Not speaking of the waste of time if one writes down parens first and then realises that curly braces are required ... if thousand developers lose only a few seconds each by that, we have already wasted hours of their time.

Simon Ochsenreither

unread,
Feb 4, 2014, 1:03:33 AM2/4/14
to scala-i...@googlegroups.com

Most of the partial functions I write have more than one case clause…

... and in cases where it is unambiguously parseable it is allowed to do that!
 

> Members, argument lists haven't. I think that’s a reasonable distinction: Don't create possibilities of syntactic variation where it is not necessary, but allow it where it is useful.

I’m still missing what you mean by these ones. E.g., aren’t argument lists surrounded by parentheses, not braces? Can you give examples?

Classes and match statements have usually more than one member/clause. So special-casing the single member/clause case doesn't add any benefit, but increases pointless syntactic variations.
Member declarations and partial functions can "sometimes–usually" have one expression, so it is reasonable to support that case, and in fact members already do, it's just partial functions which act odd here (by requiring curly braces even if the context around them wouldn't require one).

Hanns Holger Rutz

unread,
Feb 4, 2014, 3:17:35 AM2/4/14
to scala-i...@googlegroups.com
+1

In any case, syntax change suggestions should ideally emerge from
looking closely at the language specification. The spec is still very
accessible and clean. If one can define an unambiguous way to define
case clauses without the braces _and_ manage to keep the spec at the
same size or even shrink it, good. If it just means that we add context
specific syntactic rules, I would think that is a net loss.

FWIF, =?> is pretty ugly and will feed the prejudices of each and
everyone who dislikes Scala because of its cryptic syntax. If at all,
why not re-use the arrow-assoc style arrow:

type -> [-A, +B] = PartialFunction[A, B]

val x: Any -> String = {
case "foo" => "bar"
}


Best, .h.h.

Jason Zaugg

unread,
Feb 4, 2014, 3:22:33 AM2/4/14
to scala-i...@googlegroups.com
On Tue, Feb 4, 2014 at 9:17 AM, Hanns Holger Rutz <con...@sciss.de> wrote:
+1

In any case, syntax change suggestions should ideally emerge from
looking closely at the language specification. The spec is still very
accessible and clean. If one can define an unambiguous way to define
case clauses without the braces _and_ manage to keep the spec at the
same size or even shrink it, good. If it just means that we add context
specific syntactic rules, I would think that is a net loss.

FWIF, =?> is pretty ugly and will feed the prejudices of each and
everyone who dislikes Scala because of its cryptic syntax. If at all,
why not re-use the arrow-assoc style arrow:

    type -> [-A, +B] = PartialFunction[A, B]

    val x: Any -> String = {
      case "foo" => "bar"
    }

Something like this was tried briefly but reverted when we deemed that the difference in associativity with `A => B`  would have been too confusing.

User defined infix types parse like this:

A -> B -> C
(A -> B) -> C

Whereas the builtin Function syntax parses like this:

A => B => B
(A => (B => C)

-jason

Hanns Holger Rutz

unread,
Feb 4, 2014, 3:28:09 AM2/4/14
to scala-i...@googlegroups.com
Ah I didn't think about the associativity...

I just had this thought: Admittedly, one should be cautious re-using
syntax elements. We all know the lamentation about `_`.

On the other hand, we use the `->` method to create `Tuple2` instances,
most prominently as elements of a `Map`. Now a `Map[A, B]` consists of
elements of type `Tuple[A, B]`, constructed as `a -> b`.

Now funnily, a `Map[A, B]` is also a `PartialFunction[A, B]`, so perhaps
the mental load isn't too high to accept that `->` is both a type and a
method? It's not exactly as close as `object ::` versus method `::`, but
still...

...

If we would introduce `->` as right-associative to the spec, the problem
might be that the method is still left-associative...

Best, .h.h.

Naftoli Gugenheim

unread,
Feb 4, 2014, 3:57:58 AM2/4/14
to scala-internals
scala> type ->:[-T,+R] = PartialFunction[T,R]
defined type alias $minus$greater$colon

scala> def m(x: Int ->: String ->: Boolean) = null
m: (x: ->:[Int,->:[String,Boolean]])Null

scala> m{ case 20 => { case "" => true } }
res0: Null = null

Simon Ochsenreither

unread,
Feb 4, 2014, 5:51:19 PM2/4/14
to scala-i...@googlegroups.com

The spec is still very accessible and clean.

You a re probably the first person who I heard saying that. :-)


If one can define an unambiguous way to define
case clauses without the braces _and_ manage to keep the spec at the
same size or even shrink it, good. If it just means that we add context
specific syntactic rules, I would think that is a net loss.

I really hope that it doesn't make the grammar more complex, but I think that even a slight increase here would be acceptable, considering the simplification from a user perspective.

 
FWIF, =?> is pretty ugly and will feed the prejudices of each and
everyone who dislikes Scala because of its cryptic syntax.

As mentioned, =?> is not part of the proposal. I just use it for the examples, because PartialFunction[Foo, Bar] is just too painful to write.

Paolo G. Giarrusso

unread,
Feb 16, 2014, 11:18:28 PM2/16/14
to scala-i...@googlegroups.com
Only if they don't end with colon. For instance, =>: is right-associative, and I use it in all my projects. So you just need to use =?>: for partial functions — or ->: if you want (I don't like this, but that's bikeshedding and I'm not motivated for that).
Maybe you don't want this in the standard library, but it'd be still rather useful.

Naftoli Gugenheim

unread,
Feb 17, 2014, 4:13:27 AM2/17/14
to scala-internals
I guess it wasn't clear that that's what I was trying to say. :)



--

Paolo G. Giarrusso

unread,
Feb 17, 2014, 11:23:58 AM2/17/14
to scala-i...@googlegroups.com
On Monday, February 17, 2014 10:13:27 AM UTC+1, nafg wrote:
I guess it wasn't clear that that's what I was trying to say. :)

No, sorry, I simply answered an earlier post and missed yours. My bad. I should think again about posting after a night debugging Scalac.

Naftoli Gugenheim

unread,
Feb 17, 2014, 5:10:12 PM2/17/14
to scala-internals

Not at all, things like that happen all the time. No worries!

Reply all
Reply to author
Forward
0 new messages