Existenzielles Antimuster, wie man es vermeidet
Das Folgende scheint zu funktionieren ... aber es scheint ungeschickt.
data Point = Point Int Int
data Box = Box Int Int
data Path = Path [Point]
data Text = Text
data Color = Color Int Int Int
data WinPaintContext = WinPaintContext Graphics.Win32.HDC
class CanvasClass vc paint where
drawLine :: vc -> paint -> Point -> Point -> IO ()
drawRect :: vc -> paint -> Box -> IO ()
drawPath :: vc -> paint -> Path -> IO ()
class (CanvasClass vc paint) => TextBasicClass vc paint where
basicDrawText :: vc -> paint -> Point -> String -> IO ()
instance CanvasClass WinPaintContext WinPaint where
drawLine = undefined
drawRect = undefined
drawPath = undefined
instance TextBasicClass WinPaintContext WinPaint where
basicDrawText (WinPaintContext a) = winBasicDrawText a
op :: CanvasClass vc paint => vc -> Box -> IO ()
op canvas _ = do
basicDrawText canvas WinPaint (Point 30 30) "Hi"
open :: IO ()
open = do
makeWindow (Box 300 300) op
winBasicDrawText :: Graphics.Win32.HDC -> WinPaint -> Point -> String -> IO ()
winBasicDrawText hdc _ (Point x y) str = do
Graphics.Win32.setBkMode hdc Graphics.Win32.tRANSPARENT
Graphics.Win32.setTextColor hdc (Graphics.Win32.rgb 255 255 0)
Graphics.Win32.textOut hdc 20 20 str
return ()
windowsOnPaint :: (WinPaintContext -> Box -> IO ()) ->
Graphics.Win32.RECT ->
Graphics.Win32.HDC ->
IO ()
windowsOnPaint f rect hdc = f (WinPaintContext hdc) (Box 30 30)
makeWindow :: Box -> (WinPaintContext -> Box -> IO ()) -> IO ()
makeWindow (Box w h) onPaint =
Graphics.Win32.allocaPAINTSTRUCT $ \ lpps -> do
hwnd <- createWindow w h (wndProc lpps (windowsOnPaint onPaint))
messagePump hwnd
Was nun der bevorzugte Weg zu sein scheint, ist einfach zu haben
data Canvas = Canvas {
drawLine :: Point -> Point -> IO (),
drawRect :: Box -> IO (),
drawPath :: Path -> IO ()
}
hdc2Canvas :: Graphics.Win32.HDC -> Paint -> IO ( Canvas )
hdc2Canvas hdc paint = Canvas { drawLine = winDrawLine hdc paint ... }
JEDOCH...
Wir mögen es, Farben in der Nähe zu halten und sie während des gesamten Zeichenprozesses zu mutieren, da ihre Erstellung und Zerstörung teuer ist. Ein Paint kann eine Liste wie [bgColor red, fgColor blue, font "Tahoma"] oder etwas anderes sein, oder es kann ein Zeiger auf eine interne Struktur sein, die das Paint-System verwendet (dies ist eine Abstraktion über Windows GDI, wird aber letztendlich abstrakt over direct2d und coregraphics), die Objekte "malen", die ich nicht immer wieder neu erstellen und dann binden möchte.
Das Schöne an Existentials in meinem Kopf ist, dass sie etwas opak einwickeln können, um es zu abstrahieren, und wir können es irgendwo speichern, zurückziehen, was auch immer. Wenn Sie sich teilweise bewerben, besteht meines Erachtens das Problem, dass das, was Sie teilweise beworben haben, jetzt im Container "steckt". Hier ist ein Beispiel. Angenommen, ich habe ein Malobjekt wie
data Paint = Paint {
setFg :: Color -> IO () ,
setBg :: Color -> IO ()
}
Wo kann ich den Zeiger platzieren? Wie erhält er den Zeiger, wenn ich die Farbe einer Funktion in Canvas übergebe? Was ist der richtige Weg, um diese API zu entwerfen?