0

我在 haskell 程序中使用决策图库。为此,我想声明 2 种不同的(新)类型来跟踪我正在处理的决策图类型。我使用的库是 cudd,决策图基类型是一个 DdNode,但我的问题只是与 haskell 相关。

newtype Bdd = ToBdd Cudd.Cudd.DdNode
newtype Zdd = ToZdd Cudd.Cudd.DdNode

通常我想在调用函数时区分它们,但现在我想使用一个不必区分这两种类型的函数。我主要尝试通过 3 种不同的方式解决这个问题:

data Dd = ToBdd Bdd | ToZdd Zdd

printDdInfo :: Dd -> IO()
printDdInfo (ToZdd dd) = do
    putStrLn "Hello, zdd!"
    Cudd.Cudd.cuddPrintDdInfo manager dd
printDdInfo (ToBdd dd) = do
    putStrLn "Hello, bdd!"
    Cudd.Cudd.cuddPrintDdInfo manager dd

printDdInfo :: Either Bdd Zdd -> IO()
printDdInfo (ToZdd dd) = do
    putStrLn "Hello, zdd!"
    Cudd.Cudd.cuddPrintDdInfo manager dd
printDdInfo (ToBdd dd) = do
    putStrLn "Hello, bdd!"
    Cudd.Cudd.cuddPrintDdInfo manager dd
    
printDdInfo :: Either Bdd Zdd -> IO()
printDdInfo dd = case dd of
  Zdd dd -> do
    putStrLn "Hello, bdd!"
    Cudd.Cudd.cuddPrintDdInfo manager dd
  Bdd dd -> do
    putStrLn "Hello, bdd!"
    Cudd.Cudd.cuddPrintDdInfo manager dd

所有这些方法都失败了。编写此代码的最优雅的方式是什么?感谢您的关注。

4

2 回答 2

5

我没有深入研究您的代码,但从您的描述看来,您可能对幻像类型的想法感兴趣。

newtype Dd x = ToDd (Cudd.Cudd.DdNode)
data B
data Z

现在,您可以区分何时需要,并在不需要时进行多态Dd B工作。Dd ZDd x

在现代 GHC Haskell 中,如果您想指出BandZ唯一的标签,您可以使用DataKindsandKindSignatures扩展名并这样做:

newtype Dd (x :: DdTag) = ToDd (Cudd.Cudd.DdNode)
data DdTag = B | Z

在此上下文中,您将处理Dd 'Band Dd 'Z,其中单引号(发音为“tick”)将数据构造函数“提升”到类型级别。

要编写一个根据类型所具有的标记而表现不同的函数,您需要一个类。

class Zoop tag where
  zoop :: Dd tag -> Int
  zeep :: Char -> Dd tag
  zaaaaap :: Dd tag -> Dd tag

现在您可以为 (or ) 编写一个Zoop实例,为B(or )'B编写一个实例,让用户使用其中任何一个的方法。但是请记住,类型在编译时会被删除,因此无论何时您想将这些方法与多态标签一起应用,都需要一个约束。Z'ZZoop a

于 2020-07-15T21:00:05.583 回答
1

我会采用与您的第一种方法非常相似的方法,但您需要重命名与 newtype 不同的构造函数:

data Dd = FromBdd Bdd | FromZdd Zdd

printDdInfo :: Dd -> IO()
printDdInfo (FromZdd (ToZdd dd)) = do
    putStrLn "Hello, zdd!"
    Cudd.Cudd.cuddPrintDdInfo manager dd
printDdInfo (FromBdd (ToBdd dd)) = do
    putStrLn "Hello, bdd!"
    Cudd.Cudd.cuddPrintDdInfo manager dd

或者,知道所有不同的 dd 类型共享相同的底层表示,您也可以放弃存储新类型,而是Dd独立设置:

data DdKind = IsBdd | IsZdd

data Dd = Dd { ddKind :: DdKind
             , ddImplementation :: Cudd.Cudd.DdNode }

printDdInfo :: Dd -> IO()
printDdInfo (Dd ddk dd) = do
    putStrLn $ case ddk of
       IsBdd -> "Hello, bdd!"
       IsZdd -> "Hello, zdd!"
    Cudd.Cudd.cuddPrintDdInfo manager dd
于 2020-07-15T13:09:50.637 回答