Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Json.add and Json.merge operations #129

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions helios-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dependencies {

testImplementation("io.kotest:kotest-runner-junit5:$kotlinTestVersion")
testImplementation("io.kotest:kotest-assertions-arrow:$kotlinTestVersion")
testImplementation("io.arrow-kt:arrow-test:$arrowVersion")
ambrusadrianzh marked this conversation as resolved.
Show resolved Hide resolved

kaptTest project(":helios-meta")
kaptTest "io.arrow-kt:arrow-meta:$arrowVersion"
Expand Down
16 changes: 6 additions & 10 deletions helios-core/src/main/kotlin/helios/core/helios.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package helios.core

import arrow.core.*
import arrow.core.extensions.monoid
import arrow.core.extensions.option.applicative.applicative
import arrow.typeclasses.Monoid
import helios.instances.HeliosFacade
import helios.instances.jsarray.eq.eq
import helios.instances.jsnumber.eq.eq
Expand Down Expand Up @@ -76,9 +78,6 @@ sealed class Json {
is JsBoolean -> ifJsBoolean(this)
}

fun add(key: String, value: Json): JsObject =
JsObject(hashMapOf(key to value))

fun asJsString(): Option<JsString> =
(this as? JsString)?.some() ?: none()

Expand All @@ -97,12 +96,7 @@ sealed class Json {
fun asJsNull(): Option<JsNull> =
(this as? JsNull)?.some() ?: none()

fun merge(that: Json): Json =
Option.applicative().map(asJsObject(), that.asJsObject()) { (lhs, rhs) ->
lhs.toList().fold(rhs) { acc, (key, value) ->
rhs[key].fold({ acc.add(key, value) }, { r -> acc.add(key, value.merge(r)) })
}
}.fix().getOrElse { that }
fun merge(that: Json, M: Monoid<Json>): Json = M.combineAll(listOf(this, that))
ambrusadrianzh marked this conversation as resolved.
Show resolved Hide resolved

abstract fun noSpaces(): String

Expand Down Expand Up @@ -357,7 +351,9 @@ data class JsObject(val value: Map<String, Json>) : Json() {
JsObject(keyValues.map { it.a to it.b }.toMap())
}

fun toList(): List<Tuple2<String, Json>> = value.toList().map { it.first toT it.second }
fun add(key: String, value: Json): Json = JsObject(this.value + (key to value))

fun toList(): List<Tuple2<String, Json>> = value.toList().map { it.toTuple2() }

override fun noSpaces(): String =
value.map { (k, v) -> """"$k":${v.noSpaces()}""" }.joinToString(
Expand Down
7 changes: 7 additions & 0 deletions helios-core/src/main/kotlin/helios/instances/instancesEq.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import helios.instances.jsnumber.eq.eq
import helios.instances.jsobject.eq.eq
import helios.instances.json.eq.eq

@extension
interface JsStringEq : Eq<JsString> {
override fun JsString.eqv(b: JsString): Boolean = with(String.eq()) {
value.toString().eqv(b.value.toString())
}
}

@extension
interface JsObjectEq : Eq<JsObject> {
override fun JsObject.eqv(b: JsObject): Boolean = with(Json.eq()) {
Expand Down
46 changes: 46 additions & 0 deletions helios-core/src/main/kotlin/helios/instances/instancesMonoid.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package helios.instances

import arrow.extension
import arrow.typeclasses.Monoid
import helios.core.JsArray
import helios.core.JsDouble
import helios.core.JsFloat
import helios.core.JsInt
import helios.core.JsLong
import helios.core.JsObject
import helios.core.JsString

@extension
interface JsIntMonoid : Monoid<JsInt>, JsIntSemigroup {
override fun empty(): JsInt = JsInt(0)
}

@extension
interface JsLongMonoid : Monoid<JsLong>, JsLongSemigroup {
override fun empty(): JsLong = JsLong(0L)
}

@extension
interface JsFloatMonoid : Monoid<JsFloat>, JsFloatSemigroup {
override fun empty(): JsFloat = JsFloat(0f)
}

@extension
interface JsDoubleMonoid : Monoid<JsDouble>, JsDoubleSemigroup {
override fun empty(): JsDouble = JsDouble(0.0)
}

@extension
interface JsStringMonoid : Monoid<JsString>, JsStringSemigroup {
override fun empty(): JsString = JsString("")
}

@extension
interface JsArrayMonoid : Monoid<JsArray>, JsArraySemigroup {
override fun empty(): JsArray = JsArray(emptyList())
}

@extension
interface JsObjectMonoid : Monoid<JsObject>, JsObjectSemigroup {
override fun empty(): JsObject = JsObject(emptyMap())
}
63 changes: 63 additions & 0 deletions helios-core/src/main/kotlin/helios/instances/instancesSemigroup.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package helios.instances

import arrow.core.ListK
import arrow.core.extensions.listk.semigroup.semigroup
import arrow.core.extensions.semigroup
import arrow.core.k
import arrow.extension
import arrow.typeclasses.Semigroup
import helios.core.JsArray
import helios.core.JsDouble
import helios.core.JsFloat
import helios.core.JsInt
import helios.core.JsLong
import helios.core.JsObject
import helios.core.JsString
import helios.core.Json

@extension
interface JsIntSemigroup : Semigroup<JsInt> {
override fun JsInt.combine(b: JsInt): JsInt = JsInt(Int.semigroup().run {
value.combine(b.value)
})
}

@extension
interface JsLongSemigroup : Semigroup<JsLong> {
override fun JsLong.combine(b: JsLong): JsLong = JsLong(Long.semigroup().run {
value.combine(b.value)
})
}

@extension
interface JsFloatSemigroup : Semigroup<JsFloat> {
override fun JsFloat.combine(b: JsFloat): JsFloat = JsFloat(Float.semigroup().run {
value.combine(b.value)
})
}

@extension
interface JsDoubleSemigroup : Semigroup<JsDouble> {
override fun JsDouble.combine(b: JsDouble): JsDouble = JsDouble(Double.semigroup().run {
value.combine(b.value)
})
}

@extension
interface JsStringSemigroup : Semigroup<JsString> {
override fun JsString.combine(b: JsString): JsString = JsString(String.semigroup().run {
value.toString().combine(b.value.toString())
})
}

@extension
interface JsArraySemigroup : Semigroup<JsArray> {
override fun JsArray.combine(b: JsArray): JsArray = JsArray(ListK.semigroup<Json>().run {
value.k().combine(b.value.k())
})
}

@extension
interface JsObjectSemigroup : Semigroup<JsObject> {
override fun JsObject.combine(b: JsObject): JsObject = JsObject(value + b.value)
}
32 changes: 32 additions & 0 deletions helios-core/src/test/kotlin/helios/core/MonoidTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package helios.core

import arrow.test.UnitSpec
import arrow.test.laws.MonoidLaws
import arrow.test.laws.SemigroupLaws
import helios.instances.jsstring.eq.eq
import helios.instances.jsstring.monoid.monoid
import helios.instances.jsstring.semigroup.semigroup
import io.kotlintest.properties.Gen

class MonoidTest : UnitSpec() {

init {
testLaws(
SemigroupLaws.laws(
JsString.semigroup(),
JsString("a"),
JsString("b"),
JsString("c"),
JsString.eq()
)
)

testLaws(
MonoidLaws.laws(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess Monoid laws also includes Semigroup laws

JsString.monoid(),
Gen.string().map { JsString(it) },
JsString.eq()
)
)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A tests for each monoid?

}
114 changes: 45 additions & 69 deletions helios-optics/src/test/kotlin/helios/optics/instances.kt
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
package helios.optics

import arrow.core.None
import arrow.core.Option
import arrow.core.extensions.option.eq.eq
import arrow.typeclasses.Eq
import helios.arrow.UnitSpec
import helios.arrow.generators.functionAToB
import helios.arrow.generators.option
import helios.arrow.laws.LensLaws
import helios.arrow.laws.OptionalLaws
import helios.arrow.laws.TraversalLaws
import arrow.typeclasses.Eq
import arrow.typeclasses.Monoid
import helios.core.JsArray
import helios.core.JsObject
import helios.core.Json
import helios.instances.jsobject.eq.eq
import helios.instances.json.eq.eq
import helios.optics.jsarray.each.each
import helios.optics.jsarray.index.index
import helios.optics.jsobject.at.at
import helios.optics.jsobject.each.each
import helios.optics.jsobject.index.index
import helios.test.generators.alphaStr
Expand All @@ -29,66 +19,52 @@ import io.kotest.properties.Gen

class InstancesTest : UnitSpec() {

init {

testLaws(
OptionalLaws.laws(
optionalGen = Gen.alphaStr().map { JsObject.index().index(it) },
aGen = Gen.jsObject(),
bGen = Gen.json(),
funcGen = Gen.functionAToB(Gen.json()),
EQA = Eq.any(),
EQOptionB = Eq.any()
)
)

testLaws(
OptionalLaws.laws(
optional = JsArray.index().index(1),
aGen = Gen.jsArray(),
bGen = Gen.json(),
funcGen = Gen.functionAToB(Gen.json()),
EQA = Eq.any(),
EQOptionB = Eq.any()
)
)
init {

testLaws(
TraversalLaws.laws(
traversal = JsObject.each().each(),
aGen = Gen.jsObject(),
bGen = Gen.json(),
funcGen = Gen.functionAToB(Gen.json()),
EQA = Eq.any(),
EQOptionB = Eq.any(),
EQListB = Eq.any()
)
)
testLaws(
OptionalLaws.laws(
optionalGen = Gen.alphaStr().map { JsObject.index().index(it) },
aGen = Gen.jsObject(),
bGen = Gen.json(),
funcGen = Gen.functionAToB(Gen.json()),
EQA = Eq.any(),
EQOptionB = Eq.any()
)
)

testLaws(
TraversalLaws.laws(
traversal = JsArray.each().each(),
aGen = Gen.jsArray(),
bGen = Gen.json(),
funcGen = Gen.functionAToB(Gen.json()),
EQA = Eq.any(),
EQOptionB = Eq.any(),
EQListB = Eq.any()
)
)
testLaws(
OptionalLaws.laws(
optional = JsArray.index().index(1),
aGen = Gen.jsArray(),
bGen = Gen.json(),
funcGen = Gen.functionAToB(Gen.json()),
EQA = Eq.any(),
EQOptionB = Eq.any()
)
)

testLaws(LensLaws.laws(
lensGen = Gen.alphaStr().map { JsObject.at().at(it) },
aGen = Gen.jsObject(),
bGen = Gen.option(Gen.json()),
funcGen = Gen.functionAToB(Gen.option(Gen.json())),
EQA = JsObject.eq(),
EQB = Option.eq(Json.eq()),
MB = object : Monoid<Option<Json>> {
override fun Option<Json>.combine(b: Option<Json>) = flatMap { a -> b.map { a.merge(it) } }
override fun empty(): Option<Json> = None
}
))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we deleting the Lens law test?

testLaws(
TraversalLaws.laws(
traversal = JsObject.each().each(),
aGen = Gen.jsObject(),
bGen = Gen.json(),
funcGen = Gen.functionAToB(Gen.json()),
EQA = Eq.any(),
EQOptionB = Eq.any(),
EQListB = Eq.any()
)
)

}
testLaws(
TraversalLaws.laws(
traversal = JsArray.each().each(),
aGen = Gen.jsArray(),
bGen = Gen.json(),
funcGen = Gen.functionAToB(Gen.json()),
EQA = Eq.any(),
EQOptionB = Eq.any(),
EQListB = Eq.any()
)
)
}
}