假设我有以下 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/