5457


現在、Haskellを少しでも勉強しようと思い、『関数型プログラミングの楽しみ』という本を写経しています。その中のコードの中に、下記のようなコードが含まれています([全体のコードはこのようになっています](http://www.cs.ox.ac.uk/publications/books/fop/dist/fop/chapters/1/BinaryHeapTrees.hs))。

この中に、

[code]

    data Ord a => Tree a = Null | Fork a (Tree a) (Tree a)
    
[/code]

というコードが出てきます。

おそらくこれは、ドキュメントに書かれてある [Data type
contexts](https://downloads.haskell.org/~ghc/7.6.3/docs/html/users_guide/data-
type-extensions.html) であると推測しています。実際に`runhaskell`の`v
7.6.3`では`-XDatatypeContexts`オブションを使うことで動かせることがメッセージで示唆されます。

ですが、このオブションを実際に指定すると:

[code]

    on the commandline: Warning:
        -XDatatypeContexts is deprecated: It was widely considered a misfeature, and has been removed from the Haskell language.
    
[/code]

という、「非推奨であるという警告」が出てきてしまいます。

おそらく、なんらかの形で、この`data`を理想的なかたちで修正する方法があり、そのようにする必要があると思われるのですが、理解不足のため、どのように修正すればいいのかがよくわかりません。

[本家Stackoverflow](https://stackoverflow.com/questions/7438600/datatypecontexts-
deprecated-in-latest-ghc-
why)にも似たような問題として質問に取り上げられてありましたが、若干問題が違うようにも感じられました。今回の場合、どのように修正するのが適切でしょうか。


CC BY-SA 3.0
2015-01-27T11:01:49.610
5426
2015-01-27T17:11:40.523
2017-05-23T12:38:56.083
-1
939
question
haskell
"-XDatatypeContexts is deprecated"と出てしまうようなコードに対しての、適切な修正方法


警告が出る理由は、Data type context は設計ミスだったから使って欲しくないというところにあるようです。  
代案を2つ出してみます。いずれも、Data type context を使った定義を用いて作った関数群がそのまま使えると思います。

**案1: GADT-style declaration を使う**  
本家で提案されていた方法。Data type context の代わりを求めるとここに行き着くかと思います。

[code]

    data Tree a where
        Null :: Tree a -- ん？ Ord a => Tree a とした方がいいかな？
        Fork :: Ord a => a -> Tree a -> Tree a -> Tree a
    
[/code]

**案2: data にコンテキストを指定しない**  
各関数にコンテキストを設定してしまえばそれで十分じゃないか、という考え方です。  
Haskell の勉強のためあえていろんな使い方をしたい、という場合には不適切かもですが。

[code]

    data Tree a = Null | Fork a (Tree a) (Tree a)
    
[/code]


CC BY-SA 3.0
2015-01-27T16:48:54.137
5456
2015-01-27T16:48:54.137
7831
5426
answer


まず結論から言うと、警告が出ないよう修正するにはデータ型から単に制約を取り除いて

[code]

    data Tree a = Null | Fork a (Tree a) (Tree a)
    
[/code]

とすればよいです。

DataTypeContextsはデータ型定義中の型パラメタに制約を掛けられる機能ですが、  
質問文で示されている本家stackOverflowの記事や、以下のproposal

[Proposal: NoDatatypeContexts](https://ghc.haskell.org/trac/haskell-
prime/wiki/NoDatatypeContexts)

で示されているように、この制約はそのデータ型を使用する関数等では考慮されず思ったような挙動をしてくれないため、Deprecated扱いの機能になっています。  
つまり、元コードの

[code]

    data Ord a => Tree a = Null | Fork a (Tree a) (Tree a)
    
[/code]

は、「二分探索木のパラメタ（要素の型）は順序を考えられる型でなければならない」という制約を表わそうとしているのですが、この制約のようにOrd制約を付けてもTree
aは「ただの二分木」を表わす型にしかなりません。

実際、「関数型プログラミングの楽しみ」の中にあるコードにもOrd制約は各関数毎につけられており、  
例えば次のようにmerge関数からOrd制約を抜いてみると型チェックに通らないことが確認できます。

[code]

    -- エラー！ ： 型aの２つの値が"<="で比較できるとは限らない
    merge :: Tree a -> Tree a -> Tree a
    merge a Null = a
    merge Null b = b
    merge a b
       | minElem a <= minElem b = join a b
       | otherwise = join b a
    
[/code]

**追記**

yoh2さんや本家の記事の回答のようにGADTsを使えば、コンストラクタにOrdの制約が付けられるので関数側のコンテクストからOrd制約が取り除けるようです。無意味なパターンマッチが必要ですし、あまり行儀のいいコードには見えませんが……

[code]

    {-# Language GADTs #-}
    
    -- Code from Section 1.1; it won't work since join is left unimplemented!
    
    module BinaryHeapTrees where
    
    data Tree a where
      Null :: Ord a => Tree a
      Fork :: Ord a => a -> Tree a -> Tree a -> Tree a
    
    isEmpty :: Tree a -> Bool
    isEmpty Null = True
    isEmpty (Fork x a b) = False
    
    minElem :: Tree a -> a
    minElem (Fork x a b) = x
    
    deleteMin :: Tree a -> Tree a
    deleteMin (Fork x a b) = merge a b
    
    insert :: a -> Tree a -> Tree a
    insert x a@Null = merge (Fork x Null Null) a
    insert x a@(Fork _ _ _) = merge (Fork x Null Null) a
    
    merge :: Tree a -> Tree a -> Tree a
    merge a Null = a
    merge Null b = b
    merge a@(Fork _ _ _) b
       | minElem a <= minElem b = join a b
       | otherwise = join b a
    
    join (Fork x a b) c = Fork x undefined undefined
    
[/code]


CC BY-SA 3.0
2015-01-27T16:50:30.457
5457
2015-01-27T17:11:40.523
2015-01-27T17:11:40.523
2781
2781
5426
answer