...Основная засада в том, что большинство систем типов (даже самые академические!) терпят неудачу в ситуациях вроде исключения в конструкторе. Мы хотели бы стереть с помощью типов грань между временем выполнения и временем компиляции, но это требует от системы типов наличия условий и итераций (зависимостей от значений), тьюринговой полноты, и тут в любом случае возникают большие сложности применения типизации к значениям в рантайме.
Вообще, это большая тема, насколько активно и как правильно использовать конструкторы.
Универсального ответа, что делать с исключением в конструкторе, у меня нету :) Если язык достаточно продвинутый, на сегодня в мэйнстриме это наверное только TypeScript (что немудрено, его автор Хейлсберг ранее придумал Delphi и C#), тогда можно например задать тип конструктора как
Dwarf | ValidationException
Если же в случае ошибки вы хотите возвращать null, то тогда используйте фабричный метод, который выдаст Dwarf | null
В общем случае, проблема не в поведении времени выполнения, а в том, насколько соответствующий исходный код типизирован и выразителен.
В Python например вы не используете new, и конструктор там выглядит как функция, что очень здорово :)
dwarf = Dwarf()
И как раз за счёт своей гибкости Python позволяет (как и TS) наглядно записывать типы-объединения (PEP 604), поэтому мы можем конструктор и фабричный метод типизировать совершенно по-разному, и это просто великолепно.
Dwarf | None
Self | None
Тут можно строить очень интересные паттерны, сравнимые с продвинутыми фабричными методами, в том числе "умные конструкторы", которые скрывают недостатки исходного конструктора и принуждают использовать более тонкие подходы. Python, Kotlin, Swift, Go, Rust -- список нонконформистов можно продолжать. Например, в Kotlin есть extension methods, которые отнюдь не синтаксический сахар или эквивалент статическим методам Java. Наверно, можно даже сказать, что это в некотором смысле тайп-классы из ФП, или монады из LINQ...
Ладно, больше не буду морочить вам голову :)
Post #825
1.16K
- ❤ 12
- 🫡 7
- 👍 5