Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The argument types of an anonymous function must be fully known. (SLS 8.5)

I have a function literal

{case QualifiedType(preds, ty) =>
               t.ty = ty ;
               Some((emptyEqualityConstraintSet,preds)) }

Which results in an error message

missing parameter type for expanded function The argument types of an anonymous function
must be fully known. (SLS 8.5) Expected type was:
? => Option[(Typer.this.EqualityConstraintSet, Typer.this.TypeRelationSet)]

I looked in SLS 8.5, but didn't find an explanation.

If I expand the function myself to

{(qt : QualifiedType) =>
  qt match {case QualifiedType(preds, ty) =>
               t.ty = ty ;
               Some((emptyEqualityConstraintSet,preds)) }}

the error goes away.

(a) Why is this an error?

(b) What can I do to fix it?

I tried the obvious fix, which was to add : QualifiedType between the pattern and the =>, but this is a syntax error.


One thing I noticed is that the context makes a difference. If I use the function literal as an argument to a function declared as expecting a QualifiedType => B, there is no error. But if I use it as an argument to a function expecting an A => B, there is an error. I expect that what is going on here is that, as the pattern could conceivably be applied to an object whose type is a supertype of QualifiedType, the compiler is not willing to assign the obvious type without assurance that the function won't be applied to anything that isn't a QualifiedType. Really what I'd like is to be able to write {QualifiedType( preds, ty) => ...} and have it mean the same thing as Haskell's \QualifiedType(preds,ty) -> ....

like image 240
Theodore Norvell Avatar asked Oct 13 '12 01:10

Theodore Norvell


3 Answers

{ case X(x) => ... } is a partial function, but the compiler still doesn't know what your input type is, except that it's a supertype of X. Normally this isn't a problem because if you're writing an anonymous function, the type is known from the context. But here is how you can provide the type:

case class Foo(x: Int)

// via annotation
val f: Foo => Int = { case Foo(x) => x }

// use pattern matching
val f = (_: Foo) match { case Foo(x) => x }

// or more normally, write as a method
def f(a: Foo) = a match { case Foo(x) => x }
def f(a: Foo) = a.x

As you've probably noticed, using function literals / pattern matching here is pretty pointless. It seems in your case you just need a regular method:

def whatever(qt: QualifiedType) = {
  t.ty = qt.ty
  Some((emptyEqualityConstraintSet, qt.preds)) 
}

although you should refactor to remove that mutable state.

like image 69
Luigi Plinge Avatar answered Oct 13 '22 11:10

Luigi Plinge


Here's the SLS quote, for the rest of us:

The expected type of such an expression must in part be defined. It must be either scala.Functionk[S1, . . . , Sk, R] for some k > 0, or scala.PartialFunction[S1, R], where the argument type(s) S1, . . . , Sk must be fully determined, but the result type R may be undetermined.

Otherwise, you answered your question.

like image 7
2 revs, 2 users 91% Avatar answered Oct 13 '22 11:10

2 revs, 2 users 91%


Here is why I wanted to use a function literal and didn't like having to repeat the type twice. I was trying to build my own control construct to factor out all the option matching code. If there is too much overhead, then the control construct doesn't help any. Here is what I wanted to do

//This is the control construct definition
def switch[A,B]( x : Option[A], noneFun : =>B, someFun : A=>B) = x match {
    case None => noneFun 
    case Some(y) => someFun(y) }

//And this is an example of using it.
def foobar( qt : Option[QualifiedType]  ) = 
    switch( qt, {reportError("SNAFU");None},
            {case QualifiedType(preds, ty) =>
               Some((emptyEqualityConstraintSet,preds)) } ) 

The switch control construct compiles fine, but the usage caused an error because the SLS says that where I have an A, I should have a "definite type". That's because this kind of function literal (the kind with "case") is intended for partial functions where the argument could be legitimately any thing at all. I could argue my function literal with an int and that would not be a type error, but merely a matter of all the patterns failing. So the compiler needs some "top down" information to know what type I intend for the parameter of the "expanded function literal", i.e. what to put for X in the following

{(x : X) => x match {case Some(QualifiedType(preds, ty)) =>
               Some((emptyEqualityConstraintSet,preds)) } }

I wonder why the compiler could't use the type of switch to see that I don't intend a partial function and then unify A with QualifiedType. But it doesn't.

Anyway it doesn't compile. But replacing A with Any eliminates the error. The following code actually compiles. What I lose is some type checking.

//This is the control construct definition
def switch[A,B]( x : Option[A], noneFun : =>B, someFun : A=>B) = x match {
    case None => noneFun 
    case Some(y) => someFun(y) }

//And this is an example of using it.
def foobar( qt : Option[QualifiedType]  ) = 
    switch( qt, {reportError("SNAFU");None},
            {case QualifiedType(preds, ty) =>
               Some((emptyEqualityConstraintSet,preds)) } ) 

I'd be interested to know (a) if the above definition of switch can be improved, and (b) if there is already a library function that does what I want.


Added after Luigi's Comment

Here is the final code. Yes, I think it is a fold (catamorphism).

def switch[A,B]( x : Option[A])(noneFun : =>B, someFun : A=>B) = x match {
    case None => noneFun 
    case Some(y) => someFun(y) }

def foobar( qt : Option[QualifiedType]  ) : Option[(EqualityConstraintSet, TypeRelationSet)] =
    switch( qt )({reportError("SNAFU");None},
            {case QualifiedType(preds, ty) =>
               Some((emptyEqualityConstraintSet,preds)) } ) 

Thanks to Luigi.

like image 4
Theodore Norvell Avatar answered Oct 13 '22 10:10

Theodore Norvell