Определение типа функции

#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 , что охватывает любое число, включая действительные числа, тогда как имеет смысл повторять что-либо только целое число раз.