假设我们想为某人的健康状况建模。该人可能生病并且有Flu
,Cold
,Allergy
或Healthy
。我们可以通过多种方式在Haskell中定义它,其中两种方法是:
data Condition = Flu | Cold | Allergy | Healthy
或
data Condition = Flu | Cold | Allergy
data Sickness = Sickness (Maybe Condition)
这类似于定义Tree
结构,因为Tree
元素可以是Node
个Tree
s或Empty
:
data Tree a = Node (Tree a) (Tree a) | Empty
或者Tree
也可以使用Maybe
定义:
data Node a = Node (Tree a) (Tree a)
data Tree a = Tree (Maybe (Node a))
每种选择都有其优点和缺点。如果不使用Maybe
,我们会在单个数据声明中捕获Tree
或Health
的所有可能值,因此范围非常明确。另一方面,如果我们使用Mayb
e,那么我们捕获数据模型中可能存在或可能不存在某些内容的本质,并避免创建像Empty
或{{}这样的无定义数据类型1}}并且只需使用Healthy
来模拟缺席。这稍微简化了模型,但数据类型的声明不再包含在单个代码中。
这两种方法的其他优点和缺点是什么?
答案 0 :(得分:2)
显然,正如有些人所指出的,任何给出的答案在某种程度上都是主观的。然而,代码不仅仅是使计算机执行计算的一种方式 - 它也是一种通信媒介。
您可以更清楚地向将来阅读代码的人传达您的意图,您就能更好地解决这个问题。从the Zen of Python
获取提示显式优于隐式
这种启发式技术在许多不同的编程语言和场景中都很有用。一个必然结果是在面向对象的编程中,应该favour explicit domain objects over primitive obsession。
我倾向于在OP这样的情况下应用类似的经验法则,如果他们更好地传达意图,则倾向于明确命名类型。
答案 1 :(得分:1)
我认为决定的很大一部分取决于你认为Healthy
作为条件的有意义的。如果答案“不是很多”,那么最好使用Maybe
编码(参见here)来讨论更明确的案例。顺便说一句,如果疾病不是相互排斥的,并且还有其他因素导致疾病,我们将不得不采用一种不同的编码:
import qualified Data.Set as Set
import Data.Set (Set)
data Sickness = Flu | Cold | Allergy
deriving (Eq, Ord)
data Alertness = Sleepy | Distracted | Caffeinated
deriving (Eq, Ord)
data Condition = Condition (Set Sickness) (Set Alertness)
我觉得在Healthy
作为一种单独的条件的情况下,这种情况会明显减少。
回到原来的表述,值得一提的是有一个中间的第三个选项:滚动你自己的专门的,特定领域的Maybe
(参见this question):
data Sickness = Flu | Cold | Allergy
data Condition = Healthy | Sick Sickness
通过这样做,您将错过Maybe
- 处理工具,如果您想使用它们,您可能必须自己提供一些实例和功能(尽管这可能不是问题,取决于你打算如何使用Condition
)。