如何定义结合了多个原始后端的图后端

时间:2018-08-20 13:19:54

标签: haskell haskell-diagrams

我想使同一程序使用两个不同的Diagrams后端,尤其是diagrams-rasterific来生成PNG,并使用diagrams-svg来从同一图中生成SVG。由于图似乎是围绕使用单个后端设计的,因此我试图定义一个组合后端,但是在为Backend实例定义renderToTree时遇到麻烦:

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)

1 个答案:

答案 0 :(得分:2)

我同意其他丹尼尔(Daniel)关于保持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 all 实例的格式为(Renderable p SVG, Renderable p Rasterific) => Renderable p Multi,足以写成toA。但是我们(AFAIK)无法向GHC承诺这一点,因为Renderable是一个开放类型类。