Ранние возвраты могут быть полезны в языках, основанных на операторах, таких как Java или C #, для реструктуризации кода и устранения вложенности.

При первом использовании F # одной из проблем, с которыми я боролся, было отсутствие ранних возвратов. Концепция раннего возврата не имеет смысла в языке, основанном на выражениях, где мы составляем значения вместе для получения новых значений, а не упорядочиваем утверждения. Что же тогда делать с вложенностью в F #?

Классическое решение: привязка и вычислительные выражения

Вот код, который просматривает две вещи, а затем объединяет их, если они оба имеют значение:

Один уровень вложенности - это неплохо, но вы можете видеть, как это может быстро выйти из-под контроля.

Этот шаблон, при котором мы замыкаем короткое замыкание при сбое или продолжаем успешно работать, достаточно распространен, чтобы мы могли использовать Option.bind для его инкапсуляции:

Возможно, это немного лучше, поскольку было удалено утомительное сопоставление None -> None, но оно все еще вложено.

Мы можем использовать вычислительные выражения, чтобы исключить вложенность:

Проницательные читатели заметят, что здесь мы также можем использовать Option.map2 для устранения вложенности, не прибегая к выражению вычисления. Дело в том, что существует множество существующей литературы о том, как работать с таким кодом, когда вы замыкаетесь при неудаче и продолжаете добиваться успеха.

В глуши

Однако иногда вы можете захотеть прервать короткое замыкание в случае успеха и продолжить неудачу. Представьте, например, что вы разрешаете пользователю выполнить действие над сообщением в блоге. Вы можете сначала проверить, является ли он суперпользователем, а если нет, вы можете проверить, является ли он автором сообщения или, по крайней мере, соавтором, прежде чем в конечном итоге сообщить, что они не авторизованы.

Это работает, но было бы неплохо устранить некоторую вложенность. В связи с этим было бы неплохо сделать проверки как можно более независимыми и компонуемыми, чтобы упростить сопровождение кода. Мы можем начать с написания каждой проверки в отдельной функции следующим образом:

Большой. Теперь давайте объединим проверки в последовательность:

Теперь нам просто нужен способ объединить эти результаты. В этом случае мы хотим получить первый успешный результат и, если не найдем, вернуть NotAuthorized. Давайте напишем комбинированную функцию, которая выражает это:

Теперь мы можем передать нашу последовательность в combineResults, чтобы получить общий результат:

Самое замечательное в том, чтобы сделать это с seq, состоит в том, что он будет генерировать значения только по мере необходимости, потенциально экономя вам некоторые поиски в базе данных или дорогостоящие вычисления, если предыдущая проверка завершилась успешно.

Если ничего не помогает, примите вложение

Наконец, одно предостережение. Устранение гнездования - не самоцель. Вы должны использовать методы, подобные перечисленным выше, когда они помогают с удобочитаемостью и обслуживанием кода. Несколько совпадений вложенных шаблонов - это нормально, и они могут быть самыми простыми и удобными для новичков.

Выражения и статическая типизация здесь ваши друзья: компилятор обеспечит совпадение всех ваших типов.

Этот пост является частью Адвент-календаря F # 2018