Судьба завела меня (программиста практика, в основном использующего C#) на проект, в котором основной функционал разрабатывается на Go.
Изучая Go обратил внимание на непривычную практику обработки ошибок. Почитав разъяснения в статьях Ошибки — это значения и в Почему «ошибки это значения» в Go отметил, что предлагаемые там решения заставляют вспомнить одну особенность Visual Basic, которую очень не лестно комментировали программисты.
Суть в следующем. Существуют жалобы программистов про проверку ошибок в Go. Возьмём пример из статьи Ошибки — это значения:
Мы видим, что присутствует повторяющий код, связанный с обработкой ошибок, который и вызывает нарекания. Вроде бы, почему бы не использовать стандартную практику try-catch и ошибка будет обрабатываться в одном месте?
Далее в статье Ошибки — это значения предлагается решение:
Здесь мы видим, что обработка ошибки теперь происходит один раз. Вроде бы, код упростился.
И тут, у меня возникло ощущение, что что-то подобное в истории было. И было это в Visual Basic и связано это было с оператором If. Буду говорить о Visual Basic в прошедшем времени, так как давно не работал с ним.
В Visual Basic присутствовал оператор If и в отличии от C++ и Java отсутствовал оператор ?:. Таким образом, записывался такой код:
Со стороны VB программистов были жалобы: много повторяющегося кода, хорошо бы его сократить до одной строки и иметь возможность использовать inline. Так, возможно, появился IIf:
Всё бы хорошо, но у народа начало нарастать другое возмущение, так как здесь оказался подвох. Дело в том, что помимо кода, когда в IIf используются значения, существует код, когда в место значений указываются функции и ожидается, что будет вызвана одна из функций в зависимости от условия, как в операторе If:
И для многих программистов было неожиданностью то, что в данном коде будут вызваны обе функции: GetTrueAValue и GetFalseAValue. Хотя они ожидали, что IIf будет работать, как оператор If (или как ?: в С++), т.е. они не понимали, какой смысл вызывать вторую функцию, когда её результирующее значение не имеет смысла.
Дело оказалось в том, что некоторые программисты по началу воспринимали IIf как оператор If, но на деле IIf оказался не оператором, а функцией. А чтобы вызвать функцию требуется вычислить все её аргумент, т.е. потребуется вызвать все указанные функции в аргументах. Причем, IIf был так удобно подпихнут, что не все программисты улавливали сразу, что это не оператор, а обычная функция.
Суть оператор If в Visual Basic (да и в других языках) определять участки кода, которые должны выполняться в зависимости от условия. Попытка использовать IIf дало лишь поверхностное решение и не оправдало основного ожидания от неё – вызывать ту или иную функцию в зависимости от условия.
Так вот. Вернёмся к Go и к практике, которую предлагают для работы с ошибками. У меня, сложилось такое впечатление, что выше приведённая практика обработки ошибок очень напоминает IIf в Visual Basic. Расширим пример:
Соответственно, здесь возникает вопрос: если в строке ew.write(getAB()) возникает ошибка, то почему выполняются getCD() и getEF(), когда в их результирующих значениях смысла нет?
Почему возникает такой вопрос? Дело в том, что ошибка — это значение, которое определяет, какой код должен выполняться. И это четко видно в первоначальном способе обработки ошибок в Go.
Значение ошибки обрабатывается оператором if. И обработка значения ошибки в if как раз и определяет, какой код должен быть выполнен дальше. При чём в большинстве случаев для принятия решения, важно наличие ошибки, а не её содержания.
По моему мнению, ty-catch-finally как раз и занимаются тем, чем нужно, определяют код, который будет выполняться в зависимости от возникшего исключения.
P.S.: Тут вспомнилось из VB:
Но лучше бы не вспоминалось. Чур меня, чур.
Изучая Go обратил внимание на непривычную практику обработки ошибок. Почитав разъяснения в статьях Ошибки — это значения и в Почему «ошибки это значения» в Go отметил, что предлагаемые там решения заставляют вспомнить одну особенность Visual Basic, которую очень не лестно комментировали программисты.
Суть в следующем. Существуют жалобы программистов про проверку ошибок в Go. Возьмём пример из статьи Ошибки — это значения:
_, err = fd.Write(p0[a:b])
if err != nil {
return err
}
_, err = fd.Write(p1[c:d])
if err != nil {
return err
}
_, err = fd.Write(p2[e:f])
if err != nil {
return err
}
// and so on
Мы видим, что присутствует повторяющий код, связанный с обработкой ошибок, который и вызывает нарекания. Вроде бы, почему бы не использовать стандартную практику try-catch и ошибка будет обрабатываться в одном месте?
Далее в статье Ошибки — это значения предлагается решение:
func (ew *errWriter) write(buf []byte) {
if ew.err != nil {
return
}
_, ew.err = ew.w.Write(buf)
}
w := &errWriter{w: fd}
ew.write(p0[a:b])
ew.write(p1[c:d])
ew.write(p2[e:f])
// and so on
if ew.err != nil {
return ew.err
}
Здесь мы видим, что обработка ошибки теперь происходит один раз. Вроде бы, код упростился.
И тут, у меня возникло ощущение, что что-то подобное в истории было. И было это в Visual Basic и связано это было с оператором If. Буду говорить о Visual Basic в прошедшем времени, так как давно не работал с ним.
В Visual Basic присутствовал оператор If и в отличии от C++ и Java отсутствовал оператор ?:. Таким образом, записывался такой код:
Dim a As Integer
If CheckState() Then
a = 12
Else
a = 13
End If
Со стороны VB программистов были жалобы: много повторяющегося кода, хорошо бы его сократить до одной строки и иметь возможность использовать inline. Так, возможно, появился IIf:
a = IIf(CheckState(), 12, 13)
Всё бы хорошо, но у народа начало нарастать другое возмущение, так как здесь оказался подвох. Дело в том, что помимо кода, когда в IIf используются значения, существует код, когда в место значений указываются функции и ожидается, что будет вызвана одна из функций в зависимости от условия, как в операторе If:
a = IIf(CheckState(), GetTrueAValue(), GetFalseAValue())
И для многих программистов было неожиданностью то, что в данном коде будут вызваны обе функции: GetTrueAValue и GetFalseAValue. Хотя они ожидали, что IIf будет работать, как оператор If (или как ?: в С++), т.е. они не понимали, какой смысл вызывать вторую функцию, когда её результирующее значение не имеет смысла.
Дело оказалось в том, что некоторые программисты по началу воспринимали IIf как оператор If, но на деле IIf оказался не оператором, а функцией. А чтобы вызвать функцию требуется вычислить все её аргумент, т.е. потребуется вызвать все указанные функции в аргументах. Причем, IIf был так удобно подпихнут, что не все программисты улавливали сразу, что это не оператор, а обычная функция.
Суть оператор If в Visual Basic (да и в других языках) определять участки кода, которые должны выполняться в зависимости от условия. Попытка использовать IIf дало лишь поверхностное решение и не оправдало основного ожидания от неё – вызывать ту или иную функцию в зависимости от условия.
Так вот. Вернёмся к Go и к практике, которую предлагают для работы с ошибками. У меня, сложилось такое впечатление, что выше приведённая практика обработки ошибок очень напоминает IIf в Visual Basic. Расширим пример:
w := &errWriter{w: fd}
ew.write(getAB())
ew.write(getCD())
ew.write(getEF())
// and so on
if ew.err != nil {
return ew.err
}
Соответственно, здесь возникает вопрос: если в строке ew.write(getAB()) возникает ошибка, то почему выполняются getCD() и getEF(), когда в их результирующих значениях смысла нет?
Почему возникает такой вопрос? Дело в том, что ошибка — это значение, которое определяет, какой код должен выполняться. И это четко видно в первоначальном способе обработки ошибок в Go.
_, err = fd.Write(getAB())
if err != nil {
return err
}
_, err = fd.Write(getCD())
if err != nil {
return err
}
_, err = fd.Write(getEF())
if err != nil {
return err
}
Значение ошибки обрабатывается оператором if. И обработка значения ошибки в if как раз и определяет, какой код должен быть выполнен дальше. При чём в большинстве случаев для принятия решения, важно наличие ошибки, а не её содержания.
По моему мнению, ty-catch-finally как раз и занимаются тем, чем нужно, определяют код, который будет выполняться в зависимости от возникшего исключения.
P.S.: Тут вспомнилось из VB:
On Error Resume Next
Но лучше бы не вспоминалось. Чур меня, чур.