Search code examples
scalascala-2.8named-parameters

Scala 2.8: type inference of anonymous functions as default parameters


In Scala 2.8.0 RC 2 this definition:

  def buttonGroup[T](values: Array[T], textProvider: T => String = (t: T => t.toString)) = ...

gives the error message:

not found: value t

  def buttonGroup[T](values: Array[T], textProvider: T => String = (_.toString)) = ...

gives

missing parameter type for expanded function ((x$1) => x$1{}.toString{}){}

Only this works:

 textProvider: T => String = (_:T).toString

Why?


Solution

  • Either of these work, without type annotation:

    def buttonGroup[T](values: Array[T], textProvider: T => String = (t: T) => t.toString) = 0
    
    def buttonGroup[T](values: Array[T], textProvider: T => String = {t: T => t.toString}) = 0
    

    But why don't your variations work?

    The first one actually isn't valid Scala in any context:

    scala> (t: Any => t.toString)
    <console>:1: error: ';' expected but ')' found.
           (t: Any => t.toString))
                                 ^
    

    The second expression _.toString uses the placeholder syntax for anonymous functions, and only works if the expression has an expected type.

    scala> def foo[T] = { (_.toString) : (T => String) }
    foo: [T](T) => String
    

    The problem is that the default expression for a parameter whose type depends an type parameter doesn't have an expected type. This seems counter-intuitive, why wouldn't it have the declared type of the argument as the expected type? It turns out that the expression can have a more specific type, and that type checking is deferred to the call-site:

    scala> def foo[T](t: T = "string-t") = t
    foo: [T](t: T)T
    
    scala> foo(1)
    res4: Int = 1
    
    scala> foo()
    res5: java.lang.String = string-t
    
    scala> foo[Int]()
    <console>:7: error: type mismatch;
     found   : java.lang.String
     required: Int
    Error occurred in an application involving default arguments.
           foo[Int]()
    

    If the type of textProvider doesn't include the type parameter T, the default expression has an expected type, and you can use the placeholder syntax:

    scala> def buttonGroup[T](values: Array[T], textProvider: Any => String = _.toString) = 0
    buttonGroup: [T](values: Array[T],textProvider: (Any) => String)Int
    

    For a detailed explanation of the design of named and default parameters, I recommend Lucas Rytz's Scala Days presentation.