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

Data structures 在Haskell代数数据类型中选择备选方案,data-structures,haskell,types,algebraic-data-types,Data Structures,Haskell,Types,Algebraic Data Types,当类型X定义为: data X = X { sVal :: String } | I { iVal :: Int } | B { bVal :: Bool } 我想要Int在X值内,如果有,否则为零 returnInt :: X -> Int 如何确定returnInt的参数是哪种类型的X?使用模式匹配 returnInt :: X -> Int returnInt (I x) = x returnInt _ = 0 给定这样一个函数: ret

当类型
X
定义为:

data X = 
    X { sVal :: String } |
    I { iVal :: Int } |
    B { bVal :: Bool }
我想要
Int
X
值内,如果有,否则为零

returnInt :: X -> Int
如何确定
returnInt
的参数是哪种类型的
X

使用模式匹配

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

给定这样一个函数:

returnInt :: X -> Int
returnInt x = {- some integer -}
x
的类型始终为
x
。您关心的是
x
是否使用
x
I
B
类型构造函数

使用模式匹配来区分差异:

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"

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

returnInt :: X -> Maybe Int
returnInt (I i) = Just i
returnInt _ = Nothing
然后,您可以使用所需的特定默认值-0可能是有效值(这称为):

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

*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
要获得更大的灵活性:

*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]

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

data SomeType = X { myString :: String} | I {myInt :: Int} | B {myBool :: Bool}
在此定义中,没有X、I和B类型。十、 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
请记住,模式匹配是按顺序进行的:如果值与某行中的模式匹配,则不会执行下面行中的模式

请注意,当您像以前一样定义类型时,使用所谓的记录语法(请看这里:),您可以免费获得这样的函数

尝试查看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
的行为。奇怪的错误消息只是告诉您,函数不知道如何处理与
(ix)
不匹配的SomeType类型的成员

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

data SomeType2 = X String | I Int | B Bool
然后你就失去了那些好的录音功能


错误消息终止程序的执行。这有时很烦人。如果您的功能需要更安全的行为,GBacon的答案就是这样做的。了解
可能是
类型,并使用它来处理这种需要返回某些值或不返回任何值的计算(请尝试此:)。

要在内部添加一些内容,您的定义基本上是说类型X要么是类型构造函数X,要么是类型构造函数I,要么是类型构造函数B。您的函数接受X的任何成员,因此您可以只使用模式匹配I,它是X的成员。@codebliss:Minor nitpick:这三个都是数据构造函数。例如,类型构造函数是
Maybe
,它有一种
*->*
:对它应用一个类型,就得到了一个具体的类型。在上一个示例中,它的
mzero
方法使用
MonadPlus
。在最近设计库时,我遇到了这种情况,并决定使用
return
fail
Monad
类中返回一个值比使用
MonadPlus
更好。我知道
fail
被一些人视为一个缺点,但我决定使用它是一个更好的推广,因为我们将使用
MonadPlus
仅用于其mzero方法。你的想法是什么?@jberryman这取决于上下文。我通常倾向于使用更优雅的代码为库用户提供更好的抽象。你的问题会成为一个有趣的社区维基!
*Main> myInt (I 5)
5

*Main> myInt (B False)
*** Exception: No match in record selector Main.myInt
data SomeType2 = X String | I Int | B Bool