4

我想让同一个程序使用两个不同的Diagrams后端,特别是diagrams-rasterific从同一个图中生成 PNG,以及diagrams-svg生成 SVG。由于 Diagrams 似乎是围绕使用单个后端设计的,我试图定义一个组合后端,但在renderToTreeBackend实例定义时遇到了麻烦:

import Diagrams.Core
import Diagrams.Core.Types

import qualified Diagrams.Backend.Rasterific as BackendA
import qualified Diagrams.Backend.SVG as BackendB

tokenA :: BackendA.B
tokenA = BackendA.Rasterific

tokenB :: BackendB.B
tokenB = BackendB.SVG

data Multi = Multi
  deriving (Eq, Ord, Read, Show)

type B = Multi

data MultiResult n = MultiResult (Result BackendA.B V2 n) (Result BackendB.B V2 n)
-- alternatively:
-- data MultiResult n =
--     ResultA (Result BackendA.B V2 n)
--   | ResultB (Result BackendB.B V2 n)

type instance V Multi = V2
type instance N Multi = Double

instance (TypeableFloat n, Show n) => Backend Multi V2 n where
  data Render Multi V2 n =
      RenderMulti
          { renderA :: Render BackendA.B V2 n
          , renderB :: Render BackendB.B V2 n
          }
  -- alternatively:
  -- data Render Multi V2 n =
  --     RenderA (renderA :: Render BackendA.B V2 n)
  --   | RenderB (renderB :: Render BackendB.B V2 n)

  type Result Multi V2 n = MultiResult n

  data Options Multi V2 n = MultiOptions

  renderRTree _ o tree = MultiResult
      (renderRTree tokenA (toOptA o) (treeToA tree))
      (renderRTree tokenB (toOptB o) (treeToB tree))

我不清楚如何在renderRTree这里遵循各个后端实现的功能。在任一替代结构中(将 Render 和 Result 类型作为总和或产品),我都无法使类型匹配。具体来说,在这种方法中,我被困在

treeToA :: RTree Multi V2 n a -> RTree BackendA.B V2 n a
treeToA = fmap f
  where
    f (RAnnot a) = RAnnot a
    f (RStyle s) = RStyle s
    f REmpty = REmpty
    f (RPrim x) = RPrim (toA x)

toA :: Prim Multi V2 n -> Prim BackendA.B V2 n
toA = ???

但我不相信这甚至是要走的路。

toOptAtoOptB没问题,我可以在需要时填写。我也可以Renderable使用任何一种方法为此后端提供实例,例如

instance (Show n, TypeableFloat n) => Renderable (Path V2 n) Multi where
  render _ x = RenderMulti (render tokenA x) (render tokenB x)
4

1 回答 1

3

我同意其他丹尼尔关于保持Diagram多态性并将其用于两种类型的观点。就像是:

dia :: forall b. (Renderable (Path V2 Double) b, V b ~ V2, N b ~ Double) => Diagram b
dia = circle 1

当您添加原始类型时,类型信号只会变得更糟,所以我可能会为我想要的所有原始类型定义一个约束:

type Back b = (V b ~ V2, N b ~ Double, 
    Renderable (Path V2 Double) b, Renderable (Text Double) b)

dia2 :: Back b => Diagram b
dia2 = circle 1 # fc blue

我不认为我们可以renderRTree为您编写Multi并对其进行类型检查。我们期望 的所有实例都是Renderable _ Multi这种形式(Renderable p SVG, Renderable p Rasterific) => Renderable p Multi,应该足够写了toA。但是我们不能(AFAIK)向 GHC 承诺这一点,因为Renderable它是一个开放类型的类。

于 2018-08-22T02:37:56.737 回答