A friendly newtype library for Scala 3.
"io.github.kitlangton" %% "neotype" % "0.3.0"
- Compile-time Checked Values
- Write validations as plain, old Scala expressions
- Helpful compilation errors (see below)
- No runtime allocations (Thanks to
inline
andopaque type
) - Integrates with other libraries (e.g.
zio-json
,circe
,tapir
, etc.)
Here is how to define a compile-time validated Newtype.
import neotype.*
// 1. Define a newtype.
object NonEmptyString extends Newtype[String]:
// 2. Optionally, define a validate method.
override inline def validate(input: String): Boolean =
input.nonEmpty
// 3. Construct values.
NonEmptyString("Hello") // OK
NonEmptyString("") // Compile Error
Attempting to call NonEmptyString("")
would result in the following compilation error:
Error: /src/main/scala/examples/Main.scala:9:16
NonEmptyString("")
^^^^^^^^^^^^^^^^^^
—— Newtype Error ——————————————————————————————————————————————————————————
NonEmptyString was called with an INVALID String.
input: ""
check: input.nonEmpty
———————————————————————————————————————————————————————————————————————————
Neotype integrates with the following libraries:
- JSON
- DATABASE
- MISCELLANEOUS
- zio-test
DeriveGen
- zio-config
- zio-schema
- tapir
- chimney
- caliban
- zio-test
import neotype.*
type NonEmptyString = NonEmptyString.Type
object NonEmptyString extends Newtype[String]:
override inline def validate(value: String): Result =
if value.nonEmpty then true else "String must not be empty"
import neotype.interop.ziojson.given
import zio.json.*
case class Person(name: NonEmptyString, age: Int) derives JsonCodec
val parsed = """{"name": "Kit", "age": 30}""".fromJson[Person]
// Right(Person(NonEmptyString("Kit"), 30))
val failed = """{"name": "", "age": 30}""".fromJson[Person]
// Left(".name(String must not be empty)")
By importing neotype.ziojson.given
, we automatically generate a JsonCodec
for NonEmptyString
. Custom
failure messages are also supported (by overriding def failureMessage
in the Newtype definition).
Note that import neotype.interop.ziojson.given
needs to be in the same file as Person
, not NonEmptyString
.
The generated JsonCodec
is not made available to the entire project, but only to the file where it is imported.