Давайте посмотрим на тип snd
:
snd :: (foo, x) -> x
(я переименовал переменные типа для ясности)
Что такое состояние типа для кортежа с типами foo
и x
, вернуть что-то типа x
.Здесь важно знать, что система ценностей ака.среда выполнения в Haskell ленива, система типов Haskell строгая, это означает, что оба типа foo
и x
должны быть известны до вызова snd
.
В первом случае, когда вы простоиметь Num b => (b -> b, String)
, вызов snd
оставит b
неоднозначным, потому что вы нигде не упоминаете его конкретный тип, и он не может быть выведен из возвращаемого типа, потому что foo ~ b
, который отличается от x
,Другими словами: поскольку (b, b)
может быть кортежем любого числового типа, а средство проверки типов не может выяснить, какой из них, это неоднозначно.Хитрость заключается в том, что у нас будут введены правила по умолчанию для Haskell, которые утверждают, что если числовой тип является неоднозначным, по умолчанию он должен быть Integer
.Если бы вы включили предупреждения с помощью -Wall
, он бы сказал, что это происходит.Таким образом, наш тип становится (Integer -> Integer, String)
и может вызываться snd
.
Однако во втором случае нам все же удается вывести b
с помощью правил по умолчанию, но по умолчанию Arrow
нетза a
, поэтому мы застряли!Вы должны явно указать, какую стрелку вы хотите, чтобы продолжить!Вы можете сделать это, сначала используя значение aTuple10
где-то еще:
let bla = aTuple10 -- We do this because `aTuple10` can have type variables, but `bla` cannot (by default)
fst bla (23 :: Int) -- This fixes the type of `bla`, so that `a ~ (->)` and `b ~ Int`
print $ snd bla -- So the arrow isn't ambiguous here
... или вы можете просто указать нужный тип:
print $ snd (aTuple10 :: (Int -> Int, String))
PS Если вы хотите изменить тип неоднозначных чисел по умолчанию, ключевое слово default
может вам помочь.