看看下面的函数:readCSVFile :: :: (MonadResource m, CSV ByteString a) => CSVSettings -> FilePath -> m [a]
调用起来相对简单,因为我们只需要一个CSVSettings,例如defCSVSettings,和一个FilePath(又名String),"file.csv" 或其他东西。
因此,在调用之后,我们得到(MonadResource m, CSV ByteString a)。我们可以一次解决这个问题,以找出合适的类型。我们在这个操作中执行IO,所以对于MonadResource m,m 应该只是ResourceT IO,这恰好是MonadBaseControl IO 的一个实例,正如runResourceT 所要求的那样。这是conduit 特定的事情。
对于CSV ByteString a,我们需要找到CSV的哪些实例。为此,请转到http://hackage.haskell.org/packages/archive/csv-conduit/0.2.1.1/doc/html/Data-CSV-Conduit.html#t:CSV(在我看来,该包的文档有点令人讨厌地全部塞进类型类中......)并单击实例以查看我们拥有CSV ByteString a 形式的可用实例。这两个选项是CSV ByteString ByteString 和CSV ByteString Text。
在这两个中,Text 更可取,因为它处理 unicode,而 CSV 不太可能包含二进制数据。 ByteString 或多或少类似于[Word8],而Text 更类似于[Char],这可能是您想要的。因此,a 应该是 Text(尽管 ByteString 仍然可以使用)。
这意味着函数调用的结果是ResourceT IO [Row Text]。我们对此无能为力,但因为ResourceT 是一个monad 转换器,我们可以使用runResourceT 函数轻松“弹出” monad 转换层。因此,
readFile :: FilePath -> IO [Row Text]
readFile = runResourceT . readCSVFile defCSVSettings
这很容易在 main 中使用以获取 [Row Text],然后您可以使用 map 或 fold 进行迭代以获取各个行。
要在 GHCI 中运行这类事情,您绝对必须明确指出类型。原因是结果类实例不依赖于任何参数;因此,对于CSVSettings 和FilePath 的任何集合,readCSVFile 可以返回任意数量的不同类型,只要它们m 是MonadResource m 的一个实例并且a 是CSV ByteString a 的一个实例.因此,我们必须向 GHCi 明确指出您想要哪种类型。