我一直在研究学习你一个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
Run Code Online (Sandbox Code Playgroud)
这用于给定客户列表的练习,我必须找到列表中每个性别的数量.我开始通过过滤得到一个只有个人的列表,因为只有他们有性别类型,但我的方法似乎是完全错误的:
listIndividuals :: [Client] -> [Client]
listIndividuals xs = filter (\x -> x == Individual) xs
Run Code Online (Sandbox Code Playgroud)
我怎样才能获得这个功能,我可以检查客户端的"类型"是什么.另外对于记录语法,我的编码风格如何?太不一致了?
首先,我建议不要使用具有代数类型的记录类型,因为最终会得到部分访问器功能.例如,拥有代码是完全合法的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
Run Code Online (Sandbox Code Playgroud)
然后你可以拥有
data Client
= GovOrg GovClient
| Company CompanyClient
| Individual IndividualClient
deriving (Show)
Run Code Online (Sandbox Code Playgroud)
现在您也可以将您的功能定义为
isIndividualClient :: Client -> Bool
isIndividualClient (Individual _) = True
isIndividualClient _ = False
listIndividuals :: [Client] -> [IndividualClient]
listIndividuals clients = filter isIndividualClient clients
Run Code Online (Sandbox Code Playgroud)
或者更无点的形式
listIndividuals = filter isIndividualClient
Run Code Online (Sandbox Code Playgroud)
在这里,为了做出决定,我只是在一个单独的函数中使用模式匹配来确定使用了哪个Client构造函数.现在你可以获得记录和代数类型的全部功能,只需要担心更多的代码,但更安全.例如,您永远不会意外地调用期望单个客户端上的政府客户端的函数,因为它不会进行类型检查,而对于您当前的实现,它将更有可能.
如果您关注较长的名称,我建议最终查看lens旨在帮助您相对轻松地操作复杂的记录类型树的库.
根据您当前的实现,您还可以执行与最终解决方案非常相似的操作:
isIndividualClient :: Client -> Bool
isIndividualClient (Individual _ _) = True
isIndividualClient _ = False
listIndividuals :: [Client] -> [Client]
listIndividuals clients = filter isIndividualClient clients
Run Code Online (Sandbox Code Playgroud)
这里的主要区别是Individual需要两个字段,所以我_在模式中有两个通配符匹配,listIndividuals现在是类型[Client] -> [Client].