Когда Haskell жалуется на неправильную типизацию в функциях?

Я новичок в Haskell, пытающийся обернуть голову вокруг привязки типов в функциях и о том, как Haskell применяет ее. Например, даже если для функции fst указан тип fst :: (a, b) -> a, компилятор не жалуется на функцию fst'. Но компилятор жалуется на привязки типов для функции elem'.

fst' :: (a,a) -> a
fst' s = fst s

elem' :: (Eq a, Eq b) => a -> [b] -> Bool
elem' x xs = elem x xs

Ответ 1

fst имеет тип fst :: (a, b) -> a, так что это означает, что можно определить функцию:

fst' :: (a, a) -> a
fst' = fst

Ваша функция fst' является более строгой, чем функция fst. Независимо от того, что вы заменяете a в вашей функции fst', которая подходит для fst. Например, если выполняется a ~ Bool, вы вызываете fst с подписью fst :: (Bool, Bool) -> Bool. Но поскольку fst может работать со всеми a и b, хорошо, что оба элемента кортежа являются Bool, поэтому данный fst может обрабатывать кортежи для всех возможных типов как для первого, так и для второго элемент из 2-х кортежей, это совершенно нормально, если два элемента имеют одинаковый тип.

Последнее не в порядке, здесь вы определяете:

elem' :: (Eq a, Eq b) => a -> [b] -> Bool
elem' = elem

но elem имеет тип elem :: Eq a => a -> [a] -> Bool. Сигнатуры, которые вы можете сделать с помощью функции elem', не являются подмножеством сигнатур функции elem, поскольку вы можете установить a ~ Int и b ~ Bool. В этом случае вы ожидаете elem :: Int -> [Bool] -> Bool, но, очевидно, это не выполняется, поскольку типы Int и Bool являются двумя разными типами, а в сигнатуре elem они оба являются a.