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

Fake constraint for newtypes with no underlying validation #122

Closed
antonkw opened this issue May 26, 2023 · 4 comments · Fixed by #123
Closed

Fake constraint for newtypes with no underlying validation #122

antonkw opened this issue May 26, 2023 · 4 comments · Fixed by #123
Labels
enhancement New feature or request

Comments

@antonkw
Copy link
Contributor

antonkw commented May 26, 2023

RefinedTypeOps and accessing inner values of opaque types (with value call) make iron very good in domain modeling.

One potentially missing part is usage without actual validation (to simply separate value types).

I suggest introducing a fake constraint with easy runtime construction.

Not sure about the naming. I like Pure or Newtype

Ex.

opaque type FirstName = String :| Pure
object FirstName extends RefinedTypeOps[FirstName]

And runtime is safe and straightforward.

val firstName: FirstName = FirstName.pure(anyRuntimeInput)

What is important is if the user refactors type to String :| ValidUUID then pure stops compiling and requires updates.

@antonkw antonkw added the enhancement New feature or request label May 26, 2023
@Iltotore
Copy link
Owner

Iltotore commented May 26, 2023

True seems to be the constraint you're looking for. This way you can do that:

opaque type FirstName = String :| True
object FirstName extends RefinedTypeOps[FirstName]
val firstName = FirstName("Il_totore") //This is not my real first name

What is important is if the user refactors type to String :| ValidUUID then pure stops compiling and requires updates.

I don't really see the difference with apply provided by RefinedTypeOps. If my constraint on FirstName is not True anymore, then the value passed to apply will either be checked at compile-time or not compile at all.

@antonkw
Copy link
Contributor Author

antonkw commented May 26, 2023

@Iltotore
Yes, when I played around, True is an exact implementation in terms of predicate logic.

The naming is not precise, though. It doesn't reflect the notion of "let use me if you just want to create a lightweight wrapper to have neat domain model and don't need validation at all"

I don't really see the difference with apply provided by RefinedTypeOps.

The emphasis here is unlocking easy creation for runtime values. Please take a look at the example above.

And with apply the following won't compile:

type LastName = String :| True
object LastName extends RefinedTypeOps[LastName]
val anyString: String = ""
val value = LastName(anyString) // Cannot refine non full inlined input at compile-time.

The option is the usage of LastName.applyUnsafe (or assume), which won't prevent from changing Constraint to something that really can fail.

As a summary, the intention is to have DSL for anybody who wants a synonym to @newtype case class LastName(value: String).

  1. value call for opaque types is in place which is great.
  2. LastName(runtimeValue)-like construction is required. There are unsafe options that will hit the user if he will update constraints from fake to real.

@Iltotore
Copy link
Owner

This issue was an additional restriction which is probably not needed anymore. Fixing it should remove the need to create a pure method.

I will be busy for the next weeks but will look at it ASAP.

@Iltotore
Copy link
Owner

Iltotore commented Jun 3, 2023

See #125

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

Successfully merging a pull request may close this issue.

2 participants