Я пишу библиотеку игр. У меня он работает с иерархией экземпляровИнъекционный тип
class Animation a where
event :: a -> Event -> Writer [Event] a
paint :: a -> IO()
Здесь event
обрабатывает событие и, возможно, выделяет новые события для его родителя, чтобы увидеть (например, выход 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
(instance Animation LibWrapper anyevent
) содержит более ограниченный MyChild
(instance Animation MyChild MyEvent
). Я попробовал параметризацию LibWrapper
и имел instance Animation (LibWrapper event) event
, но Haskell, кажется, принимает два вхождения event
как не связанные, и я не знаю, что с этим делать.
Я также рассмотрел
class Animation a where
event :: a e -> e -> Writer [e] (a e)
paint :: a e -> IO()
Тогда это LibWrapper MyEvent
, который содержит MyChild MyEvent
и это нормально. Но у меня нет способа определить instance MyChild MyEvent
, не так ли?
Я бы предпочел иметь MyEvent
, указанный в типе MyRoot
однако, если существует способ передать его в качестве параметра в мой библиотечный модуль, это тоже будет приемлемым.
EDIT
Так же, как я разместил вопрос, я хоть попробовать
class Animation a e where
event :: a e -> e -> Writer [e] (a e)
paint :: a e -> IO()
... на всякий случай. Конечно, это сработало. Я до сих пор не совсем понимаю, как это происходит. Я был бы признателен за объяснение.
Ваши спецификации для «разрешить пользовательские события» неясны. Поэтому я не могу советовать о правильном способе структурирования этого. Не могли бы Вы уточнить? –
Как я писал, у меня был «data LibEvent = MouseMove Int Int | KeyPress Char | ... ', определенном в моем модуле библиотеки. Прошедшее событие поколение ничего в моей библиотеке фактически не использовало эти события, поэтому я думал, что должен иметь возможность писать 'данные MyEvent = MouseMove Int Int | SomethingRelevantToTheGame | ... 'и функцию' LibEvent -> MyEvent' и чтобы мои экземпляры 'Animation' использовали' MyEvent', как если бы они были определены в библиотеке. –
Я думаю, что событие должно быть классом типа. Вы можете сделать что-то вроде 'instance Event e => Animation (Libwrapper e) e где ...' –