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

3

46

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) -> ....

Joellyn answered 13/10, 2012 at 1:48 Comment(1)
The accepted answer only addresses question (a). Question (b) is answered in my answer.Joellyn
D
7

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.

Duaneduarchy answered 13/10, 2012 at 1:49 Comment(1)
In my case, in the context of a for loop (map()) the return value of the function needed some clarification. This compiled: def aMethodRetSeq(root: T): Seq[U] whereas this did not: def aMethodRetSeq(root: T): U.Flyfish
N
41

{ 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.

Nary answered 13/10, 2012 at 3:6 Comment(2)
Thanks. All the local defs repeat the Foo, which is what I wanted to avoid. It seemed redundant, but now I see why it is not. See my answer for a workaround.Joellyn
further option to add the type info (here: directly into the case clause itself): e.g. myListMap.map[Int,Int]( { case (x: Int, y: Int) => (x+1, y+2) } )Sphygmo
D
7

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.

Duaneduarchy answered 13/10, 2012 at 1:49 Comment(1)
In my case, in the context of a for loop (map()) the return value of the function needed some clarification. This compiled: def aMethodRetSeq(root: T): Seq[U] whereas this did not: def aMethodRetSeq(root: T): U.Flyfish
J
4

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.

Joellyn answered 13/10, 2012 at 19:52 Comment(3)
Try splitting your parameter list of switch in two so that the type of A can be inferred in your functions. You still don't need to use the extractor. Existing construct is option.map(function if some).getOrElse(function if none), but Scala 2.10 has a fold method on Option to do the same thing.Nary
Thanks a lot. See the solution in my edit to my answer. It is interesting that currying has such a strong effect on typing.Joellyn
So in the end I just reinvented Option.fold. (I think I was still using 2.09 at the time, which is why I didn't find fold to start with.) Some people don't like Option.fold because it is not the same as fold on a one-element sequences. Others because they'd prefer the arguments the other way around. The way I see it, the argument order is similar to that of Seq.fold and there are both pragmatic and theoretical reasons that it should differ from fold on a sequences of length 1.Joellyn

© 2022 - 2024 — McMap. All rights reserved.