Подтвердить что ты не робот

Сигнатуры функции перегрузки haskell

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

Повторяющаяся подпись типа:
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? Было бы трудно добавить к этому языку? Или там есть драконы?

4b9b3361

Ответ 1

Ad-hoc-полиморфизм (и перегрузка имен) предоставляются в 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 на вызывать сайт. Обычно вы хотите, чтобы классы типов, когда на самом деле было другое поведение или логика, а не разборчиво.

Ответ 2

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

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

Haskell поддерживает более безопасную версию ad-hoc-полиморфизма через классы классов.

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

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

Вот так:

findVal :: Num a => [ValPair] -> a -> Double
{-# SPECIALISE findVal :: [ValPair] -> Int -> Double #-}
{-# SPECIALISE findVal :: [ValPair] -> Double -> Double #-}
findVal = ...

Ответ 3

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

findVal whatever 42

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

Ответ 4

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

Ответ 5

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

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