views:

113

answers:

4

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?

A: 

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)
Rex Kerr
But is that a valid *implementation* of the `def` I gave?
oxbow_lakes
(The answer is **NO**) - I can easily cache my expensive op by declaring a `val` - my question was about this being an implementation of a `def`, which it is not
oxbow_lakes
(Never mind, I just saw your edits above--this comment doesn't apply.)
Rex Kerr
+1  A: 

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
  }
}
Mitch Blevins
Yeah - this is exactly what I did but it seems a shame from a "unified access principle" perspective. It's no more "pretty" a solution than just holding the list and calling `filter` on it when you need to
oxbow_lakes
+2  A: 

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

Rex Kerr
But can I implement the `def` with a `def` which takes parameters? The answer is of course a firm "NO"! I get your argument, which is no doubt correct in its details but the two items *should* be equivalent. The only logical problem I foresaw was that of variable arity; i.e. `varargs`
oxbow_lakes
They're logically equivalent assuming that (1) you do nothing with the returned object aside from call its apply, and (2) you don't care about implementation details (e.g. which might be relevant to performance) so you don't care that there's an extra level of indirection. This might be an argument that the compiler should turn every method into a function, or that you lose the normal getter for a val that overrides a def with arguments and replace it with a specialized getter that also calls apply (and hence fits the type signature of the def).
Rex Kerr
+2  A: 

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.

Daniel
My work has been a bit (read: a lot) frenetic over the last few days (read: months) and I haven't been keeping up with the mailing lits! It still seems to me something that *could* be equivalent but I'm no language designer and have never written a compiler in my life! So I'm not sure there'll be a reference implementation coming from me...
oxbow_lakes
I'm now catching up with the mailing lists. How odd that there's a huge discussion on a subject which I just happened to be thinking about at exactly the same time!
oxbow_lakes