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"
需要调用适当的circle
或square
函数的问题。如果我要使用类型类来实现它,我可以执行以下操作:
instance Lookup Circle where
lookupF2I "radius" = Just $ IntegerE radius
lookupF2I _ = Nothing
lookupF2P "origin" = Just $ PointE origin
lookupF2P _ = Nothing
但是这让我不得不弄清楚在查找函数上强制执行哪种类型,更糟糕的是必须为我想要使用它的每个(许多)记录手动编写实例。
Circle
注意:和Square
可以使用单个 ADT 表示这一事实对我的问题是偶然的,因为这是一个人为的例子。实际的代码将包含各种非常不同的记录,它们唯一的共同点是具有相同类型的字段。