Home > database >  How to pattern match for a True/False within a function in Haskell?
How to pattern match for a True/False within a function in Haskell?

Time:10-27

I am trying to handle file errors manually so I can print my own message and currently I have this code:

handleFileError :: FileError -> IO a
handleFileError (FileError errorKind) = do
  case errorKind of
    NotFound -> undefined
    NoPermission -> undefined
    IsDirectory -> undefined

fileRead :: String -> IO String
fileRead file = do
  pathExists <- doesPathExist file
  notDirectory <- doesFileExist file

  -- These two must be handled before `System.Directory.getPermissions` is called
  -- or else it will error.

  permissions <- getPermissions file
  let hasReadPermissions = readable permissions

  if hasReadPermissions then undefined -- This is the success case
  else handleFileError $ FileError NoPermissions

I would like to check if any of the 3 booleans (pathExists, notDirectory, and hasReadPermissions) are false, and then act accordingly. I tried to implement this using a case with False, however this just always runs the first branch.

CodePudding user response:

I have solved this - I didnt realise that it was possible to nest if statements in a way that functions like else-if in other languages:

if not (pathExists) then handleFileError $ FileError NotFound
  else if not (notDirectory) then handleFileError $ FileError IsDirectory
  else do
    permissions <- getPermissions file
    let hasReadPermissions = readable permissions

    if hasReadPermissions then undefined -- success
    else handleFileERror $ FileError NoPermissions

CodePudding user response:

One way would be to use MultiWayIf:

do
  pathExists <- doesPathExist file
  notDirectory <- doesFileExist file
  permissions <- unsafeInterleaveIO (getPermissions file)
  if
    | not pathExists -> -- ...
    | not notDirectory -> -- ...
    | not permissions -> -- ...
    | otherwise -> -- ...

If you're allergic to extensions, the old-fashioned way to get this feature is using guards, as in:

  case () of
    _ | not pathExists -> -- ...
      | not notDirectory -> -- ...
      | not permissions -> -- ...
      | otherwise -> -- ...

But I recommend neither of these. Instead, just do something with the file, and catch the exception; otherwise there are race conditions with the file system changing out from under you between the check and the file use. Like this:

fileRead :: String -> IO String
fileRead = catch (undefined {- success case -}) $ \e -> if
  | isDoesNotExistError e -> -- ...
  | isPermissionError e -> -- ...
  | otherwise -> throw e
  • Related