Reputation: 3066
In my understanding: List , Option are kinds which have the shape like * -> * Does the kind in scala is the same as in Haskell?
If they are the same, then List[Int] and List[String] are two different types.
However the following codes give the error double definition
which says have same type after erasure
def fun1(a:Option[String]):Option[String] = a
def fun1(a:Option[Integer]):Option[Integer] = a
def fun2(a:List[String]) = a(0) + a(1)
def fun2(a:List[Int]) = (a(0) + a(1)).toString
Upvotes: 1
Views: 105
Reputation: 12123
This is partially Java legacy. The generics type-parameters aren't present at the run-time. List[Int]
is bare List
. Theoretically the right implementation could be selected at compile-time, but e.g. reflection-wise they are the same.
In Haskell you don't have function overloading, and you have to give different names to the function. This is good practice anyway. In fact, even using Haskell type-classes you can't have different implementations for [Int]
and [String]
, without enabling dangerous language extensions (FlexibleInstances
).
EDIT: java example:
package foo;
import java.util.List;
class Foo {
void foo(List<Double> arr) {}
void foo(List<String> arr) {}
}
fails with similar error
foo.java:7: error: name clash: foo(List<String>) and foo(List<Double>) have the same erasure
void foo(List<String> arr) {}
^
1 error
The Haskell example using FlexibleInstances
extension:
{-# LANGUAGE FlexibleInstances #-}
class Foo a where
foo :: a -> String
instance Foo (Maybe String) where
foo = maybe "" id
instance Foo (Maybe Int) where
foo = maybe "" show
And it will work:
*Main> foo (Just "bar")
"bar"
Yet you can easily implement this example without FlexibleInstances
, using newtype
s:
class Foo a where
foo :: a -> String
newtype MaybeString = MaybeString (Maybe String)
newtype MaybeInt = MaybeInt (Maybe Int)
instance Foo MaybeString where
foo (MaybeString ms) = maybe "" id ms
instance Foo MaybeInt where
foo (MaybeInt mi) = maybe "" show mi
*Main> foo (MaybeString (Just "bar"))
"bar"
Why FlexibleInstances
are dangerous? That is a question worth own StackOverflow thread. Shortly: it's very easy to introduce undecidable situations i.e. compiler won't know which instance to pick:
class Foo a where
foo :: a -> String
newtype MaybeString = MaybeString (Maybe String)
newtype MaybeInt = MaybeInt (Maybe Int)
instance Foo MaybeString where
foo (MaybeString ms) = maybe "" id ms
instance Foo MaybeInt where
foo (MaybeInt mi) = maybe "" show mi
If we try the same example input as before, we get an error:
Overlapping instances for Foo (Maybe [Char])
arising from a use of ‘foo’
Matching instances:
instance Foo (Maybe String) -- Defined at Example.hs:6:10
instance Show a => Foo (Maybe a) -- Defined at Example.hs:9:10
In the expression: foo (Just "bar")
In an equation for ‘it’: it = foo (Just "bar")
You can use the typeclass approach in Scala as well. As it relies on implicits, and you can explicitly specify implicit parameter, the approach is safer than in Haskell.
trait Foo[A] {
def foo(opt: Option[A]): String
}
implicit val fooString: Foo[String] = new Foo[String] {
def foo(opt: Option[String]): String = opt match {
case Some(value) => value + " is string"
case None => ""
}
}
implicit def fooA[A]: Foo[A] = new Foo[A] {
def foo(opt: Option[A]): String = opt match {
case Some(value) => value.toString
case None => ""
}
}
def foo[A](x: Option[A])(implicit fInstance: Foo[A]): String =
fInstance.foo(x)
Seems that scalac chooses the most specific instance:
scala> foo(Option("bar"))
res4: String = bar is string
scala> foo(Option(2))
res5: String = 2
Yet we can force scalac to use fooA
even with String
:
scala> foo(Option("bar"))(fooA)
res6: String = bar
Upvotes: 3