Показать экземпляр для неопределенного

Можно ли что-нибудь сделать, чтобы определить экземпляр Show для неопределенного значения? Может быть, существуют какие-то расширения GHC? Я хочу что-то вроде этого:

> print (1,undefined)

(1,"undefined")

person user1374768    schedule 24.05.2012    source источник
comment
Вы можете создавать экземпляры только для типов. undefined — это value, и у него нет определенного типа, это просто Bottom, который может иметь любой тип.   -  person leftaroundabout    schedule 24.05.2012
comment
Я согласен с @leftaroundabout, я не вижу возможностей для этого.   -  person Riccardo T.    schedule 24.05.2012
comment
Каков ваш вариант использования? Если вы хотите перехватить исключение, это можно сделать, но не следует делать в экземпляре Show.   -  person Don Stewart    schedule 24.05.2012
comment
Что бы вы ни хотели, вам лучше Maybe.   -  person leftaroundabout    schedule 24.05.2012


Ответы (4)


Согласно отчету Haskell 2010, глава 9, вычисление undefined всегда должно вызывать ошибку :

-- It is expected that compilers will recognize this and insert error
-- messages that are more appropriate to the context in which undefined
-- appears.
undefined :: a
undefined = error "Prelude.undefined"

Поскольку печать значения включает его оценку, это всегда будет приводить к ошибке.

person evilcandybag    schedule 24.05.2012
comment
однако передача чего-либо в print не обязательно должна его оценивать, в зависимости от экземпляра Show - person newacct; 24.05.2012

Нижнее значение (одним из вариантов которого является undefined) — это значение, которое никогда не создается и, следовательно, не может наблюдаться. Это означает, что вы также не можете его распечатать. Это значение нельзя сравнивать с null из других языков, которые обычно можно наблюдать и даже сравнивать.

Полезно думать о undefined, а также error "blah" и всех других основаниях как эквивалентных результатам бесконечных циклов. Результат бесконечного цикла никогда не создается и, следовательно, не может наблюдаться.

person ertes    schedule 24.05.2012
comment
Теоретически это так, но практически я сейчас печатаю, смотрите мой ответ :) - person Riccardo T.; 24.05.2012

Более концептуально: «неопределенное» не является значением, подобным «X». Значение 'X' имеет тип Char. Какой тип имеет «неопределенное»? Символ "undefined" полиморфен, может иметь любой тип (любой тип вида *).

Типовые классы, такие как «Show t», отправляются на тип t. Таким образом, разные типы могут иметь и имеют разные функции отображения, которые их отображают. Какая функция получает ваше «неопределенное», зависит от типа.

В GHCI для большинства полиморфных типов по умолчанию используется (), поэтому он может выполнять команду. Можно сделать функцию show для нового типа, которая не смотрит на значение:

Prelude> data Test = Test
Prelude> instance Show Test where show x = "I did not look at x"
Prelude> show Test
"I did not look at x"
Prelude> show (undefined :: Test)
"I did not look at x"

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

Вы можете создать свой собственный класс типов и механизм печати, который работает в IO, перехватывает ошибки и делает то, что вы хотите:

import Control.Exception
perr s = do x <- try (evaluate (show s)) :: IO (Either SomeException  String)
            return (either show id x))

Приведенное выше переводит ошибки в строковую форму ошибки:

Prelude Control.Exception> perr True
"True"
Prelude Control.Exception> perr (undefined :: Bool)
"Prelude.undefined"

Примечание. Лучший «perr» должен принудительно использовать всю строку, а не только WHNF.

person Chris Kuklewicz    schedule 24.05.2012

Несмотря на то, что (как уже указывали другие) вы не можете указать экземпляр Show для undefined, вы можете найти обходной путь, используя catch, как в следующем коде:

import qualified Control.Exception as C
import System.IO.Unsafe (unsafePerformIO)

showCatch :: (Show a) => a -> IO String
showCatch = showCatch' "undefined"

showCatch' :: (Show a) => String -> a -> IO String
showCatch' undef x = C.catch (C.evaluate (show x)) showUndefined
  where
    showUndefined :: C.ErrorCall -> IO String
    showUndefined _ = return undef

unsafeShowCatch :: (Show a) => a -> String
unsafeShowCatch x = unsafePerformIO (showCatch x)

Но этот пример будет работать только для простых выражений:

*Main> let v1 = showCatch 1
v1 :: IO String
*Main> let v2 = showCatch $ if True then undefined else 0
v2 :: IO String
*Main> v1
"1"
*Main> v2
"undefined"
*Main> let v3 = unsafeShowCatch 1
v3 :: String
*Main> let v4 = unsafeShowCatch $ undefined
v4 :: String
*Main> v3
"1"
*Main> v4
"undefined"

Это не будет работать для таких вызовов, как

showCatch (1,undefined)
person Riccardo T.    schedule 24.05.2012
comment
Я понятия не имею, почему, но опция +t GHCI говорит, что showCatch 1 имеет тип String, но на самом деле это IO String. Я исправил это в ответе. - person Riccardo T.; 24.05.2012