2016-08-15 1 views
2

Я пересмотрел Haskell lateley и создал парсер/интерпретатор языка программирования игрушек. Использование Parsec для лексинга и синтаксического анализа и отдельный интерпретатор. Я сталкиваюсь с некоторыми проблемами с подачей результата от анализатора на мой интерпретатор и обработкой потенциальной ошибки как с интерпретатором, так и с парсером. Я получаю что-то вроде этого:Haskell: Обработка в результате Либо из вычислений

main = do 
    fname <- getArgs 
    input <- readFile (head fname) 
    case lparse (head fname) input of 
    Left msg -> putStrLn $ show msg 
    Right p -> case intrp p of 
     Left msg -> putStrLn $ show msg 
     Right r -> putStrLn $ show r 

Это не выглядит совсем неважно. Моя проблема заключается в том, что lparse возвращает Either ParseError [(String, Stmt)] и itrp возвращает тип Either ItrpError Stmt, так что я имею реальное трудное время кормления в Right результат от парсера к переводчику и в то же время под залог и печать возможный ParseError или IntrpError.

Ближайший к тому, что я хочу что-то вроде этого

main = do 
    fname <- getArgs 
    input <- readFile (head fname) 
    let prog = lparse (head fname) input 
    (putStrLn . show) (intrp <$> prog) 

Но это не удивительно выход вложенной Либо и не печатать довольно либо.

Итак, есть ли какой-нибудь хороший идеологический способ Haskell для выполнения этих потоков из одного вычисления в другой и обработки ошибок (Lefts) красивым способом без случаев гнездования?

Редактировать

добавляющие типы lparse и itrp

lparse :: Text.Parsec.Pos.SourceName -> String -> Either Text.Parsec.Error.ParseError [([Char], Stmt)] 

intrp :: [([Char], Stmt)] -> Either IntrpError Stmt 

ответ

3

Пока не совершенны, я бы создать вспомогательную функцию для встраивания любого Show способной ошибки из Either в MonadError:

{-# LANGUAGE FlexibleContexts #-} 
import Control.Monad.Except 

strErr :: (MonadError String m, Show e) => Either e a -> m a 
strErr = either (throwError . show) return 

Тогда, если у вас есть вычисление, которое может завершиться с ошибками, как

someFn :: ExceptT String IO() 
someFn = strErr (Left 42) 

вы можете запустить его (ошибки печати в стандартный вывод) в

main :: IO() 
main = runExceptT someFn >>= either putStrLn return 

В вашем случае это было бы что-то вроде

main = either putStrLn return <=< runExceptT $ do 
    fname <- liftIO getArgs 
    input <- liftIO $ readFile (head fname) 
    prog <- strErr $ lparse (head fname) input 
    r <- strErr $ interp prog 
    print r 
2

Ну, если вы хотите цепи успешных вычислений, вы всегда можете использовать >>=, чтобы сделать это. Например, в вашем случае:

lparse (head fname) input >>= intrp 

И если вы хотите, чтобы распечатать или Ваше сообщение об ошибке вы можете использовать either класс, который принимает два обработчика функции, один для случая, когда у вас есть Left a (ошибка в вашем случае) и другой для Right b (в вашем случае успешная вещь). Пример:

either (putStrLn . show) (putStrLn . show) (lparse (head fname) input >>= intrp) 

И если что-то не удается в вашей цепочке (любой шаг вашей монадической цепи становится Left a) он останавливается и может, например, напечатать сообщение об ошибке в приведенном выше случае.

+1

Вы, вероятно, хотите использовать 'hPutStrLn stderr' вместо' putStrLn' в 'случае Left', так что сообщение об ошибке переходит к стандартной ошибке вместо стандартного вывода. – chepner

+0

@chepner, возможно. Я просто делаю то, что делает OP. Но да, лучше использовать 'hPutStrLn stderr' в случае сообщений об ошибках. – sham1

+1

Невозможно использовать '>> =' в этом случае, так как типы 'lparse' и' intrp' отличаются. т.е. '' lparse :: SourceName -> String -> Или ParseError [([Char], Stmt)] '' и '' intrp :: [([Char], Stmt)] -> либо IntrpError Stmt'' – Patrik

Смежные вопросы