Функция перегрузки сигнатур haskell - PullRequest
23 голосов
/ 25 мая 2011

При компиляции появляется следующее сообщение об ошибке:

Подпись дубликата типа:
weightedMedian.hs: 71: 0-39: findVal :: [ValPair] -> Double ->Double
weightedMedian.hs: 68: 0-36: findVal :: [ValPair] -> Int -> Double

Мое решение состоит в том, чтобы найти findValI и findValD.Однако findValI просто преобразует тип Int в тип Double и вызывает findValD.

Также я не могу сопоставить шаблон с типами Num (Int, Double), поэтому я не могу просто изменить сигнатуру типа на

findVal :: [ValPair] -> Num -> Double   

Во многих языках мне не нужны разные имена.Зачем мне нужны разные имена в Haskell?Будет ли это трудно добавить к языку?Или там есть драконы?

Ответы [ 5 ]

32 голосов
/ 25 мая 2011

Специальный полиморфизм (и перегрузка имен) предоставляются в Haskell с помощью классов типов:

class CanFindVal a where
          findVal :: [ValPair] -> a -> Double

instance CanFindVal Double where
     findVal xs d = ...

instance CanFindVal Int where
     findVal xs d = findVal xs (fromIntegral d :: Double)

Обратите внимание, что в этом случае, поскольку findVal "действительно" требуется Double, я быпросто всегда нужно, чтобы это заняло двойное число, а когда мне нужно было передать ему int, просто используйте fromIntegral на сайте вызовов.Как правило, вам нужны классы типов, когда в действительности задействовано другое поведение или логика, а не беспорядочные.

16 голосов
/ 25 мая 2011

Для поддержки findVal :: [ValPair] -> Double -> Double и findVal :: [ValPair] -> Int -> Double требуется специальный полиморфизм (см. http://www.haskell.org/haskellwiki/Ad-hoc_polymorphism), который обычно опасен. Причина в том, что специальный полиморфизм позволяет изменять семантику с одинаковым синтаксисом.

Хаскелл предпочитает то, что называется параметрическим полиморфизмом. Вы видите это все время с сигнатурами типов, где у вас есть переменная типа.

Haskell поддерживает более безопасную версию специального полиморфизма с помощью классов типов.

У вас есть три варианта.

  1. Продолжите то, что вы делаете, с явным именем функции. Это разумно, он даже используется некоторыми библиотеками c, например, opengl.
  2. Использовать класс пользовательского типа. Это, вероятно, лучший способ, но он тяжелый и требует большого количества кода (по очень компактным стандартам haskells). Посмотрите на ответ sclv для кода.
  3. Попробуйте использовать существующий класс типов и (если вы используете GHC) получите производительность со специализациями.

Как это:

findVal :: Num a => [ValPair] -> a -> Double
{-# SPECIALISE findVal :: [ValPair] -> Int -> Double #-}
{-# SPECIALISE findVal :: [ValPair] -> Double -> Double #-}
findVal = ...
6 голосов
/ 25 мая 2011

Haskell не поддерживает перегрузку в стиле C ++ (хорошо, это sortof делает с классами типов, но мы не используем их одинаково). И да, есть некоторые драконы, связанные с его добавлением, в основном связанные с выводом типа (становится экспоненциальным или неразрешимым или что-то в этом роде). Тем не менее, видеть такой «удобный» код довольно редко в Haskell. Какой это, Int или Double? Поскольку ваш метод Int делегируется методу Double, я предполагаю, что Double является "правильным". Просто используйте это. Из-за буквальной перегрузки вы все равно можете назвать это как:

findVal whatever 42

И 42 будет рассматриваться как Double. Единственный случай, когда это происходит, - если вы получили что-то, что является по существу и Int где-то, и вам нужно передать это в качестве аргумента. Затем используйте fromIntegral. Но если вы будете стремиться к тому, чтобы ваш код везде использовал «правильный» тип, этот случай будет необычным (и когда вам придется конвертировать, стоит обратить на это внимание).

3 голосов
/ 25 мая 2011

В этом случае, я думаю, легко написать функцию, которая обрабатывает как Int, так и Double для второго аргумента. Просто напишите findVal, так что это вызовы realToFrac во втором аргументе. Это преобразует Int в Double и просто оставит Double в покое. Тогда пусть компилятор выведет тип для вас, если вы ленивый.

0 голосов
/ 02 августа 2017

Во многих других языках программирования вы можете объявлять (своего рода) функции, имеющие одно и то же имя, но разные вещи в своих сигнатурах, например, разные типы параметров. Это называется перегрузкой и, безусловно, является наиболее популярным способом достижения специального полиморфизма.

Haskell сознательно НЕ поддерживает перегрузку, потому что его дизайнеры не считают это лучшим способом достижения специального полиморфизма. Путь Haskell скорее ограниченный полиморфизм и включает в себя объявление классов типов и экземпляров классов

...