(我对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引入的类型安全。
有人可以解释这究竟意味着什么吗?
答案 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 }