Почему вывод типа Scala не такой мощный, как у Haskell? - PullRequest
43 голосов
/ 29 августа 2011

Механизм логического вывода в Haskell намного мощнее, чем в Scala. В Haskell мне редко приходится явно писать типы, тогда как в Scala типы можно выводить только в выражениях, но не в определениях методов.

Например, см. Следующий фрагмент кода на Haskell:

size xs = loop xs 0
  where
    loop [] acc = acc
    loop (_ : xs) acc = loop xs (acc+1)

Возвращает размер списка. Компилятор Haskell может распознать, какие типы используются и каково определение функции. Эквивалентный код Scala:

def size[A]: List[A] => Int = xs => {
  def loop: (List[A], Int) => Int = {
    case (Nil, acc) => acc
    case (_ :: xs, acc) => loop(xs, acc+1)
  }
  loop(xs, 0)
}

Или с определениями метода:

def size[A](xs: List[A]) = {
  def loop(xs: List[A], acc: Int): Int = xs match {
    case Nil => acc
    case _ :: xs => loop(xs, acc+1)
  }
  loop(xs, 0)
}

У меня вопрос: почему я не могу написать их следующим образом?

def size = xs => {
  def loop = {
    case (Nil, acc) => acc
    case (_ :: xs, acc) => loop(xs, acc+1)
  }
  loop(xs, 0)
}

Еще раз с определениями метода:

def size(xs) = {
  def loop(xs, acc) = xs match {
    case Nil => acc
    case _ :: xs => loop(xs, acc+1)
  }
  loop(xs, 0)
}

Это потому, что никто еще не реализовал это? Разве система типов Scala не настолько мощна, как это необходимо для этого случая? Или есть другие причины?

Ответы [ 4 ]

54 голосов
/ 29 августа 2011

Основная причина в том, что система типов Scala допускает подтипы, которые алгоритм вывода типов не поддерживает

.

В Haskell нет подтипов, поэтому алгоритм работает там гораздо лучше, хотя многие популярные расширения системы типов, поддерживаемые GHC, приводят к тому, что вывод типов снова завершается ошибкой, заставляя вас предоставлять явные подписи типов для некоторых выражений.

В конце концов, это компромисс между мощью системы типов и количеством выводов типов, которые могут быть сделаны. Scala и Haskell просто сделали разные компромиссы.

24 голосов
/ 30 августа 2011

Я думаю, что основные причины уже приведены, но я нахожу эту цитату создателя Scala Мартина Одерского особенно информативной,

Причина, по которой у Scala нет вывода типа Хиндли / Милнера, заключается в том, что это очень трудно сочетать с такими функциями, как перегрузка ( вариант ad-hoc, не классы типов), выбор записей и подтипы. Я не говорю, что невозможно - существует ряд расширений, которые включить эти функции; на самом деле я был осторожен с некоторыми из них себя. Я просто говорю, что очень трудно сделать эту работу хорошо в практика, где нужно иметь небольшие выражения типа и хорошо Сообщения об ошибках. Это не закрытый случай - многие исследователи работая над расширением границ здесь (посмотрите, например, на Реми MLF). Но сейчас это компромисс между лучшим выводом типа против лучшая поддержка этих функций. Вы можете сделать компромисс как пути. Тот факт, что мы хотели интегрироваться с Java, сказался на весах в пользу подтипов и от Хиндли / Милнера.

Источник: комментарий к сообщению Вывод универсального типа - плохая вещь .

19 голосов
/ 29 августа 2011

Хаммар дал самую большую причину. Вот два других:

  • Scala использует типы для разрешения имен

Рассмотрим

def foo(x) = x.a + x.b

Как Скала могла определить тип аргумента? Должен ли он искать каждый класс с полями a и b? Что делать, если их больше 1? В Хаскеле

foo x = (a x) + (b x)

имена записей уникальны, что создает свои собственные проблемы, но означает, что вы всегда можете сделать вывод, к какой записи относится ссылка.

  • Для вашего примера: case выражения в Scala могут быть разнородными

В Scala тип сопоставляемого объекта можно использовать либо как часть сопоставления, так и для определения способа сопоставления. Поэтому, даже если все конструкторы в case предназначены для List, вы могли бы захотеть передать ему что-то, кроме списка, и у него это не получилось.

13 голосов
/ 29 августа 2011

Другая вещь, которая не подходит для вывода типа Хиндли-Милнера, - это перегрузка метода и связанные с ним функции, такие как аргументы по умолчанию и переменные.Вот почему так сложно писать такие вещи, как zipWithN в Haskell (что тривиально в Scala).

...