我 24 小时前开始学习 Haskell 语言。我开始明白了,但目前还不像Python那么简单。我已经尝试了很多并阅读了文档,但我没有取得进展。也就是说,我无法从 URL 读取 CSV 格式,我怀疑我的 ByteString 库有问题,有人知道吗?
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Conduit (simpleHttp)
import Data.Csv
import qualified Data.ByteString.Lazy as BL
import qualified Data.Vector as V
readCSV :: String -> IO (Either String (V.Vector BL.ByteString))
readCSV url = do
csvData <- simpleHttp url
return $ decode HasHeader csvData
main :: IO ()
main = do
let url = "https://earthquake.usgs.gov/fdsnws/event/1/query?format=csv&starttime=2023-12-30&endtime=2023-12-31&minlatitude=32&maxlatitude=42&minlongitude=-124&maxlongitude=-114&minmagnitude=3"
result <- readCSV url
case result of
Left err -> print err
Right csv -> print csv
编辑:
错误信息。
• Couldn't match expected type ‘BL.ByteString’
with actual type ‘bytestring-0.11.5.3:Data.ByteString.Lazy.Internal.ByteString’
NB: ‘BL.ByteString’
is defined in ‘Data.ByteString.Lazy.Internal’
in package ‘bytestring-0.12.0.2’
‘bytestring-0.11.5.3:Data.ByteString.Lazy.Internal.ByteString’
is defined in ‘Data.ByteString.Lazy.Internal’
in package ‘bytestring-0.11.5.3’
• In the second argument of ‘decode’, namely ‘csvData’
In the second argument of ‘($)’, namely ‘decode HasHeader csvData’
In a stmt of a 'do' block: return $ decode HasHeader csvData
|
11 | return $ decode HasHeader csvData
我无法重现您的错误,但您的
bytestring
版本之间似乎不匹配。解决方案可能是放宽您的 cabal
文件中的限制。
但是,即使我们修复了该特定错误,您的代码也将无法工作,因此让我们解决实际问题。
在类型签名
readCSV :: String -> IO (Either String (V.Vector BL.ByteString))
中,Either
的第二个参数(在我们的例子中为 V.Vector BL.ByteString
)将告诉我们 decode
尝试将数据解码为哪种数据类型。因此,我们试图告诉它将文件的每一行解码为字节串。这实际上没有意义,因为 CSV 包含多行记录,但我们告诉它将记录的每个字段合并为一个。
相反,我们真正想要的是将文件解码为记录向量。事实上,
cassava
中有一个类型别名可以帮助解决这个问题:Record
!如果我们将类型签名更改为 readCSV :: String -> IO (Either String (V.Vector Record))
,我们的代码就可以工作了!现在,如果您运行代码,它将打印数据中字段列表的列表。
这可能还不太令我们满意,因为所有字段现在都是字节串,选择特定字段的唯一方法是用整数对其进行索引。我想说不是很方便或类型安全。
相反,我们可能希望有自己的类型来表示数据,例如(为了简洁而缩写):
data EarthQuake = EarthQuake
{ time :: String
, latitude :: Double
, longitude :: Double
}
但是,当然
decode
无法神奇地知道如何使用它,所以我们不能只是将它放入类型签名中并完成它。我们将使该类型成为类型类的实例 FromRecord
:
instance FromRecord EarthQuake where
parseRecord v =
EarthQuake
<$> v .! 0
<*> v .! 1
<*> v .! 2
这个实例将告诉
decode
函数如何将记录转换为我们自己的数据类型。现在我们已经完成了,我们可以再次修改我们的类型签名:readCSV :: String -> IO (Either String (V.Vector EarthQuake))
。方便多了。