С обработкой ошибок в Rust надо явно что-то придумывать. Мне категорически нравится, что в языке нет исключений, но без синтаксического сахара вроде хаскелевской конструкции do код с аккуратной обработкой ошибок визуально превращается в простыню с перекладыванием значений из одних типов в другие
(
Read more... )
Comments 27
Там не то чтобы этого не любят, там это можно делать слишком многими способами.
> непонятно как правильно реализовать сахар типа do
Он не нужен. Т.е. с ним конечно проще, но в цацкеле зачастую удобнее использовать операторы из class Monad напрямую. А конкретно для парсеров можно попробовать приспособить аппликативный интерфейс (Control.Applicative) - его, зачастую, достаточно, и вот его -то по идее должно быть возможно сделать. Тем более, что TCO в расте нет, а значит в монадическом коде можно легко попасть на растущий стек.
Reply
Насчёт монадического кода - если конвертировать по Чёрчу, то переполнения стека не будет.
Reply
Но в лоб всё равно не срастается: в хаскеле оно красиво сработает, потому что егонный список - это функтор. А в Rust используется механизм итераторов (в примере выше split возвращает итератор подстрок), который, вроде бы, по-сути тоже ленивый список, только в профиль, но при этом не функтор. Почему так - я не готов теоретизировать: подозреваю, что не может быть выполнено правило "fmap id = id", в том числе, потому что итератор может быть мутабельный.
Так что надо придумывать какие-то пути обхода.
Reply
Другое дело, что в этом случае скорее всего придется выкинуть getopts и писать разбор параметров ручками, но это может и к лучшему.
Reply
Например, ASSIGN_OR_RETURN(auto foo, GetFoo()), или RETURN_IF_ERROR(DoBar()). И коротко, и понятно, где может произойти ошибка и как она и куда передается. Как я понял, это примерно то, что делает try!. Можешь привести пример, когда читаемо написать не получается, а с настоящей монадой бы получилось?
Reply
Это всё, что я могу сказать. ;)
Reply
Это хорошо работает, но только в случае линейного кода. Например, вместо такого:
for value in values.split(',') {
match value.parse() {
Ok(v) =>
result.push(v),
Err(e) =>
return Err(ParamError::SampleValue(value.to_owned(), e)),
}
}
После некоторой подготовки можно написать так:
for value in values.split(',') {
result.push(try!(value.parse()))
}
Но try! теряет полезность в замыканиях, потому что возвращает управление только из анонимной функции, а не из родительской (в КЛ, например, для этого есть return-from, которым можно вернуть управление из любого уровня вложенности).
Например, вот так можно собрать в массив длины всех строк, поданных на stdin:
let stdin = io::stdin();
let all_lines: Vec = stdin ( ... )
Reply
Reply
wtf = do
foo <- GetFoo
doSomethingWith foo
довольно читабельно, хотя и вырожденный случай.
Reply
Не очень красиво, конечно - но если оценивать по критерию "количество строк", то получается столько же; по критерию "понятно ли, что именно происходит", то может быть даже и лучше (по "<-" не всегда понятно, какая монада имеется в виду); по критерию "сколько ошибок происходит из-за неправильного понимания такого или похожего кода" - не могу припомнить ни одной ни в кодревью, ни в продакшне...
Reply
Reply
Reply
Поэтому всё более-менее просто, даже do не надо. Разбор строки даёт Either (parse :: String -> Either String a), наличие/отсутствие параметра Maybe. Поэтому разбор комбинируется просто: fmap parseVec64f как раз будет иметь тип Maybe String -> Maybe (Either String Vec64f).
Reply
Reply
Кстати, вот ты сходу предлагаешь все возможные ошибки унифицировать в тип String - если я правильно понимаю, это просто сообщение об ошибке.
А как быть, если внутри parseVec64f используется функция parse64f, тип ошибки у которой другой - например, перечисление вроде InvalidSymbol Char | Overflow | EmptyString. И, например, используется какая-то другая функция с другим типом ошибки.
Как это всё аккуратно принести наружу из parseVec64f? Например, мне в логике выше нужно делать разные действия, в зависимости от разных типов ошибок?
Reply
Выражение fmap parse не изменится, если ты изменишь тип ошибки.
Reply
Reply
Reply
Leave a comment