davegurnell / checklist Goto Github PK
View Code? Open in Web Editor NEWValidation library for Scala.
License: Apache License 2.0
Validation library for Scala.
License: Apache License 2.0
It's common for some validation rules to be performed against a database (e.g. duplicate checking). Checklist could handle this in one of two ways: either require the user to load related data before running the tests (dodging the issue) or weaving a monad like DBIO
in amongst the validation rules.
It may be worth checking rewriting Rule[A, B]
as Rule[F[_]: Monad, A, B]
to see if this produces something usable.
Should remove the scalaz dependency. They're at milestone 2 for the cats version now, so maybe not just yet.
hey I just started working on a validation lib as well: https://github.com/amilkov3/validen, however mine involves specifying validations via a simple spec language as opposed to programmatically (though there is a validation AST). i think our libraries would compliment each other well as they currently address separate use cases but share commonalities (ValidatedNel
from cats and well a functional approach in general). would you want to collaborate and maybe eventually combine the two?
Hi. Thanks for the great work. I discovered checklist when searching for an alternative for ValidationNel with support for soft errors, which is a very compelling use case.
However my code base is based in scalaz, so I have forked and ported checklist to scalaz in this branch
Would you be interested in this?
I don't know how to put this in to words, so I'll throw it in an example..
case class AddressInput(house: Option[Int], street: Option[String], street2: Option[String])
case class ValidatedAddress(house: Int, street: String, street2: Option[String])
implicit val checkAddress: Rule[AddressInput, ValidatedAddress]
Rule[AddressInput]
.field(_.house)(req andThen gte(1))
.field(_.street)(req andThen trimString andThen nonEmpty)
.field(_.street2)(optional(trimString))
.map { addr =>
ValidatedAddress(addr.house.get, addr.street.get, addr.street2.get
}
Having to use .get
in there is pretty unfortunate. If instead running .field
would return some sort of RuleBuilder[A, B]
where A
is the input type, and B
is the output type, you could do something like
case class AddressInput(house: Option[Int], street: Option[String], street2: Option[String])
case class ValidatedAddress(house: Int, street: String, street2: Option[String])
implicit val checkAddress: Rule[AddressInput, ValidatedAddress]
Rule[AddressInput]
.field(_.house)(req andThen gte(1)) // RuleBuilder[AddressInput, Int :: HNil]
.field(_.street)(req andThen trimString andThen nonEmpty) // RuleBuilder[AddressInput, Int :: String :: HNil]
.field(_.street2)(optional(trimString)) // RuleBuilder[AddressInput, Int :: String :: Option[String] :: HNil]
.build {
case street2 :: street :: house :: HNil => ValidatedAddress(street2, street, house)
}
This could be expanded upon and you could have the builder function derived from shapeless Generic
as well. When working with scalapb
especially this feature would be useful. A builder which just returns a Rule[A, A]
would also be possible, but I think that the Rule
class itself should have a method for running a sanitization and returning the validated (and unsanitized) result.
I'll consider making a PR as a POC sometime soon if I can find time.
The Checked
type is restrictive. It doesn't allow the user to add their own data to error messages. We could consider generalising it to allow more customisation.
Checked
essentially encompass two concepts:
We could potentially extract these out into type classes to allow developers to customise the error type. We may need to parameterise the entire rule-definition API by the error type to keep things simple.
I think it'd be very helpful to provide integrations with json/xml/ parsing libs. Ideally we'd provide a function decoder a -> validator a b -> decoder b
. This way users can transparently use checklist for validation during parsing.
Example for something like circe:
def validatingDecoder[A, B](
handleWarnings: (WarningMessages, B) => DecodeResult[B])(
implicit decoder: Decoder[A], validator: Validator[A, B]
): Decoder[B] =
decoder.emap(validator.validate(_).fold(
handleErrors = validatorErrorToCirceError,
handleWarnings = handleWarnings,
handleSuccess = DecodeResult.success(_)
))
I think we should change the default semantics of Checked
. This thread is intended for that discussion. To provide more detail...
The semantics of Ior
allow the user to map on the right hand side, regardless of what values are on the left hand side. Most rules in checklist allow the user to signal errors and retain a value on the right. This gives slightly odd semantics where error handling doesn't fail fast when the programmer expects it to.
@Jacoby6000 provided a workaround for this in #9 by adding "strict mode" checks that return an Ior.left
on failure instead of an Ior.both
. However, I still think this is too fiddly. IMO a developer should be able to choose failure semantics up front and not have to worry about them afterwards. See #2 for thoughts on allowing abstraction of the Checked
type.
Regardless of whether we can abstract over different implementations of Checked
, we still need to choose a default failure semantics. These are the properties I believe most developers (myself included) will expect:
map
and flatMap
;field
and fieldWith
ap
and mapN
);The problem with this is that flatMap
and ap
have inconsistent semantics. This is something that Cats and Scalaz fundamentally avoid. However, I believe that in the case of checklist, a non-rules-compliant custom Checked
monad will be a reasonable thing to create.
What do people think? Is this an ok idea? Is it an ok idea if we allow users to swap out Checked
for Either
or Ior
as suggested in #2?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.