3

我面临的问题非常简单:基本上我正在尝试计算 Int 和 Double 的乘积。在普通的 Haskell 中,我会跑

product = (fromIntegral int_val) * double_val

但是我无法弄清楚如何在 esqueleto 中做到这一点。我有一个表 B 有一个 Int 类型的列“数量”和一个表 C 有一个 Double 类型的列“价格”。当尝试提取两者并计算产品时,像这样

(b ^. BAmount) *. (c ^. CPrice) 

我收到类型错误(如预期的那样):

Couldn't match type ‘Double’ with ‘Int’
Expected type: EntityField Drink Int
  Actual type: EntityField Drink Double

我在文档中找不到任何对我有帮助的东西,而且我实际上不知道如何继续。(有关更多代码,请参见下面的完整示例)。

可能的解决方案:我当然可以将价格存储为 Int,但如果可以使用 esqueleto 来完成,我很感兴趣。

完整示例

数据库:

表 A:ID|名称

表 B:Id|AId|BId|Amount 其中 Amount 是 Int,AId 和 BId 是对表 A 和 B 的引用。

表 C: Id|Name|Price ,这里是 Price a Double

我写的查询如下:

result <- liftIO $ runDb $ select $
            from $ \(a, b, c) -> do
              where_ (a ^. AId ==. b ^. BAId)
              where_ (b ^. BCId ==. c ^. CId)
              let product = (b ^. BAmount) *. (c ^. CPrice)
              let total = sum_ product :: SqlExpr (Value (Maybe Double))
              groupBy $ a ^. AName
              return (a ^. AName)

编辑

我尝试过使用fmapfromIntegral喜欢这样:

let product = fmap fromIntegral (b ^. BAmount) *. (c ^. CPrice)

这会导致两个错误: No instance for (Functor SqlExpr)No instance for (Num (Value Double))

正如评论中所建议的(@Thomas M. DuBuisson),我尝试了:

let product = fmap (fmap fromIntegral) (b ^. BAmount) *. (c ^. CPrice)

这解决了第二个问题,但我仍然得到No instance for (Functor SqlExpr).

编辑 2

我在 Yesod 邮件列表上问过这个问题。可以在这里找到讨论。

4

2 回答 2

1

我不是专家,但您可以尝试应用_floor到您的整数参数:

floor_ :: (..., PersistField a, Num a, PersistField b, Num b)
       => expr (Value a) -> expr (Value b)

这可能能够将您的整数变成双精度数,因为a并且b可以不同。通常您使用它将双精度数转换为整数,但它也可能以相反的方式工作。

一些类似的 Esqueleto 功能也可能起作用。

是的,这是一个黑客。我希望一些专家可以提出更好的解决方案。

于 2015-07-02T20:44:33.447 回答
0

正如这里的讨论中提到的,现在(从 esqueleto 版本 2.2.9 开始)为此目的使用 castNum :

castNum :: (Num a, Num b) => expr (Value a) -> expr (Value b)
于 2015-07-16T05:07:56.597 回答