Underscore in List.filter

前端 未结 3 1899
夕颜
夕颜 2020-12-02 00:15

Why this is not working:

List(true,false).filter(_).size

The error says:

:8: error: missing parameter type f         


        
相关标签:
3条回答
  • 2020-12-02 00:50

    The first error is because Scala doesn't know what to make of _. So try this...

    List(true,false).filter(_:Boolean).size
    

    After that, you get more info:

    <console>:8: error: type mismatch;
    found   : Boolean
    required: (Boolean) => Boolean
     List(true,false).filter(_:Boolean).size
    

    It's just evaluating the _ just as the value and not as function. Per the ScalaDoc

    filter (pred: (A) ⇒ Boolean): GenTraversable[A] 
    
    0 讨论(0)
  • 2020-12-02 00:55

    Handling of _ is a bit tricky in Scala and as a side note I think error handling should be improved a bit. Back to topic, have a look at this example:

    def twice(i: Int) = i * 2
    def gt2(j: Int) = j > 2
    
    List(1,2,3) filter gt2
    

    This compiles fine and works as expected. However trying to compose functions results with cryptic error:

    List(1,2,3) filter gt2(twice(_))
    
      error: missing parameter type for expanded function ((x$1) => twice(x$1))
              List(1,2,3) filter gt2(twice(_))
                                         ^ 
    

    What happened? Basically when Scala compiler sees underscore it binds it into the most immediate context, which is twice(_) in this case. This means we are now calling gt2() with a function twice as an argument. What compiler knows is that this function takes an argument and returns the same type. Arguably it should figure out the type of this argument and return type is Int based on twice() signature, however it uses x$1 placeholder temporarily until he figures that out later.

    Unfortunately it is unable to do that since gt2() expects an Int while we are providing a function (at least that is what the compiler thinks).

    So why does:

    List(1,2,3) filter {k => gt2(twice(k))}
    

    work? The compiler does not know the type of k in advance. However it knows that gt2 returns Boolean and expected an Int. It also knows that twice() expects an Int and returns one as well. This way it infers the type of k. On the other hand the compiler knows from the beginning that filter expects Int => Boolean.


    That being said back to your case. The underscore alone ((_)) is not consider a "context" so the compiler searches for another most immediate enclosing context. The !_ would have been considered a function on its own, as well as _ == true. But not the underscore alone.

    So what is the closest immediate context (I'm sure there is a scientific name for that...) in this case? Well, the whole expression:

    (x$1) => List(true, false).filter(x$1).size
    

    The compiler thinks you are trying to create a function that takes some parameter of unknown type and returns something of the type of an expression: List(true, false).filter(x$1).size. Again arguably it should be able to figure out that filter takes Boolean => Boolean and returns Int (.size), but apparently it doesn't.


    So what can you do? You have to give the compiler a hint that the underscore should be interpreted in smaller context. You can say:

    List(true,false) filter (_ == true)
    List(true,false) filter (i => i)
    List(true,false) filter identity
    
    0 讨论(0)
  • 2020-12-02 00:55

    I see the error message is much improved! Let's see what the error message said you wrote, and your working version:

    ((x$1) => List(true, false).filter(x$1).size)
              List(true,false).filter(a => a).size
    

    Or, adjusting spaces, parenthesis and variable names:

    a => List(true, false).filter(a     ).size
         List(true, false).filter(a => a).size
    

    Does it look the same now?

    Briefly, when you pass underscore in place of a parameter, you are doing partial function application. You are probably more familiar with underscores being used as placeholders for parameters in anonymous functions, which is what happens when it appears in an expression, like _ + 1. These two uses are different, even if they both result in an anonymous function.

    0 讨论(0)
提交回复
热议问题