Skip to content

Commit 7342456

Browse files
authored
Merge pull request #96 from lucapiccinelli/konad
Konad example (#95)
2 parents 9ebd335 + 323b6fa commit 7342456

File tree

5 files changed

+124
-11
lines changed

5 files changed

+124
-11
lines changed

.github/workflows/runOnGitHub.yml

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ on:
1313
jobs:
1414
gradle:
1515
runs-on: ubuntu-latest
16-
env:
17-
TOKEN: ${{ secrets.GITHUB_TOKEN }}
1816
steps:
1917
- uses: actions/checkout@v1
2018
- uses: actions/setup-java@v1
@@ -30,15 +28,6 @@ jobs:
3028
wrapper-cache-enabled: true
3129
dependencies-cache-enabled: true
3230
configuration-cache-enabled: true
33-
- id: buildscan
34-
name: Add Build Scan URLs to Pull Request
35-
uses: mshick/add-pr-comment@v1
36-
if: env.TOKEN
37-
with:
38-
repo-token: ${{ secrets.GITHUB_TOKEN }}
39-
message: |
40-
Buildscan url for run [${{ github.run_id }}](https://github.com/LouisCAD/kotlin-libraries-playground/actions/runs/${{ github.run_id }})
41-
${{ steps.gradle.outputs.build-scan-url }}
4231
- id: artifact
4332
name: Add build scan to the artifact
4433
uses: actions/upload-artifact@v2

kotlin-jvm/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ dependencies {
3636
implementation("com.squareup.sqldelight:sqlite-driver:_")
3737
implementation("com.tinder.statemachine:statemachine:_")
3838
implementation("com.uchuhimo:konf:_")
39+
implementation("io.github.lucapiccinelli:konad:_")
3940
implementation("io.github.serpro69:kotlin-faker:_")
4041
implementation("it.skrape:skrapeit-core:_")
4142
implementation("org.jetbrains.exposed:exposed-core:_")
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
@file:Suppress("PackageDirectoryMismatch")
2+
3+
package playground.konad
4+
5+
import io.konad.*
6+
import io.konad.Maybe.Companion.maybe
7+
import io.konad.Maybe.Companion.nullable
8+
import io.konad.applicative.builders.on
9+
import playground.shouldBe
10+
11+
/**
12+
* lucapiccinelli/konad
13+
*
14+
* [GitHub](https://github.com/lucapiccinelli/konad)
15+
*/
16+
17+
fun main() {
18+
println()
19+
println("# lucapiccinelli/konad")
20+
println("Simple Kotlin monads for the everyday error handling. Brings simple nulls composition")
21+
println()
22+
23+
println("Deal with nullables. How to compose nullables")
24+
25+
var foo: Int? = 1
26+
var bar: String? = "2"
27+
var baz: Float? = 3.0f
28+
29+
fun useThem(x: Int, y: String, z: Float): Int = x + y.toInt() + z.toInt()
30+
31+
val result1: Int? = ::useThem.curry()
32+
.on(foo.maybe)
33+
.on(bar.maybe)
34+
.on(baz.maybe)
35+
.nullable
36+
37+
result1 shouldBe 6
38+
39+
println("How to provide error messages when a null is not an acceptable value")
40+
foo = null
41+
bar = null
42+
baz = null
43+
val result2: Result<Int> = ::useThem.curry()
44+
.on(foo.ifNull("Foo should not be null"))
45+
.on(bar.ifNull("Bar should not be null"))
46+
.on(baz.ifNull("Baz should not be null"))
47+
.result
48+
49+
println("How to access the content of a Result")
50+
val expectedResultMessage = "Foo should not be null,Bar should not be null,Baz should not be null"
51+
52+
println("When style")
53+
when (result2) {
54+
is Result.Ok -> result2.toString()
55+
is Result.Errors -> result2.description(",")
56+
} shouldBe expectedResultMessage
57+
58+
println("Fold style")
59+
result2.fold(
60+
{ it.toString() },
61+
{ it.description(",") }) shouldBe expectedResultMessage
62+
63+
println("Map style")
64+
result2
65+
.map { it.toString() }
66+
.ifError { it.description(",") } shouldBe expectedResultMessage
67+
68+
69+
println()
70+
println("How to create a Type-system with Result. See below the defitions of User, Email and PhoneNumber")
71+
val user: Result<User> = ::User.curry()
72+
.on("foo.bar")
73+
.on(Email.of("foo.bar")) // This email is invalid -> returns Result.Errors
74+
.on(PhoneNumber.of("xxx")) // This phone number is invalid -> returns Result.Errors
75+
.on("Foo")
76+
.result
77+
78+
when (user) {
79+
is Result.Ok -> user.toString()
80+
is Result.Errors -> user.description(" - ")
81+
} shouldBe "foo.bar doesn't match an email format - xxx should match a valid phone number, but it doesn't"
82+
83+
84+
println()
85+
println("How to deal with collection of nullables")
86+
val collectionOfNullables: List<Int?> = listOf(1, 2, null, 4)
87+
val nullableCollection: Collection<Int>? = collectionOfNullables.flatten()
88+
89+
nullableCollection shouldBe null
90+
91+
println("How to deal with collection of Results")
92+
val collectionOfResult: List<Result<Int>> = listOf("error1".error(), 2.ok(), "error3".error(), 4.ok())
93+
val resultCollection: Result<Collection<Int>> = collectionOfResult.flatten()
94+
95+
resultCollection.fold(
96+
{ it.toString() },
97+
{ it.description(",") }) shouldBe "error1,error3"
98+
}
99+
100+
const val EMAIL_REGEX =
101+
"(?:[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)*[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])"
102+
const val PHONE_NUMBER_REGEX = "^(\\+\\d{1,3})?\\s?(\\d\\s?)+\$"
103+
104+
data class User(val username: String, val email: Email, val phoneNumber: PhoneNumber, val firstname: String)
105+
106+
data class Email private constructor(val value: String) {
107+
companion object {
108+
fun of(emailValue: String): Result<Email> = if (Regex(EMAIL_REGEX).matches(emailValue))
109+
Email(emailValue).ok()
110+
else "$emailValue doesn't match an email format".error()
111+
}
112+
}
113+
114+
data class PhoneNumber private constructor(val value: String) {
115+
companion object {
116+
fun of(phoneNumberValue: String): Result<PhoneNumber> = if (Regex(PHONE_NUMBER_REGEX).matches(phoneNumberValue))
117+
PhoneNumber(phoneNumberValue).ok()
118+
else "$phoneNumberValue should match a valid phone number, but it doesn't".error()
119+
}
120+
}

kotlin-jvm/src/main/kotlin/playground/_main.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ fun main() {
1818
playground.hoplite.main()
1919
playground.klaxon.main()
2020
playground.kodein.db.main()
21+
playground.konad.main()
2122
playground.konf.main()
2223
playground.kotlin.collections.main()
2324
playground.kotlinfaker.main()

versions.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ version.google.dagger=2.29.1
6262
## # available=2.30
6363
## # available=2.30.1
6464

65+
version.io.github.lucapiccinelli..konad=1.0.6
66+
6567
version.io.github.serpro69..kotlin-faker=1.5.0
6668
## # available=1.6.0-rc.0
6769

0 commit comments

Comments
 (0)