Это меня подтолкнуло на такую мысль: а может абстрагируемся еще больше, и отбросим арифметические операции этого "интэграл"
Не совсем так, числа в кодировке Черча это как раз один из вариантов реализации натуральных чисел, так что уровень абстракции тут ниже. Type class это просто требования к типу — функция опирающаяся на такое требование будет работать с любым типом, в том числе и с числами Черча (хотя конечно натуральные числа у`же чем целые, которые описывает Integral)
Опять громкое название и незаметный дисклеймер в конце статьи. Систему нельзя проектировать исходя из устройства базы данных. Апи к базе данных — возможно.
Если необходимо формировать порядок полей в рантайме, то я бы предпочел вместо объявления объектов на каждое поле объявить мапу Map[String, SortField] — все равно поля вероятно прийдут в виде строк.
Делать отдельные кейсы для Asc/Desc/Keep мне кажется легким оверинжинирингом когда можно обойтись Order(asc: Boolean, nullsFirst: Boolean) или Option[Order] если очень нужен Keep.
sealed trait SortField[E]
object SortField {
case class Optional[E, T](field: E => Option[T], order: Ordering[T])
extends SortField[E]
case class Mandatory[E, T](field: E => T, order: Ordering[T])
extends SortField[E]
def apply[E, T: Ordering](f: E => T): SortField[E] =
Mandatory(f, Ordering[T])
def optional[E, T: Ordering](f: E => Option[T]): SortField[E] =
Optional(f, Ordering[T])
}
case class SortOrder(asc: Boolean = true, nullsFirst: Boolean = true)
def orderBy[E](fields: (SortField[E], SortOrder)*): Ordering[E] = {
def initial = Ordering.by[E, Int](_ => 0)
def direction[T](ordering: Ordering[T], asc: Boolean) =
if (asc) ordering else ordering.reverse
val comparator = fields
.map {
case (SortField.Mandatory(field, ordering), SortOrder(asc, _)) =>
direction(Ordering.by(field)(ordering), asc)
case (SortField.Optional(field, ordering), SortOrder(asc, true)) =>
Ordering.by(field)(Ordering.Option(direction(ordering, asc)))
case (SortField.Optional(field, ordering), SortOrder(asc, false)) =>
val order = direction(Ordering.Option(direction(ordering, !asc)), asc = false)
Ordering.by(field)(order)
}
.foldLeft(initial: Comparator[E])(_ thenComparing _)
Ordering.comparatorToOrdering(comparator)
}
Рекомендация избегать исключений так как они ухудшают производительность это преждевременная оптимизация — вполне вероятно в вашем проекте полный отказ от них не повысит производительность и на 2%.
С другой стороны приведённый пример с возвратом пустой строки противоречит второму совету — повышать понятность кода: пустая строка неотличима от реального имени и такой метод может привести к трудноуловимым ошибкам.
В данном случае функциональный подход рекомендует не просто отказаться от исключения, а явно отобразить исключение в возвращаемом значении, например используя Optional.
Это возможно, только если условие res.isEmpty() действительно всегда истинно!
Вот это не совсем верно — условие не полное, так как на самом деле это возможно если cur =! -1 || res.isEmpty()
То есть строчка 'C' действительно недостижима, а 'B' при этом может выполняться. Это возможно, только если условие res.isEmpty() действительно всегда истинно!
Не совсем так, на самом деле всегда истинно cur =! -1 || res.isEmpty(), т.е. как только список перестает быть пустым cur никогда не становится -1, а пока он -1 список пуст.
Хорошо, что статический анализатор находит ошибку в плохом коде, плохо что после исправления ошибки код остается плохим.
Да, функционал одновременно и рискованный и мощный. Многие варианты использования имплиситов неуместны, а неявные преобразования и вовсе будут исключены из языка. С другой стороны этот механизм позволяет реализовать очень интересный механизм — автоматический вывод структур. Например если существует json формат для чисел и строк, и показано как строить форматы для списков и мап, то формат для Map[String, Seq[Int]] выводится автоматически и на стадии компиляции. При этом это не вшито в библиотеку json, а работает для любых типов.
Когда код простой, можно и в процедурном стиле написать, то, что бэк на ноде выглядит функционально это скорее особенность ноды — в той же java обычный бэкэндщик будет вызывать блокирующие процедуры.
Что касается переиспользования дивов — реакт с его функциональным подходом давно так делает, работа же с ивентами без стримов быстро превращается в спагетти.
Конечно качественно мы имеем две категории — исключения и АТД, но поскольку таких типов в Scala несколько интересно не только сравнить эти две категории, но и несколько разных подходов к обработке ошибок с помощью АТД.
Исключения без трейса это интересный кандидат, пожалуй добавлю его к сравнению.
Не совсем так, числа в кодировке Черча это как раз один из вариантов реализации натуральных чисел, так что уровень абстракции тут ниже. Type class это просто требования к типу — функция опирающаяся на такое требование будет работать с любым типом, в том числе и с числами Черча (хотя конечно натуральные числа у`же чем целые, которые описывает Integral)
А чем вам не угодил jss?
Опять громкое название и незаметный дисклеймер в конце статьи. Систему нельзя проектировать исходя из устройства базы данных. Апи к базе данных — возможно.
Увы,
Ordering.orElse
появился только в 13 скале, мой код на 12Если необходимо формировать порядок полей в рантайме, то я бы предпочел вместо объявления объектов на каждое поле объявить мапу
Map[String, SortField]
— все равно поля вероятно прийдут в виде строк.Делать отдельные кейсы для
Asc/Desc/Keep
мне кажется легким оверинжинирингом когда можно обойтисьOrder(asc: Boolean, nullsFirst: Boolean)
илиOption[Order]
если очень нуженKeep
.Нет, должно быть
cats(_.owner)(nullsLast)
так какnullsLast
заменяет собой неявный порядок по-умолчанию.Что вы имеете в виду говоря про "перебор"?
Если кодировать порядок в АДТ не является самоцелью все эти требования легко удовлетворить двумя функциями:
И то, первая нужна только чтобы не указывать типы вручную.
В результате нужный порядок описывается так:
Чем в вашем понимании отличается Event Sourcing от Службы с открытым протоколом?
Рекомендация избегать исключений так как они ухудшают производительность это преждевременная оптимизация — вполне вероятно в вашем проекте полный отказ от них не повысит производительность и на 2%.
С другой стороны приведённый пример с возвратом пустой строки противоречит второму совету — повышать понятность кода: пустая строка неотличима от реального имени и такой метод может привести к трудноуловимым ошибкам.
Подобный подход имеет место и даже имя — https://ru.m.wikipedia.org/wiki/Null_object_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F), но использовать его нужно явно и осторожно.
В данном случае функциональный подход рекомендует не просто отказаться от исключения, а явно отобразить исключение в возвращаемом значении, например используя Optional.
Не совсем так, на самом деле всегда истинно
cur =! -1 || res.isEmpty()
, т.е. как только список перестает быть пустымcur
никогда не становится -1, а пока он -1 список пуст.Хорошо, что статический анализатор находит ошибку в плохом коде, плохо что после исправления ошибки код остается плохим.
Да, функционал одновременно и рискованный и мощный. Многие варианты использования имплиситов неуместны, а неявные преобразования и вовсе будут исключены из языка. С другой стороны этот механизм позволяет реализовать очень интересный механизм — автоматический вывод структур. Например если существует json формат для чисел и строк, и показано как строить форматы для списков и мап, то формат для
Map[String, Seq[Int]]
выводится автоматически и на стадии компиляции. При этом это не вшито в библиотеку json, а работает для любых типов.Вот тут сам автор языка очень хорошо описывает основные случаи использования (осторожно, антимонгольский): https://youtu.be/br6035SKu-0
Когда код простой, можно и в процедурном стиле написать, то, что бэк на ноде выглядит функционально это скорее особенность ноды — в той же java обычный бэкэндщик будет вызывать блокирующие процедуры.
Что касается переиспользования дивов — реакт с его функциональным подходом давно так делает, работа же с ивентами без стримов быстро превращается в спагетти.
Поправлю, по оси x доля ошибок в датасете, по оси у — время выполнения (в абстрактных попугаях, так как зависит от окружения)
Не jmh единым
Ну, теоретически такое было возможно и до 8 версии, просто выглядело более громоздко, но та же IDEA это скрывала
Я не говорю про библиотеки, с ними можно все, стандартная библиотека ни в Java 8 ни в Java 11 не поощряет такой подход
Конечно качественно мы имеем две категории — исключения и АТД, но поскольку таких типов в Scala несколько интересно не только сравнить эти две категории, но и несколько разных подходов к обработке ошибок с помощью АТД.
Исключения без трейса это интересный кандидат, пожалуй добавлю его к сравнению.
В Java к сожалению ADT почти не используются и даже стандартный Optional не рекомендуется использовать в интерфейсах и передавать между методами.