Ошибка при компиляции печати Любое значение

Я пытаюсь скомпилировать простой фрагмент кода.

main = (putStrLn . show) (Right 3.423)

Компиляция приводит к следующей ошибке:

No instance for (Show a0) arising from a use of `show'
The type variable `a0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
  instance Show Double -- Defined in `GHC.Float'
  instance Show Float -- Defined in `GHC.Float'
  instance (Integral a, Show a) => Show (GHC.Real.Ratio a)
    -- Defined in `GHC.Real'
  ...plus 42 others
In the second argument of `(.)', namely `show'
In the expression: putStrLn . show
In the expression: (putStrLn . show) (Right 3.423)

Когда я выполняю тот же фрагмент из ghci, все работает, как и ожидалось.

Prelude> let main = (putStrLn . show) (Right 3.423)
Prelude> main
Right 3.423

Так что вопрос в том, что происходит?


person Alexander    schedule 12.06.2014    source источник


Ответы (1)


Проблема в том, что GHC не может определить полный тип Right 3.423, он может только определить, что он имеет тип Either a Double, а экземпляр Show для Either выглядит как instance (Show a, Show b) => Show (Either a b). Без этого дополнительного ограничения на Either a Double GHC не знает, как его напечатать.

Причина, по которой он работает в интерактивном режиме, заключается в ужасном мономорфизме restriction, что делает GHCi более агрессивным при выборе значений по умолчанию. Это можно отключить с помощью :set -XNoMonomorphismRestriction, и это станет значением по умолчанию в будущих версиях GHC, поскольку оно вызывает много проблем у новичков.

Решение этой проблемы состоит в том, чтобы поставить сигнатуру типа на Right 3.423 в исходном коде, например

main = (putStrLn . show) (Right 3.423 :: Either () Double)

Здесь я просто использовал () вместо a, так как нас это все равно не волнует, и это самый "простой" тип, который можно показать. Вы можете поместить туда String или Int или Double или что угодно, если оно реализует Show.

Совет: putStrLn . show точно соответствует print, так что вы можете просто сделать

main = print (Right 3.423 :: Either () Double)


Как указывает @ØrjanJohansen, это не ограничение мономорфизма, а скорее расширение ExtendedDefaultRules, которое использует GHCi, которое, по сути, делает именно то, что я сделал выше, и вставляет () в переменные типа, чтобы все работало в интерактивном сеансе.

person bheklilr    schedule 12.06.2014
comment
На самом деле Data.Void.Void — самый простой тип в Show, хотя на самом деле он не может быть успешно показан. - person dfeuer; 12.06.2014
comment
@dfeuer Технически верно, но () - это довольно простой выбор. Я полагаю, что это более простое объяснение, что () часто можно использовать для заполнения переменных типа, которые на самом деле не имеют значения, но должны быть конкретными, чтобы удовлетворить экземпляр класса типов, такой как Show. Это, очевидно, не работает для всех ситуаций, но для многих из них работает довольно хорошо. - person bheklilr; 12.06.2014
comment
Это не ограничение мономорфизма, которое всегда включено по умолчанию для файловых модулей. Это из-за ExtendedDefaultRules GHCi, которые позволяют ему выбирать значение по умолчанию (фактически тот же самый (), который вы выбрали), даже когда требуется только экземпляр Show (обычно ограничения включают в себя необходимость хотя бы одного числового класса.) - person Ørjan Johansen; 12.06.2014
comment
@ØrjanJohansen Моя ошибка, я обновлю свой ответ. Я знаю, что видел ExtendedDefaultRules раньше, но моей первой мыслью, когда я вижу подобную проблему, является ограничение мономорфизма, и я, вероятно, просто поторопился с ответом на вопрос, вместо того, чтобы подумать об этом еще несколько минут. - person bheklilr; 12.06.2014
comment
Я не думаю, что вам нужно вычеркнуть больше, чем второй абзац, остальное полезно и применимо и к этому случаю. - person Ørjan Johansen; 12.06.2014