2012-11-26 10 views
15

私は、これらの2つのスニペットがいわゆる「貧しい人の厳密さ分析」の結果が異なる理由を理解するのは苦労しています。データとnewtypeの間の怠惰/厳密さ

最初の例は、(正しいApplicativeのインスタンスを想定)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の例はそうではありません。

私はthisthis、およびthisを読んでいますが、最初の例が定義されていない理由を理解するのに十分理解していません。この例では、newtypeです。dataよりも怠けていると答えています。 (少なくともone other personもこれで混乱しています)。

dataからnewtypeに変更すると、この例の定義が変更されるのはなぜですか?このインスタンスに、上記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)) 

:このApplicativeのインスタンスと、未定義の出力を上記dataパーサ:ここ


は、私が発見し、別のことです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)) 

完全なコードスニペットは:

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 
+2

このような質問をするときには、適切なコード(これは 'Functor'と' Monad'インスタンスと 'literal'を含みます)を十分に含んでいれば、ちょっとあなたが指摘したように、たとえ小さな変化であっても行動の違いを生むことができるように、関数を書いた方法を正確に推測する必要があります。 – shachaf

+1

@shachafここでの本当の質問は「私のコードを修正するにはどうしたらいいですか? - 私はすでにそれをしました - しかし、 "厳密さ/怠惰に関して「データ」と「新しいタイプ」の違いは何ですか?申し訳ありませんが、それは質問から明らかではない場合。 –

+0

はい、それでも、コードの内容を知らなくても、コードで何が起こっているのか、どうすれば説明できますか? – shachaf

答えて

18

あなたはおそらく知っているように、datanewtypeの主な違いは、newtypeが厳しいながらdatadataコンストラクタは怠惰であるということですと、すなわち、以下のタイプ

を与えられたということです
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で、「逆転」されていること。

これはあなたの例で起こっていることです。data

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

、引数のいずれか がをしている場合<*>の定義におけるパターンマッチはすぐに発散するが、newtypeでコンストラクタは無視され、それはあなたが書いたかのように ある

f <*> x = h where 

xが要求された場合は、x = ⊥の場合のみ分岐します。

+0

私はまだ分かりませんが、1)パターンマッチングの違いがHaskellのセマンティクスによって必要かどうか、2)パターンマッチングの違いがコンストラクタの厳密さの違いによるものかどうかは、まだ明確ではありません。 –

+0

@MattFenwick:1)newtypesは基本的には意味的に存在しないので、1つのパターンマッチングは基本型のパターンマッチングと同じです。パターンxはxを評価しないので、パターン「N x」。 2)いいえ。データS a = S!a; constSx(S y)= x'、それから '' 'S undefined'' seq' x =⊥'''、' constS x⊥=⊥'のようになります。 – hammar

+0

したがって、データコンストラクタの場合、コンパイラがパターンと一致するかどうかを判断するのに十分な距離を評価する必要がありますか? –

10

datanewtypeの違いは、dataが "持ち上げ"され、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プログラムを変更するには2つの方法は、おそらくあります。 Applicativeインスタンスでは反駁可能なパターンマッチを使用することになりますが、常に成功するでしょう(ただし、後で一致した値を使用すると失敗する可能性があります)。すべてのnewtypeの一致は反駁不能なパターンのように振る舞います(厳密に一致するコンストラクタがないため)。

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

他のではなくundefinedParser undefinedを使用することです:

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

にマッチされます有効なFoo値があるため、この試合は、成功します。それにはundefinedが含まれていますが、これは使用しないため意味がありません。一番上のコンストラクタを見るだけです。


あなたが与えたすべてのリンクに加えて、this articleが該当します。

関連する問題