Haskell:创建一个只有某些" kind"的列表类型?

时间:2014-08-08 18:41:09

标签: haskell types coding-style

我一直在研究学习你是一个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

我如何获得此功能,我可以检查客户端的“类型”是什么。另外对于记录语法,我的编码风格如何?太不一致了?

1 个答案:

答案 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]