【发布时间】:2012-11-14 00:54:00
【问题描述】:
我很难理解为什么这两个 sn-ps 在所谓的“穷人严格分析”下会产生不同的结果。
第一个例子使用data(假设一个正确的Applicative实例):
data Parser t a = Parser {
getParser :: [t] -> Maybe ([t], a)
}
> getParser (pure (,) <*> literal ';' <*> undefined ) "abc"
*** Exception: Prelude.undefined
第二个使用newtype。没有其他区别:
newtype Parser t a = Parser {
getParser :: [t] -> Maybe ([t], a)
}
> getParser (pure (,) <*> literal ';' <*> undefined ) "abc"
Nothing
literal x 是一个解析器,如果其参数与第一个标记匹配,则它会成功使用一个输入标记。所以在这个例子中,它失败了,因为; 不匹配a。但是,data 示例仍然看到下一个解析器未定义,而 newtype 示例没有。
我已阅读 this、this 和 this,但对它们的理解不够深入,无法理解为什么第一个示例未定义。在我看来,在这个例子中,newtype 比data 更懒惰,这与答案所说的相反。 (至少one other person 也被这个弄糊涂了)。
为什么从data 切换到newtype 会改变这个例子的定义?
这是我发现的另一件事:使用这个 Applicative 实例,上面的 data 解析器输出未定义:
instance Applicative (Parser s) where
Parser f <*> Parser x = Parser h
where
h xs =
f xs >>= \(ys, f') ->
x ys >>= \(zs, x') ->
Just (zs, f' x')
pure a = Parser (\xs -> Just (xs, a))
而对于这个实例,上面的 data 解析器确实 not 输出未定义(假设 Parser s 的 Monad 实例正确):
instance Applicative (Parser s) where
f <*> x =
f >>= \f' ->
x >>= \x' ->
pure (f' x')
pure = pure a = Parser (\xs -> Just (xs, a))
完整代码sn-p:
import Control.Applicative
import Control.Monad (liftM)
data Parser t a = Parser {
getParser :: [t] -> Maybe ([t], a)
}
instance Functor (Parser s) where
fmap = liftM
instance Applicative (Parser s) where
Parser f <*> Parser x = Parser h
where
h xs = f xs >>= \(ys, f') ->
x ys >>= \(zs, x') ->
Just (zs, f' x')
pure = return
instance Monad (Parser s) where
Parser m >>= f = Parser h
where
h xs =
m xs >>= \(ys,y) ->
getParser (f y) ys
return a = Parser (\xs -> Just (xs, a))
literal :: Eq t => t -> Parser t t
literal x = Parser f
where
f (y:ys)
| x == y = Just (ys, x)
| otherwise = Nothing
f [] = Nothing
【问题讨论】:
-
当问这样一个问题时,如果你包含所有相关代码,如果它足够小以适应(这包括
Functor和Monad实例,以及literal),通常会更好,这样人们就不必猜测您是如何编写函数的(正如您所指出的,即使是很小的更改也会对行为产生影响)。 -
@shachaf 真正的问题不是“如何修复我的代码?” -- 我已经这样做了 -- 但是“
data和newtype在严格性/懒惰方面有什么不同?”抱歉,如果问题不清楚。 -
是的,但即便如此,我们如何在不知道代码长什么样的情况下解释您的代码发生了什么?
-
可能 Applicative 实例的行为来自(在第一个示例中)模式匹配是严格的事实,即
Parser f和Parser x都是强制的。 (例如f (Just x) (Just y) = x; f (Just 1) undefined抛出异常。) -
@shachaf 希望它会有所帮助;我仍然希望在更广泛的背景下理解这个问题,而不仅仅是它如何应用于这个特定问题。
标签: haskell lazy-evaluation algebraic-data-types newtype