haskell 中 2 个新类型 'Or' 的模式匹配

Patternmatching on 'Or' of 2 newtypes in haskell

我在 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

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

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

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

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

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

现在您可以在需要时区分 Dd BDd Z,而在您不关心时使用 Dd x 进行多态工作。

在现代GHC Haskell中,如果你想表明BZonly标签,你可以使用DataKindsKindSignatures 扩展并像这样做:

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

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

要编写一个根据类型具有的标签而表现不同的函数,您需要 class.

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

现在可以为B(或'B)写一个Zoop实例,为Z(或'Z)写一个实例,让用户使用两者的方法。不过请记住,类型在编译中会被删除,因此只要您想要将这些方法与多态标记一起应用,就需要一个 Zoop a 约束。