Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Derive a custom Validate from an existing one #397

Open
hseeberger opened this issue Jan 1, 2018 · 7 comments
Open

Derive a custom Validate from an existing one #397

hseeberger opened this issue Jan 1, 2018 · 7 comments

Comments

@hseeberger
Copy link

First let me apologize for creating an issue for a simple question, but I haven't found another channel.

So, let's say I would like to refine scala.concurrent.duration.FiniteDuration, e.g. with Positive. My first idea was to simply use Positive and (contra)map it over a function extracting the length from the FiniteDuration which is a Long. But I haven't found a way to do that, i.e. no public (contra)map method on Validate.

Any help would be highly appreciated.

@fthomas
Copy link
Owner

fthomas commented Jan 1, 2018

@hseeberger Asking questions here is fine. The Gitter channel would be another option.

You're right, currently there is no API to contramap over an existing Validate instance to create a new one. In your case, just defining the instance would be the easiest option:

scala> implicit val finiteDurationValidate = Validate.fromPredicate(
  (d: FiniteDuration) => d.length > 0,
  (d: FiniteDuration) => s"$d is positive",
  Greater(shapeless.nat._0))

scala> refineV[Positive](1.minute)
res32: Either[String, Refined[FiniteDuration, Positive]] = Right(1 minute)

scala> refineV[Positive](-1.minute)
res33: Either[String, Refined[FiniteDuration, Positive]] = Left(Predicate failed: -1 minutes is positive.)

I think we should definitely make it easier to define this instance via the Validate[Long, Positive] instance.

@hseeberger hseeberger changed the title Question: Derive a custom Validate from an existing one? Derive a custom Validate from an existing one Jan 1, 2018
@hseeberger
Copy link
Author

Thanks for the quick answer!

I have changed the title (removed "question") to better reflect the new nature of this issue (now a feature requirement).

@kusamakura
Copy link
Contributor

I've been thinking about this too – we have Inference for equivalent predicates, but we don't have a way to encode type-to-type predicate relationships. For instance, a Money(amount: Long, currency: String) class can be refined based on the predicate of its amount parameter. I suppose making the contramap method on Validate public would be a good first step?

Another (more verbose) alternative I just discovered a few days ago is to use RefTypeOps.coflatMapRefine.

@fthomas
Copy link
Owner

fthomas commented Jan 8, 2018

Making Validate.contramap public is certainly an option but I think it needs a second parameter that also replaces the showExpr implementation. In the current form a Validate[FiniteDuration, Positive] derived from Validate[Long, Positive] via contramap(_.length) would reuse showExpr from the Validate[Long, Positive] instance to produce error messages like Predicate failed: -1 > 0. In this situation it would be desireable if the error message would be more specific and related to FiniteDuration.

@kevinmeredith
Copy link

kevinmeredith commented Apr 30, 2018

Is there support for Refined[FiniteDuration, Positive]?

# git clone refined
$cd refined
$ggrep -r FiniteDuration .
$ggrep -r Duration .
$

Thanks for your help and this great library!

@fthomas
Copy link
Owner

fthomas commented May 2, 2018

@kevinmeredith refined does not (yet) provide support for refining Duration or FiniteDuration but you can use the instance in #397 (comment) for that.

@Primetalk
Copy link

Primetalk commented Dec 12, 2018

For deriving a custom Validate from existing one we might need an equivalence relation:

  final class Equivalence[A, B]

  private val equivalenceInstance = new Equivalence[Any, Any]

  type `<==>`[A, B] = Equivalence[A, B]

  implicit def inferEquivalence[T,A,B](implicit eab: A ==> B, eba: B ==> A) : A <==> B = 
    equivalenceInstance.asInstanceOf[A <==> B]

And then we want to have the following validation inference rule:

  implicit def equivalenceValidation[T, A, B](implicit v: Validate[T, A], e : A <==> B): Validate[T, B] =
    v.asInstanceOf[Validate[T, B]]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants