首先,有一个为 Haskell 实现重载记录字段的计划,这将允许在不同的记录中使用相同的名称,并且您只需在编译器无法计算的情况下明确指定您想要的名称自己出去。
话虽如此 ...
我发现处理这个问题的最可靠和最方便的方法是每个消息类型一个 Haskell 类型。
你将会拥有:
data Message1 = Message1 Int Int ByteString -- can use records here
data Message2 = Message2 Double Int Int
data Message3 = Message3 { m3_a :: Double, m3_b :: Double }
-- .....
data Message256 = Message256 CustomType
-- A sum type over all possible message types:
data AnyMessage = M1 Message1
| M2 Message2
| M3 Message3
-- ...
| M256 Message256
这样做的好处包括:
- 您可以使用记录(仍然必须使用不同的前缀,但这通常就足够了)
它比跨构造函数共享记录要安全得多:
data T = A { field :: Int }
| B { field :: Int }
| C { bla :: Double } -- no field record
print (field (C 2.3)) -- will crash at runtime, no compiler warning
您现在可以编写仅适用于某些消息类型的函数。
- 您现在可以编写仅适用于消息类型的子集(例如其中 3 个)的函数:您所需要的只是另一个 sum 类型。
处理这个的代码仍然很优雅:
process :: AnyMessage -> IO ()
process anyMsg = case anyMsg of
M1 (Message1 x y bs) -> ...
...
M3 Message3{ m3_a, m3_b } -> ... -- using NamedFieldPuns
我在生产中多次使用过这种模式,它产生了非常健壮的代码。