从Haskell中的数据类型中提取字段

时间:2018-06-13 06:56:32

标签: haskell types syntax

(我对Haskell相当新)我创建了这样的数据类型 -

data MatchingCondition = MatchingHead String | MatchingBody String | MatchingTail String

现在我想写一个函数extractCondition :: MatchingCondition -> String,它从这个数据类型中提取字符串。

执行此操作的一种方法是明确写入

extractCondition (MatchingHead x) = x
extractCondition (MatchingBody x) = x
extractCondition (MatchingTail x) = x

目前,这些案例很少,所以我可以很容易地写下这个函数,但是如果有更多的案例,或者将来我要为我的数据类型添加更多条件,那将会非常痛苦。那么有没有简单的方法来做到这一点。

此外,当我的朋友看到​​这段代码时,他评论说:这似乎首先打败了sum-type引入的类型安全。

有人可以解释这究竟意味着什么吗?

3 个答案:

答案 0 :(得分:8)

为构造函数编写下划线是一件好事:

extractCondition (_ x) = x

不幸的是你无法在Haskell中编写这样的代码。

但您可以随时重构代码以将标记移动到单独的字段中:

data MatchingType = Head | Body | Tail
data MatchingCondition = Match MatchingType String

extractCondition (Match _ x) = x

有些技术只需要编写类似extract @String myMatching的内容,它会自动从每个构造函数返回String。而且你根本不需要编写任何代码! (见Scrap your boilerplate)。虽然它可能不是你想要的。

另一个答案中基于记录的解决方案也是一种有效的解决方案。虽然你应该小心在总和类型中记录。这可能很危险!

重新类型安全失败:更好地问你的朋友,因为他说了这个。但是如果不看整个代码就很难说出他的意思。

答案 1 :(得分:6)

我只会谈到这一点:

  

此外,当我的朋友看到​​这段代码时,他评论说:这似乎首先打败了sum-type引入的类型安全。

     

有人可以解释这究竟意味着什么吗?

你的朋友可能担心将来你会添加一些没有String的构造函数。

考虑:

data T = A String | B String

getString :: T -> String
getString (A s) = s
getString (B s) = s

只要T被置于石头上,就没有错。

相反,如果稍后更改T,我们可能会以

结束
data T = A String | B String | C NotAString

getString :: T -> String
getString (A s) = s
getString (B s) = s
getString (C n) = error "not a string, let's make the program crash!"

如果T以这种方式扩展,getString将成为部分功能,应避免使用此功能。在一个函数周围有这样的函数很容易编写代码,如

foo :: T -> ...
foo t = use (getString t)

并“忘记”关于非字符串的情况,可能导致程序崩溃。如果foo必须在所有构造函数上进行模式匹配,我们也会记住这种情况。

T被扩展时,类型getString :: T -> String会成为用户的谎言。它告诉他们将始终生成String,但事实并非如此。 典型的解决方案包括删除getString并让foo执行所有模式匹配,或者,如果“大多数”案例中包含字符串,请保留getString,但将其更改为getString :: T -> Maybe String这样foo现在将被迫处理“无字符串”的情况。

我还要提到要避免的常见设计错误,这是“布尔盲”的情况。一些程序员很想保持getString :: T -> String部分,并添加辅助函数

hasString :: T -> Bool

打算使用

foo t = if hasString t
   then use (getString t)
   else handleNoString

此处的问题是,必须记住每个getString来电必须由hasString保护。编译器无法帮助程序员。这给程序员带来了更多的负担,程序员必须积极避免危险情况。如果我们使用Maybe代替,则不会出现此问题。

foo t = case getString t of
   Just s  -> use s     -- now use (getString t) would be a type error, and rightly so!
   Nothing -> handleNoString

这种设计存在于某些Java库中,这可能有助于它的普及。 (我被告知许多Java程序员现在认为这是一个糟糕的设计。)

答案 2 :(得分:4)

一种方法是使用记录:

data MatchingCondition = MatchingHead { extractCondition :: String }
                       | MatchingBody { extractCondition :: String }
                       | MatchingTail { extractCondition :: String }