Как функции, типы и парадигмы помогают описывать программу?

Язык программирования задаёт средства записи алгоритма и модель выполнения, а функция выделяет именованное преобразование с контрактом входа и результата. Разные языки предлагают различные системы типов и парадигмы, но качество решения определяется тем, насколько явно выражены данные, зависимости и границы ответственности.

Контракт функции

Предусловие описывает допустимые аргументы, постусловие — результат и изменённое состояние. Чистая функция не меняет внешние данные и при одинаковом входе возвращает одинаковый результат, поэтому её проще тестировать. Побочный эффект допустим, если он является частью явно названной задачи.

Передача данных

Параметр может передавать значение, ссылку или объект с собственными правилами изменяемости. Нужно понимать семантику выбранного языка: изменение содержимого объекта и переназначение локальной переменной — не одно действие. Неясная передача владения рождает неожиданные связи между модулями.

Система типов

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

Парадигмы

Процедурный стиль организует шаги и состояние, объектный объединяет данные с поведением, функциональный подчёркивает композицию и неизменяемость, декларативный описывает требуемое свойство результата. Реальные языки часто многопарадигменны, поэтому задаче важнее выбранная модель, чем ярлык языка.

Второй язык

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

Контракт делает функцию переносимой между реализациями

Функция бинарного поиска требует отсортированную последовательность и возвращает индекс первого подходящего элемента либо специальное отсутствие. Аннотации типов описывают форму данных, но не проверяют сортировку; это семантическое предусловие остаётся в документации и тестах.

def lower_bound(values: list[int], target: int) -> int:
    """Return the first index whose value is not less than target."""
    left, right = 0, len(values)
    while left < right:
        middle = (left + right) // 2
        if values[middle] < target:
            left = middle + 1
        else:
            right = middle
    return left

assert lower_bound([1, 3, 3, 8], 3) == 1

Функция чистая: не меняет вход и зависит только от аргументов. Тот же контракт можно реализовать на другом языке, даже если синтаксис, модель чисел и коллекции отличаются.

Практика: один контракт на двух языках

Запишите предусловие, постусловие и граничные случаи lower_bound, затем перенесите алгоритм на второй знакомый язык. Сравните целочисленное деление, диапазоны индексов и способ выразить отсутствие результата. Добавьте property-тест: индекс находится от 0 до n, все элементы слева меньше target, а элемент по индексу при наличии не меньше target. Объясните, какие свойства проверяет типизация, а какие остаются логикой программы.

Источники