Reputation: 2033
I have this method:
private def getMachineResponse(computeFunc: (Any*) => Option[List[MyThing]], any: Any*): Route = {
get {
val things = computeFunc(any)
// now do some stuff
}
}
I would like to call this method with two different computeFunc as parameters, one with 2 arguments and the other with 4 arguments. However the calls do not compile. I am trying this:
getMachineResponse(computeKnownItems, 1, "s")
where computeKnownItems is as follows:
private def computeKnownItems(mtype: Int, id: String): Option[List[MyThing]] = {
// get the things
}
The error is "type mismatch, found (Int, String), required (Any*)".
Is there a way to achieve what I'm trying or should I make an overload for each type of computeFunc I need to pass in? (Like use (int, String) => Option[List[MyThing]] for computeFunc then create an overload that takes a computeFunc: (int, int, int, String) =>... etc)
Thank you.
Later edit: changed the parameter name to mtype (as it is in my actual code, I prettified it too much when posting here!)
Upvotes: 0
Views: 1762
Reputation: 13985
First of all... I don't see this thing compiling. There are lots of problems other than co-variance
and contra-variance
issues.
Most obvious problem is here:
private def computeKnownItems(type: Int, id: String): Option[List[MyThing]] = {
// get the things
}
No... you can not use type
as an identifier... type
is a keyword in Scala.
Other problem is that, something like this will not compile:
def simple( func: ( Int* ) => Int, ints: Int* ): Int = func( ints )
error: type mismatch;
found : Seq[Int]
required: Int
def func( ints: Int* ): Int = func( ints )
Neither will this:
def a( func: (Int*) => Int )( ints: Int* ): Int = func( ints )
error: type mismatch;
found : Seq[Int]
required: Int
def a( func: (Int*) => Int )( ints: Int* ): Int = func( ints )
^
Nor will this:
scala> def fund( ints: Int* ): Int = ints.toList.sum
fund: (ints: Int*)Int
scala> def func( ints: Int* ): Int = fund( ints )
<console>:34: error: type mismatch;
found : Seq[Int]
required: Int
def func( ints: Int* ): Int = fund( ints )
^
The problem with all above was that the function was expecting multiple Int
parameters, but was getting a single parameter which was a sequence ( *-parameters
are implicitly received as Seq
in function body ) . Correct way to pass vairable size parameters is following,
scala> def simple( func: ( Int* ) => Int, ints: Int* ): Int = func( ints: _* )
simple: (func: Int* => Int, ints: Int*)Int
_*
is the special annotation
that is usable only in arguments to *-parameter
of a function. It makes any sequence to be passed as a *-parameter
.
scala> val l = List( 4, 5, 6, 7, 3, 6, 3 )
l: List[Int] = List(4, 5, 6, 7, 3, 6, 3)
scala> def pp( ints: Int* ) = println( ints )
pp: (ints: Int*)Unit
// All Int* is implicitly received as a Seq[ Int ]
// ( WrappedArray[ int ] in this case ) in function body.
scala> pp( 4, 5, 6 )
WrappedArray( 4, 5, 6 )
// Received as List[ Int ] in this case
scala> pp( l: _* )
List(4, 5, 6, 7, 3, 6, 3)
scala> def a( ints: Int* ) = ints.sum
a: (ints: Int*)Int
// passing a list wont work
scala> a( l )
<console>:11: error: type mismatch;
found : List[Int]
required: Int
a( l )
^
// list passed as multiple parameters
scala> a( l: _* )
res8: Int = 34
Now, coming to main issue... seems like you are have not considered two concepts known as co-variance
and contra-variance
.
While both of these are very general concepts... let me explain these in reference to types
and classes
.
Lets say, we have two types Animal
and Horse
where Horse
is a sub-type of Animal
or Horse <: Animal
.
Now, Lets consider a famous type - List[ +T ]
. Now why that +
sign... Basically that +
indicates that List[ T ]
is co-variant
for type T
, which means that if Horse <: Animal
then List[ Horse ] <: List[ Animal ]
. Which in turn means that you can pass an instance of List[ Horse ]
wherever an instance of List[ Animal ]
is required. If you think about it, it kind of true even for English.
But, if you talk about functions, they are contra-variant
, consider Function1[ -T, +R ]
which means that if Horse <: Animal
then Function1[ Animal, R ]
is a subtype of Function1[ Horse, R ]
or Function1[ Animal, R ] <: Function1[ Horse, R ]
.
In your case, your type (Any*) => Option[ List[ MyThing ] ]
is contra-variant
wrt. its arguments which means (int, String) => Option[ List[ MyThing ] ]
is super-type
of (Any*) => Option[ List[ MyThing ] ]
which again means you can not use (int, String) => Option[ List[ MyThing ] ]
in place of (Any*) => Option[ List[ MyThing ] ]
.
What, you can do is define functions for Map[ String, Any ]
def getMachineResponse(computeFunc: ( Map[ String, Any ] ) => Option[List[MyThing]], any: Map[ String, Any ] ): Route = {
get {
val things = computeFunc(any)
// now do some stuff
}
}
private def computeKnownItems(myMap: Map[ String, Any] ): Option[ List[ MyThing ] ] = {
val myType = myMap.get( "type" ).asInstanceOf[ Int ]
val myId = myMap.get( "id" ).asInstanceOf[ String ]
// get the things
}
Upvotes: 0