源自我之前的问题:Calculation difference between Days
我不会将函数应用于自定义数据结构Item
的列表。问题是我仍然不知道如何正确处理IO
,所以这是我的代码:
data Item = Item
{ name :: String
, expire :: Day
, stock :: Integer
, price :: Float
} deriving (Show)
totalLoss :: IO [Item] -> Float
totalLoss items = sum $ map loss items
loss :: Item -> Float
loss x = (price x) * fromIntegral (stock x)
让我们说,我不能仅仅得到[Item]
,所以我必须适当处理IO
。但是无论如何我仍然会遇到这些错误:
• Couldn't match expected type ‘[IO Item]’
with actual type ‘IO [Item]’
• In the second argument of ‘map’, namely ‘items’
In the second argument of ‘($)’, namely ‘map loss items’
In the expression: sum $ map loss items
|
48 | totalLoss items = sum $ map loss items
| ^^^^^
对于loss :: IO Item -> Float
,这是相同的错误,对于loss :: IO [Item] -> Float
,我会遇到另一个错误。
• Couldn't match expected type ‘[IO [Item]]’
with actual type ‘IO [Item]’
• In the second argument of ‘map’, namely ‘items’
In the second argument of ‘($)’, namely ‘map loss items’
In the expression: sum $ map loss items
|
48 | totalLoss items = sum $ map loss items
| ^^^^^
我应该如何解决这个问题?
答案 0 :(得分:5)
totalLoss
是一个纯函数,因此请相应地更改其类型:
totalLoss :: [Item] -> Float
totalLoss items = sum $ map loss items
这个问题的根本问题似乎是项目列表源自不纯的来源(这是很自然的事情)。
编写尽可能多的代码作为纯函数,然后在main
函数中(或尽可能接近)用不纯的输入组成这些纯函数:
main :: IO ()
main = do
items <- ioItems -- :: IO [Item]
print $ totalLoss items
如注释所暗示,ioItems
具有类型IO [Item]
,但是您可以通过totalLoss
表示法将items
与do
组成。
如果您不想依靠do
表示法,也可以在没有语法糖的情况下编写函数:
main :: IO ()
main = fmap totalLoss ioItems >>= print
main
的这两个变体是等效的。
答案 1 :(得分:2)
将totalLoss
的类型更改为[Item] -> Float
,然后使用fmap
将函数应用于类型为IO [Item]
的值:
totalLoss :: [Item] -> Float
totalLoss items = sum $ map loss items
loss :: Item -> Float
loss x = (price x) * fromIntegral (stock x)
-- fmap totalLoss :: Functor f => f [Item] -> f Float
-- so if you have a value of v :: IO [Item], then
-- fmap totalLoss v :: IO Float