在haskell中设计具有大量构造函数的数据类型

时间:2013-07-24 21:03:29

标签: haskell

有没有另外一种方法来写这样的东西:

data Message = Message1 Int Int ByteString
             | Message2 Double Int Int
             | Message3 Double Double
             .....
             | Message256 CustomType

有太多的构造函数,并且很难使用记录语法。我真正想要做的是编写一个解析器,是否有一些替代方法呢?

parse :: Bytestring -> Parser Message

1 个答案:

答案 0 :(得分:2)

首先,有一个Plan来实现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个)的函数:您需要的只是另一种和类型。
  • 处理此问题的代码仍然非常优雅:

    process :: AnyMessage -> IO ()
    process anyMsg = case anyMsg of
        M1 (Message1 x y bs) -> ...
        ...
        M3 Message3{ m3_a, m3_b } -> ... -- using NamedFieldPuns
    

我在制作中多次使用过这种模式,它会产生非常强大的代码。