Пустая истина (1)
Расскажу, как однажды погорел на забавной вещи под названием “пустая истина”. Это было лет пять назад, когда я ничего про это не знал.
Значит, смотрите: когда мускулистые греки работали над логикой, они ввели в том числе предикаты. Например, белый? – это предикат. Если применить его к любому предмету, получим истину или ложь.
Также греки придумали пакетную версию предикатов, батч, так сказать. Это супер-предикаты “каждый из”, “любой из”, “ни один из”. Все они принимают другой предикат и множество объектов. Далее они редьюсят множество логических результатов в один (простите за функциональные термины).
Если на столе три белых камня, то выражение “все камни – белые” вернет истину. Если один – тоже истину. Если сто белых и один черный – ложь. А что случится, если камней нет? Греки почесали бороды и сказали – будет тоже истина, только назовем ее пустой.
В результате: если на столе нет камней, выражение “все камни – белые” будет истинно. Таким же истинным будет выражение “все камни – черные”, в крапинку и полоску. Все камни обладают какими угодно свойствами одновременно. Одна беда – их нет.
На мой взгляд, греки подложили нам свинью. Истина, как известно, одна – не бывает двух разных истин. Когда вводят истину с какой-то характеристикой, получается черт знает что. Вроде бы это истина, но какая-то другая, что требует особого обращения с ней.
Напоминает диалог Булгакова об осетрине второй свежести: голубчик, это вздор! Свежесть бывает только одна – первая, она же последняя. Если осетрина второй свежести, значит, она тухлая.
Знал же человек!
Интересно, что греческая вторая свежесть, тьфу, пустая истина идеально ложится на быдлокод! Это при том, что программировать греки не умели.
Предположим, нужно написать функцию, которая принимает предикат и список объектов. Функция возвращает истину, если предикат справедлив для каждого элемента. Вот как выглядит самая тупая реализация:
func is_every(fn_pred, items):
for item in items:
if not fn_pred(item):
return false;
return true;
Все просто: бежим по списку, как только предикат вернул ложь, мы тоже возвращаем ложь. Если прошли по всем элементам, значит, лжи ни разу не было, и мы возвращаем истину. Если список пуст, мы благополучно пропустим цикл и перейдем к выражению с истиной.
Как это связано с моей работой? Несколько лет назад в Exoscale я делал систему
прав доступа, аналог IAM в Амазоне. Из базы читались разрешения, и нужно было
проверить, что каждое из них совпадает с политикой ресурса. Для этого я вызывал
кложурную функцию every?
:
(every? policy-match? permissions)
Оказалось, Рич Хикки знал эти штучки, и для пустого множества every?
возвращает истину. Это легко проверить:
(every? int? [1 2 3])
true
(every? int? [1 "a" 3])
false
(every? int? [])
true
Вышло так, что если у пользователя вообще не было прав, то список был пуст и every? возвращала истину. В результате пользователь, который не имел доступа ни к чему, имел доступ ко всему – из-за моих бедных знаний в этой области.
С тех пор у меня отпечаталось в подкорке, что перед every?
должна быть
проверка на пустоту. Пустая истина может трактоваться как угодно, но мне нужна
точность.
Второе – я не согласен с греками. Видимо, они еще не знали про NULL и
неопределенность, плохо понимали троичную логику. Увы, наш мир сложнее, чем
true
и false
, есть нуллы и другие досадные вещи. Но нужно жить с ними, а не
сводить к каким-то сомнительным истинам.
Кстати, пустая истина позволяет сказать жене: все мои любовницы – брюнетки. Если у вас нет любовниц, это тоже будет истиной. Разве что придется потратить время на объяснение, но ничего. Истина дороже.
Нашли ошибку? Выделите мышкой и нажмите Ctrl/⌘+Enter
Alex Mos, 15th Jul 2025, link
Сказал однажды математик: — Приду со множеством подруг. И вроде ясно, что пустое, Но вдруг…