Я пытаюсь написать язык разметки для моего приложения на Haskell, которое поддерживает плагины.Авторы плагинов должны иметь возможность не только быстро использовать его, но и расширять его функциональность и сами создавать средства визуализации.Вот почему я создал класс Renderable.
class Renderable a b where
render :: a b -> b
Чтобы отобразить элемент, вы можете сделать:
data SomeElement b = SomeElement ...
instance SomeElement SomeGUI where
render = ...
Вы также можете создавать элементы, которые содержат другие элементы:
data ListLayout b = ListLayout [b]
instance ListLayout SomeGUI where
render = ...
В конце концов, вы можете сделать любую (ab) для b, если существуют экземпляры Renderable ab:
let (myGUI :: b) = render (myLayout :: a b)
Проблема возникает, когда существует несколько экземпляров Renderable, и я хочу отобразитьодно и то же значение для нескольких выходов рендеринга:
data SomeElement b = SomeElement
instance Renderable SomeElement GuiA
instance Renderable SomeElement GuiB
renderGuiA :: GuiA -> IO ()
renderGuiB :: GuiB -> IO ()
renderGuis layout = do
renderGuiA (render layout)
renderGuiB (render layout)
main :: IO ()
main = do
let layout = SomeElement
renderGuis layout
Компиляция выводит тип макета (GuiA), поскольку GuiA - это тип, который ожидает renderGuiA.В результате, renderGuiB, очевидно, не будет компилироваться, поскольку типы не совпадают.Точно так же, попытка дать renderGuis аннотацию типа не работает вообще.
renderGuis :: (Renderable a GuiA, Renderable a GuiB) => a (GuiA or GuiB) -> IO ()
Я думал сделать что-то вроде этого:
renderGuis :: (Renderable a GuiA, Renderable a GuiB) => a ['GuiA, 'GuiB] -> IO ()
Однако у меня нетНоу-хау и ощущение, что я мог бы столкнуться с множеством других проблем на этом пути.
Может кто-нибудь придумать, как сделать это без ущерба для функциональности или расширяемости?Любая помощь будет принята с благодарностью!