Сигнатуры функции перегрузки 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? Было бы трудно добавить к этому языку? Или там есть драконы?
Ответы
Ответ 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 скорее является ограниченным полиморфизмом, и он включает объявление классов классов и экземпляров классов