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

1 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
Run Code Online (Sandbox Code Playgroud)

这用于给定客户列表的练习,我必须找到列表中每个性别的数量.我开始通过过滤得到一个只有个人的列表,因为只有他们有性别类型,但我的方法似乎是完全错误的:

listIndividuals :: [Client] -> [Client]
listIndividuals xs = filter (\x -> x == Individual) xs
Run Code Online (Sandbox Code Playgroud)

我怎样才能获得这个功能,我可以检查客户端的"类型"是什么.另外对于记录语法,我的编码风格如何?太不一致了?

bhe*_*ilr 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
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].