我有一个工作函数,它使用警卫来确定如何创建自定义数据类型的副本(在本例中为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
复制实例的函数。
答案 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
这会创建一个名为uid
,amount
的镜头,依此类推。
您现在可以将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
。