GHCi игнорирует подпись типа
Prelude> let myprint = putStrLn . show
Prelude> :t myprint
myprint :: () -> IO ()
Хорошо, здесь нет ничего необычного. Просто правила GHCi по умолчанию, я думаю...
Prelude> let myprint = (putStrLn . show) :: Show x => x -> IO ()
Prelude> :t myprint
myprint :: () -> IO ()
Какое колдовство это? Вы просто игнорируете мое объявление типа? О_О
Есть ли способ убедить GHCi делать то, что я на самом деле намеревался сделать?
Ответы
Ответ 1
Мы можем сделать следующее с ограничением мономорфизма на:
>let myprint :: Show x => x -> IO (); myprint = putStrLn . show
>:t myprint
myprint :: Show x => x -> IO ()
Это не то же самое, что let myprint = putStrLn . show :: Show x => x -> IO ()
. В первом случае мы имеем привязку с сигнатурой типа, в последнем случае мы имеем привязку let
с аннотацией типа внутри правой стороны. Мономорфизм проверяет сигнатуры верхнего уровня, но не локальные аннотации.
Ответ 2
Добавление аннотации типа к выражению, как в
e :: type
заставляет компилятор проверить, что e
имеет type
, а также использовать type
для создания экземпляров переменных экземпляра и выбора экземпляра. Однако, если type
является полиморфным, он все равно может быть создан позже. Рассмотрим, например,
(id :: a -> a) "hello"
Выше, a
будет создаваться на String
, несмотря на мою аннотацию. Далее,
foo :: Int -> Int
foo = (id :: a -> a)
сделает a
для последующего экземпляра Int
. Вышеупомянутая аннотация id
не дает никакой информации для GHC: она уже знает, что id
имеет этот тип.
Мы могли бы удалить его, не затрагивая проверку типов вообще. То есть выражения id
и id :: a->a
не только динамически эквивалентны, но и статически таковы.
Аналогично, выражения
putStrLn . show
и
(putStrLn . show) :: Show x => x -> IO ()
являются статически эквивалентными: мы просто комментируем код с типом GHC. Другими словами, мы не предоставляем какую-либо информацию GHC, которую она еще не знает.
После проверки типа аннотации GHC может затем создать экземпляр x
далее. Ограничение мономорфизма делает это в вашем примере. Чтобы этого избежать, используйте аннотацию для привязки, которую вы вводите, а не выражение:
myprint :: Show x => x -> IO ()
myprint = (putStrLn . show)