我一直在研究学习你是一个Haskell和开始Haskell并且遇到了一个有趣的问题。作为序言,我通常是一名C ++程序员,如果我不知道我在说什么,请原谅我。
Beginning Haskell的一个练习让我创建了一个类型的客户端,可以是政府组织,公司或个人。我决定尝试使用记录语法。
data Client = GovOrg { name :: String }
| Company { name :: String,
id :: Integer,
contact :: String,
position :: String
}
| Individual { fullName :: Person,
offers :: Bool
}
deriving Show
data Person = Person { firstName :: String,
lastName :: String,
gender :: Gender
}
deriving Show
data Gender = Male | Female | Unknown
deriving Show
这用于给定客户列表的练习,我必须找到列表中每个性别的数量。我开始通过过滤得到一个只有个人的列表,因为只有他们有性别类型,但我的方法似乎完全错误:
listIndividuals :: [Client] -> [Client]
listIndividuals xs = filter (\x -> x == Individual) xs
我如何获得此功能,我可以检查客户端的“类型”是什么。另外对于记录语法,我的编码风格如何?太不一致了?
答案 0 :(得分:7)
首先,我建议不要使用具有代数类型的记录类型,因为最终会得到部分访问器功能。例如,拥有代码position (Individual (Person "John" "Doe" Male) True)
是完全合法的,但它会引发运行时错误。相反,请考虑更像
data GovClient = GovClient {
govName :: String
} deriving Show
data CompanyClient = CompanyClient {
companyName :: String,
companyID :: Integer, -- Also, don't overwrite existing names, `id` is built-in function
companyContact :: String,
companyPosition :: String
} deriving Show
data IndividualClient = IndividualClient {
indvFullName :: Person,
indvOffers :: Bool
} deriving Show
然后你可以
data Client
= GovOrg GovClient
| Company CompanyClient
| Individual IndividualClient
deriving (Show)
现在您也可以将您的功能定义为
isIndividualClient :: Client -> Bool
isIndividualClient (Individual _) = True
isIndividualClient _ = False
listIndividuals :: [Client] -> [IndividualClient]
listIndividuals clients = filter isIndividualClient clients
或者更无点的形式
listIndividuals = filter isIndividualClient
这里,为了做出决定,我只是在一个单独的函数中使用模式匹配来确定使用了Client
个构造函数中的哪一个。现在你可以获得记录和代数类型的全部功能,只需要担心更多的代码,但更安全。例如,您永远不会意外地调用期望单个客户的政府客户的功能,因为它不会进行类型检查,而对于您当前的实施,它将更有可能。
如果您关注较长的名称,我建议您最终查看lens
库,该库旨在帮助您相对轻松地操作复杂的记录类型树。
使用您当前的实现,您还可以执行与最终解决方案非常相似的操作:
isIndividualClient :: Client -> Bool
isIndividualClient (Individual _ _) = True
isIndividualClient _ = False
listIndividuals :: [Client] -> [Client]
listIndividuals clients = filter isIndividualClient clients
这里的主要区别是Individual
有两个字段,因此我在模式中有两个_
通配符匹配,而listIndividuals
的类型现在是[Client] -> [Client]
。