Показать для типов ввода-вывода

У меня есть тип данных, который содержит IORef как важный элемент. Это означает, что нет простого способа сделать его членом класса типов show. Это не так уж плохо, так как у меня есть функция print в монаде IO для этого типа. Но в GHCi это раздражает тем, что каждый раз, когда я возвращаю одну из этих вещей, в результате я получаю сообщение об ошибке, в котором говорится, что это невозможно показать.

Есть ли способ заставить GHCi, который в любом случае работает в монаде IO, использовать действие IO для отображения результата? Если нет, будут ли негативные последствия написания show a = unsafePerformIO $ print a?


person John F. Miller    schedule 30.11.2011    source источник
comment
Насколько мне известно, нет способа указать ghci использовать другую функцию, кроме show, для отображения результатов. Однако вы можете определить экземпляр show для вашего типа данных, который просто отображает ‹ioref› или что-то подобное для ioref. Это, вероятно, немного чище, хотя и менее удобно, чем использование unsafePerformIO.   -  person sclv    schedule 01.12.2011


Ответы (2)


Рассматривали ли вы добавление в ваш файл .ghci что-то вроде:

instance (Show a) => Show (IORef a) where
    show a = show (unsafePerformIO (readIORef a))

Это совсем не безопасно, но если это только для вашего личного использования, возможно, это нормально.

Для более общего использования ранее данные ответы выглядят хорошо для меня. То есть либо определить статическое сообщение «Я не могу это показать»:

instance Show (IORef a) where
    show _ = "<ioref>"

Это даст что-то вроде:

> runFunc
MyStruct <ioref> 4 "string val"

Или используйте пользовательскую функцию. Я предлагаю создать класс и поднять все экземпляры Show:

class ShowIO a where
    showIO :: a -> IO String

instance Show a => ShowIO a where
    showIO = return . show
instance ShowIO a => ShowIO (IORef a) where
    showIO a = readIORef a >>= showIO

Вывод (непроверенный, написанный от руки):

> myFunc >>= showIO
MyStruct "My String in an IORef" 4 "string val"
person Thomas M. DuBuisson    schedule 30.11.2011
comment
Обратите внимание, что для этих ShowIO экземпляров требуется расширение OverlappingInstances. Это довольно хитроумно, хотя, возможно, в меньшей степени, чем unsafePerformIO. Конечно полезно для развития. - person John L; 01.12.2011

ghci имеет три случая для возвращаемых значений:

  1. Show a => a: Просто запустите шоу и распечатайте его
  2. Show a => IO a: выполнить действие, запустить шоу и распечатать
  3. IO (): ничего не печатать

Поэтому обычно, если вы вводите действие ввода-вывода, оно выполняется, и результат печатается, если он не (). Давай попробуем:

ghci>15
15
ghci>'a' : 'b' : 'c' : []
"abc"
ghci>putStrLn "Hello, world!"
Hello, world!
ghci>putStrLn "Hello, world!" >> return 42
Hello, world!
42
ghci>

Если вы хотите напечатать что-то другое, лучший способ, вероятно, написать пользовательскую функцию и вставить ее перед каждой строкой, которую вы хотите увидеть:

myShowFun :: ... -> IO String

ghci> myShowFun $ ...
foobar
person fuz    schedule 30.11.2011