haskellesqueleto

Multiplying Int and double values in esqueleto?


The problem I'm facing is pretty simple: Basically I'm trying to calculate the product of an Int and a Double. In plain Haskell I would just run

product = (fromIntegral int_val) * double_val

However I can't figure out how to do it in esqueleto. I'm having a table B that has a column "amount" of type Int and a table C that has a column "price" of type Double. When trying to extract both and calculate the product, like this

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

I'm getting a type error (as expected):

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

I couldn't find anything in the docs that helped me and I actually have no idea how to go on. (For more code see the full example below).

Possible Solution: I could of course just store the price as an Int, but I'm interested if this can be done with esqueleto.

Full example:

Database:

Table A: Id|Name

Table B: Id|AId|BId|Amount where Amount is an Int and AId and BId are references to Table A and B.

Table C: Id|Name|Price , here is Price a Double

The query I've written is as follows:

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)

EDIT:

I've tried using fmap and fromIntegral like this:

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

which results in two errors: No instance for (Functor SqlExpr) and No instance for (Num (Value Double))

As suggested in the comments (by @Thomas M. DuBuisson), I tried:

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

which solves the second problem, but I still get No instance for (Functor SqlExpr).

EDIT 2:

I've asked about this on the Yesod mailing list. The discussion can be found here.


Solution

  • As mentioned in the discussion here there is now (since esqueleto version 2.2.9) castNum for this purpose:

    castNum :: (Num a, Num b) => expr (Value a) -> expr (Value b)