UProLa

Неокріпші думки

Option type

leave a comment »

Нехай ми маємо функцію, котра робить корисне перетворення. Проте не будь-яка комбінація вхідних параметрів є коректною в контексті призначення функції. Який інтерфейс потрібно надати програмісту, щоб він зміг правильно використати результати роботи функції?

Простий і “життєвий” приклад: перетворити рядок в число. Не будь-який рядок можна перетворити в число, а описати усі причини, по яким це не вдалося зробити, може бути дуже складно. Тому потрібно розрізняти два стани: 1) перетворення вдалося і ми маємо результат, 2) перетворення не вдалося, відповідно результат нам не важливий.

1. Exception

Функція викидує виключення, якщо перетворення рядка в число не вдається зробити. Відповідно, обробка результату приймає наступний вигляд:

string input = ...;
try
{
    var result = int.Parse(input);
    // ... оперуємо результатом
}
catch (IntFromStringParseException)
{
    // обробка відсутності результату
}

2. Паттерн TryParse

У цьому випадку результат роботи функції є булевим признаком вдалого перетворення. Сам де результат передається по посилання як один з аргументів функції.

string input = ...;
int result;
if (int.TryParse(input, out result))
{
    // ... оперуємо результатом
}
else
{
    // обробка відсутності результату
}

3. Модифікований TryParse – тапл на виході

Приведу на цей раз код у стилі Python, бо на C# буде сильно багатослівно

isCorrect, result = int.TryParse(input)
if isCorrect:
    # ... оперуємо результатом
else:
    # обробка відсутності результату

4. Розширення типу результату

Замість введення нової змінної, можна розширити тип результату ще одним унікальним значенням – НІЩО (і способом отримання першопочаткового типу з даного)

int? preResult = int.TryParse(input);
if (preResult != null)
{
    var result = (int)preResult;
    // ... оперуємо результатом
}
else
{
    // обробка відсутності результату
}

Приведені вище прийоми повинні бути добре знайомими сучасним програмістам. Я ж приведу ще один спосіб, який застосовується в ML як option type, а в Haskell – як Maybe.

5. Option type

Даний спосіб узагальнює варіант 4 на усі можливі типи (в C# є обмеження – символ ? не може бути застосований для більшості класів). Синтаксис #light F#

match int.TryParse(input) with
| Some result ->
    // ... оперуємо результатом
| None ->
    // обробка відсутності результату

Як видно, рішення настільки ж коротке, як і варіант 3 на пітоні, проте краще за рахунок двох особливостей:

  • немає змінної isCorrect
  • коли функція не змогла виконатись, то у нас немає результату

    Проте є ще одна важлива перевага даного методу. ”’ Відсутність “обробки відсутності результату” може бути виявлена компілятором на етапі компіляції ”’. Це є наслідок продуманої системи типів. В попередніх 4 варіантах для даної ситуації потрібно застосовувати евристики (наприклад, ReSharper робить деякі).

    Звісно, це не є універсальним вирішенням проблеми NullReferenceException, проте це є хорошим способом вираження своїх думок. Пам’ятайте,

  • перший варіант хороший, але застосовний далеко не завжди
  • другий варіант хороший, але передача результату по посиланню, а не як “результуючого значення функції” є незручною технікою
  • ідею “або є значення або нема” без лишніх слів виражає тільки 5-ий варіант

    Дана стаття – вільний переклад аналогічної Брайана Макнамара.

  • Written by danbst

    Вересень 23, 2011 at 23:04

    Залишити відповідь

    Заповніть поля нижче або авторизуйтесь клікнувши по іконці

    Лого WordPress.com

    Ви коментуєте, використовуючи свій обліковий запис WordPress.com. Log Out / Змінити )

    Twitter picture

    Ви коментуєте, використовуючи свій обліковий запис Twitter. Log Out / Змінити )

    Facebook photo

    Ви коментуєте, використовуючи свій обліковий запис Facebook. Log Out / Змінити )

    Google+ photo

    Ви коментуєте, використовуючи свій обліковий запис Google+. Log Out / Змінити )

    З’єднання з %s

    %d блогерам подобається це: