Question

I can implement a def with a val where the def takes no arguments:

trait T { def foo: Int }
class C(val foo: Int) extends T

Why can this not be extended to implementing a def taking N args to a val which is a FunctionN? I want it possible to implement something like:

def expensiveOperation(p: Int => Boolean) : List[Int]

With a lazy function val. Something like:

val expensiveOperation = {
    val l = //get expensive list
    l.filter _ //partially applied function
}

I know that this syntax does not seem to work on 2.8. Is there something I'm missing, why can I not implement a def taking parameters as a functional val?

Was it helpful?

Solution

Now, post-edits, I think I understand what you're after. But you can't do what you want because the type signatures don't match.

def x: Int = 5
val x: Int = 5

In both cases you supply nothing and get back an Int (in this case 5). Great!

def expensive(p: Int => Boolean): List[Int]

Now you supply something. But a val is just a stored object somewhere. You can supply something to the object that the label refers to, but that's not the same as supplying something to the label 'x'.

Here's the def you need to use if you want a val to override it:

def expensive: (Int=>Boolean)=>List[Int]

Now you have something that you call with no parameters and it returns something that can take a Int=>Boolean function and give you a List[Int] in return. That's exactly what you get with the val--in both cases, you have the name of something that returns an object that has the functionality you want.

(In Scala, vals are actually implemented as hidden fields with getter methods that take no parameters and return whatever's in the hidden field. So it really is a method just like the def is.)

OTHER TIPS

A val doesn't take arguments, because it is computed and stored in a field. But I might just refer you to the extensive posts I did the last two days on the mailing lists.

Or, rather, let's consider it from a Java point of view, since Scala is compatible with Java at the jvm level, and it surely must obey jvm rules. Let's start with the first class:

abstract class X {
  def expensiveOperation(p: Int => Boolean) : List[Int] 
}

Now, let's extend it:

abstract class Y extends X {
  override val expensiveOperation: ((Int) => Boolean) => List[Int]
}

So, from Java, we know that class X has the method expensiveOperation, which receives Function1[Int, Boolean] and returns List[Int].

Now we go to class Y. Naturally, it has to define the same method, but it also has to define the getter expensiveOperation, which receives no arguments and returns Function1[Function1[Int, Boolean],List[Int]].

It might be workable, as long as this additional method didn't exist in X too. So let's define it:

class Z extends Y {
  override val extensiveOperation = new Function1[Function1[Int, Boolean], List[Int]] {
    def apply(p: Int => Boolean) = List range (1, 10) filter p
  }
}

How does that get defined? Does Scala copy apply's body as a body for expensiveOperation (the one receiving a parameter, not the getter one)? It might still be workable. Let's try something else, though:

class W(f: ((Int) => Boolean) => List[Int]) extends Y {
  override val extensiveOperation = f
}

Now, how do we override the parameter-receiving extensiveOperation? I suppose we could write it like this:

override def extensiveOperation(p: Int => Boolean) = extensiveOperation.apply(p)

That's workable. But I, personally, think it is a bit convoluted. My suggestion: write up a short SID, and get some agreement on a Scala mailing list. Without code to implement it, though, I don't think it has much chance of being adopted -- Scala has to track every function-typed val to figure if it is overriding a def or not.

You could always just forward it to your val:

trait T {
  def expensiveOperation(p: Int => Boolean) : List[Int]
}

class C extends T {
  def expensiveOperation(p: Int => Boolean): List[Int] = {
      expensiveOperationVal(p) 
  }
  val expensiveOperationVal = { p: (Int=>Boolean) =>
    // ... lazy stuff
    List(1,2,3)
  }
}

And, although this doesn't answer your question, It looks like unless your // ... get expensive list code depends on the predicate p, then you could just do something similar to:

class C extends T {
  def expensiveOperation(p: Int => Boolean): List[Int] = {
      myExpensiveList filter p 
  }
  lazy val myExpensiveList = {
    val l = // ... expensive stuff
    l
  }
}

Edit: okay, I didn't figure out what you actually meant. But if you had meant what I thought...

You can, using abbreviated syntax, create a val that applies an operation:

val expensive = (p: (Int) => Boolean) => {
  val l = List(1,2,3,4)
  l filter p
}
scala> expensive(_<3)
res1: List[Int] = List(1,2)

But this doesn't actually cache the list, which is what I think you want. The reason is that this short-hand syntax puts everything after => into the apply method of Function1. You probably want the list to be stored like so:

val expensive = new Function1[Int=>Boolean,List[Int]] {
  lazy val l = List(1,2,3,4)
  def apply(p: (Int) => Boolean) = { l filter p }
}

and for that there isn't a great shorthand that I know of.

Edit: If you're happy to create the list outside of that block, then there is a shorthand (see comment also):

val expensive = List(1,2,3,4).filter _

scala> expensive(_ < 3)
res6: List[Int] = List(1, 2)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top