0

ゲームライブラリを書いています。私はそれをインスタンスの階層で動作させています

class Animation a where
    event :: a -> Event -> Writer [Event] a
    paint :: a -> IO ()

Hereeventはイベントを処理し、場合によってはその親が確認できるように新しいイベントを発行し (たとえば、出口Buttonは を待ってMouseClickEvent発行することができますCloseEvent)、paint描画を行います。私の一般的なユースケースは

--a user defined Animation, say a button
data MyChild = MyChild
instance Animation Child where
    ... anything

--a library defined Animation which is a composition of other animations
data LibWrapper = LibWrapper (Event -> Writer [Event] LibWrapper) (IO ())
mkWrapper :: (Animation a) => a -> LibWrapper
mkWrapper a = LibWrapper (\ev -> mkWrapper <$> event a ev) (paint a)
instance Animation LibWrapper where
    event (LibWrapper e _) = e
    paint (LibWrapper _ p) = p

--a user defined Animation for which the 'event' and 'paint' will be called 
data MyRoot = MyRoot LibWrapper
instance Animation MyRoot where
    event (MyRoot a) ev = MyRoot <$> event a ev
    paint (MyRoot a) = paint a

game = MyRoot (mkWrapper Child)

ここで、カスタム イベントを許可します。あれは、

class Animation a e where
    event :: a -> e -> Writer [e] a
    paint :: a -> IO ()

問題は、 ( ) にさらに制限されたLibWrapper( ) を含めることができないことです。私はパラメータ化して持ってみましたが、Haskell は の 2 つの出現を無関係なものと見なしているようで、どうすればよいかわかりません。instance Animation LibWrapper anyeventMyChildinstance Animation MyChild MyEventLibWrapperinstance Animation (LibWrapper event) eventevent

私も検討しました

class Animation a where
    event :: a e -> e -> Writer [e] (a e)
    paint :: a e -> IO ()

次に、それLibWrapper MyEventは a を含み、それでMyChild MyEvent問題ありません。でも、もう定義する方法がありinstance MyChild MyEventませんよね?

MyEventの型で指定したいのMyRootですが、ライブラリモジュールにパラメーターとして渡す方法があれば、それも受け入れられます。

編集

質問を投稿したのと同じように、試してみました

class Animation a e where
    event :: a e -> e -> Writer [e] (a e)
    paint :: a e -> IO ()

...念のため。もちろん、うまくいきました。ここで行われている型マジックについてはまだよくわかりません。説明をいただければ幸いです。

4

1 に答える 1

1

私が説明できる魔法は、明確にするために無視しeventます。コンパイラは見ます

module Anim where

class Animation a e where
    paint :: a -> IO ()

module A where

data A1 ; data E1

instance Animation A1 E1 where paint A1 = print "i"

module Main where

import Anim; import A

main = paint A1

「ペイント」は何をすべきですか?paint A1についての情報がないことに注意してくださいE1

ここで、モジュール B を追加して、これを main にインポートするとします。

module B where

import Anim; import A

data E2

instance Animation A1 E2 where paint A1 = print "j"

明らかに、あなたmain = paint A1が意味するインスタンスを区別できません。Haskell 標準では、import Bへの追加module Mainが以前に動作していたコードで使用されていたインスタンスに影響を与えないようにする必要があります。Somainは の有無にかかわらず拒否されmodule Bます。

この種のことは、タイプとデータの「ファミリ」の目的です (および古い機能依存関係)。これを完全に理解するには、 GHC のユーザー マニュアルをさらに読む必要があります。しかし利点は、A1 は常に E1 を暗示する必要があることを GHC に伝える方法が少なくとも 3 つあることです。たとえば、関連付けられた型のシノニムは次のように機能します。

class Animation a where
    type Event a :: *
    event :: a -> Event a -> Writer [Event a] a
    paint :: a -> IO ()

instance Animation A1 where
    Event A1 = E1
    event = ...
    paint = ...
于 2013-09-01T13:51:30.910 に答える