2016-06-15 1 views
14

GHCでコンパイルして実行すると、ループが検出されて中断します。Haskellプログラムは "loop"で中断しますが、私はそうではないはずです。

data Foo = Foo() 
deriving (Eq,Show) 

type Foop = Foo -> ((),Foo) 

noOp :: Foop 
noOp st = ((),st) 

someOp :: Foop 
someOp [email protected](Foo x) = ((),st) 

(<+>) :: Foop -> Foop -> Foop 
(<+>) f g st = let ((_,st'),(_,st'')) = ((f st),(g st')) in ((),st'') 

main = print $ (noOp <+> someOp) $ Foo() 

私はそうすべきではないと思いますが、ここにいくつかの変更があります。これはGHCのバグです

  • 変更data Foo(noOp <+> someOp)解体@(Foo x)

  • (someOp <+> noOp)に削除変更
  • newtype Fooのか、それは私の欠如である:それらのそれぞれは、ループが離れて行かせます評価プロセスの理解?

  • 答えて

    8

    これはバグではありません。ちょうど怠惰なパターンセマンティクスの難しい隅が見つかりました。私は単純なケースを提示してみましょう:

    > data Id a = Id a 
    > let Id a = undefined ; Id b = Id 3 in b 
    3 
    > let (Id a, Id b) = (undefined, Id 3) in b 
    *** Exception: Prelude.undefined 
    

    違いは、第二は、最初はない限りundefinedを評価しません

    case (undefined, Id 3) of 
        ~(Id a, Id b) -> b 
    

    いる間に最初のlet

    case undefined of 
        ~(Id a) -> case Id 3 of 
           ~(Id b) -> b 
    

    と同等であるということですaが要求されます(これは起こりません)。

    いずれか変数は、プロセスの両方undefinedId 3を強制的に、要求されとすぐに両方のパターンId aId bに対する第二の意志パターンマッチ。この問題が原因で、パターン~(K1 (K2 x) (K3 y))~(K1 ~(K2 x) (K3 y))~(K1 (K2 x) ~(K3 y))~(K1 ~(K2 x) ~(K3 y))は異なる意味を持っている、

    注意。あなたのコードを修正するには

    (f st, g st')

    let (~(_,st'),~(_,st'')) = ((f st),(g st')) in ((),st'') 
    

    または

    let (_,st') = f st ; (_,st'') = g st' in ((),st'') 
    
    +0

    let((_、st ')、〜(_、st' '))=(f st、g st') 'という反駁できないマッチを使用すれば十分です。 – leftaroundabout

    +0

    @leftaroundabout「st」は最初に要求されたものではありませんか? 'let ... in(()、st '')'。さらに、一般的な場合、 'g'は非厳密である可能性があります。 – chi

    +0

    ありがとう、それは洞察力があった。今、私のコードでは 'undefined 'はありません。なぜそれがループするのかまだ分かりません。あなたはまたそれについていくつか光を当てることができますか? –

    12
    • パターンマッチ(_, _)需要のWHNFを試してみてください。簡単です。
    • パターン一致(_, (_,_))は、WHNFがg st'であることを要求します。 gは厳格なので、ここに問題があります。したがって、最初にWHNFにst'が必要です。実行時にはst'がパターン((_,st'), (_,_))にあるので、実際にはst'にトラバースする前に、両方のタプルをWHNFする必要があります。しかし、gは厳密ですので、最初にst' ...などが必要です。

    あなたが怠惰な反論できないパターンで

    (<+>) f g st = let ((_,st'), ~(_,st'')) = (f st, g st') in ((),st'') 
    

    gの結果と一致した場合は、このは、このように評価されるため、問題は、離れて行く:

    • パターンマッチ(_, _)需要(f st, g st')のWHNF 。簡単です。
    • パターン一致(_, ~(_,_))は当面は何も求めません。
    • パターン一致((_,st'), ~(_,_))は、WHNFがf stであることを要求します。幸いにも、stはパターンに依存しないので、これを実現できます。
    • パターンマッチが完了したので、ランタイムはすでにin ((),st'')に進むことができます。この時点でのみ反駁可能なパターン~(_,st'')が強制されましたが、st'がここで使用できるので、これはもう問題にはなりません。したがって、gを一度計算するだけです。

      gは本当に、その時に見てする必要はありません、それがなければ@(Foo _)

      解体を削除:あなたはg非厳密を作ることに、すべての量を試してみた修正が

    つまり、タプルマッチ(_,st'')は、最初にWHNFがst'でなくても成功することができます。

    変更data Foo

    newtype Fooから

    これはFooコンストラクタは、実行時に実際には存在しないので、パターン[email protected](Foo _)が強制する何もないという効果があります。

    変更noOp <+> someOp

    someOp <+> noOpから

    私はgが厳格であるため、ループのみについて来て、言ったように。厳密ではないfをその位置に置くと、問題はありません。

    +0

    ありがとう、これは私がそれを理解するのを助けるための良いステップかもしれません:しかし、私はまだ最初にWHNFの左のタプルを取得することはできません理解していないし、取得する右のタプル'st'''?右タプルをWHNFingするには左タプルをWHNFingする必要がありますが、左タプルをWHNFingすると問題は見えません。 –

    +0

    最初に左タプルをWHNFできますが、それは 'st'を与えません。それは '(_、〜st ')'だけをあなたに与えます。 – leftaroundabout

    +0

    特定の場合、 '(_、st ')= f st = noOp st =(()、st)'なので、 'st' = st = Foo()'これは 'g'エイリアス'someOp'です。これを計算することはできませんか? –

    関連する問題