Я хотел бы прочитать некоторые данные, которые сами определяют тип данных для использования.
Например, давайте предположим, что могут быть пользовательские данные, подобные этим:
integer pair 1 2
integer triple 1 2 3
real pair 1 2
real triple 1 2 3
и существует тип данных для его представления:
data (MValue a) => T a = TP (Pair a) | TT (Triple a)
deriving (Show, Eq)
data Pair a = Pair a a deriving (Show, Eq)
data Triple a = Triple a a a deriving (Show, Eq)
, где разрешенные типы значений должны принадлежать классу MValue
:
class (Num a, Read a) => MValue a where
typename :: a -> String
readval :: [String] -> Maybe a
instance MValue Int where
typename _ = "integer"
readval [s] = maybeRead s
readval _ = Nothing
instance MValue Double where
typename _ = "real"
readval [s] = maybeRead s
readval _ = Nothing
maybeRead s =
case reads s of
[(x,_)] -> Just x
_ -> Nothing
Я могу легко написать читателей для Pair
с и Triple
с:
readPair (w1:w2:[]) = Pair <$> maybeRead w1 <*> maybeRead w2
readTriple (w1:w2:w3:[]) = Triple <$> maybeRead w1 <*> maybeRead w2 <*> maybeRead w3
Проблема в как мне написать полиморфный ридер для всего T a
типа ?:
readT :: (MValue a, Read a) => String -> Maybe (T a)
Я хочу:
- Тип
a
выбирается вызывающим абонентом.
readT
должен выдавать Nothing
, если ввод пользователя несовместим с a
.
readT
должно выдавать Just (T a)
, если ввод действителен.
- Числа должны читаться как целые или как двойные в зависимости от ввода.
Наивная реализация
readT :: (MValue a, Read a) => String -> Maybe (T a)
readT s =
case words s of
(tp:frm:rest) ->
if tp /= typename (undefined :: a)
then Nothing
else case frm of
"pair" -> TP <$> readPair rest
"triple" -> TT <$> readTriple rest
_ -> Nothing
_ -> Nothing
выдает ошибку в строке if tp /= typename (undefined :: a)
:
rd.hs:45:17:
Ambiguous type variable `a' in the constraint:
`MValue a' arising from a use of `typename' at rd.hs:45:17-41
Probable fix: add a type signature that fixes these type variable(s)
Failed, modules loaded: none.
Ошибка исчезнет, если я уберу эту проверку, но как я могу проверить, совместим ли пользовательский ввод с типом данных, выбранным вызывающим абонентом? Решением может быть использование отдельных readTInt
и readTDouble
, но я бы хотел, чтобы тот же readT
работал полиморфно так же, как read
.