使用foldr不确定函数的正确类型签名

时间:2016-01-26 16:33:12

标签: haskell

所以我有一个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也将非常感激。

1 个答案:

答案 0 :(得分:2)

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

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

现在我开会了 - 我会在一两个小时内解释一下。

编辑 - 说明

首先是的,你是正确的,你的功能的类型签名是不正确的。

在下文中,我将假设正确的类型签名是

calculateBalance :: [Transaction] -> Double

因为你写了。

  

我正在尝试编写一个函数来计算给定[Transaction]

的帐户的余额
  • 首先是

    calculateBalance [Transaction{..}] =
    

    仅匹配单例列表,transactionInflow扩展名提供的transactionOutflowRecordWildCards仅匹配左侧的值。所以“折叠功能”实际上是一个不变的。

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

    的类型签名
    foldr :: (a -> b -> b) -> b -> [a] -> b
    

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

    • 函数参数:

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

      Transaction的记录创建了可以从transactionInflow/transactionOutflow中提取Double - 值的函数t :: Transaction。我不认为你可以在lambda表达式中使用RecordWildCards,这样可以省去你输入的t,我认为这不是一个大问题(如果你想要你可以在{{中提取函数定义) 1}}子句,这很少是一个坏主意。)

    • 然后你需要累加器值,它由给定顶级函数签名的类型约束专门用于let/where(即必须与最终结果相同)。

      Double

注意:如果您查看calculateBalance = foldr aux 0 where aux Transaction{..} acc = acc + transactionInflow - transactionOutflow foldr的{​​{1}}的类型签名,则Data.List晚于4.8,

base

这是由Foldable-Traversable-Proposal (FTP)引起的,并且是我上面列出的那个的推广,但对于foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b ,它的行为完全相同。