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

Add support for several types #69

Merged
merged 11 commits into from
May 31, 2019
11 changes: 5 additions & 6 deletions helios-core/src/main/kotlin/helios/core/helios.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const val MinLongString = "-9223372036854775808"

sealed class Json {

inline val isNull inline get() = this === JsNull

companion object {
fun fromValues(i: Iterable<Json>): JsArray = JsArray(i.toList())

Expand Down Expand Up @@ -132,8 +134,7 @@ sealed class JsNumber : Json() {
abstract fun toLong(): Option<Long>

fun toByte(): Option<Byte> {
val ml = toLong()
return when (ml) {
return when (val ml = toLong()) {
is Some<Long> -> {
val asByte: Byte = ml.t.toByte()
if (asByte.compareTo(ml.t) == 0) Some(asByte) else None
Expand All @@ -143,8 +144,7 @@ sealed class JsNumber : Json() {
}

fun toShort(): Option<Short> {
val ml = toLong()
return when (ml) {
return when (val ml = toLong()) {
is Some<Long> -> {
val asShort: Short = ml.t.toShort()
if (asShort.compareTo(ml.t) == 0) Some(asShort) else None
Expand All @@ -154,8 +154,7 @@ sealed class JsNumber : Json() {
}

fun toInt(): Option<Int> {
val ml = toLong()
return when (ml) {
return when (val ml = toLong()) {
is Some<Long> -> {
val asInt: Int = ml.t.toInt()
if (asInt.compareTo(ml.t) == 0) Some(asInt) else None
Expand Down
154 changes: 58 additions & 96 deletions helios-core/src/main/kotlin/helios/instances/instances.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
package helios.instances

import arrow.core.*
import arrow.core.extensions.eq
import arrow.data.extensions.list.foldable.forAll
import arrow.core.extensions.either.applicative.applicative
import arrow.core.extensions.either.applicative.map2
import arrow.extension
import arrow.higherkind
import arrow.typeclasses.Eq
import helios.core.*
import helios.instances.jsarray.eq.eq
import helios.instances.jsnumber.eq.eq
import helios.instances.jsobject.eq.eq
import helios.instances.json.eq.eq
import helios.typeclasses.*

fun Int.Companion.encoder() = object : Encoder<Int> {
Expand Down Expand Up @@ -42,122 +36,90 @@ fun String.Companion.decoder() = object : Decoder<String> {
}.toEither { StringDecodingError(value) }
}

@extension
interface OptionEncoderInstance<in A> : Encoder<Option<A>> {

fun encoderA(): Encoder<A>
fun <A> Option.Companion.encoder(encoderA: Encoder<A>) = object : Encoder<Option<A>> {

override fun Option<A>.encode(): Json =
fold({ JsNull }, { encoderA().run { it.encode() } })
fold({ JsNull }, { encoderA.run { it.encode() } })

}

@extension
interface Tuple2EncoderInstance<A, B> : Encoder<Tuple2<A, B>> {
fun <A> Option.Companion.decoder(decoderA: Decoder<A>) = object : Decoder<Option<A>> {

fun encoderA(): Encoder<A>
override fun decode(value: Json): Either<DecodingError, Option<A>> =
if (value.isNull) None.right() else decoderA.decode(value).map { Some(it) }

fun encoderB(): Encoder<B>
}

override fun Tuple2<A, B>.encode(): Json = JsArray(
listOf(
encoderA().run { a.encode() },
encoderB().run { b.encode() }
)
)
fun <A, B> Either.Companion.encoder(encoderA: Encoder<A>, encoderB: Encoder<B>) = object : Encoder<Either<A, B>> {

override fun Either<A, B>.encode(): Json =
fold({ encoderA.run { it.encode() } },
{ encoderB.run { it.encode() } })

}

@extension
interface Tuple3EncoderInstance<A, B, C> : Encoder<Tuple3<A, B, C>> {
fun <A, B> Either.Companion.decoder(decoderA: Decoder<A>, decoderB: Decoder<B>) = object : Decoder<Either<A, B>> {

fun encoderA(): Encoder<A>
override fun decode(value: Json): Either<DecodingError, Either<A, B>> =
decoderB.decode(value).fold({ decoderA.decode(value).map { it.left() } },
{ v -> v.right().map { it.right() } })

fun encoderB(): Encoder<B>
}

fun encoderC(): Encoder<C>
fun <A, B> Tuple2.Companion.encoder(encoderA: Encoder<A>, encoderB: Encoder<B>) = object : Encoder<Tuple2<A, B>> {

override fun Tuple3<A, B, C>.encode(): Json = JsArray(
override fun Tuple2<A, B>.encode(): Json = JsArray(
listOf(
encoderA().run { a.encode() },
encoderB().run { b.encode() },
encoderC().run { c.encode() }
encoderA.run { a.encode() },
encoderB.run { b.encode() }
)
)

}

private inline val Json.isNull inline get() = this === JsNull
fun <A, B> Tuple2.Companion.decoder(decoderA: Decoder<A>, decoderB: Decoder<B>) = object : Decoder<Tuple2<A, B>> {

@extension
interface JsObjectEqInstance : Eq<JsObject> {
override fun JsObject.eqv(b: JsObject): Boolean = with(Json.eq()) {
[email protected](b.value.entries) { aa, bb ->
aa.key == bb.key && aa.value.eqv(bb.value)
}.forAll { it }
override fun decode(value: Json): Either<DecodingError, Tuple2<A, B>> {
val arr = value.asJsArray().toList().flatMap { it.value }
return if (arr.size >= 2)
decoderA.decode(arr[1]).map2(decoderB.decode(arr[2])) { it }.fix()
else ArrayDecodingError(value).left()
}

}

@extension
interface JsArrayEqInstance : Eq<JsArray> {
override fun JsArray.eqv(b: JsArray): Boolean = with(Json.eq()) {
[email protected](b.value) { a, b -> a.eqv(b) }
.forAll { it }
}
fun <A, B, C> Tuple3.Companion.encoder(
encoderA: Encoder<A>,
encoderB: Encoder<B>,
encoderC: Encoder<C>
) = object : Encoder<Tuple3<A, B, C>> {

override fun Tuple3<A, B, C>.encode(): Json = JsArray(
listOf(
encoderA.run { a.encode() },
encoderB.run { b.encode() },
encoderC.run { c.encode() }
)
)

}

@extension
interface JsonEqInstance : Eq<Json> {
override fun Json.eqv(b: Json): Boolean = when {
this is JsObject && b is JsObject -> JsObject.eq().run { [email protected](b) }
this is JsString && b is JsString -> String.eq().run {
[email protected]().eqv(b.value.toString())
}
this is JsNumber && b is JsNumber -> JsNumber.eq().run { [email protected](b) }
this is JsBoolean && b is JsBoolean -> Boolean.eq().run { [email protected](b.value) }
this is JsArray && b is JsArray -> JsArray.eq().run { [email protected](b) }
else -> this.isNull && b.isNull
fun <A, B, C> Tuple3.Companion.decoder(
decoderA: Decoder<A>,
decoderB: Decoder<B>,
decoderC: Decoder<C>
) = object : Decoder<Tuple3<A, B, C>> {

override fun decode(value: Json): Either<DecodingError, Tuple3<A, B, C>> {
val arr = value.asJsArray().toList().flatMap { it.value }
return if (arr.size >= 3)
Either.applicative<DecodingError>().map(
decoderA.decode(arr[1]),
decoderB.decode(arr[2]),
decoderC.decode(arr[3])
) { it }.fix()
else ArrayDecodingError(value).left()
}

}

@extension
interface JsNumberEqInstance : Eq<JsNumber> {
override fun JsNumber.eqv(b: JsNumber): Boolean = when (this) {
is JsDecimal -> when (b) {
is JsDecimal -> String.eq().run { [email protected](b.value) }
is JsLong -> String.eq().run { [email protected](b.value.toString()) }
is JsDouble -> String.eq().run { [email protected](b.value.toString()) }
is JsFloat -> String.eq().run { [email protected](b.value.toString()) }
is JsInt -> String.eq().run { [email protected](b.value.toString()) }
}
is JsLong -> when (b) {
is JsDecimal -> String.eq().run { [email protected]().eqv(b.value) }
is JsLong -> Long.eq().run { [email protected](b.value) }
is JsDouble -> Double.eq().run { [email protected]().eqv(b.value) }
is JsFloat -> Float.eq().run { [email protected]().eqv(b.value) }
is JsInt -> Long.eq().run { [email protected](b.value.toLong()) }
}
is JsDouble -> when (b) {
is JsDecimal -> String.eq().run { [email protected]().eqv(b.value) }
is JsLong -> Double.eq().run { [email protected](b.value.toDouble()) }
is JsDouble -> Double.eq().run { [email protected](b.value) }
is JsFloat -> Double.eq().run { [email protected](b.value.toDouble()) }
is JsInt -> Double.eq().run { [email protected](b.value.toDouble()) }
}
is JsFloat -> when (b) {
is JsDecimal -> String.eq().run { [email protected]().eqv(b.value) }
is JsLong -> Float.eq().run { [email protected](b.value.toFloat()) }
is JsDouble -> Double.eq().run { [email protected]().eqv(b.value) }
is JsFloat -> Float.eq().run { [email protected](b.value) }
is JsInt -> Float.eq().run { [email protected](b.value.toFloat()) }
}
is JsInt -> when (b) {
is JsDecimal -> String.eq().run { [email protected]().eqv(b.value) }
is JsLong -> Long.eq().run { [email protected]().eqv(b.value) }
is JsDouble -> Double.eq().run { [email protected]().eqv(b.value) }
is JsFloat -> Float.eq().run { [email protected]().eqv(b.value) }
is JsInt -> Int.eq().run { [email protected](b.value) }
}
}
}
85 changes: 85 additions & 0 deletions helios-core/src/main/kotlin/helios/instances/instancesEq.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package helios.instances

import arrow.core.extensions.eq
import arrow.data.extensions.list.foldable.forAll
import arrow.extension
import arrow.typeclasses.Eq
import helios.core.*
import helios.instances.jsarray.eq.eq
import helios.instances.jsnumber.eq.eq
import helios.instances.jsobject.eq.eq
import helios.instances.json.eq.eq


@extension
interface JsObjectEqInstance : Eq<JsObject> {
override fun JsObject.eqv(b: JsObject): Boolean = with(Json.eq()) {
[email protected](b.value.entries) { aa, bb ->
aa.key == bb.key && aa.value.eqv(bb.value)
}.forAll { it }
}
}

@extension
interface JsArrayEqInstance : Eq<JsArray> {
override fun JsArray.eqv(b: JsArray): Boolean = with(Json.eq()) {
[email protected](b.value) { a, b -> a.eqv(b) }
.forAll { it }
}
}

@extension
interface JsonEqInstance : Eq<Json> {
override fun Json.eqv(b: Json): Boolean = when {
this is JsObject && b is JsObject -> JsObject.eq().run { [email protected](b) }
this is JsString && b is JsString -> String.eq().run {
[email protected]().eqv(b.value.toString())
}
this is JsNumber && b is JsNumber -> JsNumber.eq().run { [email protected](b) }
this is JsBoolean && b is JsBoolean -> Boolean.eq().run { [email protected](b.value) }
this is JsArray && b is JsArray -> JsArray.eq().run { [email protected](b) }
else -> this.isNull && b.isNull
}

}

@extension
interface JsNumberEqInstance : Eq<JsNumber> {
override fun JsNumber.eqv(b: JsNumber): Boolean = when (this) {
is JsDecimal -> when (b) {
is JsDecimal -> String.eq().run { [email protected](b.value) }
is JsLong -> String.eq().run { [email protected](b.value.toString()) }
is JsDouble -> String.eq().run { [email protected](b.value.toString()) }
is JsFloat -> String.eq().run { [email protected](b.value.toString()) }
is JsInt -> String.eq().run { [email protected](b.value.toString()) }
}
AdrianRaFo marked this conversation as resolved.
Show resolved Hide resolved
is JsLong -> when (b) {
is JsDecimal -> String.eq().run { [email protected]().eqv(b.value) }
is JsLong -> Long.eq().run { [email protected](b.value) }
is JsDouble -> Double.eq().run { [email protected]().eqv(b.value) }
is JsFloat -> Float.eq().run { [email protected]().eqv(b.value) }
AdrianRaFo marked this conversation as resolved.
Show resolved Hide resolved
is JsInt -> Long.eq().run { [email protected](b.value.toLong()) }
}
is JsDouble -> when (b) {
is JsDecimal -> String.eq().run { [email protected]().eqv(b.value) }
is JsLong -> Double.eq().run { [email protected](b.value.toDouble()) }
is JsDouble -> Double.eq().run { [email protected](b.value) }
is JsFloat -> Double.eq().run { [email protected](b.value.toDouble()) }
is JsInt -> Double.eq().run { [email protected](b.value.toDouble()) }
}
is JsFloat -> when (b) {
is JsDecimal -> String.eq().run { [email protected]().eqv(b.value) }
is JsLong -> Float.eq().run { [email protected](b.value.toFloat()) }
is JsDouble -> Double.eq().run { [email protected]().eqv(b.value) }
is JsFloat -> Float.eq().run { [email protected](b.value) }
is JsInt -> Float.eq().run { [email protected](b.value.toFloat()) }
}
is JsInt -> when (b) {
is JsDecimal -> String.eq().run { [email protected]().eqv(b.value) }
is JsLong -> Long.eq().run { [email protected]().eqv(b.value) }
is JsDouble -> Double.eq().run { [email protected]().eqv(b.value) }
is JsFloat -> Float.eq().run { [email protected]().eqv(b.value) }
is JsInt -> Int.eq().run { [email protected](b.value) }
}
AdrianRaFo marked this conversation as resolved.
Show resolved Hide resolved
}
}
55 changes: 50 additions & 5 deletions helios-core/src/main/kotlin/helios/instances/instancesJava.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package helios.instances

import arrow.core.Either
import arrow.core.*
import arrow.core.extensions.either.applicative.applicative
import arrow.core.fix
import arrow.core.extensions.either.applicative.map2
import arrow.core.extensions.either.monoid.monoid
import arrow.data.extensions.list.foldable.fold
import arrow.data.extensions.list.foldable.foldLeft
import arrow.data.extensions.list.traverse.sequence
import arrow.data.fix
import arrow.typeclasses.Monoid
import helios.core.JsArray
import helios.core.JsObject
import helios.core.Json
import helios.typeclasses.Decoder
import helios.typeclasses.DecodingError
import helios.typeclasses.Encoder
import helios.typeclasses.*

interface ListEncoderInstance<in A> : Encoder<List<A>> {

Expand Down Expand Up @@ -45,3 +48,45 @@ interface ListDecoderInstance<A> : Decoder<List<A>> {
}

}

interface MapEncoderInstance<A, B> : Encoder<Map<A, B>> {

fun encoderB(): Encoder<B>

override fun Map<A, B>.encode(): Json =
JsObject(this.map { (key, value) -> (key.toString() to encoderB().run { value.encode() }) }.toMap())

companion object {
operator fun <A, B> invoke(encoderB: Encoder<B>): Encoder<Map<A, B>> =
object : MapEncoderInstance<A, B>, Encoder<Map<A, B>> {
override fun encoderB(): Encoder<B> = encoderB
}
}

}

interface MapDecoderInstance<A, B> : Decoder<Map<A, B>> {

fun decoderA(): Decoder<A>

fun decoderB(): Decoder<B>

override fun decode(value: Json): Either<DecodingError, Map<A, B>> =
value.asJsObject().fold({ ObjectDecodingError(value).left() }, { obj ->
obj.value.map { (key, value) ->
val maybeKey: Either<DecodingError, A> =
Json.parseFromString(key).mapLeft { StringDecodingError(value) }.flatMap { decoderA().decode(it) }
val maybeValue: Either<DecodingError, B> = decoderB().decode(value)
maybeKey.map2(maybeValue) { mapOf(it.toPair()) }
}.reduce { acc, either -> acc.map2(either) { it.a + it.b } }
AdrianRaFo marked this conversation as resolved.
Show resolved Hide resolved
})

companion object {
operator fun <A, B> invoke(decoderA: Decoder<A>, decoderB: Decoder<B>): Decoder<Map<A, B>> =
object : MapDecoderInstance<A, B> {
override fun decoderA(): Decoder<A> = decoderA
override fun decoderB(): Decoder<B> = decoderB
}
}

}
Loading