3

I want a generic function called "double", which behaves like this and could be applied to any type with def +(x:T):T method:

double("A")
> "AA"
double(1)
> 2
double(0.2)
> 0.4

So I write this function like this:

def double[T](x:T):T = { x+x }

But when I run it in REPL, scala compains about it:

scala> def double[T](x:T):T = { x+x }
<console>:7: error: type mismatch;
 found   : T
 required: String
       def double[T](x:T):T = { x+x }
                                  ^

I think structural type may be an approach to implement duck typing, and I tried something like this, but it doesn't work either:

def double[T <: { def +(x:T):T }](x:T):T = { x + x }
def double[T <: { def +[U<:T](x:U):U}](x:T) = { x + x }

Does anyone have ideas about this? Thanks!


I found in Haskell, the similar function can be written like this:

double x = x + x

just wondering why I can't do this in Scala...

Hanfei Sun
  • 45,281
  • 39
  • 129
  • 237
  • 6
    "I found in Haskell, the similar function can be written like this" Yeah, but in Haskell you're defining a function of type `Num a => a -> a`, not `a -> a` (which would be the equivalent of what you're trying to do in Scala), if you wrote the function as `double :: a -> a; double x = x + x`, you'd get the same error as in Scala. – sepp2k May 03 '15 at 04:21

3 Answers3

10

Not every type T has a + method, so that can't work. The strange error message comes from the compiler treating the first x as a String, because every type has a toString method, and that's the only way it can see a generic T as having +. But then T is being passed to +, instead of String, and it would require a second implicit conversion to allow this to work--and even if we did that, it would return String instead of T.

The problem is that we need a way to provide evidence that T has a + operation. There isn't anything in the standard library that does exactly this, to my knowledge, but we can create a type class that would provide the evidence that a type can be "doubled".

trait CanDouble[A] {
    def double(a: A): A
}

// Create some instances for types we know, like String, or numeric types
implicit val StringDouble: CanDouble[String] = new CanDouble[String] {
    def double(a: String): String = a + a
}

// Uses the Numeric type class to create a CanDouble for all Numeric types
implicit def numericDouble[A: Numeric]: CanDouble[A] = {
    new CanDouble[A] {
         def double(a: A): A = implicitly[Numeric[A]].plus(a, a)
    }
}

Now we can define the double method that requires a evidence of the type class CanDouble.

def double[A: CanDouble](a: A): A = implicitly[CanDouble[A]].double(a)

scala> double(1)
res4: Int = 2

scala> double(0.4)
res5: Double = 0.8

scala> double("a")
res6: String = aa

Ideally, you would put all of the type class instances like StringDouble and numericDouble within the companion object CanDouble.


I don't think a structural type can work at all here, because you are not allowed to use an abstract type parameter within the structural refinement that is defined outside of the refinement (the type parameter T). From the SLS:

Within a method declaration in a structural refinement, the type of any value parameter may only refer to type parameters or abstract types that are contained inside the refinement. That is, it must refer either to a type parameter of the method itself, or to a type definition within the refinement. This restriction does not apply to the method's result type.

Structural types should typically be avoided, anyway, as they are quite slow. Type classes should be preferred in this scenario.

Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
  • Thanks! But why could it be written simply like `double x = x + x` in Haskell... I thought Haskell is also a functional language using static type.. – Hanfei Sun May 03 '15 at 04:18
  • Scala isn't Haskell. I don't know why you can do that in Haskell, but my answer describes why you *can't* do it like that in Scala, which is the point. – Michael Zajac May 03 '15 at 04:21
  • In Haskell, there can be only one `+` function in scope at a given time. In Scala, a lot of objects can have a `+` method. Therefore, when you write `+`, Haskell already knows its signature, whereas Scala has no idea and has to be helped. – Eugene Burmako May 03 '15 at 06:37
  • @EugeneBurmako if function is a part of typeclass (`a` can steel be abstract here) - you may have multiple implementations of `+` (instances) for different types of `a` accross the build. Returning to Scala - object itself may be considered as just as parameter of function (some implementation of `a`) - then (approximately) scala's code is just a set of functions. If you add object to the signature - it's not a problem (except, maybe, type parameters and inheritance) to find appropriate. Theoretically of course :) – dk14 May 03 '15 at 08:28
  • @EugeneBurmako actually it's already done: `trait Num[T]{ val a: T; def + (b: Num[T]): Num[T] }; implicit class NumInt(val a: Int) extends Num[Int] {override def + (b: Num[Int]) = NumInt(a + b.a)}`. The problem here that you have to specify `def double[T](a: Num[T]) = a + a`, because scala can't infer input `Num[T]` – dk14 May 03 '15 at 09:00
  • @dk14 Is it possible to explicitly call a particular implementation of `+` from a specific typeclass instance? – Eugene Burmako May 03 '15 at 10:38
  • 1
    @Eugene Burmako If you mean haskell, it's all like in scala - you can't have ambigious implicits, so you need to import appropriate ones. If there is no ambiguity - just call function with parameters of appropriate type (you can specify types explicitly instead of type-inference in haskell). The principle of implits resolution is pretty simillar (you may check the end of [my answer](http://stackoverflow.com/a/30011018/1809978) with haskell's `Num` more-less implemented in scala), the only advantage of Haskell is better type-inference. – dk14 May 03 '15 at 11:11
  • @Eugene Burmako more precisely it's a bit more restricted than in scala... depending on how modules are structurized - http://stackoverflow.com/questions/8728596/explicitly-import-instances – dk14 May 03 '15 at 11:38
  • I'm trying to understand what's first-class. E.g. let's assume the definitions of https://www.haskell.org/tutorial/classes.html. We can call `1 == 1`, that's clear. But can we call `1 (Eq Integer).== 1` (the syntax is completely made-up)? I.e. is `(==) :: a -> a -> Bool` the only function there or `Eq Integer` et al also define additional functions? Btw thanks a lot for your answers! – Eugene Burmako May 03 '15 at 11:45
  • @EugeneBurmako Np. As far as I understand instances in Haskell are not first-class citizens, so no passing to functions, path-dependent implicits (same for scala, btw) etc. You can only "instantiate" data types related to them (using data-constructors). Talking about "only function" - there might be several or even something [more complex](https://downloads.haskell.org/~ghc/7.0.4/docs/html/users_guide/type-class-extensions.html) - but you have to implement all of them in your instance. Btw, I'm not big Haskell specialist I'm big "Hey! Haskell is better than Scala" specialist :) – dk14 May 03 '15 at 12:06
  • @EugeneBurmako However, I believe nothing stops you from just calling regular function (from another module) from typeclass instance's member - this function itself can be eta-abstracted to become first-class (something like selfless patern) – dk14 May 03 '15 at 12:17
  • and shortly, we can not call member of typeclass directly like memeber of object in scala. It's like calling `object.super.virtualMethod` - haskell (at least by default) doesn't give direct access to dispactching mechanizm. – dk14 May 03 '15 at 12:26
3

In haskell you can:

Prelude> let double x = x + x // (1)
Prelude> let quadruple x = double (double x) //(2)

Prelude> :t double
double :: Num a => a -> a
Prelude> :t quadruple
quadruple :: Num a => a -> a

In scala you have to specify Num explicitly

scala> def double[T: Numeric] (a: T) = implicitly[Numeric[T]].plus(a, a)
double: [T](a: T)(implicit evidence$1: Numeric[T])T

scala> def quadruple[T: Numeric](a: T) = double(double(a))
quadruple: [T](a: T)(implicit evidence$1: Numeric[T])T

Because haskell's type inferrence is smarter. (1)st line did find typeclass Num:

Prelude> :info Num
class Num a where
  (+) :: a -> a -> a //looks like structural types, but ...
  (*) :: a -> a -> a
  (-) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a
    -- Defined in ‘GHC.Num’ //... but here is implementations found accross build - they are explicitly saying that they are instances of Num

instance Num Integer -- Defined in ‘GHC.Num’
instance Num Int -- Defined in ‘GHC.Num’
instance Num Float -- Defined in ‘GHC.Float’
instance Num Double -- Defined in ‘GHC.Float’

Also Scala has problems with structural types - you can't define polymorphic structural type (not only this - you ca not define polymorphic lambdas) "Parameter type in structural refinement may not refer to an abstract type defined outside that refinement"

Otherwise Num would be defined in Scala as something like that:

implicit class Num[T <: { def +(x:T):T }](a: T) = ... //will not work, and pretty slow by the way

See other answers to find out how it's really defined (Numeric).

In the (2)nd line compiler inferred input type for x (Num x) from double's application. Scala just can't do that. Its analog to the haskell's Num would be:

scala> trait Num[T]{ val a: T; def + (b: Num[T]): Num[T] }
defined trait Num

scala> implicit class NumInt(val a: Int) extends Num[Int] {override def + (b: Num[Int]) = NumInt(a + b.a)}
defined class NumInt

scala> def double[T](a: Num[T]) = a + a
double: [T](a: Num[T])Num[T]

scala> double(5)
res4: Num[Int] = NumInt@424f5762

But the problem is still same - you have to specify input types (a: Num[T]) in scala, it cannot infer them.

However, even in Haskell you can't let's say:

Prelude> let double x = x +++ x

<interactive>:28:18:
    Not in scope: ‘+++’
    Perhaps you meant ‘++’ (imported from Prelude)
Otherwise `Num` would be defined in Scala as something like that:

And Haskell's real duck typing is not so easy to use: http://chrisdone.com/posts/duck-typing-in-haskell

Community
  • 1
  • 1
dk14
  • 22,206
  • 4
  • 51
  • 88
1

This is a perfect example of when to use typeclasses.

+ is just a function. You've given the compiler no information such as

def +(t : T, t : T) : T = ...

And you couldn't, because you don't have any idea what a T is.

Here it would work as follows. You have a type constructor, called Doubles:

trait Doubles[T]{
  def double(t : T) : T
}

Now in a companion object, just for convenience, I'm going to rewrite your double function as follows:

object Doubles{
  def double[T](t : T)(implicit doubles : Doubles[T]) =    doubles.double(t)
}

So this says, I can double a T, as long as there's a Doubles for T in scope, or you explicitly provide me with a Doubles for T. Otherwise, I won't be able to double a T, and you'll get a compiler error.

The following are going to be instances of that typeclass Doubles[T]:

object Implicits{
    //Now, you wouldn't want to have to write this for 
    //every kind of number.  Scala already provides you with a numeric
    //typeclass.  So here's a function that gives a Doubles[N] 
    //whenever you ask for a Doubles[Numeric[T]], i.e. a Doubles for a     
    //member of the Numeric typeclass:

    implicit def numDoubler[N](implicit num : Numeric[N]) : Doubles[N] = new Doubles[N]{
        def double(n : N) : N = num.plus(n,n)
    }

    implicit object stringDoubler extends Doubles[String]{
        def double(t : String) : String = t + t
    }

 //So something like this is no longer needed:
 // implicit object intDoubler extends Doubles[Int]{
 //   def double(t : Int) : Int = t + t
 // }

}
Zak Patterson
  • 389
  • 5
  • 11