Подход с классом-логгером может быть оправдан. Например, что если понадобиться логировать не только в файл, но и в память, в стандартный вывод, по сети на сервер логов, в syslog? В таком случае просто нужно будет переопределить метод записи в классе логгере. А если нужно ещё различать классы сообщений (ошибка, информационное, дебаг, предупреждение…), логировать разные классы в разные места, конфигурировать это всё с помощью файлов конфигурации, плюс в зависимости от источника лог-сообщения… Тут уж без целой иерархии классов не обойтись, иначе будет просто god-class какой-то.
Просто на этом месте меня аж передёрнуло. Переводить Zen как непонятное нерусское «Зен» — это какой-то ужас-ужас, за гранью. Остальные неточности я ещё могу простить, но изобретать новые слова, когда есть существующие канонические… Лень посмотреть в словарь?
Плохой пример, так как этот класс не просто создаёт обёртку методов вокруг LinkedHashMap, но и определяет некоторую дополнительную логику по конструированию объекта, доступа к полям разных типов и т.п. Это немного сложнее, чем пример из статьи.
То есть как не ограничивает? Если из браузера идёт XHR на домен, то сначала сам браузер делает запрос OPTIONS и смотрит на CORS-заголовки, и если сервер не разрешает, то XHR зафейлится сразу, никакого запросы отправлено не будет.
Ну и если продолжать эту аналогию, то checked exceptions в расте — это Result<T, Err>, а unchecked exceptions — это паника (panic!()).
С той разницей, что паника это вообще что-то очень-очень редкое, с ограниченным использованием, из-за чего программа полностью теряет смысл своей работы, а значит приводит к гарантированному падению потока, а Result<T, E> — обычное явление, которое компилятор заставляет хоть как-то обработать. И да, конечно можно явно Result привести к панике через unwrap(), но и в джаве это делается аналогично, только через исключения.
Я это к чему: возвращаемые из метода исключения — это по сути часть интерфейса метода, и должно кодироваться в типе, как это сделано в расте, а в джаве изначальное отсутствие дженериков это сделать не позволяло, вот и родилось полурешение с checked exceptions + throws.
И там и там мы явно сообщаем компилятору, что мы либо возвращаем какое-то полезное значение, либо ошибку.
В джаве можно при этом уровнем выше спокойно написать try{method()}catch(e:SomeException){}, в расте let _ = method() и заставить компилятор замолчать, но это делается явно.
Да и ещё в джаве throws не для всех типов исключений обязателен, в отличие от раста.
В расте просто этот подход реализован за счёт мощной системы типов, а в джаве изначально система типов была намного слабее, так что пришлось придумывать новую синтаксическую структуру со throws. Только вот создатели джавы испугались идти таким путём для всех ошибок, вот и придумали разделение checked/unchecked exceptions.
Ни про какие «флаги ошибки» в расте и речи не идёт, не говоря о сишном ужасе с выставлением глобального флага ошибки в errno.
Всё явно, в самом типе указана как возможность ошибки, так и типы ошибок.
Rand() оперирует внешним состоянием, так что это не чистая функция. Фактически в обычном императивном коде rand() неявно принимает некое состояние генератора случайных чисел извне. В функциональном коде тип этой функции будет наподобие Rand -> (Rand, float), то есть принимает некое состояние ГСЧ и возвращает новое состояние ГСЧ и случайно сгенерированное значение. Так что нет, это функция не от нуля параметров, а от одного (неявного) параметра.
Чем мне ещё нравится ФП, так это тем, что заставляет лучше видеть такие неявные параметры, к которым мы привыкли и на которые не обращаем внимание. Фактически состояние ГСЧ — это часть интерфейса rand(), просто обычно она опускается.
А ведь getString : *World -> (*World, String) по виду тоже очень похоже на поднятую в монаду функцию getString вроде getString : IO () -> IO String, контекст описывается через *World. Могу поспорить, что и монадические законы для этой штуки будут работать, хотя доказывать сейчас лень.
По сути да, но не совсем. Монада создаёт контекст, в котором вычисляется функция, при этом сама функция может сохранять ссылочную прозрачность. По сути функция, которая возвращает IO String, возвращает не строку, а контекст, из которого можно извлечь строку. А функция, которая принимает IO String, принимает не чистую строку, а опять же контекст со строкой внутри (или, если угодно, обещание строки). Получается, функция как бы не делает никаких побочных эффектов, ничего не читает и ничего не выводит, просто принимает и возвращает чистые немутабельные значения типа «контекст с чем-то внутри», и за счёт этого остаётся чистой. В итоге на выходе из программы, после соединения кучи монад, получаем чистое значение, которое ничего само по себе не читает и не пишет, а только описывает какие-то действия с различными значениями в заданном контексте. А потом уже во время работы программы «грязный» рантайм «выполняет» все эти действия в нужном контексте (например контексте ввода-вывода IO), и мы получаем все побочные эффекты и действия со значениями в них.
Это если очень упрощенно и на пальцах, поэтому более знающие товарищи могут тут за что-то зацепиться, но надеюсь сам основной смысл ясен.
Таким образом мы получаем программу в виде комбинации чистых функций, а вся «грязь» собирается в одном месте, в рантайме.
Спасибо за информацию, очень интересно было узнать о таких языках. Посмотрел по диагонали инфу об эффектах и уникальных типах. Эффекты по виду те же монады, а уникальные типы похоже выполняют ту же функцию, что и система заимствования в расте, поэтому это не совсем то. Но я могу ошибаться, так что если вы расскажете по эти сущности по подробнее или ткнёте в ссылки на статьи, буду очень благодарен.
Использование кучи требует аллокатора. Если использование кучи не отключается, то некоторые системные вещи написать не получиться, например ядро ОС, низкоуровневые драйвера/фёрмварь, прошивки (embedded software).
Ну битс, судя по описанию, закончился, не успев начаться, потому что главный автор на него просто забил. Хотя я не могу его за это судить, возвращаться к давно заброшенному, полузабытому и уже не интересному проекту — не сахар.
Вот циклон выглядел интересно, но судя по всему его убила излишняя академичность.
В любом случае ни один из них сравнивать с Растом до конца нельзя. У него открытая разработка, управляется он целой командой, а не одним BDFLом, который может потерять интерес к языку и забить, плюс достаточно большое и активное сообщество.
Дзен же, дзен. Дзен, Карл!
С той разницей, что паника это вообще что-то очень-очень редкое, с ограниченным использованием, из-за чего программа полностью теряет смысл своей работы, а значит приводит к гарантированному падению потока, а Result<T, E> — обычное явление, которое компилятор заставляет хоть как-то обработать. И да, конечно можно явно Result привести к панике через unwrap(), но и в джаве это делается аналогично, только через исключения.
Я это к чему: возвращаемые из метода исключения — это по сути часть интерфейса метода, и должно кодироваться в типе, как это сделано в расте, а в джаве изначальное отсутствие дженериков это сделать не позволяло, вот и родилось полурешение с checked exceptions + throws.
изоморфен этому:
И там и там мы явно сообщаем компилятору, что мы либо возвращаем какое-то полезное значение, либо ошибку.
В джаве можно при этом уровнем выше спокойно написать try{method()}catch(e:SomeException){}, в расте let _ = method() и заставить компилятор замолчать, но это делается явно.
Да и ещё в джаве throws не для всех типов исключений обязателен, в отличие от раста.
В расте просто этот подход реализован за счёт мощной системы типов, а в джаве изначально система типов была намного слабее, так что пришлось придумывать новую синтаксическую структуру со throws. Только вот создатели джавы испугались идти таким путём для всех ошибок, вот и придумали разделение checked/unchecked exceptions.
Ни про какие «флаги ошибки» в расте и речи не идёт, не говоря о сишном ужасе с выставлением глобального флага ошибки в errno.
Всё явно, в самом типе указана как возможность ошибки, так и типы ошибок.
Rand -> (Rand, float)
, то есть принимает некое состояние ГСЧ и возвращает новое состояние ГСЧ и случайно сгенерированное значение. Так что нет, это функция не от нуля параметров, а от одного (неявного) параметра.Чем мне ещё нравится ФП, так это тем, что заставляет лучше видеть такие неявные параметры, к которым мы привыкли и на которые не обращаем внимание. Фактически состояние ГСЧ — это часть интерфейса rand(), просто обычно она опускается.
getString : *World -> (*World, String)
по виду тоже очень похоже на поднятую в монаду функцию getString вродеgetString : IO () -> IO String
, контекст описывается через *World. Могу поспорить, что и монадические законы для этой штуки будут работать, хотя доказывать сейчас лень.Это если очень упрощенно и на пальцах, поэтому более знающие товарищи могут тут за что-то зацепиться, но надеюсь сам основной смысл ясен.
Таким образом мы получаем программу в виде комбинации чистых функций, а вся «грязь» собирается в одном месте, в рантайме.
Вот циклон выглядел интересно, но судя по всему его убила излишняя академичность.
В любом случае ни один из них сравнивать с Растом до конца нельзя. У него открытая разработка, управляется он целой командой, а не одним BDFLом, который может потерять интерес к языку и забить, плюс достаточно большое и активное сообщество.
В общем посмотрим, что будет.