VII · корректность ← Курс
Глава VII · дуга ФП и корректности

Математически корректные программы.

«Запустилось без ошибок» — не значит «верно». Верно — значит соответствует спецификации, и это можно доказать. Здесь математическая логика входит в код напрямую: по соответствию Карри-Ховарда тип — это утверждение, а программа — его доказательство. Дальше — тотальность, инварианты и спектр способов получить уверенность.

тип ≡ теоремаКарри-Ховард
totalвсюду определена
⊢доказуемость
PROBARE тип ⊢ программа propositions as types Q.E.D. тип T= утверждение программа= доказательство ⊢ проверка типов — это проверка доказательства
№ 00Что значит «верно»

Корректность — это соответствие спецификации

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

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

Математика — царица наук. — К. Ф. Гаусс
⊢
Соответствие Карри-Ховарда

Типы — это теоремы, программы — доказательства

Глубинная связь логики и программирования: каждое логическое утверждение соответствует типу, а доказательство утверждения — программе этого типа. Написать программу нужного типа — значит доказать соответствующую теорему.

Соответствие точное. Функция $A\to B$ — это импликация: имея $A$, получаем $B$. Произведение — конъюнкция, сумма — дизъюнкция, единичный тип — истина, пустой — ложь:

$A\to B \;\equiv\; A\Rightarrow B,\qquad A\times B \;\equiv\; A\wedge B,\qquad A + B \;\equiv\; A\vee B$

CURRY-HOWARDтип ↔ логика propositions as typesproofs as programs ТИП ЛОГИКА A → BA ⇒ B A × BA ∧ B A + BA ∨ B 1 (Unit)⊤ истина 0 (Void)⊥ ложь
Логика и типы — две записи одного. Поэтому проверка типов компилятором — это проверка доказательства.

Практический смысл огромен: система типов — это лёгкий доказатель. Если программа прошла проверку типов, часть свойств уже доказана машиной, а не «проверена на глаз». Чем выразительнее типы, тем больше теорем они способны нести — вплоть до полной спецификации.

∀
Тотальность

Функция, определённая для всех входов

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

TOTALISвсюду vs с дырами totalpartial тотальная входвыход частичная ⊥ падение
Тотальная функция отображает каждый вход. У частичной есть входы, на которых она падает или не завершается.

Делать функции тотальными — дисциплина: исчерпывающе разобрать все случаи (отсюда польза сумм-типов из углубления об алгебраических типах) и моделировать частичность явно — возвращать Maybe/Result вместо исключения. Языки с проверкой тотальности (например, Idris) прямо отмечают, тотальна ли функция. В Elixir это идиома честного результата:

# частичность смоделирована, а не спрятана за падением
def find(map, key) do
  case Map.fetch(map, key) do
    {:ok, v} -> {:ok, v}
    :error -> {:error, :not_found}
  end
  end
↑
Спектр уверенности

От типов — к доказательству

Уверенность в корректности набирают ступенями: чем выше — тем строже и дороже. Инженер выбирает уровень под цену ошибки.

FIDESстрогость ↑ дёшевополное доказательство типы — проверка компилятором тотальность + исчерпывающий разбор property-тесты: инварианты на сотнях входов доказательство · зависимые типы строгость
Лестница: типы → тотальность → property-тесты → доказательство и зависимые типы (Lean, Agda, Idris, Rocq).

Property-based testing — золотая середина: вместо отдельных примеров формулируют свойство-инвариант и проверяют его на сотнях сгенерированных входов. На вершине — помощники доказательств и зависимые типы (Lean, Agda, Idris, Rocq, бывший Coq): тип может зависеть от значения, и тогда спецификация записана прямо в типе, а компилятор её доказывает.

# свойство-инвариант проверяется на множестве входов (StreamData)
property "reverse дважды — тождество" do
  check all xs <- list_of(integer()) do
    assert Enum.reverse(Enum.reverse(xs)) == xs
  end
end
∎
Инварианты в типах

Спецификация, записанная в типе

Вершина строгости — зависимые типы: тип зависит от значения, и тогда инвариант проверяется на этапе компиляции. Невозможное не просто не выражается — оно доказуемо не случается.

Классический пример — вектор с длиной в типе. Сложив векторы длины $n$ и $m$, получаем вектор длины $n+m$ — и это записано прямо в сигнатуре. Компилятор доказывает совпадение длин, поэтому случай несовпадения не нужно обрабатывать: он невозможен.

-- длина в типе; компилятор доказывает n + m
append : Vect n a -> Vect m a -> Vect (n + m) a

Так инвариант («длины складываются») перестаёт быть комментарием и становится теоремой, проверенной машиной. Это и есть мост из углубления об алгебраических типах: сделать недопустимое невыразимым — и доказать допустимое.

∎Что несёт тип

• Запрет недопустимых состояний (сумма-типы).
• Гарантию обработки всех случаев (тотальность).
• Доказательство числовых и структурных инвариантов (зависимые типы).

≡Параметричность

Даже без зависимых типов полиморфная сигнатура ограничивает поведение: по типу $\forall a.\ a\to a$ функция может быть только тождеством. Тип говорит о программе больше, чем кажется.

№ 06Итог

Не «работает», а «доказано»

Корректность — это соответствие спецификации, установленное доказательством. По Карри-Ховарду тип есть утверждение, а программа — его доказательство; тотальность делает функции честными; спектр от типов через property-тесты до зависимых типов даёт уверенность под цену ошибки; инварианты, записанные в типах, доказываются машиной. Логика вошла в код.

И здесь — ответ на тезис финала: именно так со сложностью можно не сражаться. Когда инварианты, логика и доказательства под рукой, не нужно держать всё в голове — за корректность ручается тип. Дальше — глава о том, что математика становится общим языком человека и нейросети.

С инвариантами и доказательствами о сложности можно не думать — за неё ручается структура. — линия курса
→
Дальше по курсу

К языку человека и машины

Дуга корректности продолжается: от доказуемых программ — к математике как общему языку.

№ 07Материалы

Опоры