在Haskell中,您有产品类型,并且您有元组.
如果您不想将专用类型与值关联,则可以使用元组,如果您愿意,可以使用产品类型.
但是我觉得产品类型的符号有冗余
data Foo = Foo (String, Int, Char)
data Bar = Bar String Int Char
Run Code Online (Sandbox Code Playgroud)
为什么有这两种符号?有没有你更喜欢另一个的情况?
我猜你在使用元组时不能使用记录符号,但这只是一个方便的问题.另一件事可能是元组中的顺序概念,而不是产品类型,但我认为这只是由于函数fst和命名的命名snd.
@ chi的答案是关于Haskell评估模型方面的技术差异.我希望能让您深入了解这种类型编程的哲学.
在范畴理论中,我们通常使用"直到同构"的对象.你Bar当然是同构的(String, Int, Char),所以从分类的角度看它们是同一个东西.
bar_tuple :: Iso' Bar (String, Int, Char)
bar_tuple = iso to from
where to (Bar s i c) = (s, i, c)
from (s, i, c) = Bar s i c
Run Code Online (Sandbox Code Playgroud)
在某种意义上,元组是柏拉图式的产品类型,因为除了作为不同价值的集合之外,它们没有任何意义.所有其他产品类型都可以映射到普通的旧元组.
那么为什么不在任何地方使用元组,当所有Haskell类型最终归结为一系列产品?这是关于沟通.正如Martin Fowler所说,
任何傻瓜都可以编写计算机可以理解的代码.优秀的程序员编写人类可以理解的代码.
名字很重要!写下自定义产品类型
data Customer = Customer { name :: String, address :: String }
Run Code Online (Sandbox Code Playgroud)
向阅读代码的人灌输Customer具有意义的类型,不像(String, String)这意味着"两个字符串".
当您想通过隐藏数据表示并使用智能构造函数来强制实施不变量时,自定义类型特别有用:
newtype NonEmpty a = NonEmpty [a]
nonEmpty :: [a] -> Maybe (NonEmpty a)
nonEmpty [] = Nothing
nonEmpty xs = Just (NonEmpty xs)
Run Code Online (Sandbox Code Playgroud)
现在,如果您不导出NonEmpty构造函数,则可以强制人们通过nonEmpty智能构造函数.如果某人给你一个NonEmpty价值,你可以放心地认为它至少有一个元素.
当然,您可以将其Customer视为引擎盖下的元组,并展示令人回味的字段存取器,
newtype Customer = Bar (String, String)
name, address :: Customer -> String
name (Customer (n, a)) = n
address (Customer (n, a)) = a
Run Code Online (Sandbox Code Playgroud)
但这并没有真正为你带来太大的收获,除了现在转换Customer为元组现在更便宜(例如,如果你正在编写与面向元组的API一起使用的性能敏感代码).
如果你的代码旨在解决一个特定的问题 - 当然这是编写代码的全部要点 - 不仅要解决问题,还要让它看起来像你已经解决了它.有人-也许你在几年-将不得不读取这些代码并没有理解它先天的它是如何工作的知识.在这方面,自定义类型是非常重要的通信工具.