我有一个AST,其根节点的类型为E.Root
。我需要将其转换为具有类型为I.Root
的根节点的AST。
我现在可以定义一个带有类型签名的函数eToI
:
eToI :: E.Root -> AdditionalInfo -> I.Root
但是,两个AST共享很多节点。因此,函数eToF
有很多样板代码构建来自I's
节点的E's
个节点,这些节点本质上是相同的。
我想解决这两个问题:
在类型级别,避免定义I
的节点。我有所有的节点
E
定义的。我在I
中定义了哪些节点发生了变化。我可以吗?
编译器通过某种方式映射什么来生成I
中的所有类型
需要改变?
在价值级别,我只想为其定义转换 变化的节点(假设E的A映射到I的Z ..):
aToZ :: E.A -> AdditionalInfo -> I.Z
bToY :: E.B -> AdditionalInfo -> I.Y
现在,编译器可以生成像eToI这样的函数吗?
eToI :: E.Root -> AdditionalInfo -> I.Root
Haskell的惯用方法是什么?
答案 0 :(得分:2)
有时,您可以将E.Root
和I.Root
的常见方面分解为一种或多种可重用的数据类型。它通常也有助于将AdditionalInfo -> ...
隐藏在monad或applicative functor中。例如,这里有一些伪代码:
module Common where
{-# LANGUAGE DeriveTraversable #-}
data Reusable root = ... root ...
deriving (Functor, Foldable, Traversable)
module E where
import Common
data Root = Leaf (Reusable Root) | Node Root
module I where
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Common
import Data.Monoid
data Root = Root [Reusable Root]
deriving Monoid
module Transform where
import Common
import qualified E
import qualified I
import Control.Applicative
import Control.Monad.Reader
import Data.Monoid
type AdditionalInput = ...
type F = Reader AdditionalInput
convertRoot :: E.Root -> F I.Root
convertRoot (Leaf reusable) =
traverse convertRoot reusable
convertRoot (Node left right) =
liftA2 mappend (convertRoot left) (convertRoot right)
现在我可以使用traverse
在Reusable E.Root
和Reusable I.Root
之间进行转换。