Reputation:
I have list of type
PrimMonad m => m [Double]
To calculate the average I defined the following functions:
sum' :: PrimMonad m => m [Double] -> m Double
sum' xs = (sum <$> xs)
len' :: PrimMonad m => m [Double] -> m Int
len' xs = length <$> xs
avg :: PrimMonad m => m [Double] -> m Double
avg xs = (sum' xs) / (fromIntegral $ len' xs)
However, I am having problems with the avg
function. I get the following errors:
Could not deduce (Fractional (m Double)) arising from a use of ‘/’ …
from the context (PrimMonad m)
bound by the type signature for
avg :: PrimMonad m => m [Double] -> m Double
at
In the expression: (sum' xs) / (fromIntegral $ len' xs)
In an equation for ‘avg’:
avg xs = (sum' xs) / (fromIntegral $ len' xs)
Could not deduce (Integral (m Int)) …
arising from a use of ‘fromIntegral’
from the context (PrimMonad m)
bound by the type signature for
avg :: PrimMonad m => m [Double] -> m Double
at
In the expression: fromIntegral
In the second argument of ‘(/)’, namely ‘(fromIntegral $ len' xs)’
In the expression: (sum' xs) / (fromIntegral $ len' xs)
Compilation failed.
What do I need to do get this simple function defined?
Upvotes: 0
Views: 175
Reputation: 15949
the error you are facing is twofold
fromIntegral
on a monadic value.now to the solving part - easiest would be to use do
notation to circumvent the whole problem
for simplicity's sake I copied an avg function from getting-average-of-calculated-list-haskell
{-# LANGUAGE BangPatterns #-}
import Data.List
avg :: (Integral a, Fractional b) => [a] -> b
avg xs = g $ foldl' c (0,0) xs
where
c (!a,!n) x = (a+x,n+1)
g (a,n) = fromIntegral a / fromIntegral n
monAvg :: (Monad m, Integral a, Fractional b) => m [a] -> m b
monAvg mxs = do xs <- mx
return $ avg xs
but this is maybe not the thing you want it is too specialised.
The minimal thing we need is only Functor
- so fmap avg
solves your problem as well.
if you want to keep your functions - then you need <*>
from Applicatives
to combine.
appAvg :: Applicative m => m [Double] -> m Double
appAvg xs = (/) <$> sum xs <*> (fromIntegral <$> len' xs)
Upvotes: 1
Reputation: 116139
The problem lies on /
wanting a number like Double
, not a number inside a monad like m Double
.
A naive fix could be
avg :: PrimMonad m => m [Double] -> m Double
avg xs = (/) <$> sum' xs <*> (fromIntegral <$> len' xs)
but this is unsatisfactory, since it will run the monadic action xs
twice.
I'd rather use something like
avg :: PrimMonad m => m [Double] -> m Double
avg xs = (\ys -> sum ys / fromIntegral (length ys)) <$> xs
I would also avoid naming xs
a monadic action, since xs
can easily be taken for a plain list. This is however a matter of personal preference.
Even better, I'd define a non-monadic average first:
avg :: [Double] -> Double
avg xs = sum xs / fromIntegral (length xs)
avgM :: PrimMonad m => m [Double] -> m Double
avgM = fmap avg
Since avgM
is so short, I would probably omit its definition, and directly call fmap avg
where needed.
Upvotes: 7