数据和newtype之间的懒惰/严格

Mat*_*ick 16 haskell lazy-evaluation algebraic-data-types newtype

我很难理解为什么这两个片段在所谓的"穷人严格分析"下会产生不同的结果.

第一个示例使用data(假设一个正确的Applicative实例):

data Parser t a = Parser {
        getParser ::  [t] -> Maybe ([t], a) 
    }

> getParser (pure (,) <*> literal ';' <*> undefined ) "abc"
*** Exception: Prelude.undefined
Run Code Online (Sandbox Code Playgroud)

第二个用途newtype.没有其他区别:

newtype Parser t a = Parser {
        getParser ::  [t] -> Maybe ([t], a) 
    }

> getParser (pure (,) <*> literal ';' <*> undefined ) "abc"
Nothing
Run Code Online (Sandbox Code Playgroud)

literal x是一个解析器,如果其参数与第一个标记匹配,则成功使用一个输入标记.所以在这个例子中,它失败了因为;不匹配a.但是,该data示例仍然看到下一个解析器未定义,而newtype示例则没有.

我已经读过这个,这个,而且这个,但是不能很好地理解它们,以便为什么第一个例子是未定义的.在我看来,在这个例子中,newtype比懒data,的答案说的正好相反.(至少有一个人也因此而感到困惑).

为什么切换datanewtype更改此示例的定义?


这是我发现的另一件事:使用此Applicative实例,data上面的解析器输出undefined:

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))
Run Code Online (Sandbox Code Playgroud)

而对于这个实例,data上面的解析器输出undefined(假设一个正确的Monad实例Parser s):

instance Applicative (Parser s) where
  f <*> x =
      f >>= \f' ->
      x >>= \x' ->
      pure (f' x')

  pure = pure a = Parser (\xs -> Just (xs, a))
Run Code Online (Sandbox Code Playgroud)

完整代码段:

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
Run Code Online (Sandbox Code Playgroud)

ham*_*mar 21

正如你可能知道,之间的主要区别data,并newtype为与data数据构造是懒惰,而与newtype数据的构造是严格的,即给予以下几种类型

data    D a = D a 
newtype N a = N a
Run Code Online (Sandbox Code Playgroud)

那么D ? `seq` x = x,但是N ? `seq` x = ?.(其中?代表"底部",即未定义的值或错误)

然而,可能不太常见的是,当您对这些数据构造函数进行模式匹配时,角色是"反转的",即

constD x (D y) = x
constN x (N y) = x
Run Code Online (Sandbox Code Playgroud)

然后constD x ? = ?(严格),但constN x ? = x(懒惰).

这就是你的例子中发生的事情.

Parser f <*> Parser x = Parser h where ...
Run Code Online (Sandbox Code Playgroud)

有了data,<*>如果其中一个参数是?,那么定义中的模式匹配会立即发散,但是newtype忽略了构造函数,就像你写的一样

f <*> x = h where
Run Code Online (Sandbox Code Playgroud)

这将仅用于发散x = ?如果x被征求.


sha*_*haf 11

之间的区别datanewtypedata被"解禁"的newtype不是.这意味着data有一个额外的⊥ - 在这种情况下,它意味着undefined/ = Parser undefined.当您的Applicative代码模式匹配时Parser x,它会强制?构造函数的值.

当你在data构造函数上进行模式匹配时,它会被评估并拆分以确保它不是⊥.例如:

?> data Foo = Foo Int deriving Show
?> case undefined of Foo _ -> True
*** Exception: Prelude.undefined
Run Code Online (Sandbox Code Playgroud)

因此data构造函数上的模式匹配是严格的,并将强制它.newtype另一方面,A的表示方式与其构造函数包装的类型完全相同.所以在newtype构造函数上匹配绝对没有任何意义:

?> newtype Foo = Foo Int deriving Show
?> case undefined of Foo _ -> True
True
Run Code Online (Sandbox Code Playgroud)

可能有两种方法可以更改data程序,使其不会崩溃.一种方法是在您的Applicative实例中使用无可辩驳的模式匹配,这将始终"成功"(但在以后的任何地方使用匹配的值可能会失败).每个newtype匹配的行为都像一个无可辩驳的模式(因为没有构造函数可以匹配,严格程度).

?> data Foo = Foo Int deriving Show
?> case undefined of ~(Foo _) -> True
True
Run Code Online (Sandbox Code Playgroud)

另一种是使用Parser undefined而不是undefined:

?> case Foo undefined of Foo _ -> True
True
Run Code Online (Sandbox Code Playgroud)

本场比赛一定会成功,因为那里一个有效的Foo多数民众赞成被匹配的值.它碰巧包含undefined,但由于我们不使用它,因此不相关 - 我们只看最顶层的构造函数.


除了您提供的所有链接,您可能会发现此文章相关.