Syntax proposal for definition of partial functions with 'def'


#1

During the week, I ended up trying a syntax for defining a partial function that didn’t please the compiler. I found the workaround, but was left thinking maybe the thing I intuitively tried would make sense.

So I’d like to hear your replies on this. It has to do with the difference (or lack thereof) between normal and partial functions.

In short, this does not compile (on Scala 2.12.6):

scala> def pf: String = { case 0 => "zero" }

I found the workaround (using val and PartialFunction[Any,String]), but the point is. Why shouldn’t this work?

The use case is wanting to define a partial function, with an explicitly defined return type (but leaving the possible argument types to be deduced from the cases. In my case, I needed to use the pf twice, which is why anonymous pf wasn’t enough.

What I do like about this, is that use of def (currently not used for partial functions, I think) brings the partial functions closer to normal functions, in the programmer’s mental model. They are anyways connected. One can pass either one to many methods (map, collect, …) and one can ‘.lift’ a partial function to be a fully defined one.

What do you think?

I’ve programmed in Scala for years without needing this, so it’s not a major deficiency. But maybe also the inverse is true: since there’s no easy way to name a partial function, we rarely name them.


#2

I guess that you have some misunderstanding of the difference between

  • methods
  • functions
  • partial functions

You can refer to the corresponding SO answer to make out how a def is different from an instance of a function (be it assigned to a val or var).

So with respect to your post, I’d say that a partial function is certainly related to a function instance in scala, but only relatively to a method definition.

When you write

  def pf: String = ???

you’re creating a method that expects an empty argument list (no parenthesis) and a String return type.

on the other hand { case 0 => "zero"} is roughly shorthand syntax to create an instance of a partial function

new PartialFunction[Int, String] {
  def apply(in: Int): String = "zero"

  def isDefinedAt(x: Int): Boolean = x == 0
}

The pf definition expects to return a type String and you’re providing it a PartialFunction as return value.

You can compare this to doing

def pf: String = (i: Int) => "zero"

which again won’t compile, as the types don’t align correctly

I hope this somehow directs to you to figure out exactly what’s wrong with your assumptions, otherwise feel free to ask more


#3

Hello,

There are two issues here:

First, functions are objects, not methods, although the compiler may convert methods into functions (which is called lifting - but this has nothing to do with lifting partial functions into functions).

Second, you cannot omit type parameters that are needed to define the implementation of an anonymous class (if they are not needed, they will be inferred as Nothing). A Function in Scala (and in Scala, PartialFunction[T, R] is a Function1[T, R]) is an anonymous class, and the type parameters are needed to implement the apply method.

Best, Oliver


#4

In short, this does not compile (on Scala 2.12.6):

scala> def pf: String = { case 0 => "zero" }

I found the workaround (using val and PartialFunction[Any,String]), but the point is. Why shouldn’t this work?

because Any is a supertype of all types, and you want this to infer, and somehow create a PartialFunction, what would it infer as? PartialFunction[Any, String]? If not, why not?

If I have

trait A
trait B extends A
trait C extends B
case object D extends C

def foo: String = { case D => “It’s D” }

would that expand to

  • PartialFunction[Any, String]?
  • PartialFunction[A, String]?
  • PartialFunction[B, String]?
  • PartialFunction[C, String]?
  • PartialFunction[D, String]?

#5

@ivanopagano, I wasn’t confused or asking for a fix. As I wrote (seems I wrote it twice), I found the workaround alright.

So, let’s see at my use case, and I’ll share a repo where I wrote more about this as well.

you’re creating a method that expects an empty argument list (no parenthesis) and a String return type.

I’m tossing the idea that a currently invalid syntax could be used to give names to partial functions. Not in the realm of methods, though the valid occurrences of such syntax currently show up at methods, as you point out.

The repo is here: https://github.com/akauppi/scala-pf-syntax-idea#try-it-out


#6

Thanks, @martijnhoekstra, for noticing that I was suggesting a syntax extension.

would that expand to

Short answer: it should create a PartialFunction[D,String] since D is the most narrow class covering all possible inputs.

Longer answer: thanks for making me realize this doesn’t work in Scala (without the return type, that is). val pf = { case D => "It's D" } creates the same error.

So that means my innocent proposal would have larger consequences than simply using def as a syntactic sugar, as was my intention.

It might still make sense, though. Personally, I would like to see partial functions and functions closer to each other.


Having thought about this a bit, even turning the suggested syntax to val pf: PartialFunction[Any,String] = { case 0 => "zero" } would work. Since application of partial functions utilises the .isDefinedAt, there type matters less than for functions. In your sample:

trait A
case object D extends A
val foo: PartialFunction[Any,String] = { case D => "It's a D" }
foo(0)
scala.MatchError: 0 (of class java.lang.Integer)
   ...
foo(D)
res7: String = It's a D

That aside, would you see any point in the syntax?


#7

The fundamental problem is that, AFAIK, def means “define a method”. I think what you’re suggesting violates that, and therefore would make the language inconsistent.

The val version is less broken, but honestly, I don’t think it’s very useful. In particular, I disagree with:

I think most folks nowadays regard the use of PartialFunctions over Any as an anti-pattern – enough so that Akka is going through a major face-lift to get rid of that pattern. And outside of Akka, I’ve always wanted my partial functions to be more type-constrained than that.

It’s true that PartialFunction[Any, ...] is not rare. But I don’t think it’s actually something we want to encourage in the language…


#8

Thanks for the opinions.

def is used in addition to methods also in defining functions:

scala> def hello = println("hello")
hello: Unit

My suggestion could just as well render to PartialFunction[D,String]. I am not enough of a language lawyer to know what fits best.


#9

In your example you have - at least according to me, though language is muddled here in scala - defining a method that returns a value of Unit.

The following transcript may be useful:

scala> def greet(name: String) = println(s"Hello, $name") //a method that greets you
greet: (name: String)Unit

scala> val greeter = greet _ //converting the method that greets you to a function (eta expansion)
greeter: String => Unit = $$Lambda$1168/1318227846@6324186b

scala> greeter.apply("with apply") //applying the function explicitly
Hello, with apply

scala> greeter("brackets are syntactic sugar for apply")
Hello, brackets are syntactic sugar for apply

scala> //but not

scala> greet.apply("foo")
<console>:13: error: missing argument list for method greet
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `greet _` or `greet(_)` instead of `greet`.
       greet.apply("foo")
       ^

scala> //a method that takes a function argument, and executes the given function twice

scala> def doTwice(arg: String, f: String => Unit): Unit = { f(arg); f(arg); }
doTwice: (arg: String, f: String => Unit)Unit

scala> //pass the greeter

scala> doTwice("twicegreeter", greeter)
Hello, twicegreeter
Hello, twicegreeter

scala> //if a function type expected, and a method reference is given, the eta expansion happens automatically

scala> doTwice("with automatic eta expansion", greet)
Hello, with automatic eta expansion
Hello, with automatic eta expansion

scala> //except when the method has no parameter list

scala> def giveMeAFunction(f: () => Int): Int = f()
giveMeAFunction: (f: () => Int)Int

scala> def returnSeven = 7 //a method (with no parameter list)
returnSeven: Int

scala> def withEmptyParameterList() = 7 //another method (with an empty paremeter list)
withEmptyParameterList: ()Int

scala> val sevenReturner = returnSeven _ //a function
sevenReturner: () => Int = $$Lambda$1170/1850317761@7d365620

scala> val stillAFunction = withEmptyParameterList _ //also a function
stillAFunction: () => Int = $$Lambda$1171/2004279015@165a2dbc

scala> sevenReturner() //Function0.apply
res24: Int = 7

scala> //but also

scala> stillAFunction() //Function0.apply
res25: Int = 7

scala> giveMeAFunction(sevenReturner) //yup
res26: Int = 7

scala> giveMeAFunction(stillAFunction) //yup
res27: Int = 7

scala> giveMeAFunction(withEmptyParameterList) //ok, but deprecated
<console>:14: warning: Eta-expansion of zero-argument methods is deprecated. To avoid this warning, write (() => withEmptyParameterList()).
       giveMeAFunction(withEmptyParameterList) //ok, but deprecated
                       ^
res28: Int = 7

scala> giveMeAFunction(returnSeven) //nope
<console>:14: error: type mismatch;
 found   : Int
 required: () => Int
       giveMeAFunction(returnSeven) //nope
                       ^

recommended reading:

https://tpolecat.github.io/2014/06/09/methods-functions.html

While documentation (and indeed the spec) is not always clear on methods, functions, values of FunctionN, and the similarities/differences between those, the convention that a method is a def and a function is some value of the FunctionN family is (by far) the most commonly used, and the most consistent - and the same as @jducoeur, @ivanopagano and @curoli use above.


#10

yes, I understand, and I didn’t make myself clear

the point of reframing once more what your syntax means as of the current language was to highlight, as @jducoeur already pointed out, that the def syntax implies definition of a method, which is different from a function in ways that are more subtle that is practical I’m afraid.

Using def to denote the reference to a function, even if partial, would really create an inconsistency in the language that will amplify confusion over an already blurred separation line.

It would be totally different if both methods and functions were interchangeably defined with a common syntax


#11

Hello,

I think the source of confusion is that outside of Scala, function most often means something else.

(1) In the early days, programming languages had two types of subroutines: procedures and functions. Functions return a value, and procedure don’t. Likewise, there is a distinction between expressions, which are evaluated into a value, and statements, that don’t (at least not necessarily).

(2) Then came languages like C, where every statement is an expression and every subroutine is a function. Things that do not return a value are assigned a placeholder type (void) that does not have values.

(3) Then came object-oriented languages like C++. Variables and functions that are members of a type or object are called fields and methods, respectively.

(4) Some languages like Java are purely object oriented which means that everything is a member of a type, so now every variable is either a field or a local variable, and every function is a method.

(5) Scala is purely OO in the same sense as Java: every piece of code is part of some type or object and therefore a method. Scala has functions, which are objects of a certain type with an apply method. They are not subroutines like in C/C++. Java now (since 8) has a similar thing called lambdas.

If you type a lonely def in the Scala shell (aka REPL), it may not be apparent, but it is also part of an object: the Scala shell wraps every code fragment into an auto-generated object. Check it out: there is a this reference, and it gives you the enclosing object:

**Welcome to Scala 2.12.4 (OpenJDK 64-Bit Server VM, Java 1.8.0_162).
Type in expressions for evaluation. Or try :help.

println(this)
$line3.$read$$iw$$iw$@4d21c56e**

Best, Oliver


#12

There’s been some confusion around functions and methods and I agree that we shouldn’t use def for partial functions (because it defines methods, not functions) but I find the idea of a partial method interesting. I don’t know if it is actually useful but it could improve the symmetry between methods and functions.

You’d need some additional hidden methods to implement the partial function logic (but we have precedent for that with @specialized). The main source of complexity that I see is the eta-expansion rule. Eta-expansion for methods is straight-forward (create a lambda that repeats all params and passes them to the method) but for partial methods to expand into partial functions (otherwise what’s the point) you need to know about the implementation details or leave it at “magic happens here”.


#13

The idea of a partial method seems interesting, but considering all you can do with a method is applying it, a partial method would need other members to be useful, and at that point, it’s a function.


#14

@curoll Thanks for a good description. This certainly may be applicable (pun) to my case, coming to Scala from C++ and not Java.


#15

Well, that is, unless the partial method definition also supplies information equivalent to the isDefined method of the corresponding PartialFunction.

But apart from being feasible technically, the added simmetry will add some value to the language or will it create even more confusion?

From my PoV I’ve found myself complaining more than once about the fact that trying to compose methods like functions using eta-expansion is rarely a pleasure to see, and makes the code unreadable to the point that overall it loses any usefulness…

Otoh I admit to be a supporter of making all curried by default and defs and functions as equivalent as possible