在Haskell代数数据类型中选择备选方案

时间:2009-12-07 17:17:48

标签: data-structures haskell types algebraic-data-types

当类型X定义为:

data X = 
    X { sVal :: String } |
    I { iVal :: Int } |
    B { bVal :: Bool }

我希望Int位于X值内(如果有),否则为零。

returnInt :: X -> Int

如何确定X的参数returnInt的类型是什么?

4 个答案:

答案 0 :(得分:13)

使用模式匹配。

returnInt :: X -> Int
returnInt (I x) = x
returnInt _     = 0

答案 1 :(得分:10)

对所有可能的X值使用更灵活的定义:

returnInt :: X -> Maybe Int
returnInt (I i) = Just i
returnInt _ = Nothing

然后你可以使用maybe来表示你想要的特定默认值 - 0可能是一个有效值(这被称为semipredicate problem):

*Main> maybe 0 id (returnInt $ X "")
0
*Main> maybe 0 id (returnInt $ I 123)
123
*Main> maybe (-1) id (returnInt $ X "yo")
-1

相反,部分函数存在运行时异常风险:

*Main> let returnInt (I i) = i
*Main> :t returnInt
returnInt :: X -> Int
*Main> returnInt (B True)
*** Exception: <interactive>:1:4-22: Non-exhaustive patterns in function returnInt

如果你感觉真的很胖,你可以使用MonadPlus

returnInt :: (MonadPlus m) => X -> m Int
returnInt (I i) = return i
returnInt _ = mzero

获得更大的灵活性:

*Main> maybe 0 id (returnInt $ X "")
0
*Main> maybe 0 id (returnInt $ I 123)
123
*Main> returnInt (I 123) `mplus` returnInt (I 456) :: [Int]
[123,456]

答案 2 :(得分:4)

给定这样的函数:

returnInt :: X -> Int
returnInt x = {- some integer -}

... x的类型始终为X。您关心的是x是否使用XIB类型构造函数。

使用模式匹配来区分:

returnInt :: X -> Int
returnInt (X _) = error "needed an Int, got a String"
returnInt (I { iVal = n }) = n
returnInt (B _) = error "needed an Int, got a Bool"

答案 3 :(得分:3)

为了澄清这里的一点,让我重写你的数据类型,以避免X的含义含糊不清:

data SomeType = X { myString :: String} | I {myInt :: Int} | B {myBool :: Bool}

在此定义中,没有X,I和B类型。 X,I和B是构造函数,它们创建类型为 Sometype的值。注意当你问ghci用这些类型构造函数构造的任何值的类型时会发生什么:

*Main> :t (I 5)
(I 5) :: Sometype 

*Main> :t (B False)
(B False) :: Sometype

他们属于同一类型!!

正如您可以使用X,I和B来构造类型一样,您可以使用模式匹配来解构类型,就像在上面的其他答案中所做的那样:

returnInt :: SomeType -> Int 
returnInt (I x) = x        -- if the pattern matches (I x) then return x
returnInt _  = error "I need an integer value, you moron"  -- throw an error otherwise

请记住,模式匹配按顺序发生:如果值与某些行中的模式匹配,则下面行中的模式将不会被执行。

请注意,当您像使用所谓的记录语法一样定义类型时(只需查看此处:http://en.wikibooks.org/wiki/Haskell/More_on_datatypes),您可以免费获得类似的功能!!

尝试查看myInt的类型,例如:

*Main> :t myInt
myInt :: SomeType -> Int

看看这个功能的作用:

*Main> myInt (I 5)
5

*Main> myInt (B False)
*** Exception: No match in record selector Main.myInt

这正是上面定义的returnInt的行为。奇怪的错误消息只是告诉您该函数不知道如何处理与(I x)不匹配的SomeType类型的成员。

如果使用更常见的语法定义类型:

data SomeType2 = X String | I Int | B Bool

然后你放松了那些不错的记录功能。

错误消息终止程序的执行。这有时很烦人。如果您的功能需要更安全的行为,那么GBacon的答案就是实现它的方法。了解Maybe a类型并使用它来处理需要返回某些值或不返回任何值的此类计算(请尝试:http://en.wikibooks.org/wiki/Haskell/Hierarchical_libraries/Maybe)。