Ответ 1
На самом деле ваш экземпляр работает нормально. Обратите внимание:
main = print $ two (3 :: Int, 4 :: Int)
Это работает так, как ожидалось. Так почему же он не работает без аннотации типа? Ну, рассмотрим тип кортежа: (3, 4) :: (Num t, Num t1) => (t, t1)
. Поскольку числовые литералы являются полиморфными, ничто не требует, чтобы они были одного типа. Экземпляр определен для (a, a)
, но существование этого экземпляра не даст GHC унифицировать типы (по целому ряду веских причин). Если GHC не может вывести другими способами, что два типа являются одинаковыми, он не будет выбирать нужный экземпляр, даже если оба типа могут быть равными.
Чтобы решить вашу проблему, вы можете просто добавить аннотации типа, как я уже говорил выше. Если аргументы приходят из других источников, это обычно не нужно, потому что они уже будут известны как один и тот же тип, но он быстро становится неуклюжим, если вы хотите использовать числовые литералы.
Альтернативным решением является отметить, что из-за того, как работает выбор экземпляра, наличие экземпляра для (a, a)
означает, что вы также не можете написать экземпляр типа (a, b)
, даже если хотите. Поэтому мы можем немного обмануть, чтобы заставить унификацию использовать класс типа, например:
instance (a ~ b) => Pair (a,b) a where
Мне нужно расширение TypeFamilies
для контекста ~
. Это означает, что экземпляр должен совпадать с любым кортежем вначале, потому что выбор экземпляра игнорирует контекст. Однако после выбора экземпляра контекст a ~ b
утверждает равенство типов, что приведет к ошибке, если они будут отличаться, но, что более важно, здесь, если возможно, унифицируют переменные типа. Используя это, ваше определение main
работает как есть, без аннотаций.