Skip to content

Commit

Permalink
Add spec method to Exact (#20)
Browse files Browse the repository at this point in the history
* Change Exact to abstract class

* Fix README

* Turn Exact back to interface but keep spec function

* Change the position of example with delegation

* Fix documentation

* Pass ExactError on ensure

Co-authored-by: Simon Vergauwen <nomisRev@users.noreply.github.com>

---------

Co-authored-by: Simon Vergauwen <nomisRev@users.noreply.github.com>
  • Loading branch information
ustitc and nomisRev authored May 30, 2023
1 parent 9027a29 commit 07ec53d
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 140 deletions.
65 changes: 45 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.

Expand All @@ -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<String, NotBlankString> by exact({
ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") }
NotBlankString(raw)
})
value class NotBlankString private constructor(val value: String) {
companion object : Exact<String, NotBlankString> {
override fun Raise<ExactError>.spec(raw: String): NotBlankString {
ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") }
return NotBlankString(raw)
}
}
}
```

Expand All @@ -48,31 +50,54 @@ Either.Left(ExactError(message=Cannot be blank.))
<!--- KNIT example-readme-01.kt -->
<!--- TEST -->

You can also define `Exact` by using Kotlin delegation.
<!--- INCLUDE
import arrow.core.raise.ensure
import arrow.exact.Exact
import arrow.exact.ExactError
-->
```kotlin
@JvmInline
value class NotBlankString private constructor(val value: String) {
companion object : Exact<String, NotBlankString> by Exact({
ensure(it.isNotBlank()) { ExactError("Cannot be blank.") }
NotBlankString(it)
})
}
```
<!--- KNIT example-readme-02.kt -->

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.
<!--- INCLUDE
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<String, NotBlankString> by exact({
ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") }
NotBlankString(raw)
})
@JvmInline
value class NotBlankString private constructor(val value: String) {
companion object : Exact<String, NotBlankString> {
override fun Raise<ExactError>.spec(raw: String): NotBlankString {
ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") }
return NotBlankString(raw)
}
}
}
-->

```kotlin
@JvmInline
value class NotBlankTrimmedString private constructor(val value: String) {
companion object : Exact<String, NotBlankTrimmedString> by exact({
val notBlank = ensure(NotBlankString)
NotBlankTrimmedString(notBlank.value.trim())
})
value class NotBlankTrimmedString private constructor(val value: String) {
companion object : Exact<String, NotBlankTrimmedString> {
override fun Raise<ExactError>.spec(raw: String): NotBlankTrimmedString {
ensure(raw, NotBlankString)
return NotBlankTrimmedString(raw.trim())
}
}
}
```

<!--- KNIT example-readme-02.kt -->
<!--- KNIT example-readme-03.kt -->
12 changes: 7 additions & 5 deletions guide/src/commonMain/kotlin/examples/example-exact-01.kt
Original file line number Diff line number Diff line change
@@ -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<String, NotBlankString> by exact({
ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") }
NotBlankString(raw)
})
companion object : Exact<String, NotBlankString> {
override fun Raise<ExactError>.spec(raw: String): NotBlankString {
ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") }
return NotBlankString(raw)
}
}
}

fun example() {
Expand Down
16 changes: 4 additions & 12 deletions guide/src/commonMain/kotlin/examples/example-exact-02.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,11 @@ package arrow.exact.knit.example.exampleExact02
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<String, NotBlankString> by exact({
ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") }
NotBlankString(raw)
})
}

@JvmInline
value class NotBlankTrimmedString private constructor(val value: String) {
companion object : Exact<String, NotBlankTrimmedString> by exact({
val notBlank = ensure(NotBlankString)
NotBlankTrimmedString(notBlank.value.trim())
value class NotBlankString private constructor(val value: String) {
companion object : Exact<String, NotBlankString> by Exact({
ensure(it.isNotBlank()) { ExactError("Cannot be blank.") }
NotBlankString(it)
})
}
39 changes: 16 additions & 23 deletions guide/src/commonMain/kotlin/examples/example-exact-03.kt
Original file line number Diff line number Diff line change
@@ -1,34 +1,27 @@
// 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<String, NotBlankTrimmedString> by exact({
ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") }
NotBlankTrimmedString(raw.trim())
})
}

sealed interface UsernameError {
object Invalid : UsernameError
data class Offensive(val username: String) : UsernameError
class NotBlankString private constructor(val value: String) {
companion object : Exact<String, NotBlankString> {
override fun Raise<ExactError>.spec(raw: String): NotBlankString {
ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") }
return NotBlankString(raw)
}
}
}

@JvmInline
value class Username private constructor(val value: String) {
companion object : ExactEither<UsernameError, String, Username> 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)
})
value class NotBlankTrimmedString private constructor(val value: String) {
companion object : Exact<String, NotBlankTrimmedString> {
override fun Raise<ExactError>.spec(raw: String): NotBlankTrimmedString {
ensure(raw, NotBlankString)
return NotBlankTrimmedString(raw.trim())
}
}
}
39 changes: 39 additions & 0 deletions guide/src/commonMain/kotlin/examples/example-exact-04.kt
Original file line number Diff line number Diff line change
@@ -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<String, NotBlankTrimmedString> {
override fun Raise<ExactError>.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<UsernameError, String, Username> {
override fun Raise<UsernameError>.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)
}
}
}
14 changes: 8 additions & 6 deletions guide/src/commonMain/kotlin/examples/example-readme-01.kt
Original file line number Diff line number Diff line change
@@ -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<String, NotBlankString> by exact({
ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") }
NotBlankString(raw)
})
value class NotBlankString private constructor(val value: String) {
companion object : Exact<String, NotBlankString> {
override fun Raise<ExactError>.spec(raw: String): NotBlankString {
ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") }
return NotBlankString(raw)
}
}
}

fun example() {
Expand Down
18 changes: 5 additions & 13 deletions guide/src/commonMain/kotlin/examples/example-readme-02.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,11 @@ package arrow.exact.knit.example.exampleReadme02
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<String, NotBlankString> by exact({
ensure(raw.isNotBlank()) { ExactError("Cannot be blank.") }
NotBlankString(raw)
})
}

@JvmInline
value class NotBlankTrimmedString private constructor(val value: String) {
companion object : Exact<String, NotBlankTrimmedString> by exact({
val notBlank = ensure(NotBlankString)
NotBlankTrimmedString(notBlank.value.trim())
})
value class NotBlankString private constructor(val value: String) {
companion object : Exact<String, NotBlankString> by Exact({
ensure(it.isNotBlank()) { ExactError("Cannot be blank.") }
NotBlankString(it)
})
}
28 changes: 28 additions & 0 deletions guide/src/commonMain/kotlin/examples/example-readme-03.kt
Original file line number Diff line number Diff line change
@@ -0,0 +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<String, NotBlankString> {
override fun Raise<ExactError>.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<String, NotBlankTrimmedString> {
override fun Raise<ExactError>.spec(raw: String): NotBlankTrimmedString {
ensure(raw, NotBlankString)
return NotBlankTrimmedString(raw.trim())
}
}
}
Loading

0 comments on commit 07ec53d

Please sign in to comment.