Высоко-родственные типы на уровне метода в Scala - PullRequest
4 голосов
/ 30 мая 2011

Я только начал играть с типами более высокого класса в Scala, и у меня такое поведение, которого я не понимаю. Я делаю все это в REPL на Scala 2.9.0.1.

Сначала я создаю черту mapper, чтобы я мог отображать элементы любого типа M:

 trait Mapper {
    def mapper[M[_], A, B](m: M[A], f: A => B): M[B]
 }

Вот моя реализация маппера:

 val mymapper = new Mapper {
  def mapper[List, Int, Double](m: List[Int], f: Int => Double): List[Double] = m.map(f)
 }

Но REPL жалуется ...

 <console>:9: error: List does not take type parameters
   def mapper[List, Int, Double](m: List[Int], f: Int => Double): List[Double] = m.map(f)
                                                                  ^
 <console>:9: error: List does not take type parameters
   def mapper[List, Int, Double](m: List[Int], f: Int => Double): List[Double] = m.map(f)
                                    ^

Этот код работает нормально, если я переместил объявление M [_] на уровень класса:

trait Mapper[M[_]] {
  def mapper[A,B](m: M[A], f: A => B): M[B]
}
val mymapper = new Mapper[List] {
  def mapper[Int, Double](m: List[Int], f: Int => Double): List[Double] = m.map(f)
}
mymapper.mapper(List(1,2,3), (x: Int) => x.toDouble)
// returns List(1.0, 2.0, 3.0)

Почему это так? Почему Scala может определить правильный тип для M, если он расположен на уровне класса, но не работает на уровне метода?

спасибо!

1 Ответ

8 голосов
/ 30 мая 2011

Этот код не означает, что вы думаете, что означает:

def mapper[List, Int, Double](m: List[Int], f: Int => Double): List[Double] = m.map(f)

List, Int и Double здесь являются именами параметров типа, точные типы будут определены значениями, используемыми для вызова метода. Да, они также являются именами конкретных конкретных типов, но в этом случае вы скрываете это значение.

Если вы используете исходные имена M, A и B, ошибка становится более понятной:

def mapper[M, A, B](m: M[A], f: A => B): M[B] = m.map(f)

M действительно не принимает параметр типа ...


Это становится еще более очевидным, если вы сделаете то же самое с именами параметров в своем «рабочем» примере:

trait Mapper[M[_]] {
  def mapper[A,B](m: M[A], f: A => B): M[B]
}

val mymapper = new Mapper[List] {
  def mapper[A, B](m: List[A], f: A => B): List[B] = m.map(f)
}
...