当我想读取字符串以输入A时,我写read str::A
。考虑一下,我希望有一个泛型函数可以读取不同类型的字符串,所以我想写一些像read str::A|||B|||C
或类似的东西。我唯一能想到的是:
{-# LANGUAGE TypeOperators #-}
infixr 9 |||
data a ||| b = A a|B b deriving Show
-- OR THIS:
-- data a ||| b = N | A a (a ||| b) | B b (a ||| b) deriving (Data, Show)
instance (Read a, Read b) => Read (a ||| b) where
readPrec = parens $ do
a <- (A <$> readPrec) <|> (B <$> readPrec)
-- OR:
-- a <- (flip A N <$> readPrec) <|> (flip B N <$> readPrec)
return a
如果我想读点什么:
> read "'a'"::Int|||Char|||String
B (A 'a')
但这种奇怪的类型怎么办?我想将它折叠到Int
或Char
或String
......或者折叠到另一个但是“原子”(标量/简单)。最终目标是将类似"1,'a'"
的字符串读取为类似列表的[D 1, D 'a']
。这里的主要约束是结构是灵活的,因此字符串可以是"1, 'a'"
或"'a', 1"
或"\"xxx\", 1, 2, 'a'"
。我知道如何读取用分隔符分隔的内容,但是这个内容应该作为类型传递,而不是像C Char|I Int|S String|etc
这样的类型的总和。可能吗?或没有办法在没有类型总和的情况下完成它?
答案 0 :(得分:2)
使用Text.Read.readMaybe
无法使用一般,因为相同的输入字符串可能会正确解析为多个有效类型。但是,您可以使用Nothing
之类的函数执行此操作,该函数会在模糊输入上返回import Data.Maybe (catMaybes, fromJust, isJust, isNothing)
import qualified Text.Read
data AnyOf3 a b c = FirstOf3 a | SecondOf3 b | ThirdOf3 c
instance (Show a, Show b, Show c) => Show (AnyOf3 a b c) where
show (FirstOf3 x) = show x -- Can infer the type from the pattern guard.
show (SecondOf3 x) = show x
show (ThirdOf3 x) = show x
main :: IO ()
main =
(putStrLn . unwords . map show . catMaybes . map readDBS)
["True", "2", "\"foo\"", "bar"] >>
(putStrLn . unwords . map show . readIID) "100"
readMaybe' :: (Read a, Read b, Read c) => String -> Maybe (AnyOf3 a b c)
-- Based on the function from Text.Read
readMaybe' x | isJust a && isNothing b && isNothing c =
(Just . FirstOf3 . fromJust) a -- Can infer the type of a from this.
| isNothing a && isJust b && isNothing c =
(Just . SecondOf3 . fromJust) b -- Can infer the type of b from this.
| isNothing a && isNothing b && isJust c =
(Just . ThirdOf3 . fromJust) c -- Can infer the type of c from this.
| otherwise = Nothing
where a = Text.Read.readMaybe x
b = Text.Read.readMaybe x
c = Text.Read.readMaybe x
readDBS :: String -> Maybe (AnyOf3 Double Bool String)
readDBS = readMaybe'
readToList :: (Read a, Read b, Read c) => String -> [AnyOf3 a b c]
readToList x = repack FirstOf3 x ++ repack SecondOf3 x ++ repack ThirdOf3 x
where repack constructor y | isJust z = [(constructor . fromJust) z]
| otherwise = []
where z = Text.Read.readMaybe y
readIID :: String -> [AnyOf3 Int Integer Double]
readIID = readToList
。您还可以返回有效解释的元组或列表,或者具有尝试解析类型的顺序的规则,例如:尝试按照声明的顺序解析每个类型。
以下是一些示例代码,作为概念证明:
bar
第一个输出行回显每个成功解析的输入,即布尔常量,数字和引用的字符串,但不是Int
。第二个输出行回应输入的每个可能解释,即100 Integer
,Double
和/Library/LaunchDeamon
。
对于更复杂的东西,你想写一个解析器。 Haskell有一些非常好的库可以用组合器构建它们。你可能会看一下像Parsec这样的人。但是,了解幕后发生的事情仍然有帮助。