读取和表示指定要使用的数据类型的输入 [英] Reading and representing input which specifies the data type to use
问题描述
例如,我们假设可能有像这样的用户输入:
p> 整数对1 2
整数三元组1 2 3
实数对1 2
实数三元组1 2 3
并且有一个数据类型来表示它:
data(MValue a)=> T a = TP(Pair a)| TT(Triple a)
导出(Show,Eq)
数据Pair a = Pair aa导出(Show,Eq)
数据Triple a = Triple aaa导出(Show,Eq )
允许的值类型必须属于 MValue
class:
class(Num a,Read a)=> MValue a where
typename :: a - >字符串
readval :: [String] - >也许是
实例MValue Int其中
typename _ =integer
readval [s] = maybeRead s
readval _ = Nothing
实例MValue Double其中
typename _ =real
readval [s] = maybeRead s
readval _ = Nothing
maybeRead s =
case读取
[(x,_)] - >只需x
_ - > Nothing
我可以轻松地为 Pair
s和 Triple
s:
readPair(w1:w2:[] )=对< $> maybeRead w1 * * maybeRead w2
readTriple(w1:w2:w3:[])= Triple< $> maybeRead w1 * *可能读取w2 * maybeRead w3
问题是如何为整个<$ c编写一个多态读取器
$ b $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $'阅读a)=>字符串 - >也许(T a)
我想要:
- 类型
a
由调用者选择。
-
readT
如果用户的输入与a
不兼容, $ b应产生 -
readT
应该产生Just(T a)
。 b $ b - 数字应根据输入读取为整数或双精度。
Nothing
如果输入有效,$ b 幼稚的执行方式
readT ::(MValue a,Read a)=>字符串 - >可能(T a)
readT s =
(tp:frm:rest) - >
if tp / = typename(undefined :: a)
then Nothing
else case frm
pair - > TP< $> readPair rest
triple - > TT< $> readTriple休息
_ - > Nothing
_ - > Nothing
会在行中发生错误if tp / = typename(undefined: :a)
:
rd.hs:45:17:
歧义类型变量约束中的'a':
`在'rd.hs:45:17-41
'中使用'typename'引起的'MValue a'可能的修复:添加一个修复这些类型变量的类型签名s)
失败,模块加载:无。
如果删除此检查,错误消失,但我如何验证用户输入是否兼容与调用者选择的数据类型?一个解决方案可能是单独的 readTInt
和 readTDouble
,但我想要相同的 readT
以多种方式工作,方式与 read
相同。
问题是 undefined :: a
中的 a
不一样 a
作为 readT
的签名。 GHC中有一种语言扩展可以实现,称为ScopedTypeVariables。一个更便携的解决方法是引入一些额外的代码来明确地将类型绑定在一起,例如:
readT ::( MValue a,阅读a)=>字符串 - >可能(T a)
readT s =结果
其中
结果=
(tp:frm:rest) - >
if tp / = typename((const :: a - > Maybe(T a) - > a)undefined result)
then Nothing
else $ fr
对 - > TP< $> readPair rest
triple - > TT< $> readTriple休息
_ - > Nothing
_ - >没有什么
这是对代码的一个非常快速和肮脏的修改,而我的更改可能是做得更优雅,但应该有效。
I'd like to read some data which itself specifies the data type to use.
For example, let's assume there may be user inputs like these:
integer pair 1 2
integer triple 1 2 3
real pair 1 2
real triple 1 2 3
and there is a data type to represent it:
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)
where the allowed value types have to belong to MValue
class:
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
I can easily write readers for Pair
s and Triple
s:
readPair (w1:w2:[]) = Pair <$> maybeRead w1 <*> maybeRead w2
readTriple (w1:w2:w3:[]) = Triple <$> maybeRead w1 <*> maybeRead w2 <*> maybeRead w3
The problem is how do I write a polymorphic reader for the entire T a
type?:
readT :: (MValue a, Read a) => String -> Maybe (T a)
I want:
- The type
a
is chosen by the caller. readT
should produceNothing
if the user's input is incompatible witha
.readT
should produceJust (T a)
if the input is valid.- Numbers should be read as integers or as doubles depending on the input.
A naive implementation
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
gives an error in the line 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.
The error goes away if I remove this check, but how can I verify if the user input is compatible with the data type chosen by the caller? A solution might be to have separate readTInt
and readTDouble
, but I'd like the same readT
to work polymorphically the same way as read
does.
The problem is that the a
in undefined :: a
is not the same a
as the ones in readT
's signature. There is a language extension available in GHC that enables that, called "ScopedTypeVariables". A more portable fix would be to introduce a little extra code to explicitly tie the types together, for example:
readT :: (MValue a, Read a) => String -> Maybe (T a)
readT s = result
where
result =
case words s of
(tp:frm:rest) ->
if tp /= typename ((const :: a -> Maybe (T a) -> a) undefined result)
then Nothing
else case frm of
"pair" -> TP <$> readPair rest
"triple" -> TT <$> readTriple rest
_ -> Nothing
_ -> Nothing
This is a very quick and dirty modification of your code, and I'm the changes could be made more elegantly, but that should work.
这篇关于读取和表示指定要使用的数据类型的输入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!