Как использовать классы типов в функциях Haskell?

В настоящее время я пытаюсь научить себя Haskell с помощью Learn You A Haskell, и поэтому, чтобы проверить себя, я пытаюсь написать функцию, которая принимает список чисел и возвращает среднее значение.

listlen :: [a] -> Int
listlen [] = 0
listlen (x:xs) = 1 + listlen xs

avg :: (Num a) => [a] -> a
avg [] = error "Cannot average list of length 0"
avg l = ((foldr (+) 0 l) `div` (listlen l))

main = putStrLn (show (avg [1,2,3,4,5]))

Однако, когда я запускаю этот код, я получаю это сообщение об ошибке:

test.hs:7:10: error:
    • Couldn't match expected type ‘a’ with actual type ‘Int’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          avg :: forall a. Num a => [Int] -> a
        at test.hs:5:8
    • In the expression: ((foldr (+) 0 l) `div` (listlen l))
          In an equation for ‘avg’:
          avg l = ((foldr (+) 0 l) `div` (listlen l))
    • Relevant bindings include
        avg :: [Int] -> a (bound at test.hs:6:1)

Я возился с подписью, и единственный способ заставить ее работать - преобразовать типы в реальные типы, например. avg :: [Int] -> Int. Буду очень признателен за любые советы.


person Frawstcrabs    schedule 30.03.2018    source источник


Ответы (1)


Проблема отсюда:

avg l = ((foldr (+) 0 l) `div` (listlen l))

div определяется по Integrals, а не только по Nums. Кроме того, div занижает результат. Нормальное деление — (/), которое определяется Fractionals.

Также (более непосредственно) отсюда:

listlen :: [a] -> Int
listlen [] = 0
listlen (x:xs) = 1 + listlen xs

listlen возвращает Int, что делает div типом Int -> Int -> Int.

Это должно работать:

listlen :: (Num n) => [a] -> n
listlen [] = 0
listlen (x:xs) = 1 + listlen xs

avg :: (Fractional a) => [a] -> a
avg [] = error "Cannot average list of length 0"
avg l = foldr (+) 0 l / listlen l

Или с некоторыми функциями из Prelude:

avg :: (Fractional a) => [a] -> a
avg [] = error "Cannot average list of length 0"
avg l = sum l / fromIntegral (length l)
person Dannyu NDos    schedule 30.03.2018
comment
Вы тестировали это решение? - person Daniel Wagner; 30.03.2018
comment
Я только что попробовал фиксированный код, но все еще получаю аналогичную ошибку. - person Frawstcrabs; 30.03.2018
comment
length возвращает Int. Я думаю, вы еще не усвоили урок тестирования. ;-) - person Daniel Wagner; 30.03.2018