Я думаю, это был Haskell. Да он и остаётся сложным до сих пор, несмотря на то, что я свою дипломную работу написал на этом языке.
Haskell основывается на крайне сложном математическом аппарате под названием лямбда-исчисление, и в первых своих версиях он фактически и был практически реализуемой версией лямбда-исчисления.
В нём даже ввода-вывода не было.
Вследствие такой истории происхождения хаскелл содержит операции, которые в обычной работе на си-подобных языках разработчики ПО не применяют, да и даже не подозревают о том, что так вообще можно.
Например, там автоматически без каких-либо телодвижений работают
частичные вызовы функций и их частный случай
каррирование. То есть, там функции не просто объекты первого класса, которые можно передавать как аргументы при вызове других функций, вы можете передать в качестве аргумента функцию
частично вызванную, такую, которой передали не все аргументы. Даже просто представить, как это в принципе работает, уже сложно.
Или тот факт, что из-за
ленивого вычисления аргументов реальный вызов функции произойдёт не в момент достижения потоком контроля места её вызова, а фактически не раньше момента, когда данные понадобятся в I/O. То есть, вы можете написать вычисление произвольной сложности, но запущено оно будет, грубо говоря, только в момент вызова `print`. Не будет `print` - не будет вычисления. В смысле, вообще. Неважно, "объявляли" ли вы какие-то "переменные", "делали" ли эти вычисления предварительно или что-то ещё. Нет необходимости в данных - нет вычислений этих данных. Очень было необычно отлаживать программу в таких условиях.
Инкапсуляция данных делается через
монады, операции над которыми абсолютно неортодоксальны и требуют изучения
очень сложного материала. Я просто скажу одну-единственную деталь, которую более-менее понимаю в монадах: фактически они используются, чтобы осуществлять боксинг значений, однако являются они абстракцией над
процессами. Успехов вам понять, как это работает.
Взамен этого мы, конечно, получаем очень серьёзные преимущества, например, статическую корректность кода ("если оно компилируется, оно, скорее всего, работает"), отличную выразительность системы типов (мы определяем не структуру типа данных, а операции, доступные на нём), невероятно крутой компилятор, который как само собой разумеющееся умеет делать tree-shaking, распараллеливание и много других интересных вещей, о которых я не знаю, возможность формального доказательства корректности нашей программы (мы в лямбда-исчислении программируем, напомню), явное разделение кода между функциональным ядром и императивной оболочкой, если ваша архитектура, собственно, "functional core, imperative shell". Однако этот вопрос не про преимущества Haskell, а про сложные языки программирования, так что я на этом и закончу.
Процесс программирования решения задачи на хаскелле полностью отличается от программирования на си-подобных языках.