Gregor Scheidt
Gregor Scheidt

Reputation: 3982

How to declare a class as extending a function with implicit parameters

I would like to define a function that takes one explicit and one implicit parameter, like so:

def foo(a: Int)(implicit b: Int) : Int

but as a class or object, like so

object Foo extends ((Int,Int) => Int) {
  def apply(a: Int)(implicit b: Int) : Int = { ... }
}

so that the function can be called like this:

implicit val b = 2
val g = Foo(1)

I fail to get the declaration of the base from which class Foo should extend right. How can this be done?

Upvotes: 2

Views: 663

Answers (4)

Det
Det

Reputation: 36

A question is: Why do you need necessarily to pass that object Foo to "where an (Int,Int) => Int is expected" ?

I suppose that object Foo shall have some more special features which does exhance it beyond being a function?

It seems better to separate two concerns here: a) The function (Int)(implicit Int) => Int and b) the object Foo

object Foo {
  def apply(a: Int, b: Int) : Int = { ... }
}

def foo(a: Int)(implicit b: Int) : Int = Foo(a, b)

implicit val m:Int = 42

foo(5)  // --> res1: Int = 47

You may consider 'foo' a "companion function" if you like ...

Beside that: I suppose trying to apply an (Int)(implicit Int)=>Int "where an (Int,Int) => Int is expected" may not work, as these types are not identical. At least when you try to put that into a

def bar(f:(Int,Int)=>Int) = ....

The method bar would call its parameter f with two arguments anyway, as it expects it to take two, and cannot assume that the provided function in f is indeed able to work with implicits.

Upvotes: 0

Luigi Plinge
Luigi Plinge

Reputation: 51109

Rather than extending (Int, Int) => Int you could use an implicit conversion:

class Foo(f: (Int, Int) => Int) {
  def apply(a: Int)(implicit b: Int) : Int = f(a, b)
}

implicit def Foo_to_Function2(foo: Foo) = (x: Int, y: Int) => foo(x)(y)

Test:

val foo = new Foo(_ * 10 + _)
foo(4) //error since no implicit Int yet
{
  implicit val impint = 5
  foo(4)  //45
}  
def takesF2(f: (Int, Int) => Int) = f(1, 2)
takesF2(foo)  //12 - works as normal Function2

Upvotes: 0

Paul Butcher
Paul Butcher

Reputation: 10852

So there are two parts to this question - firstly, how does one create an object that implements a function with a curried parameter list. And then, how does one make one of those parameter lists implicit.

The first of those is definitely possible. Note that a curried function is a function that returns a function. So f(x: Int)(y: Int) is actually a Function1, not a Function2:

scala> def f(x: Int)(y: Int) = x + y
f: (x: Int)(y: Int)Int

scala> f _
res0: Int => Int => Int = <function1>

And you can certainly create a class that implements this:

scala> object Foo extends (Int => Int => Int) {
     |   def apply(x: Int) = {(y: Int) => x + y}
     | }
defined module Foo

scala> Foo(1)(2)
res1: Int = 3

Unfortunately, I'm not aware of any way to make the second parameter list implicit.

Upvotes: 0

Heiko Seeberger
Heiko Seeberger

Reputation: 3722

You cannot. Function2[T1, T2, R] declares the abstract method apply(t1: T1, t2: T2): R, so if you want to mix in Function2, you have to implement this arity-2 apply method, which has a different signature than the curried version you would like to use.

Upvotes: 5

Related Questions