Pull to refresh

Comments 33

Я правильно понимаю, что это работает только для валидации, которая не требует какого-то внешнего контекста? Вот например если я хочу Email проверять не только синтаксически, но ещё и на тему отсутствия в блеклисте в БД.

Второй вопрос - в сценарии с десериализацией DTO, состоящего из name/email/age - правильно понимаю, что ошибка десериализации будет содержать только первую ошибку? Т.е. если email и age оба некорректные, то на email оно упадёт, а на age даже не посмотрит?

  1. Я полагаю нет смысла лезть в БД с синтаксически неверным email. Т.е. для проверки отсутствия почты в блеклисте уже работаем с уточненным типом Email

  2. В данном случае видно, что декодер возвращает тип Either, что по умолчанию подразумевает Fail fast (падаем при первой ошибке). А для Fail slow, к примеру, может использоваться cats.Validated

UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
В качестве попытки немного обобщить и систематизировать вводную часть. Когда мы хотим определить некоторый тип через констрейны, чтобы сузить круг возможных значений, то есть два принципиально разных взаимодополняющих подхода: валидация данных в рантайме и валидация во время компиляции.
Пример ручного написания рантайм-валидации как демонстрация нарушения SRP мне кажется притянутым за уши – достаточно вынести валидацию в отдельный метод.
Если я правильно понял, то предложенная библиотека сочетает в себе генерацию валидаторов и их использование и для генерации рантайм-бойлерплейта, и для проверки доступных литералов во время компиляции.
Первый вопрос – это вариация вопроса 0xd34df00d, который в общем случае сведется к зависимым типам. А именно: есть ли у нас, например, возможнасть имея два NonNegative в компайл-тайме получать NonNegative в результате их сложения, но не получать в результате разности? Получать в результате разности только если первое значение больше/равно второго?
Теперь к разделу про взаимодействие с библиотеками. (Побуду занудой, пример, похоже не о «взаимодеиствии с библиотеками вообще», а о «взаимодействии с библиотеками, зависящими от refined» – это немного другое). Вопрос второй – по «case class Foo».
Скалу я не знаю, но знаю, что case-классы в ней – это реализация ADT плюс некоторые особенности. В данном примере кейс-класс с единственным конструктором используется из-за каких-то дополнительных особенноестей? Можно ли использовать просто value-классы или обычные мутабельные классы?
Сам я больше работаю с haxe, поэтому приведу встречные примеры, присущие этому языку. Во-первых в языке есть абстрактные типы – компайл-тайм абстрацкии над другими типами с zero-cost оверхэдом, которые среди прочего позволяют писать рантайм-валидации в теле конструтора. После компиляции тело конструктора/методов/операторов по желанию либо заинлайнятся, либо вынесутся в статический метод. Кроме того, над абстрактами можно определять операции над любыми комбинациями с другими типами. То есть в примере с NonNegative мы легко можем определить операцию сложения с гарантиями компилятора, но для вычетания – только сгенерировать рантайм-проверки. Так как зависимых типов тут нет.
Алгебраические типы в haxe тоже есть, но с абстрактами они никак не связаны и могут использоваться с ними в сочетании в любых комбинациях.
Кроме того, у haxe есть удобный апи для метапрограммирования, который позволяет делать описанное в статье. В качестве примера могу привести коллекцию библиотек haxetink.github.io.

Добавить перегруженные методы для refined типа можно, через обычные extension methods, сильно специфического для Refined ничего в этом плане нет, кроме "поднятия" обернутого типа
Зависимые типы в Scala есть, поэтому разницу двух NonNegatives на этапе компиляции сделать можно, если это литералы (значения известны также на этапе компиляции)
Refined это сторонняя библиотека и в плане использования типов ничем не отличается от любых других типов, будь то в кейс или обычных классах и полях

UFO just landed and posted this here

Каким образом? Вот я ввел с консоли два NonNegative, как ваш код вернет NonNegative для x - y?

UFO just landed and posted this here

Какой именно проверки? Есть пример на каком-то реальном языке? В Scala тоже можно потребовать какой-нибудь имлиситный параметр и создавать его в if'e :)

UFO just landed and posted this here

Тут ключевой момент вот в чём. Его тип может зависеть от соответствующих значений? Ну, чтобы тип имплиситного параметра указывал на то, что это именно свидетельство того, что x меньше y?

Я думаю, элегантного решения ни во 2, ни в 3 Scala действительно нет

UFO just landed and posted this here

Что такое x.type?

тип переменной х

Олсо, поменял местами x и y в when, код всё ещё компилируется. Ерунда какая-то.

разумеется, компилируется, ведь Lte это просто кастомный класс в сниппете. Он скомпилируется даже если назовете его Unicorn. Могу переложить в пакет Prelude, если так будет лучше :)

UFO just landed and posted this here

Помнит, x.type != Int, это тип конкретной переменной x, которая в скоупе:

scala> val x = 15
val x: Int = 15

scala> val y: x.type = x
val y: x.type = 15

scala> val y: x.type = 15
                       ^
       error: type mismatch;
        found   : Int(15)
        required: x.type
UFO just landed and posted this here

Для этого его достаточно положить в [стандартную] библиотеку, в Idris'е же оно не из вакуума появляется

UFO just landed and posted this here

А для Int'ов? Я посмотрел реализацию LTE в Idris и, кажется, придумал как сделать то же самое в Scala 3 (через https://dotty.epfl.ch/docs/reference/new-types/match-types.html)
Но тогда мне придется писать еще свою реализацию Nat, а я, право, ленивый

UFO just landed and posted this here
UFO just landed and posted this here

А можете подробнее, пожалуйста?

UFO just landed and posted this here
UFO just landed and posted this here

Я извиняюсь, что лезу не в свой огород (не пишу на Scala), но разве для подобных ограничений не существует подход с DbC (контрактным программированием)?


Он менее элегантен, однако констреинты не будут проверяться в рантайме, а значит и выше производительность. Плюс подход с пре/пост-кондишенами и инвариантами более явен и ориентирован не на сам тип "Email", а тип в рамках какого-либо скоупа/контекста (например "String" внутри "User").


Но даже не взирая на различия — задачи идентичные: Специализация примитивных типов.

Sign up to leave a comment.

Articles