haskell-jp / questions #101 at 2022-05-30 14:56:35 +0900

ある型クラス(Heap)をラップする型を定義して、それ自体も型クラスのインスタンスにしたいのですが、コンパイルエラーになってしまって悩んでいます。解決策や別実装のアプローチがあればおしえていただきたいです(詳細をスレに書きます)
{-# LANGUAGE ExistentialQuantification #-}

-- {-# LANGUAGE ScopedTypeVariables #-}
-- {-# LANGUAGE GADTs #-}

module Main where

class Heap h where
  insert :: (Ord a) => h a -> a -> h a
  empty :: (Ord a) => h a
  isEmpty :: h a -> Bool
  merge :: (Ord a) => h a -> h a -> h a

-- 任意のヒープをラップして別のヒープを構成したい
data HW a = forall h. Heap h => Node a (h a) | Empty

instance Heap HW where
  empty = Empty

  isEmpty Empty = True
  isEmpty _ = False

  insert Empty a = Node a Empty
  insert (Node x h) y = if x > y then Node y (insert h x) else Node x (insert h y)

  merge Empty h = h
  merge h Empty = h
  -- 「h1 h2の型が違う」と怒られる
  merge (Node a1 h1) (Node a2 h2) = Node (if a1 > a2 then a2 else a1) (merge h1 h2)

main :: IO ()
main = putStrLn "Hello, World!"
エラーメッセージ
 • Couldn't match type 'h1' with 'h'
      Expected: h a
        Actual: h1 a
      'h1' is a rigid type variable bound by
        a pattern with constructor:
          Node :: forall a (h :: * -> *). Heap h => a -> h a -> HW a,
        in an equation for 'merge'
        at app/Main.hs:29:23-32
      'h' is a rigid type variable bound by
        a pattern with constructor:
          Node :: forall a (h :: * -> *). Heap h => a -> h a -> HW a,
        in an equation for 'merge'
        at app/Main.hs:29:10-19
    • In the second argument of 'merge', namely 'h2'
      In the second argument of 'Node', namely '(merge h1 h2)'
      In the expression: Node (if a1 > a2 then a2 else a1) (merge h1 h2)
    • Relevant bindings include
        h2 :: h1 a (bound at app/Main.hs:29:31)
        h1 :: h a (bound at app/Main.hs:29:18)
   |
29 |   merge (Node a1 h1) (Node a2 h2) = Node (if a1 > a2 then a2 else a1) (merge h1 h2)
   |                                                                                 ^^
HW にはオリジナルのヒープの型が含まれないので、h1, h2は同じヒープをラップしているかわからないので、エラーになるのだろうなと推測しています。
h1, h2は同じヒープをラップしているかわからない
そういうことですね。HWの型にもとのヒープの型を含めるようにしてはどうですか? data HW h a = Node a (h a) | Empty instance Heap h => Heap (HW h) という感じで。
ありがとうございます。少しインスタンスの実装を修正する必要があったけどビルドできました!!

-- 任意のヒープをラップして別のヒープを構成したい
data HW h a = Node a (h a) | Empty

instance Heap h => Heap (HW h) where
  empty = Empty

  isEmpty Empty = True
  isEmpty _ = False

  insert (Empty) x = Node x empty
  insert (Node x h) y = if x > y then Node y (insert h x) else Node x (insert h y)

  merge Empty h = h
  merge h Empty = h
  -- 「h1 h2の型が違う」と怒られる
  merge (Node a1 h1) (Node a2 h2) = Node (if a1 > a2 then a2 else a1) (merge h1 h2)