7

TL;DR:我需要帮助来弄清楚如何生成代码,该代码将返回来自不同记录的各个字段的少数数据类型(可能只是 Double 和 Bool)中的一种。

长格式:假设以下数据类型

data Circle = Circle { radius :: Integer, origin :: Point }
data Square = Square { side  :: Integer }

和一些样板代码

circle = Circle 3 (Point 0 0)
square = Square 5

我正在构建一个小型 DSL,并希望用户编写如下内容

circle.origin
square.side

它会生成类似于

origin . circle
side . square

例如,在解析这个时,我会使用字符串“circle”和“origin”。我现在需要将它们转换为函数调用。我显然可以有这样的东西:

data Expr a = IntegerE (a -> Integer)
            | PointE (a -> Point)

lookupF2I "side"   = Just $ IntegerE side
lookupF2I "radius" = Just $ IntegerE radius
lookupF2I _        = Nothing

lookupF2P "origin" = Just $ PointE origin
lookupF2P _ = Nothing

每个返回的数据类型都有一个查找函数。从 DSL 的角度来看,每种数据类型拥有一个函数是实用的,因为它只会真正处理 2 或 3 种数据类型。然而,这似乎不是一种特别有效的做事方式。有没有更好的方法(肯定)这样做?如果没有,有没有一种方法可以从我希望能够从中查找字段的各种记录中生成各种查找函数的代码?

其次,仍然存在解析"circle""square"需要调用适当的circlesquare函数的问题。如果我要使用类型类来实现它,我可以执行以下操作:

instance Lookup Circle where
    lookupF2I "radius" = Just $ IntegerE radius
    lookupF2I _        = Nothing
    lookupF2P "origin" = Just $ PointE origin
    lookupF2P _        = Nothing

但是这让我不得不弄清楚在查找函数上强制执行哪种类型,糟糕的是必须为我想要使用它的每个(许多)记录手动编写实例。

Circle注意:和Square可以使用单个 ADT 表示这一事实对我的问题是偶然的,因为这是一个人为的例子。实际的代码将包含各种非常不同的记录,它们唯一的共同点是具有相同类型的字段。

4

1 回答 1

1

我尝试使用 Template Haskell 来提供一种很好且类型安全的方法来解决这个问题。为此,我从给定的字符串构造了表达式。

我想 Lens 包可以做到这一点,但它可能是一个更简单、更灵活的解决方案。

它可以这样使用:

import THRecSyntax
circleOrigin = compDSL "circle.origin.x"

并且是这样定义的:

{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH

compDSL :: String -> Q Exp
compDSL s = return 
            $ foldr1 AppE 
            $ map (VarE . mkName) 
            (reverse $ splitEvery '.' s)

所以结果表达式将是:x (origin circle)

注意:splitEvery是将列表拆分为取出给定元素的子列表的函数。示例实现:

splitEvery :: Eq a => a -> [a] -> [[a]]
splitEvery elem s = splitter (s,[])
  where splitter (rest, res) = case elemIndex elem rest of
            Just dotInd -> let (fst,rest') = splitAt dotInd rest
                            in splitter (tail rest', fst : res)
            Nothing -> reverse (rest : res)

这是使用给定语法创建嵌入式 DSL 的重量级但类型安全的方法。

于 2013-07-21T14:28:49.553 回答