我有一个工作函数,它使用 guard 来确定如何创建自定义数据类型的副本(在本例中为Transaction
),但在执行此操作时仅更改一个字段。解决这个问题的惯用方法是什么?
如果我可以使用变量作为部分中的键,我可以将函数简化为如下所示:
change n val uid list = do
let partial x = x {uid = uid, n = val}
mergeData partial uid list
但我似乎找不到一种方法来使用变量作为键或调用所有 guard 通用的函数。
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
的复制实例。
最佳答案
你可以使用 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
。
关于Haskell:使用变量创建部分自定义数据类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46482145/