#haskell #functional-programming
#haskell #функциональное программирование
Вопрос:
Я пытаюсь выяснить, как Haskell определяет тип функции. Я написал пример кода:
compareAndIncrease a b =
if a > b then a 1:b:[]
else a:b:[]
который создает список на основе сравнения a> b. Затем я проверил ее тип с :t
помощью command:
compareAndIncrease :: (Ord a, Num a) => a -> a -> [a]
Хорошо, итак, мне нужен класс типов Ord для сравнения, Num для численных вычислений (например, a 1). Затем я беру параметры a и b и получаю взамен список (a-> a-> [a]). Кажется, все в порядке. Но потом я где-то нашел функцию для репликации числа:
replicate' a b
| a ==0 = []
| a>0 = b:replicate(a-1) b
Обратите внимание, что внутри используется обычная функция репликации библиотеки, а не функция репликации. Он должен быть похож на compareAndIncrease, потому что он использует сравнение, числовые операции и возвращает список, поэтому я подумал, что это будет работать так:
replicate' :: (Ord a, Num a) => a -> a -> [a]
Однако, когда я проверил :t
, я получил этот результат:
replicate' :: Int -> t -> [t]
Я продолжил возиться с этой функцией и изменил ее имя на repval, так что теперь это:
Может ли кто-нибудь объяснить мне, что происходит?
Комментарии:
1. Кажется, здесь выводится 0::Int
Ответ №1:
GHCi — отличный инструмент для использования здесь:
*Main> :type replicate
replicate :: Int -> a -> [a]
Вы определяете replicate'
в терминах replicate (я переименовываю ваши переменные для ясности):
replicate' n e
| -- blah blah blah
| n > 0 = e : replicate (n - 1) e
Поскольку вы вызываете replicate (n - 1)
, средство проверки типов делает вывод, что n - 1
оно должно иметь тип Int
, из чего оно делает вывод, что n
оно должно иметь тип Int
, из чего оно делает вывод, что replicate'
имеет тип Int -> a -> [a]
.
Если бы вы написали свою replicate'
рекурсивно, используя replicate'
inside вместо replicate
, тогда вы получили бы
*Main> :type replicate'
replicate' :: (Ord a, Num a) => a -> a1 -> [a1]
Редактировать
Как указывает Ганеш Ситтампалам, лучше ограничить тип Integral
, поскольку на самом деле не имеет смысла копировать дробное число раз.
Комментарии:
1. На самом деле, после рекурсивной записи я получил (Ord a, Num a) вместо (Eq a, Num a), потому что я использую сравнение, но ваш ответ правильный 🙂
Ответ №2:
Ключевой недостаток в ваших рассуждениях заключается в том, что replicate
на самом деле требуется только Int
количество репликации, а не более общий числовой тип.
Если бы вы вместо этого использовали genericReplicate
, то ваш аргумент был бы примерно действительным.
genericReplicate :: Integral i => i -> a -> [a]
Однако обратите внимание, что ограничение Integral
, а не Num
потому Num
, что охватывает любое число, включая действительные числа, тогда как имеет смысл повторять что-либо только целое число раз.