将其更改为符号

时间:2014-08-12 17:27:47

标签: haskell

我正在与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"

3 个答案:

答案 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)