From 63fc030c5b2b7ed6ec01731403a4a7577a091bdd Mon Sep 17 00:00:00 2001 From: Ruslan Ustits Date: Sun, 28 May 2023 18:57:50 +0400 Subject: [PATCH 1/6] Change Exact to abstract class --- README.md | 42 ++++++---- .../kotlin/examples/example-exact-01.kt | 12 +-- .../kotlin/examples/example-exact-02.kt | 26 +++--- .../kotlin/examples/example-exact-03.kt | 37 ++++---- .../kotlin/examples/example-readme-01.kt | 14 ++-- .../kotlin/examples/example-readme-02.kt | 28 ++++--- src/commonMain/kotlin/arrow/exact/Exact.kt | 84 +++++++++++-------- src/commonMain/kotlin/arrow/exact/ExactDsl.kt | 37 +++----- 8 files changed, 156 insertions(+), 124 deletions(-) diff --git a/README.md b/README.md index 14c85ec..b5356e0 100644 --- a/README.md +++ b/README.md @@ -11,17 +11,19 @@ example easily create a `NotBlankString` type that is a `String` that is not bla the Arrow's `Raise` DSL to `ensure` the value is not blank. ```kotlin +import arrow.core.raise.Raise import arrow.core.raise.ensure import arrow.exact.Exact import arrow.exact.ExactError -import arrow.exact.exact @JvmInline -value class NotBlankString private constructor(val value: String) { - companion object : Exact by exact({ - ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } - NotBlankString(raw) - }) +value class NotBlankString private constructor(val value: String) { + companion object : Exact() { + override fun Raise.spec(raw: String): NotBlankString { + ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } + return NotBlankString(raw) + } + } } ``` @@ -52,26 +54,32 @@ You can define a second type `NotBlankTrimmedString` that is a `NotBlankString` trimmed. Since the `exact` constructor allows us to compose `Exact` instances, we can easily reuse the `NotBlankString` type. ```kotlin @JvmInline -value class NotBlankTrimmedString private constructor(val value: String) { - companion object : Exact by exact({ - val notBlank = ensure(NotBlankString) - NotBlankTrimmedString(notBlank.value.trim()) - }) +value class NotBlankTrimmedString private constructor(val value: String) { + companion object : Exact() { + override fun Raise.spec(raw: String): NotBlankTrimmedString { + ensure(raw, NotBlankString) + return NotBlankTrimmedString(raw.trim()) + } + } } ``` diff --git a/guide/src/commonMain/kotlin/examples/example-exact-01.kt b/guide/src/commonMain/kotlin/examples/example-exact-01.kt index db8f4da..e49359f 100644 --- a/guide/src/commonMain/kotlin/examples/example-exact-01.kt +++ b/guide/src/commonMain/kotlin/examples/example-exact-01.kt @@ -1,17 +1,19 @@ // This file was automatically generated from Exact.kt by Knit tool. Do not edit. package arrow.exact.knit.example.exampleExact01 +import arrow.core.raise.Raise import arrow.core.raise.ensure import arrow.exact.Exact import arrow.exact.ExactError -import arrow.exact.exact @JvmInline value class NotBlankString private constructor(val value: String) { - companion object : Exact by exact({ - ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } - NotBlankString(raw) - }) + companion object : Exact() { + override fun Raise.spec(raw: String): NotBlankString { + ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } + return NotBlankString(raw) + } + } } fun example() { diff --git a/guide/src/commonMain/kotlin/examples/example-exact-02.kt b/guide/src/commonMain/kotlin/examples/example-exact-02.kt index 8a5b2c6..9a147b6 100644 --- a/guide/src/commonMain/kotlin/examples/example-exact-02.kt +++ b/guide/src/commonMain/kotlin/examples/example-exact-02.kt @@ -1,22 +1,28 @@ // This file was automatically generated from Exact.kt by Knit tool. Do not edit. package arrow.exact.knit.example.exampleExact02 +import arrow.core.raise.Raise import arrow.core.raise.ensure import arrow.exact.Exact import arrow.exact.ExactError -import arrow.exact.exact +import arrow.exact.ensure -@JvmInline value class NotBlankString private constructor(val value: String) { - companion object : Exact by exact({ - ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } - NotBlankString(raw) - }) +@JvmInline +value class NotBlankString private constructor(val value: String) { + companion object : Exact() { + override fun Raise.spec(raw: String): NotBlankString { + ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } + return NotBlankString(raw) + } + } } @JvmInline value class NotBlankTrimmedString private constructor(val value: String) { - companion object : Exact by exact({ - val notBlank = ensure(NotBlankString) - NotBlankTrimmedString(notBlank.value.trim()) - }) + companion object : Exact() { + override fun Raise.spec(raw: String): NotBlankTrimmedString { + ensure(raw, NotBlankString) + return NotBlankTrimmedString(raw.trim()) + } + } } diff --git a/guide/src/commonMain/kotlin/examples/example-exact-03.kt b/guide/src/commonMain/kotlin/examples/example-exact-03.kt index d082a4c..7672a5d 100644 --- a/guide/src/commonMain/kotlin/examples/example-exact-03.kt +++ b/guide/src/commonMain/kotlin/examples/example-exact-03.kt @@ -1,18 +1,21 @@ // This file was automatically generated from Exact.kt by Knit tool. Do not edit. package arrow.exact.knit.example.exampleExact03 +import arrow.core.raise.Raise import arrow.core.raise.ensure import arrow.exact.Exact import arrow.exact.ExactEither import arrow.exact.ExactError -import arrow.exact.exact -import arrow.exact.exactEither +import arrow.exact.ensure -@JvmInline value class NotBlankTrimmedString private constructor(val value: String) { - companion object : Exact by exact({ - ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } - NotBlankTrimmedString(raw.trim()) - }) +@JvmInline +value class NotBlankTrimmedString private constructor(val value: String) { + companion object : Exact() { + override fun Raise.spec(raw: String): NotBlankTrimmedString { + ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } + return NotBlankTrimmedString(raw.trim()) + } + } } sealed interface UsernameError { @@ -22,13 +25,15 @@ sealed interface UsernameError { @JvmInline value class Username private constructor(val value: String) { - companion object : ExactEither by exactEither({ - val username = - ensure(NotBlankTrimmedString) { - UsernameError.Invalid - }.value - ensure(username.length < 100) { UsernameError.Invalid } - ensure(username !in listOf("offensive")) { UsernameError.Offensive(username) } - Username(username) - }) + companion object : ExactEither() { + override fun Raise.spec(raw: String): Username { + val username = + ensure(raw, NotBlankTrimmedString) { + UsernameError.Invalid + }.value + ensure(username.length < 100) { UsernameError.Invalid } + ensure(username !in listOf("offensive")) { UsernameError.Offensive(username) } + return Username(username) + } + } } diff --git a/guide/src/commonMain/kotlin/examples/example-readme-01.kt b/guide/src/commonMain/kotlin/examples/example-readme-01.kt index 208b422..8286627 100644 --- a/guide/src/commonMain/kotlin/examples/example-readme-01.kt +++ b/guide/src/commonMain/kotlin/examples/example-readme-01.kt @@ -1,17 +1,19 @@ // This file was automatically generated from README.md by Knit tool. Do not edit. package arrow.exact.knit.example.exampleReadme01 +import arrow.core.raise.Raise import arrow.core.raise.ensure import arrow.exact.Exact import arrow.exact.ExactError -import arrow.exact.exact @JvmInline -value class NotBlankString private constructor(val value: String) { - companion object : Exact by exact({ - ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } - NotBlankString(raw) - }) +value class NotBlankString private constructor(val value: String) { + companion object : Exact() { + override fun Raise.spec(raw: String): NotBlankString { + ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } + return NotBlankString(raw) + } + } } fun example() { diff --git a/guide/src/commonMain/kotlin/examples/example-readme-02.kt b/guide/src/commonMain/kotlin/examples/example-readme-02.kt index f29ec61..f93698e 100644 --- a/guide/src/commonMain/kotlin/examples/example-readme-02.kt +++ b/guide/src/commonMain/kotlin/examples/example-readme-02.kt @@ -1,22 +1,28 @@ // This file was automatically generated from README.md by Knit tool. Do not edit. package arrow.exact.knit.example.exampleReadme02 +import arrow.core.raise.Raise import arrow.core.raise.ensure import arrow.exact.Exact import arrow.exact.ExactError -import arrow.exact.exact +import arrow.exact.ensure -@JvmInline value class NotBlankString private constructor(val value: String) { - companion object : Exact by exact({ - ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } - NotBlankString(raw) - }) +@JvmInline +value class NotBlankString private constructor(val value: String) { + companion object : Exact() { + override fun Raise.spec(raw: String): NotBlankString { + ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } + return NotBlankString(raw) + } + } } @JvmInline -value class NotBlankTrimmedString private constructor(val value: String) { - companion object : Exact by exact({ - val notBlank = ensure(NotBlankString) - NotBlankTrimmedString(notBlank.value.trim()) - }) +value class NotBlankTrimmedString private constructor(val value: String) { + companion object : Exact() { + override fun Raise.spec(raw: String): NotBlankTrimmedString { + ensure(raw, NotBlankString) + return NotBlankTrimmedString(raw.trim()) + } + } } diff --git a/src/commonMain/kotlin/arrow/exact/Exact.kt b/src/commonMain/kotlin/arrow/exact/Exact.kt index fc94b43..05089ef 100644 --- a/src/commonMain/kotlin/arrow/exact/Exact.kt +++ b/src/commonMain/kotlin/arrow/exact/Exact.kt @@ -2,6 +2,7 @@ package arrow.exact import arrow.core.Either import arrow.core.raise.Raise +import arrow.core.raise.either import arrow.core.raise.ensure /** @@ -12,17 +13,19 @@ import arrow.core.raise.ensure * the Arrow's [Raise] DSL to [ensure] the value is not blank. * * ```kotlin + * import arrow.core.raise.Raise * import arrow.core.raise.ensure * import arrow.exact.Exact * import arrow.exact.ExactError - * import arrow.exact.exact * * @JvmInline * value class NotBlankString private constructor(val value: String) { - * companion object : Exact by exact({ - * ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } - * NotBlankString(raw) - * }) + * companion object : Exact() { + * override fun Raise.spec(raw: String): NotBlankString { + * ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } + * return NotBlankString(raw) + * } + * } * } * ``` * @@ -51,32 +54,38 @@ import arrow.core.raise.ensure * trimmed. Since the `exact` constructor allows us to compose `Exact` instances, we can easily * reuse the `NotBlankString` type. * * ```kotlin * @JvmInline * value class NotBlankTrimmedString private constructor(val value: String) { - * companion object : Exact by exact({ - * val notBlank = ensure(NotBlankString) - * NotBlankTrimmedString(notBlank.value.trim()) - * }) + * companion object : Exact() { + * override fun Raise.spec(raw: String): NotBlankTrimmedString { + * ensure(raw, NotBlankString) + * return NotBlankTrimmedString(raw.trim()) + * } + * } * } * ``` * * * @see ExactEither if you need to return an [Either] with a custom error type. */ -public fun interface Exact : ExactEither +public abstract class Exact : ExactEither() // TODO: Should we just use `String` ??? public data class ExactError(val message: String) @@ -87,18 +96,21 @@ public data class ExactError(val message: String) * [ExactError], we can easily combine the two by mapping from [ExactError] to our custom [E] type. * * * ```kotlin @@ -109,22 +121,26 @@ public data class ExactError(val message: String) * * @JvmInline * value class Username private constructor(val value: String) { - * companion object : ExactEither by exactEither({ - * val username = - * ensure(NotBlankTrimmedString) { - * UsernameError.Invalid - * }.value - * ensure(username.length < 100) { UsernameError.Invalid } - * ensure(username !in listOf("offensive")) { UsernameError.Offensive(username) } - * Username(username) - * }) + * companion object : ExactEither() { + * override fun Raise.spec(raw: String): Username { + * val username = + * ensure(raw, NotBlankTrimmedString) { + * UsernameError.Invalid + * }.value + * ensure(username.length < 100) { UsernameError.Invalid } + * ensure(username !in listOf("offensive")) { UsernameError.Offensive(username) } + * return Username(username) + * } + * } * } * ``` * */ -public fun interface ExactEither { +public abstract class ExactEither { + + protected abstract fun Raise.spec(raw: A): R - public fun from(value: A): Either + public fun from(value: A): Either = either { spec(value) } public fun fromOrNull(value: A): R? = from(value).getOrNull() diff --git a/src/commonMain/kotlin/arrow/exact/ExactDsl.kt b/src/commonMain/kotlin/arrow/exact/ExactDsl.kt index 5e71e14..62209eb 100644 --- a/src/commonMain/kotlin/arrow/exact/ExactDsl.kt +++ b/src/commonMain/kotlin/arrow/exact/ExactDsl.kt @@ -2,33 +2,20 @@ package arrow.exact import arrow.core.Either import arrow.core.raise.Raise -import arrow.core.raise.either +import arrow.core.raise.RaiseDSL -@DslMarker public annotation class ExactDsl - -@ExactDsl -public fun exact(construct: ExactScope.() -> R): Exact = - Exact { value -> either { construct(ExactScope(value, this)) } } - -@ExactDsl -public fun exactEither(construct: ExactScope.() -> R): ExactEither = - ExactEither { value -> either { construct(ExactScope(value, this)) } } - -public class ExactScope(public val raw: A, raise: Raise) : Raise by raise { - - @ExactDsl - public fun ensure(exact: ExactEither): B { - return when (val result = exact.from(raw)) { - is Either.Left -> raise(result.value) - is Either.Right -> result.value - } +@RaiseDSL +public inline fun Raise.ensure(raw: A, exact: ExactEither): B { + return when (val result = exact.from(raw)) { + is Either.Left -> raise(result.value) + is Either.Right -> result.value } +} - @ExactDsl - public fun ensure(exact: Exact, error: () -> E): B { - return when (val result = exact.from(raw)) { - is Either.Left -> raise(error()) - is Either.Right -> result.value - } +@RaiseDSL +public inline fun Raise.ensure(raw: A, exact: Exact, error: () -> Error): B { + return when (val result = exact.from(raw)) { + is Either.Left -> raise(error()) + is Either.Right -> result.value } } From 686ea1d70ad973394c1aa0ce2f260c848b933699 Mon Sep 17 00:00:00 2001 From: Ruslan Ustits Date: Sun, 28 May 2023 19:22:38 +0400 Subject: [PATCH 2/6] Fix README --- README.md | 12 ++++++------ .../commonMain/kotlin/examples/example-readme-01.kt | 4 ++-- .../commonMain/kotlin/examples/example-readme-02.kt | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index b5356e0..20da843 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Module Arrow Exact +# Arrow Exact Arrow Exact allows you to use Kotlin's type system to enforce exactness of data structures. @@ -21,8 +21,8 @@ value class NotBlankString private constructor(val value: String) { companion object : Exact() { override fun Raise.spec(raw: String): NotBlankString { ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } - return NotBlankString(raw) - } + return NotBlankString(raw) + } } } ``` @@ -75,10 +75,10 @@ value class NotBlankString private constructor(val value: String) { @JvmInline value class NotBlankTrimmedString private constructor(val value: String) { companion object : Exact() { - override fun Raise.spec(raw: String): NotBlankTrimmedString { + override fun Raise.spec(raw: String): NotBlankTrimmedString { ensure(raw, NotBlankString) - return NotBlankTrimmedString(raw.trim()) - } + return NotBlankTrimmedString(raw.trim()) + } } } ``` diff --git a/guide/src/commonMain/kotlin/examples/example-readme-01.kt b/guide/src/commonMain/kotlin/examples/example-readme-01.kt index 8286627..3c3f2b2 100644 --- a/guide/src/commonMain/kotlin/examples/example-readme-01.kt +++ b/guide/src/commonMain/kotlin/examples/example-readme-01.kt @@ -11,8 +11,8 @@ value class NotBlankString private constructor(val value: String) { companion object : Exact() { override fun Raise.spec(raw: String): NotBlankString { ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } - return NotBlankString(raw) - } + return NotBlankString(raw) + } } } diff --git a/guide/src/commonMain/kotlin/examples/example-readme-02.kt b/guide/src/commonMain/kotlin/examples/example-readme-02.kt index f93698e..4951646 100644 --- a/guide/src/commonMain/kotlin/examples/example-readme-02.kt +++ b/guide/src/commonMain/kotlin/examples/example-readme-02.kt @@ -20,9 +20,9 @@ value class NotBlankString private constructor(val value: String) { @JvmInline value class NotBlankTrimmedString private constructor(val value: String) { companion object : Exact() { - override fun Raise.spec(raw: String): NotBlankTrimmedString { + override fun Raise.spec(raw: String): NotBlankTrimmedString { ensure(raw, NotBlankString) - return NotBlankTrimmedString(raw.trim()) - } + return NotBlankTrimmedString(raw.trim()) + } } } From 83e43d3d2b1b062519b85cbc9da9462aa2d5ba3e Mon Sep 17 00:00:00 2001 From: Ruslan Ustits Date: Sun, 28 May 2023 21:50:06 +0400 Subject: [PATCH 3/6] Turn Exact back to interface but keep spec function --- README.md | 25 ++++++++++-- .../kotlin/examples/example-exact-01.kt | 2 +- .../kotlin/examples/example-exact-02.kt | 4 +- .../kotlin/examples/example-exact-03.kt | 35 +++-------------- .../kotlin/examples/example-exact-04.kt | 39 +++++++++++++++++++ .../kotlin/examples/example-readme-01.kt | 2 +- .../kotlin/examples/example-readme-02.kt | 4 +- .../kotlin/examples/example-readme-03.kt | 14 +++++++ src/commonMain/kotlin/arrow/exact/Exact.kt | 37 +++++++++++++----- 9 files changed, 112 insertions(+), 50 deletions(-) create mode 100644 guide/src/commonMain/kotlin/examples/example-exact-04.kt create mode 100644 guide/src/commonMain/kotlin/examples/example-readme-03.kt diff --git a/README.md b/README.md index 20da843..79e1bdb 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ import arrow.exact.ExactError @JvmInline value class NotBlankString private constructor(val value: String) { - companion object : Exact() { + companion object : Exact { override fun Raise.spec(raw: String): NotBlankString { ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } return NotBlankString(raw) @@ -51,7 +51,7 @@ Either.Left(ExactError(message=Cannot be blank.)) You can define a second type `NotBlankTrimmedString` that is a `NotBlankString` that is also -trimmed. Since the `exact` constructor allows us to compose `Exact` instances, we can easily +trimmed. Since the `ensure` allows us to compose `Exact` instances, we can easily reuse the `NotBlankString` type. + +You can also define `Exact` by using Kotlin delegation + +```kotlin +@JvmInline +value class NotBlankString private constructor(val value: String) { + companion object : Exact by Exact({ + ensure(it.isNotBlank()) { ExactError("Cannot be blank.") } + NotBlankString(it) + }) +} +``` + \ No newline at end of file diff --git a/guide/src/commonMain/kotlin/examples/example-exact-01.kt b/guide/src/commonMain/kotlin/examples/example-exact-01.kt index e49359f..38254ce 100644 --- a/guide/src/commonMain/kotlin/examples/example-exact-01.kt +++ b/guide/src/commonMain/kotlin/examples/example-exact-01.kt @@ -8,7 +8,7 @@ import arrow.exact.ExactError @JvmInline value class NotBlankString private constructor(val value: String) { - companion object : Exact() { + companion object : Exact { override fun Raise.spec(raw: String): NotBlankString { ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } return NotBlankString(raw) diff --git a/guide/src/commonMain/kotlin/examples/example-exact-02.kt b/guide/src/commonMain/kotlin/examples/example-exact-02.kt index 9a147b6..5b11cac 100644 --- a/guide/src/commonMain/kotlin/examples/example-exact-02.kt +++ b/guide/src/commonMain/kotlin/examples/example-exact-02.kt @@ -9,7 +9,7 @@ import arrow.exact.ensure @JvmInline value class NotBlankString private constructor(val value: String) { - companion object : Exact() { + companion object : Exact { override fun Raise.spec(raw: String): NotBlankString { ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } return NotBlankString(raw) @@ -19,7 +19,7 @@ value class NotBlankString private constructor(val value: String) { @JvmInline value class NotBlankTrimmedString private constructor(val value: String) { - companion object : Exact() { + companion object : Exact { override fun Raise.spec(raw: String): NotBlankTrimmedString { ensure(raw, NotBlankString) return NotBlankTrimmedString(raw.trim()) diff --git a/guide/src/commonMain/kotlin/examples/example-exact-03.kt b/guide/src/commonMain/kotlin/examples/example-exact-03.kt index 7672a5d..2f43f1f 100644 --- a/guide/src/commonMain/kotlin/examples/example-exact-03.kt +++ b/guide/src/commonMain/kotlin/examples/example-exact-03.kt @@ -1,39 +1,14 @@ // This file was automatically generated from Exact.kt by Knit tool. Do not edit. package arrow.exact.knit.example.exampleExact03 -import arrow.core.raise.Raise import arrow.core.raise.ensure import arrow.exact.Exact -import arrow.exact.ExactEither import arrow.exact.ExactError -import arrow.exact.ensure @JvmInline -value class NotBlankTrimmedString private constructor(val value: String) { - companion object : Exact() { - override fun Raise.spec(raw: String): NotBlankTrimmedString { - ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } - return NotBlankTrimmedString(raw.trim()) - } - } -} - -sealed interface UsernameError { - object Invalid : UsernameError - data class Offensive(val username: String) : UsernameError -} - -@JvmInline -value class Username private constructor(val value: String) { - companion object : ExactEither() { - override fun Raise.spec(raw: String): Username { - val username = - ensure(raw, NotBlankTrimmedString) { - UsernameError.Invalid - }.value - ensure(username.length < 100) { UsernameError.Invalid } - ensure(username !in listOf("offensive")) { UsernameError.Offensive(username) } - return Username(username) - } - } +value class NotBlankString private constructor(val value: String) { + companion object : Exact by Exact({ + ensure(it.isNotBlank()) { ExactError("Cannot be blank.") } + NotBlankString(it) + }) } diff --git a/guide/src/commonMain/kotlin/examples/example-exact-04.kt b/guide/src/commonMain/kotlin/examples/example-exact-04.kt new file mode 100644 index 0000000..63ca190 --- /dev/null +++ b/guide/src/commonMain/kotlin/examples/example-exact-04.kt @@ -0,0 +1,39 @@ +// This file was automatically generated from Exact.kt by Knit tool. Do not edit. +package arrow.exact.knit.example.exampleExact04 + +import arrow.core.raise.Raise +import arrow.core.raise.ensure +import arrow.exact.Exact +import arrow.exact.ExactEither +import arrow.exact.ExactError +import arrow.exact.ensure + +@JvmInline +value class NotBlankTrimmedString private constructor(val value: String) { + companion object : Exact { + override fun Raise.spec(raw: String): NotBlankTrimmedString { + ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } + return NotBlankTrimmedString(raw.trim()) + } + } +} + +sealed interface UsernameError { + object Invalid : UsernameError + data class Offensive(val username: String) : UsernameError +} + +@JvmInline +value class Username private constructor(val value: String) { + companion object : ExactEither { + override fun Raise.spec(raw: String): Username { + val username = + ensure(raw, NotBlankTrimmedString) { + UsernameError.Invalid + }.value + ensure(username.length < 100) { UsernameError.Invalid } + ensure(username !in listOf("offensive")) { UsernameError.Offensive(username) } + return Username(username) + } + } +} diff --git a/guide/src/commonMain/kotlin/examples/example-readme-01.kt b/guide/src/commonMain/kotlin/examples/example-readme-01.kt index 3c3f2b2..a3c6fed 100644 --- a/guide/src/commonMain/kotlin/examples/example-readme-01.kt +++ b/guide/src/commonMain/kotlin/examples/example-readme-01.kt @@ -8,7 +8,7 @@ import arrow.exact.ExactError @JvmInline value class NotBlankString private constructor(val value: String) { - companion object : Exact() { + companion object : Exact { override fun Raise.spec(raw: String): NotBlankString { ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } return NotBlankString(raw) diff --git a/guide/src/commonMain/kotlin/examples/example-readme-02.kt b/guide/src/commonMain/kotlin/examples/example-readme-02.kt index 4951646..3f183e8 100644 --- a/guide/src/commonMain/kotlin/examples/example-readme-02.kt +++ b/guide/src/commonMain/kotlin/examples/example-readme-02.kt @@ -9,7 +9,7 @@ import arrow.exact.ensure @JvmInline value class NotBlankString private constructor(val value: String) { - companion object : Exact() { + companion object : Exact { override fun Raise.spec(raw: String): NotBlankString { ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } return NotBlankString(raw) @@ -19,7 +19,7 @@ value class NotBlankString private constructor(val value: String) { @JvmInline value class NotBlankTrimmedString private constructor(val value: String) { - companion object : Exact() { + companion object : Exact { override fun Raise.spec(raw: String): NotBlankTrimmedString { ensure(raw, NotBlankString) return NotBlankTrimmedString(raw.trim()) diff --git a/guide/src/commonMain/kotlin/examples/example-readme-03.kt b/guide/src/commonMain/kotlin/examples/example-readme-03.kt new file mode 100644 index 0000000..cca5229 --- /dev/null +++ b/guide/src/commonMain/kotlin/examples/example-readme-03.kt @@ -0,0 +1,14 @@ +// This file was automatically generated from README.md by Knit tool. Do not edit. +package arrow.exact.knit.example.exampleReadme03 + +import arrow.core.raise.ensure +import arrow.exact.Exact +import arrow.exact.ExactError + +@JvmInline +value class NotBlankString private constructor(val value: String) { + companion object : Exact by Exact({ + ensure(it.isNotBlank()) { ExactError("Cannot be blank.") } + NotBlankString(it) + }) +} diff --git a/src/commonMain/kotlin/arrow/exact/Exact.kt b/src/commonMain/kotlin/arrow/exact/Exact.kt index 05089ef..53421f0 100644 --- a/src/commonMain/kotlin/arrow/exact/Exact.kt +++ b/src/commonMain/kotlin/arrow/exact/Exact.kt @@ -20,7 +20,7 @@ import arrow.core.raise.ensure * * @JvmInline * value class NotBlankString private constructor(val value: String) { - * companion object : Exact() { + * companion object : Exact { * override fun Raise.spec(raw: String): NotBlankString { * ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } * return NotBlankString(raw) @@ -51,7 +51,7 @@ import arrow.core.raise.ensure * * * You can define a second type `NotBlankTrimmedString` that is a `NotBlankString` that is also - * trimmed. Since the `exact` constructor allows us to compose `Exact` instances, we can easily + * trimmed. Since the `ensure` constructor allows us to compose `Exact` instances, we can easily * reuse the `NotBlankString` type. * * + * You can also define [Exact] by using Kotlin delegation + * + * ```kotlin + * @JvmInline + * value class NotBlankString private constructor(val value: String) { + * companion object : Exact by Exact({ + * ensure(it.isNotBlank()) { ExactError("Cannot be blank.") } + * NotBlankString(it) + * }) + * } + * ``` + * + * * @see ExactEither if you need to return an [Either] with a custom error type. */ -public abstract class Exact : ExactEither() +public fun interface Exact : ExactEither // TODO: Should we just use `String` ??? public data class ExactError(val message: String) @@ -105,7 +122,7 @@ public data class ExactError(val message: String) * * @JvmInline * value class NotBlankTrimmedString private constructor(val value: String) { - * companion object : Exact() { + * companion object : Exact { * override fun Raise.spec(raw: String): NotBlankTrimmedString { * ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } * return NotBlankTrimmedString(raw.trim()) @@ -121,7 +138,7 @@ public data class ExactError(val message: String) * * @JvmInline * value class Username private constructor(val value: String) { - * companion object : ExactEither() { + * companion object : ExactEither { * override fun Raise.spec(raw: String): Username { * val username = * ensure(raw, NotBlankTrimmedString) { @@ -134,11 +151,11 @@ public data class ExactError(val message: String) * } * } * ``` - * + * */ -public abstract class ExactEither { +public fun interface ExactEither { - protected abstract fun Raise.spec(raw: A): R + public fun Raise.spec(raw: A): R public fun from(value: A): Either = either { spec(value) } From 0348de63287729e7139c2cf02fdb7f6af26e699b Mon Sep 17 00:00:00 2001 From: Ruslan Ustits Date: Sun, 28 May 2023 22:48:59 +0400 Subject: [PATCH 4/6] Change the position of example with delegation --- README.md | 36 +++++++++--------- .../kotlin/examples/example-exact-02.kt | 22 ++--------- .../kotlin/examples/example-exact-03.kt | 22 +++++++++-- .../kotlin/examples/example-readme-02.kt | 22 ++--------- .../kotlin/examples/example-readme-03.kt | 22 +++++++++-- src/commonMain/kotlin/arrow/exact/Exact.kt | 38 +++++++++---------- 6 files changed, 81 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index 79e1bdb..cdbfb08 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,23 @@ Either.Left(ExactError(message=Cannot be blank.)) +You can also define `Exact` by using Kotlin delegation. + +```kotlin +@JvmInline +value class NotBlankString private constructor(val value: String) { + companion object : Exact by Exact({ + ensure(it.isNotBlank()) { ExactError("Cannot be blank.") } + NotBlankString(it) + }) +} +``` + + You can define a second type `NotBlankTrimmedString` that is a `NotBlankString` that is also trimmed. Since the `ensure` allows us to compose `Exact` instances, we can easily reuse the `NotBlankString` type. @@ -83,21 +100,4 @@ value class NotBlankTrimmedString private constructor(val value: String) { } ``` - - -You can also define `Exact` by using Kotlin delegation - -```kotlin -@JvmInline -value class NotBlankString private constructor(val value: String) { - companion object : Exact by Exact({ - ensure(it.isNotBlank()) { ExactError("Cannot be blank.") } - NotBlankString(it) - }) -} -``` - \ No newline at end of file + diff --git a/guide/src/commonMain/kotlin/examples/example-exact-02.kt b/guide/src/commonMain/kotlin/examples/example-exact-02.kt index 5b11cac..d8df775 100644 --- a/guide/src/commonMain/kotlin/examples/example-exact-02.kt +++ b/guide/src/commonMain/kotlin/examples/example-exact-02.kt @@ -1,28 +1,14 @@ // This file was automatically generated from Exact.kt by Knit tool. Do not edit. package arrow.exact.knit.example.exampleExact02 -import arrow.core.raise.Raise import arrow.core.raise.ensure import arrow.exact.Exact import arrow.exact.ExactError -import arrow.exact.ensure @JvmInline value class NotBlankString private constructor(val value: String) { - companion object : Exact { - override fun Raise.spec(raw: String): NotBlankString { - ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } - return NotBlankString(raw) - } - } -} - -@JvmInline -value class NotBlankTrimmedString private constructor(val value: String) { - companion object : Exact { - override fun Raise.spec(raw: String): NotBlankTrimmedString { - ensure(raw, NotBlankString) - return NotBlankTrimmedString(raw.trim()) - } - } + companion object : Exact by Exact({ + ensure(it.isNotBlank()) { ExactError("Cannot be blank.") } + NotBlankString(it) + }) } diff --git a/guide/src/commonMain/kotlin/examples/example-exact-03.kt b/guide/src/commonMain/kotlin/examples/example-exact-03.kt index 2f43f1f..c438763 100644 --- a/guide/src/commonMain/kotlin/examples/example-exact-03.kt +++ b/guide/src/commonMain/kotlin/examples/example-exact-03.kt @@ -1,14 +1,28 @@ // This file was automatically generated from Exact.kt by Knit tool. Do not edit. package arrow.exact.knit.example.exampleExact03 +import arrow.core.raise.Raise import arrow.core.raise.ensure import arrow.exact.Exact import arrow.exact.ExactError +import arrow.exact.ensure @JvmInline value class NotBlankString private constructor(val value: String) { - companion object : Exact by Exact({ - ensure(it.isNotBlank()) { ExactError("Cannot be blank.") } - NotBlankString(it) - }) + companion object : Exact { + override fun Raise.spec(raw: String): NotBlankString { + ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } + return NotBlankString(raw) + } + } +} + +@JvmInline +value class NotBlankTrimmedString private constructor(val value: String) { + companion object : Exact { + override fun Raise.spec(raw: String): NotBlankTrimmedString { + ensure(raw, NotBlankString) + return NotBlankTrimmedString(raw.trim()) + } + } } diff --git a/guide/src/commonMain/kotlin/examples/example-readme-02.kt b/guide/src/commonMain/kotlin/examples/example-readme-02.kt index 3f183e8..93b772e 100644 --- a/guide/src/commonMain/kotlin/examples/example-readme-02.kt +++ b/guide/src/commonMain/kotlin/examples/example-readme-02.kt @@ -1,28 +1,14 @@ // This file was automatically generated from README.md by Knit tool. Do not edit. package arrow.exact.knit.example.exampleReadme02 -import arrow.core.raise.Raise import arrow.core.raise.ensure import arrow.exact.Exact import arrow.exact.ExactError -import arrow.exact.ensure @JvmInline value class NotBlankString private constructor(val value: String) { - companion object : Exact { - override fun Raise.spec(raw: String): NotBlankString { - ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } - return NotBlankString(raw) - } - } -} - -@JvmInline -value class NotBlankTrimmedString private constructor(val value: String) { - companion object : Exact { - override fun Raise.spec(raw: String): NotBlankTrimmedString { - ensure(raw, NotBlankString) - return NotBlankTrimmedString(raw.trim()) - } - } + companion object : Exact by Exact({ + ensure(it.isNotBlank()) { ExactError("Cannot be blank.") } + NotBlankString(it) + }) } diff --git a/guide/src/commonMain/kotlin/examples/example-readme-03.kt b/guide/src/commonMain/kotlin/examples/example-readme-03.kt index cca5229..9134af8 100644 --- a/guide/src/commonMain/kotlin/examples/example-readme-03.kt +++ b/guide/src/commonMain/kotlin/examples/example-readme-03.kt @@ -1,14 +1,28 @@ // This file was automatically generated from README.md by Knit tool. Do not edit. package arrow.exact.knit.example.exampleReadme03 +import arrow.core.raise.Raise import arrow.core.raise.ensure import arrow.exact.Exact import arrow.exact.ExactError +import arrow.exact.ensure @JvmInline value class NotBlankString private constructor(val value: String) { - companion object : Exact by Exact({ - ensure(it.isNotBlank()) { ExactError("Cannot be blank.") } - NotBlankString(it) - }) + companion object : Exact { + override fun Raise.spec(raw: String): NotBlankString { + ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } + return NotBlankString(raw) + } + } +} + +@JvmInline +value class NotBlankTrimmedString private constructor(val value: String) { + companion object : Exact { + override fun Raise.spec(raw: String): NotBlankTrimmedString { + ensure(raw, NotBlankString) + return NotBlankTrimmedString(raw.trim()) + } + } } diff --git a/src/commonMain/kotlin/arrow/exact/Exact.kt b/src/commonMain/kotlin/arrow/exact/Exact.kt index 53421f0..57c2e2b 100644 --- a/src/commonMain/kotlin/arrow/exact/Exact.kt +++ b/src/commonMain/kotlin/arrow/exact/Exact.kt @@ -50,8 +50,25 @@ import arrow.core.raise.ensure * * * + * You can also define [Exact] by using Kotlin delegation. + * + * ```kotlin + * @JvmInline + * value class NotBlankString private constructor(val value: String) { + * companion object : Exact by Exact({ + * ensure(it.isNotBlank()) { ExactError("Cannot be blank.") } + * NotBlankString(it) + * }) + * } + * ``` + * + * * You can define a second type `NotBlankTrimmedString` that is a `NotBlankString` that is also - * trimmed. Since the `ensure` constructor allows us to compose `Exact` instances, we can easily + * trimmed. Since the [ensure] allows us to compose [Exact] instances, we can easily * reuse the `NotBlankString` type. * - * - * You can also define [Exact] by using Kotlin delegation - * - * ```kotlin - * @JvmInline - * value class NotBlankString private constructor(val value: String) { - * companion object : Exact by Exact({ - * ensure(it.isNotBlank()) { ExactError("Cannot be blank.") } - * NotBlankString(it) - * }) - * } - * ``` * * * @see ExactEither if you need to return an [Either] with a custom error type. */ -public fun interface Exact : ExactEither +public fun interface Exact : ExactEither // TODO: Should we just use `String` ??? public data class ExactError(val message: String) From 7d19191601b565b500883e14224794411224fee5 Mon Sep 17 00:00:00 2001 From: Ruslan Ustits Date: Tue, 30 May 2023 20:59:20 +0400 Subject: [PATCH 5/6] Fix documentation --- guide/src/commonMain/kotlin/examples/example-exact-03.kt | 3 +-- src/commonMain/kotlin/arrow/exact/Exact.kt | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/guide/src/commonMain/kotlin/examples/example-exact-03.kt b/guide/src/commonMain/kotlin/examples/example-exact-03.kt index c438763..40f11a4 100644 --- a/guide/src/commonMain/kotlin/examples/example-exact-03.kt +++ b/guide/src/commonMain/kotlin/examples/example-exact-03.kt @@ -7,8 +7,7 @@ import arrow.exact.Exact import arrow.exact.ExactError import arrow.exact.ensure -@JvmInline -value class NotBlankString private constructor(val value: String) { +class NotBlankString private constructor(val value: String) { companion object : Exact { override fun Raise.spec(raw: String): NotBlankString { ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } diff --git a/src/commonMain/kotlin/arrow/exact/Exact.kt b/src/commonMain/kotlin/arrow/exact/Exact.kt index 57c2e2b..df076c5 100644 --- a/src/commonMain/kotlin/arrow/exact/Exact.kt +++ b/src/commonMain/kotlin/arrow/exact/Exact.kt @@ -77,8 +77,7 @@ import arrow.core.raise.ensure * import arrow.exact.ExactError * import arrow.exact.ensure * - * @JvmInline - * value class NotBlankString private constructor(val value: String) { + * class NotBlankString private constructor(val value: String) { * companion object : Exact { * override fun Raise.spec(raw: String): NotBlankString { * ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") } From 00c580b32a57975521f88b9fab17641c3bae0718 Mon Sep 17 00:00:00 2001 From: Ruslan Ustits Date: Tue, 30 May 2023 21:02:11 +0400 Subject: [PATCH 6/6] Pass ExactError on ensure Co-authored-by: Simon Vergauwen --- src/commonMain/kotlin/arrow/exact/ExactDsl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commonMain/kotlin/arrow/exact/ExactDsl.kt b/src/commonMain/kotlin/arrow/exact/ExactDsl.kt index 62209eb..f1d2468 100644 --- a/src/commonMain/kotlin/arrow/exact/ExactDsl.kt +++ b/src/commonMain/kotlin/arrow/exact/ExactDsl.kt @@ -13,9 +13,9 @@ public inline fun Raise.ensure(raw: A, exact: ExactEi } @RaiseDSL -public inline fun Raise.ensure(raw: A, exact: Exact, error: () -> Error): B { +public inline fun Raise.ensure(raw: A, exact: Exact, error: (ExactError) -> Error): B { return when (val result = exact.from(raw)) { - is Either.Left -> raise(error()) + is Either.Left -> raise(error(result.value)) is Either.Right -> result.value } }