-->

数据和NEWTYPE之间懒惰/严格(Laziness/strictness between data

2019-08-03 05:38发布

我挣扎理解为什么这两个片段,所谓“穷人的严格分析”下产生不同的结果。

第一个例子使用data (假设为正确应用型实例):

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例子没有。

我读过这个 , 这个和这个 ,但不明白他们不够好,知道为什么第一个例子是不确定的。 在我看来,在这个例子中, newtype 不止懒惰data ,答案是什么的对面说。 (至少一人已被此迷惑过)。

为什么从没有交换datanewtype改变这个例子的definedness?


下面是我发现的另一件事情:使用此应用型情况下, 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解析器以上输出不确定的(假设为正确的单子实例Parser s ):

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

  pure = pure a = Parser (\xs -> Just (xs, a))

完整的代码片段:

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

Answer 1:

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

data    D a = D a 
newtype N a = N a

然后D ⊥ `seq` x = x ,但N ⊥ `seq` x = ⊥(其中, 表示“底部”,即不定值或错误)

什么是可能不太众所周知的,然而,就是这些数据构造,当你的模式匹配的角色“颠倒”,即用

constD x (D y) = x
constN x (N y) = x

然后constD x ⊥ = ⊥ (严格),但constN x ⊥ = x (懒惰)。

这是正在发生的事情在你的榜样。

Parser f <*> Parser x = Parser h where ...

随着data ,在定义模式匹配<*>会马上岔开,如果其中一个参数是 ,但newtype构造函数被忽略,它就像你写

f <*> x = h where

这将仅发散为x = ⊥如果x被征求。



Answer 2:

之间的差别datanewtypedata被“解禁”和newtype不是。 这意味着data有一个额外的⊥ -在这种情况下,这意味着undefined / = Parser undefined 。 当你的Applicative上的码型,匹配Parser x ,它迫使如果构造值。

当您在模式匹配data的构造,它的评估,并采取分开,以确保它不是⊥。 例如:

λ> data Foo = Foo Int deriving Show
λ> case undefined of Foo _ -> True
*** Exception: Prelude.undefined

所以图案匹配的data构造是严格的,而且会迫使它。 甲newtype ,在另一方面,在完全相同的方式为它的构造包裹类型表示。 因此,在匹配newtype构造也绝对没有什么:

λ> newtype Foo = Foo Int deriving Show
λ> case undefined of Foo _ -> True
True

可能有两个方法来改变你的data的程序,使得它不会崩溃。 人们会在你使用一个不争的模式匹配Applicative实例,这将始终是“成功”(但使用匹配的值后的任何地方可能会失败)。 每newtype比赛表现得像一个无可辩驳的模式(因为没有构造函数来匹配,严格明智)。

λ> data Foo = Foo Int deriving Show
λ> case undefined of ~(Foo _) -> True
True

另一个办法是使用Parser undefined的,而不是undefined

λ> case Foo undefined of Foo _ -> True
True

本场比赛一定会成功,因为一个有效Foo多数民众赞成被匹配的值。 它发生在包含undefined ,但由于我们不使用它,这不是相关的-我们只能看最上面的构造函数。


除了把你给的链接,你可能会发现这篇文章有关。



文章来源: Laziness/strictness between data and newtype