Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use "implicit" as apply() parameter?

I want to do this:

abstract class Context {
    def getInt(id: Int): Int
}

abstract class Dependency[+T]
(val name: String, val id: Int)
extends Function1[Context,T]

class IntDependency(name: String, id: Int)
extends Dependency[Int](name, id) {
    def apply(implicit context: Context): Int =
        context.getInt(id)
}

But then I get an error message like this:

class IntDependency needs to be abstract, since method apply in trait
Function1 of type (v1: Context)Long is not defined (Note that T1 does
not match Context)

I understand that implicits should normally be part of the second parameter list, but I can't work out how to code it so it compiles, and gives the result I want.

Explanation: I'm trying to create a framework where one can define "Function" object, which can depend on other functions to compute their value. All functions should only take a single Context parameter. The context know the "result" of the other functions. The function instances should be immutable, with the state residing in the context. I want the functions to create "dependency" fields at creation time, which take the context implicitly, and return the value of the dependency within that context, so that accessing the dependency inside of the apply method "feels like" accessing a parameter or field, that is without explicitly giving the context as parameter to the dependency.

like image 360
Sebastien Diot Avatar asked Jun 19 '11 09:06

Sebastien Diot


4 Answers

Are you sure you need your Dependency to extend a Function? Because if you don't, just leave the extends Function1[Context,T] part out and your code will work.

If you really need to extend a Function than I don't know of a solution in your case. But there are cases where you could try to overload the apply method. Like here:

scala> val sum = new Function1[Int, Function1[Int, Int]] {
         |      def apply(a: Int) = (b: Int) => a + b
         |      def apply(a: Int)(implicit b: Int) = a + b
         |}
sum: java.lang.Object with (Int) => (Int) => Int{def apply(a:Int)(implicit b: Int): Int} = <function1>

scala> sum(2)(3)
res0: Int = 5

scala> implicit val b = 10
b: Int = 10

scala> sum(2)
res1: Int = 12
like image 145
agilesteel Avatar answered Oct 18 '22 22:10

agilesteel


A method may have its final parameter section marked implicit; it need not be the second section, although that is most commonly seen.

But it seems that when a subclass marks a parameter section implicit, it is no longer considered to override the method in the superclass.

scala> new (Int => Int) { def apply(implicit i: Int) = i }
<console>:8: error: object creation impossible, since method apply in trait Function1 of type (v1: Int)Int is not defined
(Note that T1 does not match Int)
       new (Int => Int) { def apply(implicit i: Int) = i }
           ^

scala> trait F1 { def f(a: Any) }; new F1 { def f(implicit a: Any) = () }
<console>:8: error: object creation impossible, since method f in trait F1 of type (a: Any)Unit is not defined
       trait F1 { def f(a: Any) }; new F1 { def f(implicit a: Any) = () }

                                       ^

The spec does not specifically mention this (§5.1.4 Overriding), so it may be an implementation restriction, or an bug.

like image 37
retronym Avatar answered Oct 18 '22 21:10

retronym


Its sure, that your apply method signature with implicit doesn´t conform with the signature of Function1.apply. Hopefully I get your problem right, so what about (assuming that your context is mutable and perhaps singleton) having the implicit context injected at creation time? Is that possible in your case?

class IntDependency(id: Int)(implicit context: Context) extends Dependency[Int](id)

But then I wonder (and still was wondering before) what to do with the context argument at the apply method.

like image 2
Peter Schmitz Avatar answered Oct 18 '22 22:10

Peter Schmitz


Here is the working solution:

abstract class Context {
    def getInt(id: Int): Int
}

abstract class Dependency[+T]
(val name: String, val id: Int) {
    def get(context: Context): T
}

class IntDependency(name: String, id: Int)
extends Dependency[Int](name, id) {
    def get(context: Context): Int =
        context.getInt(id)
}

implicit def intDep2Int(dep: IntDependency)
(implicit context: Context): Int =
dep.get(context)
like image 2
Sebastien Diot Avatar answered Oct 18 '22 22:10

Sebastien Diot