Haskell:使用变量创建自定义数据类型的部分

时间:2017-09-29 05:07:56

标签: haskell

我有一个工作函数,它使用警卫来确定如何创建自定义数据类型的副本(在本例中为Transaction),但这样做只改变了一个字段。解决这个问题的惯用方法是什么?

如果我可以在我的partials中使用变量作为键,我可以将函数简化为如下所示:

change n val uid list = do
    let partial x = x {uid = uid, n = val}
    mergeData partial uid list

但我似乎无法找到一种方法将变量用作键或调用所有守卫共同的功能。

change :: [Char] -> [Char] -> String -> [Transaction] -> IO ()
change n val uid list
    | n == "amount" = do
        let partial x = x {uid = (id uid), amount = (id val)}
        mergeData partial uid list
    | n == "user" = do
        let partial x = x {uid = (id uid), user = (id val)}
        mergeData partial uid list
    | n == "category" = do
        let partial x = x {uid = (id uid), user = (id val)}
        mergeData partial uid list
    | n == "description" = do
        let partial x = x {uid = (id uid), description = (id val)}
        mergeData partial uid list

P上。 S. mergeData是另一个使用已编辑字段创建此Transaction复制实例的函数。

1 个答案:

答案 0 :(得分:2)

你可以使用lens吗?

你需要

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens (makeLenses, (.~))

在您的模块文件中。

然后,猜测Transaction看起来像什么,你可以这样定义:

data Transaction = Transaction {
    _uid :: String
  , _amount :: String
  , _user :: String
  , _description :: String }
  deriving (Show, Eq)
makeLenses ''Transaction

这会创建一个名为uidamount的镜头,依此类推。

您现在可以将change功能简化为:

change' :: (Transaction -> Transaction) -> String -> [Transaction] -> IO ()
change' l uidValue = mergeData ((uid .~ uidValue) . l) uidValue

为了进行测试,我首先定义了这样的mergeData,因为它似乎符合OP change函数中使用的类型:

mergeData :: (Transaction -> Transaction) -> String -> [Transaction] -> IO ()
mergeData f _ = mapM_ (print . f)

以下是一些例子:

λ> ts = [Transaction "1" "42" "Joan" "Foo", Transaction "2" "1337" "Nigel" "Bar"]
λ> change' (amount .~ "0") "7" ts
Transaction {_uid = "7", _amount = "0", _user = "Joan", _description = "Foo"}
Transaction {_uid = "7", _amount = "0", _user = "Nigel", _description = "Bar"}
λ> change' (user .~ "Jane") "7" ts
Transaction {_uid = "7", _amount = "42", _user = "Jane", _description = "Foo"}
Transaction {_uid = "7", _amount = "1337", _user = "Jane", _description = "Bar"}

如您所见,amount .~ "0"将所有_amount标签设置为"0"user .~ "Jane"将所有_user标签设置为"Jane"。两个表达式都具有Transaction -> Transaction

类型
λ> :type amount .~ "0"
amount .~ "0" :: Transaction -> Transaction
λ> :type user .~ "Jane"
user .~ "Jane" :: Transaction -> Transaction

如果您不喜欢唱片公司标签中的下划线,则可以使用makeLensesFor代替makeLenses

相关问题