Вы можете заменить строку:
doesDirectoryExist a >>= \case
с:
((&&) <$> (pure $ not $ null a) <*> doesDirectoryExist a) >>= \case
Это называется «аппликативный стиль» (если вы его не знали ;-)); Я объединяю две операции ввода-вывода, которые возвращают логическое значение, используя оператор AND (&&).
Выражение (pure $ not $ null a) проверяет, что строка не пустая, и использует «pure» для переноса чистой операции в IO (поэтому мы можем объединить ее с doDirectoryExist в выражение, которое проверяет оба условия).
EDIT:
Как заметил Джозеф, вышеприведенное выполняет doDirectoryExist независимо от того, нужно оно или нет; вы обычно хотите этого избежать (если только вы не полагаетесь на его побочные эффекты); вы можете избежать этого с предложением Брэдра; Вы также можете сделать это:
(if null a then pure False else doesDirectoryExist a) >>= \case
В качестве альтернативы, поскольку вам не нравится нотация if-then-else (как и я):
(case null a of True -> pure False; False -> doesDirectoryExist a) >>= \case
Не совсем чище, хотя; Вы можете импортировать Data.Bool и сделать это:
-- import Data.Bool
(bool (doesDirectoryExist a) (pure False) $ null a) >>= \case
("bool" - просто более функциональная запись для старого доброго if-then-else)