【问题标题】:Unsure of correct type signature for function using foldr不确定使用 foldr 的函数的正确类型签名
【发布时间】:2016-01-26 16:33:12
【问题描述】:

所以我有一个如下定义的 Database.Persist.TH 模型:

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Transaction json
    date Day
    payee String
    categoryId Int
    memo String
    outflow Double
    inflow Double
    deriving Show Generic
|]

我正在尝试编写一个函数来计算给定[Transaction] 的帐户余额。

到目前为止,我已经推断出以下几点:

calculateBalance :: [Transaction] -> Transaction -> Double
calculateBalance [Transaction{..}] = foldr(\x -> transactionInflow - transactionOutflow) (+)

这就是说,我认为,我们正在使用 foldr 获取交易列表 - 这意味着该列表将“解构”每个单独的交易,减法将在两个双精度之间执行,Double 将被退回。

但是,我正在做的事情似乎是错误的。

这样的一个错误是:

Couldn't match type ‘Transaction’ with ‘t0 a0’
Expected type: Transaction -> Double
  Actual type: t0 a0 -> Double
In the expression:
  foldr (\ x -> transactionInflow - transactionOutflow) (+)
In an equation for ‘calculateBalance’:
    calculateBalance [Transaction {..}]
      = foldr (\ x -> transactionInflow - transactionOutflow) (+)    

然而,更改 calculateBalance :: [Transaction] -> t0 Transaction -> Double 似乎并不能解决问题,calculateBalance :: [Transaction] -> Transaction -> t0 Transaction -> Double 也不能解决问题,所以我对如何继续感到有些困惑。

我可能做错了什么?任何其他制作这个“惯用”Haskell 的技巧也将不胜感激。

【问题讨论】:

  • 您的模式匹配中有[Transaction{..}],这只会匹配Transactions 的单个元素列表。此外,您将两个函数传递给foldr,但它只需要一个。 foldr 的第一个参数必须有两个参数,但是你给它一个只带一个参数的 lambda,即xfoldr 的第二个参数必须是要开始的初始值,第三个参数是要折叠的值列表。我建议先使用foldr 使用更简单的示例,以直观了解它的工作原理,然后扩展到您的用例。

标签: haskell


【解决方案1】:

您出错的原因是您仅对单个元素列表进行了事务分解,并且您提供了两个函数作为 foldr 的参数。

calculateBalance = foldr (\t acc -> acc +  transactionInflow t - transactionOutflow t) 0 

我现在有个会议——我会在一两个小时内解释更多。

编辑 - 说明

起初是的,你是对的,你的函数的类型签名不正确。

在下面我将假设正确的类型签名是

calculateBalance :: [Transaction] -> Double

因为你写的。

我正在尝试编写一个函数来计算给定[Transaction]的帐户的余额

  • 第一件事就是

    calculateBalance [Transaction{..}] =
    

    仅匹配单例列表,RecordWildCards 扩展提供的 transactionInflowtransactionOutflow 仅匹配左侧的值。所以“折叠函数”其实是一个常数。

  • 接下来是折叠函数的类型错误/应用于错误的参数。 foldr 的类型签名

    foldr :: (a -> b -> b) -> b -> [a] -> b
    

    所以第一个参数是 2 个参数的函数,然后是初始累加器值,然后需要列表。

    • 函数参数:

      (\t acc -> transactionInflow t - transactionOutflow t + acc)
      

      Transaction 的记录创建函数transactionInflow/transactionOutflow,可以从t :: Transaction 中提取Double 值。我认为您不能在 lambda 表达式中使用RecordWildCards,这样可以节省您输入ts,我认为这没什么大不了的(如果您愿意,可以在let/where 子句中提取函数定义,即很少是一个坏主意)。

    • 然后您需要累加器值,该值通过给定顶级函数签名的类型约束专门用于Double(即必须与最终结果相同)。

      calculateBalance = foldr aux 0
        where aux Transaction{..} acc = acc + transactionInflow - transactionOutflow  
      

注意:如果您在Data.List 中查看foldr 的类型签名,base 晚于 4.8,它是

foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b

这是由于Foldable-Traversable-Proposal (FTP) 造成的,它是我上面列出的那个的概括,但对于t a = [a],它的作用完全相同。

【讨论】:

  • 非常感谢。我在尝试使用transactionInflow t - transactionOutflow t 时仍然遇到一些类型错误,因此我定义了一个小函数netTransaction :: Transaction -> Double 其中netTransaction t = transactionInflow t - transactionOutflow t。然后我将calculateBalance 的类型更改为[Transaction] -> Double 并编译!
猜你喜欢
  • 1970-01-01
  • 2022-07-05
  • 2018-05-25
  • 1970-01-01
  • 2015-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-10
相关资源
最近更新 更多