Haskell类型的具体数据构造函数

标签 haskell types constructor

假设我有以下 Haskell 代码:

data Option
    = Help
    | Opt1 Int Double String
    -- more options would be here in a real case

handleOption :: Option -> IO ()
handleOption option = case option of
    Help -> handleHelp
    Opt1 n f s -> handleOpt1 n f s

handleHelp :: IO ()
handleHelp = print "help"

handleOpt1 :: Int -> Double -> String -> IO ()
handleOpt1 n f s = print (n, f, s)

在上面的代码中,在我看来,提前解构对象是一种浪费,因为我可以将数据整齐地捆绑在一起。现在我必须单独传递 Opt1 的每个部分或创建一个单独的数据类型来拖拽它们。是否可以将整个Opt1传递给handleOpt1,同时不允许传递通用的Option实例,例如使handleOpt1求助编译错误?

下面的示例伪代码:

<小时/>
data Option
    = Help
    | Opt1 Int Double String

handleOption :: Option -> IO ()
handleOption option = case option of
    Help -> handleHelp
    opt1 @ Opt1{} -> handleOpt1 opt1

handleHelp :: IO ()
handleHelp = print "help"

handleOpt1 :: Option:Opt1 -> IO ()
handleOpt1 (Opt1 n f s) = print (n, f, s)

最佳答案

您可以使用GADTs为此。

{-# LANGUAGE GADTs #-}

data Option a where
    Help :: Option ()
    Opt1 :: Int -> Double -> String -> Option (Int, Double, String)

handleOption :: Option a -> IO ()
handleOption option = case option of
    Help          -> handleHelp
    opt1 @ Opt1{} -> handleOpt1 opt1

handleHelp :: IO ()
handleHelp = print "help"

handleOpt1 :: Option (Int, Double, String) -> IO ()
handleOpt1 (Opt1 n f s) = print (n, f, s)

使用 GADT,您可以向编译器提供更多类型信息。对于handleOpt1,由于它只接受Option (Int, Double, String),编译器知道Option ()(即Help) 永远不会被传入。

也就是说,使用 GADT 会让很多其他事情变得更加困难。例如,自动导出(例如 deriving (Eq, Show))通常不适用于它们。您应该仔细考虑在您的案例中使用它们的利弊。

关于Haskell类型的具体数据构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16113833/

相关文章:

haskell - 如何在应用类型默认规则的 ghci 中打印多态函数(或值)的类型?

mysql - PostgreSQL 中 varchar 类型的等价物是什么?

php - 如何在 MySQL 中设置年份数据类型

Java:构造函数不工作

java - 在 Java 中调用模糊重载的构造函数

Haskell Prelude.head 错误,空列表

haskell - 通过递归折叠实现极小极大

java - 为什么我们在 Java 的枚举中使用非修改构造函数

haskell - 在 Haskell 中遵循斐波那契函数 O(n)

Java 11 功能流程在方法调用上推断出错误的类型