Да, я знаю, это звучит неразумно. Думаю, есть лучшие подходы, чем просто скрытие экземпляра.
Я работаю над библиотекой изображений. Внутри одного модуля есть тип enum для размеров изображений:
module Sizes (Sizes(..)) where
data Size = Original
| Regular
| Small
| Thumb
| Mini
Теперь мне нужно проанализировать размеры из строк, особенно без учета регистра (например, "original", "Original", "ORIGINAL" все должны быть проанализированы на Original). Я планировал следующее:
Read для SizereadSize для анализа строки, используя read и readMaybeRead от пользователей, чтобы они по ошибке не использовали read или что-то еще, например, при разборе "ORIGINAL"Достаточно грязный. Во всяком случае, вот первые два шага, и я понятия не имею, как выполнить шаг 3.
data Size = ...
deriving (Read)
readSize :: String -> Maybe Size
readSize s
| (toLower <$> s) `elem` ["original", "regular", "small", "thumb", "mini"] = (read . onlyCapitaliseFirst) s
| otherwise = readMaybe s
where onlyCapitaliseFirst "" = ""
onlyCapitaliseFirst (c:xs) = toUpper c : (toLower <$> xs)
Почему бы мне просто не написать экземпляр самому, спросите вы?
Ну потому что слишком некрасиво и утомительно такое реализовывать readsPrec,
что было бы намного лучше, если бы компилятор просто использовал какое-то волшебство, чтобы завершить его за меня.
instance Read Size where
readsPrec _ = \case ('o' : 'r' : 'i' : 'g' : 'i' : 'n' : 'a' : 'l' : xs) -> [(Original, XS)]
... -- it's not even correct, for only considering all lowercase case
Обычно вы пишете автономную функцию синтаксического анализатора вместо использования Read для таких случаев, в немалой степени из-за подобных проблем (во-первых, Read не так часто используется в IRL, потому что вы обычно выбираете какой-то другой формат). чем условия haskell для сериализации в любом случае).
Обратите внимание, что сгенерированный компилятором код для экземпляров Read и Show взаимно совместим. Это означает, что они являются своего рода инверсиями: x == read (show x) (то же самое относится к s == show (read s) с точностью до пробелов). Следовательно, хорошей/общепринятой практикой является следование этому правилу «инверсии». Следовательно, лучше не создавать экземпляр класса типов Read самостоятельно. Итак, определите свою собственную функцию синтаксического анализа и не слишком беспокойтесь о реализации Read typeclass, а если вы ее реализуете, лучше используйте реализацию компилятора.





В вашем случае я предпочитаю делать так:
Получение экземпляров Enum и Show (не для чтения)
data Size = Original | ...
deriving (Enum, Show)
Сделать функцию разбора
readSize :: String -> Maybe Size
readSize str = lookup (toLower <$> str) size_map
where
size_map = [(toLower <$> show size, size) | size <- [Original ..]]
Нет, вы не можете скрыть экземпляр. НО библиотеки синтаксических анализаторов Haskell являются лучшими в своем классе, поэтому хорошая новость заключается в том, что действительно легко написать то, что вы хотите, не создавая экземпляр!