3

次のコードを GHC から取得するのに苦労しています。

getFirstChild :: (WidgetClass w1, WidgetClass w2) => w1 -> IO (Maybe w2)
getFirstChild parent = do
   -- check if parent is a container
   if parent `isA` gTypeContainer
      -- if parent is a container get the first child
      then do children <- containerGetChildren $! castToContainer parent
              return $! Just $! children !! 0
      else return Nothing

一見 Gtk2hs の質問のように見えますが、実際には Haskell 型システムに関するものです。

このコードを GHC でコンパイルしようとすると、次のエラー メッセージが表示されます。

Could not deduce (w2 ~ Widget)
from the context (WidgetClass w1, WidgetClass w2)
  bound by the type signature for
             getFirstChild :: (WidgetClass w1, WidgetClass w2) =>
                              w1 -> IO (Maybe w2)
  at HsFu\Gtk\Widget.hs:(6,4)-(12,28)
  `w2' is a rigid type variable bound by
       the type signature for
         getFirstChild :: (WidgetClass w1, WidgetClass w2) =>
                          w1 -> IO (Maybe w2)
       at HsFu\Gtk\Widget.hs:6:4
Expected type: [w2]
  Actual type: [Widget]
In the first argument of `(!!)', namely `children'
In the second argument of `($!)', namely `children !! 0'

のタイプcontainerGetChildrenは次のとおりです。

containerGetChildren :: ContainerClass self => self -> IO [Widget]

Widget型自体が のインスタンスであるため、関数の戻り値の型をのインスタンスとして指定WidgetClassできない理由がわかりません。getFirstChildw2WidgetClass

のようなものを使わずに Haskell でこれを表現する方法はありunsafeCoerce ますか?

ティア

4

2 に答える 2

8

いいえ、方法はありません。あなたの宣言

getFirstChild :: (WidgetClass w1, WidgetClass w2) => w1 -> IO (Maybe w2)

あなたの関数は、WidgetClass にある限り任意のw2 を返すことができると言っていますが、Widgets のみを返すため、これは嘘です。Haskell では、このようにプログラマーを誤解させることはできません。

私があなたのモジュールをインポートして書いたとしましょう

data MyWodget = ....
instance WidgetClass MyWodget where ....

Maybe MyWodget次に、別の MyWodget 内にいくつかの MyWodgets を配置する限り、関数が a を返す可能性があることを型シグネチャから期待するのは合理的ですが、定義で使用した関数は Widgets でのみ機能するため、Haskell MyWodget では使用できず、私のコードはタイプチェックする場合としない場合があります。

MyWodget は決してウィジェットではないため、unsafeCoerce でこれを修正することはできません。haskell 型クラスを使用すると、同じ機能インターフェイスでまったく異なるデータ型を使用できるため、強制することはできません。

独自のクラスを定義できます

class FromWidget w where -- not a good name, I admit
    fromWidget :: Widget -> w

次に、 fromWidget を使用して、関数をより一般的なものに書き換えることができます。

getFirstChild :: (WidgetClass w1, FromWidget w2) => w1 -> IO (Maybe w2)

私が作成する限り、MyWodgetで使用できます

instance FromWidget MyWodget where ....

しかし、WidgetClass のすべてをこの方法で作成する方法があるとは思えません。興味のあるすべての WidgetClass を作成できるかもしれませんが、実際に Widgets だけに興味があるかどうかを確認する必要があるかもしれません。

于 2012-09-15T08:21:30.863 に答える
2

関数のタイプに応じて、関数は任意w1の にw2属している必要がありますWidgetClass。しかし、より具体的な type を返していますWidget。このような疑問がある場合は、関数に型を提供せず、どの型ghciが推論されるかを確認してください。ghci常により一般的なタイプを推測します。

于 2012-09-15T08:22:04.337 に答える