Bobby Tables
Bobby Tables

Reputation: 1184

Underscore Causing Difficulties

I have the following code, which is supposed to search through an array and see if the anything matches the second argument.

def any(check: Set[Any], expr: Boolean): Boolean = {
  var checked = check.filter(_ => expr)
  if (checked == Set())
    return false
  else
    return true
}

It is supposed to be called like this: any(Set(3, 4, 5, 6), _ > 5)

But when I call it:

error: missing parameter type for expanded function ((x$1) => x$1.$greater(5))

I have very little experience with functional languages, and Scala, so, please give me a thorough explanation of what is going on and how to fix it!

Upvotes: 0

Views: 230

Answers (4)

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297155

There are some misconceptions about Scala in this code. The first one that needs to be addressed is this:

def any(check: Set[Any], expr: Boolean): Boolean = {
  var checked = check.filter(_ => expr)
  if (checked == Set())
    return false
  else
    return true
}

any(Set(3, 4, 5, 6), _ > 5)

First, expr is a Boolean. A Boolean is either true or false -- it can't be anything else. On the other hand, expr comes from "expression", which I think of as some formula that must be evaluated. So, expr and Boolean are unlikely matches, the first clue that some conceptual problem is afoot.

Now, you pass _ > 5 as expr parameter, but _ > 5 is neither true nor false. 10 > 5 would be true, 3 > 5 would be false, but _ > 5 is a function, and a function is not a Boolean.

Let's consider the underscore... _ > 5 means (x) => x > 5. _ > _ means (x, y) => x > y, and so on. If you don't understand underscore well, don't use it. Use the full syntax instead. You'll gain understanding later, no need to slow down your learning with it.

Now, I bring that up because of the other conceptual problem, which is this:

_ => expr

This is more or less equivalent to z => expr, that is, a function which completely ignores the parameter it has received. Notice that the meaning of underscore here is different than above. In fact, I've once counted 11 different meanings for underscore, in this answer. One more reason to avoid using underscores until you understand them.

So, the problem with your code is that:

  1. You are receiving a Boolean where you wanted a Function1[Any, Boolean].
  2. You are ignoring the element being filtered when computing whether it should be filtered or not (I assume because that's the only syntax that compiled, because of 1).
  3. You are passing a function where it is expected a Boolean.

Now, the code won't work even if you fix 1 and 2 because _ > 6 is not a valid Function1[Any, Boolean], since Any does not implement >.

I suspect you come from a dynamic language background, and use Any as a quick "fallback" to not having to tell what is the type of the things you are handling. Don't do it -- using Any doesn't make things easier, it makes things harder.

There are two other answers that show how to write that method correctly, and I'll defer to them the particulars. I only wanted to address the general problem, and see if I can't guide you into avoiding such problems in the future.

Upvotes: 4

kiritsuku
kiritsuku

Reputation: 53348

In addition to @RayToals answer:

If you create multiple parameter lists, it is possible to use Scalas type inference:

def any[T](check: Set[T])(test: T => Boolean): Boolean = {
  val checked = check.filter(test)
  checked.nonEmpty
}

scala> any(Set(3,4,5,6))(_>5)
res6: Boolean = true

scala> any(Set(3,4,5,6))(_>6)
res7: Boolean = false

Note: Don't use return statements (in particular don't return true and false explicitly) - most times they are not needed. In addition, don't use a var when you are not exactly sure you need it. At last, is is possible to write the whole method in a single expression:

def any[T](check: Set[T])(test: T => Boolean): Boolean =
  check.filter(test).nonEmpty

Upvotes: 3

Ray Toal
Ray Toal

Reputation: 88378

The issue is one of the time in which you evaluate expressions. In your call

check.filter(_ => expr)

you are filtering check by the value of an expression passed in. But in your call, you are passing a function, not a boolean.

What you mean to do, I think, is to pass the function, then evaluate it in your check call. For example:

def any[T](check: Set[T], test: T => Boolean): Boolean = {
  var checked = check.filter(test)
  if (checked == Set())
    return false
  else
    return true
}

Now you can call this as follows:

any(Set(3, 4, 5, 6), (_:Int) > 5)

Upvotes: 6

xiefei
xiefei

Reputation: 6599

Set(3, 4, 5, 6).exists(_ > 5)

Upvotes: 4

Related Questions