我正在与Hspec和Parsec进行一个项目,偶然发现了以下代码。
stringLiteralSpec :: Spec
stringLiteralSpec =
describe "SimpleExpression:StringLiteral" $
it "Is able to parse correct string literals" $
stringLiteral `shouldParse` [
"\"Hello World\"" `to` StringLiteral "Hello World",
"\'Hello World\'" `to` StringLiteral "Hello World"]
shouldParse :: (Show a, Eq a) => Parser a -> [(String, a)] -> Expectation
to = (,)
是否有可能以某种方式提出to
的另一个定义,以便列表符号可以用这样漂亮的方式编写?
stringLiteral `shouldParse` $ do
"\"Hello World\"" `to` StringLiteral "Hello World"
"\'Hello World\'" `to` StringLiteral "Hello World"
答案 0 :(得分:4)
如果我们使用Writer
Monad
,我们可以一起收集单件列表。在Writer
解释Monoid
时,mappend
会跟踪[a]
可以Monoid
件事物Monad
Applicative
to :: a -> b -> Writer [(a, b)] ()
x `to` y = tell [(x, y)]
/ stringLiteral `shouldParse` execWriter (do
"\"Hello World\"" `to` StringLiteral "Hello World"
"\'Hello World\'" `to` StringLiteral "Hello World")
行动。它看起来像这样
to
现在我们可以写:
Monad
以下是此λ> execWriter $ do { 1 `to` 2; 10 `to` 100 }
[(1,2),(10,100)]
实现如何工作的示例,w.r.t。其Writer
实例
Monad
请注意,您必须删除do
a
b
"包装"从我们实际想要达到的价值来看。
另外,我们实际上并没有充分利用a >> b
,因为我们从未将任何内容绑定到名称,我们只是忽略了结果。注意
a >>= (\_ -> b)
与
相同(>>)
需要产生与
相同的值a *> b
这是Applicative
的默认实现。
这也是与
相同的值do
来自相应的Writer
实例。因此,这仅用于利用Writer
符号的符号方便性,但由于额外的[]
包装,我们失去了一些。在内部,Monad
只是一对,所以我们仍然需要从该对的第一个元素中提取列表。没有办法解决这个问题。
newtype
[]
对此无效,因为它不会以这种方式附加操作的结果。由于(>>=) :: [a] -> (a -> [b]) -> [b]
方法(或更重要的是(>>) :: [a] -> [b] -> [b]
方法,因此无法在a
周围实施精简的b
包装器。方法)不能以这种方式表现,主要是因为它不知道[]
和{{1}}是否是同一类型,所以它不能只追加这两个列表(为了便于阅读,我将这里的类型专门用于{{1}}实例。
我会坚持使用您的原始列表符号,因为它更简洁,更容易立即理解。
答案 1 :(得分:0)
在该片段中,String与要通过函数to
直接解析 的基准配对。另一种选择是通过monadic绑定将字符串的列表与每个元素应解析的数据配对:
-- Implement this function which, for instance, maps "\"Hello World\""
-- to StringLiteral "Hello World"
stringToDatum :: String -> a
-- With stringToDatum in hand, we can coerce it into a bind by pairing its
-- output with its input, and using the list return.
["\"Hello World\"", "\'Hello World\'"] >>= (\str -> return (str, stringToDatum str))
然后您可以执行类似
的操作stringLiteral `shouldParse` $ do
str <- listOfStrings
return (str, stringToDatum str)
获得类似
的内容stringLiteral `shouldParse` $ do
"\"Hello World\"" `to` StringLiteral "Hello World"
"\'Hello World\'" `to` StringLiteral "Hello World"
to
的输出必须是一个列表,并且在标准列表monad下,对于第一个给出的列表的每个值,do块的第二行上的表达式的值将被复制。 do块的行,绝对不是我们想要的。
答案 2 :(得分:0)
stringLiteral `shouldParse` [
"\"Hello World\"" `to` StringLiteral "Hello World"
, "'Hello World'" `to` StringLiteral "Hello World" ]
这是非常丑陋吗?我能想到用do-notation编写它的唯一方法是将列表绑定到一个值并返回它,你仍然需要显式括号。
另一方面,如果你有一个可以将每个引用的单词映射到其对应单词的函数,你可以做以下几点:
-- f is your quote stripping function
map (\str -> (str, f str)) ["\"Hello World\"", "'Hello World'"]
-- or
[(str, f str) | str <- ["\"Hello World\"", "'Hello World'"]]
-- which can be rewritten as
do
str <- ["\"Hello World\"", "'Hello World'"]
return (str, f str)