Арсений Жижелев @primetalk
Scala архитектор
Information
- Rating
- Does not participate
- Location
- Воронеж, Воронежская обл., Россия
- Registered
- Activity
Specialization
Backend Developer, Software Architect
Lead
From 700,000 ₽
Git
Linux
Docker
PostgreSQL
Golang
Scala
Автор заявляет о революции — о наличии линейного решения для NP-полной задачи. Такое заявление требует убедительных доказательств, т.к. если бы это было правдой, то была бы опровергнута гипотеза P ≠ NP. Обычно же это свидетельствует о том, что в ходе рассуждений где-то закралась ошибка.
Немного критики
Скорость выполнения алгоритма на конкретном компьютере не имеет отношения к линейному/нелинейному времени. На мой взгляд, это только добавляет "воды" в статью.
Краеугольным камнем P≠NP является оценка сложности. Хотелось бы, конечно же, видеть алгоритм на каком-нибудь псевдо-коде, и видеть оценку в стандартной О-нотации.
это утверждение противоречит формулировке задачи, данной в разделе "1. Введение". Там задача сформулирована детерминировано, как и положено задачам класса NP.
Таким образом, автор подменяет одну задачу другой.
В детерминированном алгоритме решения NP-полных задач таких решений быть не может. Таким образом, предложенное автором решение не является решением задачи n-Queens Completion problem
Разные цели/задачи — разные языки.
Бизнес-логику вполне можно писать на Scala с помощью подходящего DSL. В том числе, и не столь прямолинейную.
"Премудрости" дают инструменты для хороших библиотек и DSL. На прикладном уровне хорошие библиотеки использовать достаточно легко, и можно в большинстве случаев обходиться поверхностными знаниями, не вникая в детали.
Для некоторых задач отсутствие адекватных по сложности инструментов приводит к тому, что программы получаются не очень хорошими (многословными, с повторами похожего кода, с необходимостью заново тестировать похожую функциональность).
Есть хорошие книги и курсы, с помощью которых можно освоить абстракции, используемые в функциональном программировании. Например, FP in Scala (либо аналогичная книжка на Haskell'e — haskellbook.com). На coursera есть специализация "Functional Programming in Scala".
Ну а собственно
lift
из обычной функции делает функцию, работающую с какой-то структурой. Например,а универсальность позволяет писать код, который не зависит от конкретной структуры:
такой алгоритм будет работать с любыми структурами данных (для которых есть
Functor
) и любыми числовыми типами (для которых естьNumeric
).COBOL не работает на таком уровне абстракций.
lift
— мощный универсальный инструмент, который затруднительно даже представить в слабо-абстрактных языках.То же самое на Haskell:
не то, чтобы стало сильно легче...
Видимая сложность функции
lift
связана с её универсальностью. При использовании, вообще говоря, всё довольно удобно и не выглядит сложным:По поводу паролей — (1) разбить конфигурацию на 2 части — общая и секретная; (2) предусмотреть макрос или скрипт сборки, который будет забирать пароли из хранилища и запекать в конфигурацию; (3) по-старинке, предусмотреть runtime-загрузку паролей при старте приложения.
Поздравляю с победой! Похоже, пользователи лайкают то, что уже лайкали раньше. И собственно контент не очень важен.
Как мне кажется, статья не отражает "Haskell подход". В Haskell — простая структура данных (cons-list) + рекурсия позволяют эффективно решать перечисленные задачи. Предлагаемые в статье алгоритмы выглядят неэффективно.
Например,
size
использует методtail
типаList
, что, в случае array-list'а будет копировать почти весь лист (O(N)
по времени и по памяти), а затем не использует хвостовую рекурсию (O(N)
по памяти), чтобы прибавить 1. Тем самым общая вычислительная сложность оказываетсяO(N^2)
, что отличается от обычной сложностиO(N)
по времени иO(1)
по памяти.В примере с хвостовой рекурсией автор также использует
when(xs.size)
внутри функции. В случае, если List — односвязный список, опять окажется, что каждый вызовO(N)
. В итоге хвостовая рекурсия не спасает, получаемO(N^2)
. (Можно было бы использоватьwhen(xs.isEmpty)
сO(1)
.)В нашем проекте мы схожую проблему решаем с помощью gradle. В перспективе можно будет отправить maven на заслуженный отдых (люди к maven привыкли/прикипели и не готовы сразу с ним расстаться).
Вроде, теперь у espruino лицензия Mozilla MPLv2 и Creative Commons
Насколько я понимаю, в ScalaCheck ключевым является понятие "свойства". А генераторы используются постольку поскольку, как средство, позволяющее убедиться, что свойство выполняется в широком диапазоне значений.
ScalaCheck не подходит для замены Unit-тестов во всех случаях. Этот подход прекрасно работает тогда, когда удаётся найти те самые свойства, справедливые для многих случаев. Если программа представляет собой много отдельных несвязанных случаев — switch/case/if, то маловероятно, что такие обобщённые свойства будет легко обнаружить. А вот если тестируется универсальный алгоритм, то мы можем ожидать определённой стабильности его результатов.
Если термин "свойство-ориентированное тестирование" не нравится, то можно поискать альтернативы для слова "свойство". Например, "инвариант" — тестирование на основе инвариантов. Или "предикат" — тестирование на основе валидации предикатов. Или "ограничение" — проверка ограничений, тестирование ограничений.
Насколько я понимаю, в данном случае функтор, который конструируется из монады — единственный. Морфизм
f: A=>B
конвертируется вM[A] => M[B]
путём использования двух операций, предоставляемых монадой. Вначале функциюf
превращаем в вычисление, применяя нулевое вычисление η к результату функции:φ = λa.ηfa
, а затем конструируем результирующую функцию в lifted-категории с использованием моноидного умножения μ:λma.μma φ
.В Scala это выглядит, как привычный способ выразить
map
черезflatMap
иpure
:Эндофункторов, над которыми строится моноид, действительно много, но ни один из них, насколько я могу судить, не имеет такой формы, как этот —
(A=>B) => (M[A] => M[B])
.Я рассматриваю монаду с точки зрения программирования. В статье Monads for functional programming (Philip Wadler) монада определяется как тройка, состоящая из конструктора типа, отображения, превращающего обычное значение в этот тип ("нулевое" вычисление), и отображения, связывающего два вычисления в одно.
То, что монада также является функтором, следует из того, что монада (как и функтор) позволяет преобразовать простые функции над базовыми типами в lifted-функции над сконструированным типом, то есть позволяет выполнить отображение категории обычных функции в категорию lifted-функций.
Несмотря на это, монада — более универсальная конструкция, чем функтор. Монада не только отображает обычные функции в lifted, но и предоставляет дополнительные возможности работы с функциями типа
A => M[B]
, которые включают конструируемый тип — "вычислениями". При этом, сам конструируемый типM[B]
оказывается включен в класс объектов категории (поэтому, по-видимому, категория эндофункторов; при этом конструктор типаM[?]
не включен в объекты категории). И вместо двух изолированных категорий, связанных функтором, мы оперируем одной категорией, в структуре которой выделены функции особого вида — вычисления, — и объекты —M[B]
, инкапсулирующие (потенциальный) результат вычисления. Для этих функций/вычислений монада предоставляет базовый набор действий, соответствующий моноиду, — нулевое действие (без вычислений), и комбинирование двух вычислений.Да, вы правы. Сокрытие и инкапсуляция хоть и идут рука об руку во многих языках, всё-таки концептуально отличаются. Неизменяемые типы данных сами по себе не исключают инкапсуляцию, а лишь подрывают рациональные основания для сокрытия данных.
Инкапсуляция, как мне кажется, подразумевает, что как только мы объявили методы доступа к данным, следующий логичный шаг — скрыть прямой доступ к этим данным, чтобы исключить нарушение абстракции. Без сокрытия от инкапсуляции остаётся лишь возможность сложить в один объект данные и функции, что, по-видимому, имеет ограниченную ценность. А вот инкапсуляция вместе с сокрытием — хороший инструмент абстрагирования.
Похоже на то. В cats:
В scalaz похожая ситуация.
Конечно, можно. Лицензия — BSD-like.
Мы сейчас этот подход используем и развиваем. В целом — достаточно удобно. При наличии неполных данных в БД (nullable колонок) — очень удобно.
(Здесь мы используем оператор
with
, который соответствует конъюнкции. Для проверки принадлежности мы инвертируем условие с помощью оператора <:<2. Экономия классов не входит в цели фреймворка. Суть заключается в разрезании целого класса на отдельные компоненты и возможность оперировать данными, используя эти компоненты (слоты) в качестве ключа.
3. Пример с delta. Пусть класс Person содержит 100 полей. И пусть среди этих полей есть свойство типа T: Numeric (например, Int, Double и т.п.). Тогда мы можем объявить специальный класс PropertyDelta, который несёт изменение этого свойства
Это может быть экономнее, чем копировать оставшиеся 99 полей (при сохранении в БД, при передаче по сети и т.п.). Кроме того, здесь мы явно видим, что именно изменилось, можем проверить допустимость изменения этого свойства согласно каким-либо правилам.
Теперь мы можем этот объект использовать для того, чтобы увеличить значение свойства на указанную величину:
Или немного поинтереснее:
vs.