worldterminator
worldterminator

Reputation: 3066

How does scala deal with higher kind type?

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

Answers (1)

phadej
phadej

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 newtypes:

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

Related Questions