理解 Haskell (<-) 语法糖

Understanding Haskell (<-) syntactic sugar

我正在尝试使用 Cassava 解析 CSV 文件。我想要一个函数,如果解析不成功,returns Nothing,否则 Just (V.Vector (String, String, String))

我正在使用以下代码:

  {-# LANGUAGE ScopedTypeVariables #-}
module Lib
    ( someFunc
    ) where

import qualified Data.ByteString.Lazy as BL
import Data.Csv
import qualified Data.Vector as V

type Dataset = (String, String, String)
someFunc :: Maybe (V.Vector Dataset)
someFunc = do
    csvData <- BL.readFile "TAEE3.SA.csv"
    case decode HasHeader csvData :: Either String (V.Vector (String, String, String)) of
      Left a -> Nothing
      Right v -> Just v

错误是:

  • Couldn't match type ‘IO’ with ‘Maybe’
      Expected type: Maybe BL.ByteString
        Actual type: IO BL.ByteString
    • In a stmt of a 'do' block: csvData <- BL.readFile "TAEE3.SA.csv"
      In the expression:
        do csvData <- BL.readFile "TAEE3.SA.csv"
           case  decode HasHeader csvData ::
                   Either String (V.Vector (String, String, String))
           of
             Left a -> Nothing
             Right v -> Just v
      In an equation for ‘someFunc’:
          someFunc
            = do csvData <- BL.readFile "TAEE3.SA.csv"
                 case  decode HasHeader csvData ::
                         Either String (V.Vector (String, String, String))
                 of
                   Left a -> Nothing
                   Right v -> Just v
   |
14 |     csvData <- BL.readFile "TAEE3.SA.csv"
   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^

好像<-功能完全没有用。难道不应该 return 一个 a in IO a monad 吗?

虽然 <- 确实在 IO a 中为您提供了 a,但它并不是通过将其从 IO monad 中取出来实现的。一般来说,不可能从 monad 中取出一个值。它实际上所做的是将 do 块的其余部分也放入 monad 中。为了解决这个问题,您需要使函数 return 的结果在 IO 中,然后添加一个 return 以将最终的 Maybe 包装回 IO :

  {-# LANGUAGE ScopedTypeVariables #-}
module Lib
    ( someFunc
    ) where

import qualified Data.ByteString.Lazy as BL
import Data.Csv
import qualified Data.Vector as V

type Dataset = (String, String, String)
someFunc :: IO (Maybe (V.Vector Dataset))
someFunc = do
    csvData <- BL.readFile "TAEE3.SA.csv"
    return $ case decode HasHeader csvData :: Either String (V.Vector (String, String, String)) of
      Left a -> Nothing
      Right v -> Just v