Как следовать выводам типа в Haskell?

#haskell

#haskell

Вопрос:

Не удалось действительно следовать выводам типа ниже, где случай 1 сработал, но случай 2 не удался, почему?

 ghci> :t sqrt . maximum . map ( 1)   -- case 1

(Floating c, Ord c) => [c] -> c

ghci> :t sqrt . maximum . map length -- case 2

Could not deduce (Floating Int) arising from a use of ‘sqrt’
from the context (Foldable t)
bound by the inferred type of it :: Foldable t => [t a] -> Int
 

@РЕДАКТИРОВАТЬ

В ООП Num обычно является нижней границей для всех его subtypes , например Int , и Float . Следовательно, Int будет логически приемлемым, если Num является квалифицированным типом, но не наоборот.

Кроме того, на C-подобных языках встроенное преобразование чисел может автоматически выполнять регистр с более низкой точностью до более высокой, например, от Int до Float .

В отличие от этого, в Haskell с системой типов HM Num является class для всех его instances , например Int , и subclasses , например Floating . Квалифицированные типы могут быть выведены между предком и потомком, например, от Num до Int , Floating или наоборот, но не между Int и Floating .

Чтобы исправить случай 2, Int сначала его следует адаптировать к Num by fromIntegral или приложить Data.List.genericLength усилия для создания Num — логически квалифицированного типа для Floating этого sqrt требуется.

Давайте применим вышеупомянутые пункты, чтобы следовать выводам типа ниже,

 ghci> :t ( )
Num a => a -> a -> a

ghci> :t 1.1
Fractional a => a

ghci> :i Fractional
class Num a => Fractional a
instance Fractional Float
instance Fractional Double

ghci> :t 1
Num a => a

ghci> :t length [1]
Int 

ghci> :i Int
instance Num Int    
instance Real Int 
instance Integral Int 

ghci> :t 1.1   1          -- case 1
Fractional a => a

ghci> :t 1.1   length [1] -- case 2
No instance for (Fractional Int) arising from the literal ‘1.1’
 

Комментарии:

1. length Функция возвращает Int , но sqrt ожидает тип с Floating экземпляром. Попробуйте genericLength Data.List вместо этого использовать from , который может создавать любой тип с Num экземпляром.

2. Ваше резюме неверно. Классы типов не являются типами. Вы не можете преобразовать Num в Int или Int в Floating. Это яблоки и апельсины. Классы типов ограничивают тип. У вас может быть, например, Floating a => a -> a -> a который говорит, что a должен иметь плавающий экземпляр. Float делает, Int не делает.

3. @erisco, дело type inference в том, что моя формулировка может ввести вас в заблуждение, заставляя думать о преобразовании между type class и т.д. type

4. Литерал 1 может быть выведен как любой тип Num. В первом случае sqrt имеет ограничение с плавающей запятой и 1 ограничение Num, что упрощает просто ограничение с плавающей запятой (потому что плавающее значение подразумевает Num). Во втором случае length имеет ограничение (~) Int , но оно не выполняется, поскольку Int не имеет плавающего экземпляра.

5. Также я должен добавить, fromIntegral ослабляет интегральное ограничение на ограничение Num. fromIntegral :: (Num b, Integral a) => a -> b . Возможно, этого не должно быть из-за неблагоприятных последствий, таких как fromIntegral 1000 :: Word8 .

Ответ №1:

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

Однако length , в частности, приведет Int к . Он не может выдавать числа любого другого типа. So maximum . map length может взять любой список и выдать результат типа Int , а не какого-либо другого числового типа.

Теперь sqrt его аргументом должно быть число с плавающей запятой. Итак, в первом случае вывод типа показывает, что вы должны предоставить список чисел с плавающей запятой, поэтому результатом maximum . map ( 1) будет число с плавающей запятой, которому можно передать sqrt .

Однако второй случай просто не может работать, потому Int что не является типом с плавающей запятой и maximum . map length не может выдавать ничего, кроме an Int . Таким образом, это вызывает ошибку.

Вы можете использовать fromIntegral функцию для преобразования результата length в любой числовой тип, чтобы заставить второй код работать.

Комментарии:

1. не могли бы вы любезно взглянуть на мой отредактированный пост, чтобы проверить, правильно ли он обобщен? Спасибо!