2010-09-29 1 views
2

Как использовать любой тип пользовательских данных, который выводится из Show с Debug.Observe.observe?с помощью Hood with Show instance

data MyData = Foo | Bar deriving (Show) 

myFunc = consumMyData . observe "Debug: " . produceMyData 
consumMyData :: MyData -> ... 
produceMyData :: ... -> MyData 

main = runO $ myFunc 

Так что я могу избежать написания:

instance Observable MyData where 
    observer my = send (show my) (return my) 

все время, когда все, что мне нужно, это имя данных.

Наблюдаемый экземпляр определен только для всех стандартных типов данных, но Show.

Или, если я что-то вроде:

unshow :: Unshow a => String -> a 

Я мог бы сделать:

myFunc consumMyData . unshow . oberserve "Debug: " . show . produceMyData 

+0

Как насчет чего-то вроде «watchShow :: Show a => String -> a -> a», который заставляет «(показать мой)», но возвращает только «мой» и только когда «мой» на самом деле используется –

+0

Я неправильно понял ранее, предыдущий комментарий удален. Как насчет этой функции 'observShow a = send (показать a) (return a)'? Непроверенный, но он должен работать. –

+0

Конечно, он будет работать, только если вы можете вызвать функцию напрямую, если вам нужен экземпляр Observable. –

ответ

2

Прежде всего, я бы настоятельно рекомендуем вам просто предоставить все Необходимые наблюдаемые экземпляры. Вы можете использовать DrIFT, который поддерживает Observable напрямую, или Derive, чтобы автоматически выводить экземпляры, поэтому вам не нужно писать их самостоятельно.

Альтернативы менее острые.

unshow :: Unshow a => String -> a 

Это обеспечивается классом Read. Единственная операция - read, которая имеет именно тот тип, который вы даете. Функции read и show должны быть обратными, так что read . show эквивалентно id. Большинство стандартных типов данных предоставляют Read, хотя сторонние библиотеки могут не работать.

Это будет намного менее эффективно, чем экземпляр Observable, потому что вы будете десериализовать все свои данные каждый раз, когда вы его наблюдаете. Если у вас есть данные любого значительного размера, это будет большим хитом.

Другой альтернативой является создание экземпляра Observable для каждого типа.

instance Show a => Observable a where 
    observer my = send (show my) (return my) 

Опять же, это не значит «Когда„а“является членом шоу, используйте этот Observable экземпляр.» Это означает «Использовать этот экземпляр Observable для всего, и это ошибка, если нет экземпляра Show в области видимости». Это требует много отрывочных расширений и приводит к проблемам с перекрывающимися экземплярами.

Если вы позаботились о том, чтобы использовать капюшон, я не понимаю, почему вы хотели бы иметь этот экземпляр.

Edit:

Оказывается, что преобразование типа имеет более высокий приоритет, чем функции применения: putStrLn . show $ read "Foo"::MyData -- error putStrLn . show $ (read "Foo"::MyData)

Это довольно распространенная проблема.Рассмотрим функцию:

f1 :: String -> String 
f1 = show . read 

GHC этого не допустит. read :: Read a => String -> a и show :: Show a => a -> String, , но когда вы объединяете два, GHC не может понять, какой тип для создания переменной типа a. Другими словами, у вас есть строка, вы десериализируете ее, а затем повторно сериализуете ее, но какой тип вы десериализируете? Вы должны сообщить GHC, используя явную подпись типа для чтения или показа, что и делает ваш второй пример. Это не преобразование типа, это то, что вы написали код, который не может найти без помощи.

+0

Похоже, что преобразование типов имеет более высокий приоритет, чем функция: putStrLn. show $ read "Foo" :: MyData - ошибка putStrLn. show $ (read «Foo» :: MyData) –

+0

Иногда проще использовать «читать. наблюдать. показывать», чем классы экземпляров, особенно если производительность не является проблемой. –

+0

Но не будет ли «экземпляр Show a => Observable a» ограничивать использование наблюдений для экземпляров Show в пределах модуля? –