开发者

Passing a function in for processing a CSV file: type error

开发者 https://www.devze.com 2023-04-03 05:21 出处:网络
Problem: Process a CSV file and test a condition on it. Current code simply prints instead of testing for the condition.

Problem: Process a CSV file and test a condition on it. Current code simply prints instead of testing for the condition.

Issue: Type inference fails. I do not follow why it fails.

Here's the code, less the import boilerplate.

--------------------------------------------------
has_empty_string :: [String] -> Bool
has_empty_string col =
  any null col

----------------------------------------
get_hashrow :: [[String]] -> [String]
get_hashrow sheet =
  -- looking at column 5
  map (\row -> row !! 5) sheet

------------------------------
process_lines :: (String -> b) -> Handle -> IO ()
process_lines func inh = do
    ineof <- hIsEOF inh
    if ineof
      then return ()
      else do inpStr <- hGetLine inh
              result <- func inpStr
              putStrLn $ show result
              process_lines func inh


------------------------------
process_lines_in_file :: (String -> b) -> FilePath -> IO ()
process_lines_in_file func filename =
  do inh <- openFile filename ReadMode
         process_lines func inh

----------------------------------------
test_csv_row :: String -> Bool
test_csv_row row =
  has_empty_string ( get_hashrow ( readCSV row))

----开发者_运维百科------------------------------------
main :: IO ()
main = do
  [filename] <- getArgs
  process_lines_in_file test_csv_row filename
  return ()

And here's the error:

 Couldn't match expected type `b' against inferred type `IO a'
      `b' is a rigid type variable bound by
          the type signature for `process_lines' at content-hash-p.hs:29:28
    In a stmt of a 'do' expression: result <- func inpStr
    In the expression:
        do { inpStr <- hGetLine inh;
             result <- func inpStr;
               putStrLn $ show result;
             process_lines func inh }
    In the expression:
        if ineof then
            return ()
        else
            do { inpStr <- hGetLine inh;
                 result <- func inpStr;
                   putStrLn $ show result;
                 .... }


(In the future, please include the import boilerplate.)

Type inference is not failing -- since you're not asking the compiler to do any type inference! However, type-checking is failing. Let's see why.

You claim process_lines :: (String -> b) -> Handle -> IO (). Experienced Haskeller's will already be shuddering at this type. Why? This type claims that its first argument can be any function at all which does something to a String. But this is an odd claim to make, since the return type of this function doesn't appear anywhere else in the type of process_lines -- meaning, we can call this function, but never use its result! Thanks to laziness, this means that the call will never actually happen.

So it's a weird type. Let's see if we can take the argument above and find out where it fails in your code; that should help point to the problem.

process_lines func inh = do
    ineof <- hIsEOF inh
    if ineof
      then return ()
      else do inpStr <- hGetLine inh
              result <- func inpStr -- HERE
              putStrLn $ show result
              process_lines func inh

Take a look at the line marked HERE. This is the only occurrence of func in our implementation. According to the argument above, we can never use the output of func, yet here we seem to be using the output of func. What type are we using it at? Well, we're using it at an IO {- something -} type, since it's in a do-block; furthermore, since we bind result and then call show result, the {- something -} must be some type that we can call show on -- that is, a member of the Show class. So the type of func is not as unrestricted as String -> b; it's the more restricted Show b => String -> IO b. A similar argument applies to process_lines_in_file, so that its updated type ought to be process_lines_in_file :: Show b => (String -> IO b) -> FilePath -> IO ().

(Indeed, if you leave off the type signatures, type inference will infer exactly these types for you.)

Now that process_lines_in_file demands a function that does IO, we can no longer pass test_csv_row as-is. You can choose either to call process_lines_in_file (return . test_csv_row) filename in main or to change the implementation of test_csv_row to call return (which does the trivial IO action: no input or output, just do a pure computation and pretend it did IO).

With these changes, the code compiles.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号