Различные типы в функции суммы
Я новичок в Хаскеле, так что это может быть глупым вопросом. Я читаю книгу, где написано :type sum
должен показать sum :: (Num a) => [a] -> a
, Вместо этого сообщение sum :: (Num a, Foldable t) => t a -> a
, Как я видел в https://www.haskell.org/hoogle/?hoogle=Sum эта разница обусловлена - я думаю - существованием двух разных функций суммы. Может быть, это что-то вроде полиморфизма в Java, я только начинаю и понятия не имею, как работает Haskell.
Итак, мои вопросы: как я могу использовать функцию суммы, какой тип sum :: (Num a) => [a] -> a
вместо другого? Не могли бы вы объяснить мне, что здесь происходит?
1 ответ
Как я видел в https://www.haskell.org/hoogle/?hoogle=Sum эта разница обусловлена - я думаю - существованием двух разных функций суммы. Может быть, это что-то вроде полиморфизма в Java
Это действительно полиморфизм, хотя и не таким образом (см. PS в конце этого ответа). Обратите внимание, что...
sum :: (Num a) => [a] -> a
... уже полиморфна в типе суммируемых чисел, поэтому она будет работать, например, со списками Integer
и списки Double
, Разница между этим и...
sum :: (Num a, Foldable t) => t a -> a
... это sum
Также полиморфен в типе контейнера:
GHCi> -- +t makes GHCi print the types automatically.
GHCi> :set +t
GHCi> sum [1 :: Integer, 2, 3]
6
it :: Integer
GHCi> sum [1 :: Double, 2, 3]
6.0
it :: Double
GHCi> import qualified Data.Set as S
GHCi> :t S.fromList
S.fromList :: Ord a => [a] -> S.Set a
GHCi> sum (S.fromList [1 :: Double, 2, 3])
6.0
it :: Double
Для типа контейнера, который будет использоваться с sum
, он должен иметь экземпляр Foldable
класс, который охватывает функции, которые, как sum
, может быть выражено как сведение контейнера в список, а затем как-то сворачивать его.
PS: ваша книга говорит нечто иное, чем то, что вы видели, потому что до недавнего времени sum
функция в Prelude имела менее общий тип, относящийся к списку, и ваша книга предшествует изменению. Наличие двух разных функций называется sum
даже если один из них носит более общий характер, чем другой, приведет к конфликту имен (по той же причине я импортировал Data.Set
модуль квалифицирован в примере выше - это хорошая идея, потому что он определяет несколько функций, таких как map
это столкновение с функциями Prelude, и квалифицируя их, скажем, S.map
избегает любых проблем).