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

标签 haskell

我有一个工作函数,它使用 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

这会创建名为 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

关于Haskell:使用变量创建部分自定义数据类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46482145/

相关文章:

string - 在 Haskell 中剥离换行符

haskell - 在emacs中编码Haskell时找不到"Stack"

haskell - `Data.Proxy` 的目的是什么?

javascript - 我应该学习 haskell 来理解 javascript 的函数式编程部分吗?

haskell - 当函数使用 IO 时使用 fmap

haskell - 哪些编译器支持 Haskell FFI

Haskell 提前退出状态 monad(守卫?)

haskell - 仅使用一元绑定(bind)语法表达 do block

haskell - 尝试编译时找不到模块 `XMonad`

algorithm - 合并排序比插入排序更快的方式让我困惑