I have a map of String
to Function
s which details all of the valid functions that are in a language. When I add a function to my map, I am required to specify the type (in this case Int
).
var functionMap: Map[String, (Nothing) => Any] = Map[String, (Nothing) => Any]()
functionMap += ("Neg" -> expr_neg[Int])
def expr_neg[T: Numeric](value: T)(implicit n: Numeric[T]): T = {
n.negate(value)
}
Instead, how can I do something like:
functionMap += ("Neg" -> expr_neg)
without the [Int]
and add it in later on when I call:
(unaryFunctionMap.get("abs").get)[Int](-45)
You can't. Because
expr_neg
is a method with a type parameterT
and an implicit argumentn
depending on that parameter. For Scala to lift that method to a function, it needs to capture the implicit, and therefore it must know what kind of type you want.You're trying to build your function using type classes (in this case,
Numeric
). Type classes rely on implicit parameters. Implicits are resolved at compile time. Your function name string values are only known at runtime, therefore you shouldn't build your solution on top of type classes like this.An alternative would be to store a separate function object in your map for each parameter type. You could store the parameter type with a
TypeTag
:No type class implicit needs to be resolved to call
callFn()
, because the implicitNumeric
was already resolved on the call toaddFn
.What happens if we try to resolve the type class when the function is called?
The first problem is that a
Function1
(orFunction2
) can't have implicit parameters. Only a method can. (See this other question for more explanation.) So if you want something that acts like aFunction1
but takes an implicit parameter, you'll need to create your own type that defines theapply()
method. It has to be a different type fromFunction1
, though.Now we get to the main problem: all implicits must be able to be resolved at compile time. At the location in code where the method is run, all the type information needed to choose the implicit value needs to be available. In the following code example:
We don't really need to specify that our value type is
Int
, because it can be inferred from the value-45
itself. But the fact that our method uses aNumeric
implicit value can't be inferred from anything in that line of code. We need to specify the use ofNumeric
somewhere at compile time.If you can have a separate map for unary functions that take a numeric value, this is (relatively) easy:
You can make the function trait generic on the type class it requires, letting your map hold unary functions that use different type classes. This requires a cast internally, and moves the specification of
Numeric
to where the function is finally called:But you still have to specify
Numeric
somewhere.Also, neither of these solutions, as written, support functions that don't need type classes. Being forced to be this explicit about which functions take implicit parameters, and what kinds of implicits they use, starts to defeat the purpose of using implicits in the first place.