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?

Antworten auf die Frage(1)

Ihre Antwort auf die Frage