Hacker News new | ask | show | jobs
by frankpf 2198 days ago
I don't think you can use _refinement types_ for this, at least not as of now (e.g. you can't do {.requires validEmail(email).}), but you can use distinct types[1] in nim to model what you want:

    type Email = distinct string
Now Email is a type that is incompatible with string, even though its runtime representation is a string (i.e. no additional overhead). To convert a string to an Email, you can call Email("a"). In real-world code, you would call that constructor only in a few "safe" functions:

   # I'm raising an exception, but you could use an option or a result type
   proc validateEmail(str: string): Email =
      if email.contains("@"):
          return Email(str)
      raise newException(InvalidEmail, "not valid")
Then, in your client code you can have:

    proc createUser(email: Email, password: Password) =
       ...
And the type system guarantees that you only call createUser with "Email"s, not plain strings. Of course, you have to be dilligent not to use the Email constructor directly and instead use the validateEmail function to create "Email"s.

[1]: https://nim-lang.org/docs/manual.html#types-distinct-type

EDIT: The sibling comment mentioned io-ts.

The pattern for doing this in io-ts (and TypeScript in general) is very similar to Nim, although they call it "branding"[2] as it abuses the structural type system to simulate nominal typing.

[2]: https://michalzalecki.com/nominal-typing-in-typescript/#appr...