def weirdfunc(message: String, f: (Int, Int) => Int){
println(message + s" ${f(3,5)}")
}
I have the function as above. How do I make it so that the function f is generic for all Numeric types?
What you want is called a higher-ranked type (specifically, rank 2). Haskell has support for these types, and Scala gets a lot of its type theory ideas from Haskell, but Scala has yet to directly support this particular feature.
Now, the thing is, with a bit of black magic, we can get Scala to do what you want, but the syntax is... not pretty. In Scala, functions are always monomorphic, but you want to pass a polymorphic function around as an argument. We can't do that, but we can pass a polymorphic function-like object around that looks and behaves mostly like a function. What would this object look like?
trait NumFunc {
def apply[A : Numeric](a: A, b: A): A
}
It's just a trait that defines a polymorphic apply. Now we can define the function that you really want.
def weirdfunc(message: String, f: NumFunc) = ???
The trouble here, as I mentioned, is that the syntax is really quite atrocious. To call this, we can't just pass in a function anymore. We have to create a NumFunc and pass that in. Essentially, from a type theoretic perspective, we have to prove to the compiler that our function works for all numeric A. For instance, to call the simple weirdfunc that only takes integers and pass the addition function is very simple.
weirdfunc("Some message", (_ + _))
However, to call our "special" weirdfunc that works for all number types, we have to write this mess.
weirdfunc("Hi", new NumFunc {
override def apply[A : Numeric](a: A, b: A): A = {
import math.Numeric.Implicits._
a + b
}
})
And we can't hide that away with an implicit conversion because, as I alluded to earlier, functions are monomorphic, so any conversion coming out a function type is going to be monomorphic.
Bottom line. Is it possible? Yes. Is it worth the costs in terms of readability and usability? Probably not.
Scala has a typeclass for this, so it's quite easy to achieve using a context bound and the standard lib.
def weirdfunc[T: Numeric](message: String, x: T, y: T, f: (T, T) => T) {
println(message + s" ${f(x, y)}")
}
def test[T](a: T, b: T)(implicit ev: Numeric[T]): T = ev.plus(a, b)
weirdFunc[Int]("The sum is ", 3, 5, test)
// The sum is 8
Sorry cktang you cannot generify this. The caller gets to set the generic parameter.. not the called function.. just like the caller passes function parameters.
However you can use currying so that you pass the 'f' of type Int once, and then pass different Int pairs. Then you may pass 'f' of type Double, and pass different Double pairs.
def weirdfunc[A](message: String, f: (A, A) => A)(x: A, y: A){
println(message + s" ${f(x, y)}")
}
def g(x: Int, y: Int): Int = x * y
val wierdfuncWithF = weirdfunc("hello", g) _
wierdfuncWithF(3, 5)
wierdfuncWithF(2, 3)
In particular what you want cannot be done as it will break generics rules.
Related
I have a function def f(a: Int, b: Int, c: Int, d: Int, ...) and I'd like to supply a list of consecutive integers as parameters (in a unit test).
Is there a neat way to supply (1 to N).toList to f? Since the function is not def f(x: Int*) I cannot simply use list: _* with list the list of integers.
I don't think you can do it in standard Scala in a typesafe way, but it's possible to do it using the Shapeless library. Even if you don't use this library in the project, you can depend on it only for the test scope.
Here is the code sample:
import shapeless._
import shapeless.ops.function._
import shapeless.ops.traversable._
def applyToList[F, HL <: HList, R](f: F)(args: List[Any])(implicit
// convert `(T1, T2, ...) => R` to a function from a single HList argument `HL => R`
fnToProduct: FnToProduct.Aux[F, HL => R],
// convert the argument list to the HList, expected by the converted function
toHList: FromTraversable[HL]
): R = {
toHList(args) match {
case Some(hargs) => fnToProduct(f)(hargs)
case None => sys.error("argument list not applicable to function")
}
}
Using it is as simple as this:
def f(a: Int, b: Int, c: Int, d: Int): Any = ???
applyToList(f _)(List(1, 2, 3, 4))
But notice the need for an explicit eta-conversion f _, because the Scala compiler doesn't really know, that F is a function.
You can change the definition to return Option[R], because it's impossible to know at compile-time if the provided List will fit the function or not. For example, the argument list may have a non-matching number of elements. But if this is for unit-tests, you may as well just throw an exception.
If you can't modify f() then you're kinda stuck. One (not terrific) thing you could do is write an implicit translator.
Let's pretend that f() takes 4 Int args.
implicit class Caller4[A,R](func: (A,A,A,A)=>R) {
def call(as :A*) :R = func(as(0),as(1),as(2),as(3))
}
(f _).call(1 to 4:_*) //call f(1,2,3,4)
The good new is that this will work for any/all methods that take 4 parameters of the same type.
The bad news is that you need a different translator for every required arity, and the compiler won't catch it if you invoke call() with too few parameters.
I have defined an abstract base class like following:
abstract class Base() {
val somevariables
}
And then, I extend this class like following:
case class Derived (a: SomeOtherClass, i: Int) extends Base {
//Do something with a
}
Then, I have a method (independent of classes) that is as follows:
def myMethod (v1: Base, v2: Base, f:(Base, Base) => Int ): Int
And I want to use the above method as myMethod(o1, o2, f1), where
o1, o2 are objects of type Derived
f1 is as follows def f1(v1: Derived, v2: Derived): Int
Now, this gives me an error because myMethod expects the function f1 to be (Base, Base) => Int, and not (Derived, Derived) => Int. However, if I change the definition of f1 to (Base, Base) => Int, then it gives me an error because internally I want to use some variable from SomeOtherClass, an argument that Base does not have.
You should use type parameters to make sure that the types in myMethod line up correctly.
def myMethod[B <: Base](v1: B, v2: B)(f: (B, B) => Int): Int
Or perhaps a bit more general:
def myMethod[B <: Base, A >: B](v1: B, v2: B)(f: (A, A) => Int): Int
If you want to be able to use function f1 where function f2 is expected, f1 must either be of the same type (both input parameters and return value) or a subclass of f2. Liskov Substitution Principle teaches us that for one function to be a subclass of another, it needs to require less (or same) and provide more (or same).
So if you have a method that as a parameter takes a function of type (Fruit, Fruit) => Fruit, here are types for some valid functions that you can pass to that method:
(Fruit, Fruit) => Fruit
(Fruit, Fruit) => Apple
(Any, Any) => Fruit
(Any, Any) => Apple
This relates to covariance/contravariance rule; for example, every one-parameter function in Scala is a trait with two type parameters, Function2[-S, +T]. You can see that it is contravariant in its parameter type and covariant in its return type - requires S or less ("less" because it's more general, so we lose information) and provides T or more ("more" because it's more specific, so we get more information).
This brings us to your problem. If you had things the other way around, trying to fit (Base, Base) => Int in the place where (Derived, Derived) => Int is expected, that would work. Method myMethod obviously expects to be feeding this function with values of type Derived, and a function that takes values of type Base will happily accept those; after all, Derived is a Base. Basically what myMethod is saying is: "I need a function that can handle Deriveds", and any function that knows how to work with Bases can also take any of its subclasses, including Derived.
Other people have pointed out that you can set the type of function f's parameters to a subtype of Base, but at some point you will probably want to use v1 and v2 with that function, and then you will need to revert to downcasting via pattern matching. If you're fine with that, that you can also just pattern match on the function directly, trying to figure out what's its true nature. Either way, pattern matching sucks in this case because you will need to fiddle around myMethod every time a new type is introduced.
Here is how you can solve it more elegantly with type classes:
trait Base[T] {
def f(t1: T, t2: T): Int
}
case class Shape()
case class Derived()
object Base {
implicit val BaseDerived = new Base[Derived] {
def f(s1: Derived, s2: Derived): Int = ??? // some calculation
}
implicit val BaseShape = new Base[Shape] {
def f(s1: Shape, s2: Shape): Int = ??? // some calculation
}
// implementations for other types
}
def myMethod[T: Base](v1: T, v2: T): Int = {
// some logic
// now let's use f(), without knowing what T is:
implicitly[Base[T]].f
// some other stuff
}
myMethod(Shape(), Shape())
What happens here is that myMethod says: "I need two values of some type T and I need to have an implicit Base[T] available in scope (that's the [T: Base] part, which is a fancy way of saying that you need an implicit parameter of type Base[T]; that way you would access it by its name, and this way you access it via implicitly). Then I know I will have f() available which performs the needed logic". And since the logic can have different implementation based on the type, this is a case of ad-hoc polymorphism and type classes are a great way of dealing with that.
What's cool here is that when a new type is introduced that has its own implementation of f, you just need to put this implementation in the Base companion object as an implicit value, so that it's available to myMethod. Method myMethod itself remains unchanged.
According to my (very simple) tests, this change...
def myMethod[B <: Base](v1: Base, v2: Base, f:(B, B) => Int ): Int = ???
...will allow either of these methods...
def f1(a: Derived, b:Derived): Int = ???
def f2(a: Base, b:Base): Int = ???
...to be accepted as a passed parameter.
myMethod(Derived(x,1), Derived(x,2), f1)
myMethod(Derived(x,1), Derived(x,2), f2)
I am trying to write a generic weighted average function.
I want to relax the requirements on the values and the weights being of the same type. ie, I want to support sequences of say: (value:Float,weight:Int) and (value:Int,weight:Float) arguments and not just: (value:Int,weight:Int)
To do so, I first need to implement a function that takes two generic numerical values and returns their product.
def times[A: Numeric, B: Numeric](x: B, y: A): (A, B) : ??? = {...}
Writing the signature and thinking about the return type, made me realise that I need to define some sort of hierarchy for Numerics to determine the return type. ie x:Float*y:Int=z:Float, x:Float*y:Double=z:Double.
Now, Numeric class defines operations plus, times, etc. only for arguments of the same type. I think I would need to implement a type:
class NumericConverter[Numeirc[A],Numeric[B]]{
type BiggerType=???
}
so that I can write my times function as:
def times[A: Numeric, B: Numeric](x: B, y: A): (A, B) :
NumericConverter[Numeirc[A],Numeric[B]].BiggerType= {...}
and convert the "smaller type" to the "bigger one" and feed it to times().
Am I on the right track? How would I "implement" the BiggerType?
clearly I can't do something like:
type myType = if(...) Int else Float
as that is evaluated dynamically, so it worn't work.
I understand that I might be able to do this Using Scalaz, etc. but this is an academic exercise and I want to understand how to write a function that statically returns a type based on the argument types.
Feel free to let me know if there is a whole easier way of doing this.
update:
this is what I came up with it.
abstract class NumericsConvert[A: Numeric,B: Numeric]{
def AisBiggerThanB: Boolean
def timesA=new PartialFunction[(A,B), A] {
override def isDefinedAt(x: (A, B)): Boolean = AisBiggerThanB
override def apply(x: (A, B)): A = implicitly[Numeric[A]].times(x._1, x._2.asInstanceOf[A])
}
def timesB=new PartialFunction[(A,B), B] {
override def isDefinedAt(x: (A, B)): Boolean = !AisBiggerThanB
override def apply(x: (A, B)): B = implicitly[Numeric[B]].times(x._1.asInstanceOf[B], x._2)
}
def times: PartialFunction[(A, B), Any] = timesA orElse timesB
}
def times[A: Numeric, B: Numeric](x: B, y: A)= implicitly[NumericsConvert[A,B]].times(x,y)
which is silly as I will have to create implicits for both
IntDouble extends NumericsConvert[Int,Double]
and
DoubleInt extends NumericsConvert[Double,Int]
not to mention that the return type of times is now Any, but regardless, I am getting errors for my times functions. I thought I would add it here in case it might help with arriving at a solution. so side question: how I can pass context bound types of one class/function to another like I am trying to do in times.
I think you're making this harder than it needs to be.
You need "evidence" that both parameters are Numeric. With that established let the evidence do the work. Scala will employ numeric widening so that the result is the more general of the two received types.
def mult[T](a: T, b: T)(implicit ev:Numeric[T]): T =
ev.times(a,b)
If you want to get a little fancier you can pull in the required implicits. Then it's a little easier to read and understand.
def mult[T: Numeric](a: T, b: T): T = {
import Numeric.Implicits._
a * b
}
Proof:
mult(2.3f , 7) //res0: Float = 16.1
mult(8, 2.1) //res1: Double = 16.8
mult(3, 2) //res2: Int = 6
For more on generic types and numeric widening, this question, and its answer, are worth studying.
I saw examples, where a conversion function T => S is passed as an implicit parameter. Scala calls this function view and even provides special syntax sugar -- view bound -- for that case .
However we already have implicit conversions ! Can I replace these views (i.e. conversion functions passed as implicit parameters) with implicit conversions ? ? What I can do with views what I can't with implicit conversions ?
My understanding of your question is, what would be the advantage of
case class Num(i: Int)
implicit def intToNum(i: Int) = Num(i)
def test[A <% Num](a: A): Int = a.i
test(33)
over
def test2(a: Num): Int = a.i
test2(33)
Yes? Well the meaning of view is exactly that: the type T can be viewed as another type S. Your method or function might want to deal with T in the first place. An example is Ordered:
def sort[A <% Ordered[A]](x: A, y: A): (A, A) = if (x < y) (x, y) else (y, x)
sort(1, 2) // --> (1,2)
sort("B", "A") // --> (A,B)
Two more use cases for view bounds:
you may want to convert from T to S only under certain circumstances, e.g. lazily
(this is in a way the same situation as above: you basically want to work with T)
you may want to chain implicit conversions. See this post: How can I chain implicits in Scala?
What you call implicit conversions is nothing more than a view in global scope.
View bounds are necessary in when using type parameters, as it is a sign that an implicit conversion is necessary. For example:
def max[T](a: T, b: T): T = if (a < b) b else a
Because there's no constrains whatsoever on T, the compiler doesn't know that a < method will be available. Let's see the compiler let you go ahead with that, then consider these two calls:
max(1, 2)
max(true, false)
There's nothing in the signature max[T](a: T, b: T): T that tells the compiler it should not allow the second call, but should allow the first. This is where view bounds come in:
def max[T <% Ordered[T]](a: T, b: T): T = if (a < b) b else a
That not only tells the compiler where the < method comes from, but also tells the compiler that max(true, false) is not valid.
I am trying to learn Scala now, with a little bit of experience in Haskell. One thing that stood out as odd to me is that all function parameters in Scala must be annotated with a type - something that Haskell does not require. Why is this? To try to put it as a more concrete example: an add function is written like this:
def add(x:Double, y:Double) = x + y
But, this only works for doubles(well, ints work too because of the implicit type conversion). But what if you want to define your own type that defines its own + operator. How would you write an add function which works for any type that defines a + operator?
Haskell uses Hindley-Milner type inference algorithm whereas Scala, in order to support Object Oriented side of things, had to forgo using it for now.
In order to write an add function for all applicable types easily, you will need to use Scala 2.8.0:
Welcome to Scala version 2.8.0.r18189-b20090702020221 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_15).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import Numeric._
import Numeric._
scala> def add[A](x: A, y: A)(implicit numeric: Numeric[A]): A =
| numeric.plus(x, y)
add: [A](x: A,y: A)(implicit numeric: Numeric[A])A
scala> add(1, 2)
res0: Int = 3
scala> add(1.1, 2.2)
res1: Double = 3.3000000000000003
In order to solidify the concept of using implicit for myself, I wrote an example that does not require scala 2.8, but uses the same concept. I thought it might be helpful for some.
First, you define an generic-abstract class Addable:
scala> abstract class Addable[T]{
| def +(x: T, y: T): T
| }
defined class Addable
Now you can write the add function like this:
scala> def add[T](x: T, y: T)(implicit addy: Addable[T]): T =
| addy.+(x, y)
add: [T](T,T)(implicit Addable[T])T
This is used like a type class in Haskell. Then to realize this generic class for a specific type, you would write(examples here for Int, Double and String):
scala> implicit object IntAddable extends Addable[Int]{
| def +(x: Int, y: Int): Int = x + y
| }
defined module IntAddable
scala> implicit object DoubleAddable extends Addable[Double]{
| def +(x: Double, y: Double): Double = x + y
| }
defined module DoubleAddable
scala> implicit object StringAddable extends Addable[String]{
| def +(x: String, y: String): String = x concat y
| }
defined module StringAddable
At this point you can call the add function with all three types:
scala> add(1,2)
res0: Int = 3
scala> add(1.0, 2.0)
res1: Double = 3.0
scala> add("abc", "def")
res2: java.lang.String = abcdef
Certainly not as nice as Haskell which will essentially do all of this for you. But, that's where the trade-off lies.
Haskell uses the Hindley-Milner type inference. This kind of type-inference is powerful, but limits the type system of the language. Supposedly, for instance, subclassing doesn't work well with H-M.
At any rate, Scala type system is too powerful for H-M, so a more limited kind of type inference must be used.
I think the reason Scala requires the type annotation on the parameters of a newly defined function comes from the fact that Scala uses a more local type inference analysis than that used in Haskell.
If all your classes mixed in a trait, say Addable[T], that declared the + operator, you could write your generic add function as:
def add[T <: Addable[T]](x : T, y : T) = x + y
This restricts the add function to types T that implement the Addable trait.
Unfortunately, there is not such trait in the current Scala libraries. But you can see how it would be done by looking at a similar case, the Ordered[T] trait. This trait declares comparison operators and is mixed in by the RichInt, RichFloat, etc. classes. Then you can write a sort function that can take, for example, a List[T] where [T <: Ordered[T]] to sort a list of elements that mix in the ordered trait. Because of implicit type conversions like Float to RichFloat, you can even use your sort function on lists of Int, or Float or Double.
As I said, unfortunately, there is no corresponding trait for the + operator. So, you would have to write out everything yourself. You would do the Addable[T] trait, create AddableInt, AddableFloat, etc., classes that extend Int, Float, etc. and mix in the Addable trait, and finally add implicit conversion functions to turn, for example, and Int into an AddableInt, so that the compiler can instantiate and use your add function with it.
The function itself will be pretty straightforward:
def add(x: T, y: T): T = ...
Better yet, you can just overload the + method:
def +(x: T, y: T): T = ...
There's a missing piece, though, which is the type parameter itself. As written, the method is missing its class. The most likely case is that you're calling the + method on an instance of T, passing it another instance of T. I did this recently, defining a trait that said, "an additive group consists of an add operation plus the means to invert an element"
trait GroupAdditive[G] extends Structure[G] {
def +(that: G): G
def unary_- : G
}
Then, later, I define a Real class that knows how to add instances of itself (Field extends GroupAdditive):
class Real private (s: LargeInteger, err: LargeInteger, exp: Int) extends Number[Real] with Field[Real] with Ordered[Real] {
...
def +(that: Real): Real = { ... }
...
}
That may be more than you really wanted to know right now, but it does show both how to define generic arguments and how to realize them.
Ultimately, the specific types aren't required, but the compiler does need to know at least the type bounds.