List 将函数应用于自定义数据结构的列表
根据我先前的问题: 我不想将函数应用于自定义数据结构List 将函数应用于自定义数据结构的列表,list,haskell,List,Haskell,根据我先前的问题: 我不想将函数应用于自定义数据结构项的列表。问题是我仍然不知道如何正确处理IO,因此下面是我的代码: data Item = Item { name :: String , expire :: Day , stock :: Integer , price :: Float } deriving (Show) totalLoss :: IO [Item] -> Float totalLoss items = sum $ map lo
项的列表
。问题是我仍然不知道如何正确处理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
| ^^^^^
我到底该如何解决这个问题呢?
totaloss
是一个纯函数,因此相应地更改其类型:
totalLoss :: [Item] -> Float
totalLoss items = sum $ map loss items
这个问题的根本问题似乎是,物品清单源自不纯净的来源(这是一件完全自然的事情)
编写尽可能多的纯函数代码,然后在main
函数中(或尽可能接近)使用不纯输入编写这些纯函数:
main :: IO ()
main = do
items <- ioItems -- :: IO [Item]
print $ totalLoss items
main
的这两种变体是等效的。将totaloss
的类型更改为[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
没有(合理的)方法将类型
IO a
作为输入并输出非IO
类型。您的totaloss
不涉及IO
但是-它只需要一个项的列表
s并输出一个Float
,因此给它一个适当的类型签名,它应该可以正常工作。(作为一般规则,你永远不应该使用IO
,除非你必须这样做——也就是说,当你必须与程序之外的世界进行交互时。)嗯,我必须使用IO,因为时间计算和加载CSV。我得到的Item
s列表是IO[Item],因此没有办法绕过它……我想你误解了。作为一个整体,您的程序可能需要使用IO来获取应用此函数的数据。但函数本身应该尽可能保持“纯”(即不涉及IO)。正如我所说,您已经在totaloss
中编写了一个类型为[Item]->Float
的非常好的“pure”函数,编译器不喜欢它的唯一原因是因为您给了它错误的类型签名。在代码的另一部分中,您可以使用>=
运算符或do
块从IO操作获取项的列表,并将其提供给此函数。
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