読者です 読者をやめる 読者になる 読者になる

きくらげ観察日記

好きなことを、適当に。

型クラスのインスタンスが複数ある場合の話(Haskell編)

inkar-us-i.hatenablog.com

こちらで出した例はScalaでしたが、Haskellでも同様の問題は起こりえます。

-- TreeSet.hs
module TreeSet (
    Cmp(..)
  , TreeSet()
  , empty
  , insert
  , fromList
  , elem
  ) where

import Prelude hiding (elem)

class Cmp a where
  eq :: a -> a -> Bool
  lt :: a -> a -> Bool

data TreeSet a
  = Branch a (TreeSet a) (TreeSet a)
  | Leaf

empty :: TreeSet a
empty = Leaf

insert :: Cmp a => a -> TreeSet a -> TreeSet a
insert x Leaf = Branch x Leaf Leaf
insert x t@(Branch y left right)
  | eq x y    = t
  | lt x y    = Branch y (insert x left) right
  | otherwise = Branch y left (insert x right)

fromList :: Cmp a => [a] -> TreeSet a
fromList xs = foldl (flip insert) Leaf xs

elem :: Cmp a => a -> TreeSet a -> Bool
elem _ Leaf = False
elem x (Branch y left right)
  | eq x y    = True
  | lt x y    = elem x left
  | otherwise = elem x right
-- ModA.hs
module ModA (someSet) where

import qualified TreeSet as TS

-- 通常の順序
instance TS.Cmp Int where
  eq = (==)
  lt = (<)

someSet :: TS.TreeSet Int
someSet = TS.fromList [5, 3, 7, 1, 6]
-- ModB.hs
module ModB (is7InSet) where

import qualified TreeSet as TS

-- 逆順
instance TS.Cmp Int where
  eq = (==)
  lt = (>)

is7InSet :: TS.TreeSet Int -> Bool
is7InSet set = TS.elem 7 set
-- ModC.hs
module ModC where

import qualified ModA
import qualified ModB
import qualified TreeSet as TS

main = do
  if ModB.is7InSet ModA.someSet
    then putStrLn "7 is in someSet"
    else putStrLn "7 is not in someSet"

実行結果:

$ runghc ModC.hs
7 is not in someSet

Orphan instanceの危険性が分かってきました。