Skip to content

Commit

Permalink
The Arrow Query Language. (#1079)
Browse files Browse the repository at this point in the history
The Arrow query language offers SQL, Monoid comprehensions and LINQ
style queries generalized to all data types.
  • Loading branch information
raulraja committed Nov 11, 2018
1 parent 1472fa5 commit 6192fb2
Show file tree
Hide file tree
Showing 57 changed files with 2,307 additions and 46 deletions.
14 changes: 14 additions & 0 deletions modules/aql/arrow-query-language/build.gradle
@@ -0,0 +1,14 @@
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
compile project(':arrow-annotations')
compile project(':arrow-mtl')
kapt project(':arrow-annotations-processor')
kaptTest project(':arrow-annotations-processor')
compileOnly project(':arrow-annotations-processor')
testCompileOnly project(':arrow-annotations-processor')
testCompile "io.kotlintest:kotlintest:$kotlinTestVersion"
testCompile project(':arrow-test')
}

apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
apply plugin: 'kotlin-kapt'
4 changes: 4 additions & 0 deletions modules/aql/arrow-query-language/gradle.properties
@@ -0,0 +1,4 @@
# Maven publishing configuration
POM_NAME=Arrow Query Language
POM_ARTIFACT_ID=arrow-query-language
POM_PACKAGING=jar
@@ -0,0 +1,25 @@
package arrow.aql

import arrow.core.*
import arrow.data.ForListK
import arrow.data.fix
import arrow.data.k
import arrow.instances.monoid
import arrow.typeclasses.Foldable

interface Count<F> {

fun foldable(): Foldable<F>

fun <A, Z> Query<F, A, Z>.count(): Query<ForListK, Long, Long> =
foldable().run {
Query(
select = ::identity,
from = listOf(from.size(Long.monoid())).k()
)
}

fun Query<ForListK, Long, Long>.value(): Long =
this@value.from.fix().firstOrNone().getOrElse { 0L }

}
26 changes: 26 additions & 0 deletions modules/aql/arrow-query-language/src/main/kotlin/arrow/aql/From.kt
@@ -0,0 +1,26 @@
package arrow.aql

import arrow.core.Tuple2
import arrow.core.Tuple3
import arrow.core.toT
import arrow.typeclasses.Applicative

interface From<F> {

fun applicative(): Applicative<F>

infix fun <A, B> Source<F, A>.join(fb: Source<F, B>): Source<F, Tuple2<A, B>> =
applicative().run { this@join.product(fb) }

fun <A, B, C> Source<F, Tuple2<A, B>>.join(fc: Source<F, C>, dummy: Unit = Unit): Source<F, Tuple3<A, B, C>> =
applicative().run { this@join.product(fc) }

infix fun <A, B, Z, X> Query<F, A, Z>.join(query: Query<F, B, X>): Query<F, Tuple2<A, B>, Tuple2<Z, X>> =
applicative().run {
Query(
select = { select(a) toT query.select(b) },
from = from.product(query.from)
)
}

}
@@ -0,0 +1,29 @@
package arrow.aql

import arrow.core.*
import arrow.data.ForListK
import arrow.data.fix
import arrow.data.mapOf
import arrow.typeclasses.Foldable

interface GroupBy<F> {

fun foldable(): Foldable<F>

infix fun <A, Z, X> Query<F, A, Z>.groupBy(group: Z.() -> X): Query<ForId, Map<X, List<Z>>, Map<X, List<Z>>> =
foldable().run {
Query(select = ::identity, from =
Id.just(from.foldLeft(emptyMap()) { map, a ->
val z: Z = select.invoke(a)
val key: X = group(z)
val grouped: List<Z> = map[key]?.let { it + z } ?: listOf(z)
map + mapOf(key toT grouped)
}))
}

fun <Z, X> Query<ForListK, Map<X, List<Z>>, Map<X, List<Z>>>.value(): Map<X, List<Z>> =
foldable().run {
this@value.from.fix().firstOrNone().getOrElse { emptyMap() }
}

}
@@ -0,0 +1,60 @@
package arrow.aql

import arrow.core.ForId
import arrow.core.identity
import arrow.core.value
import arrow.instances.id.applicative.just
import arrow.typeclasses.Foldable
import arrow.typeclasses.Order

sealed class Ord<X> {
abstract val order: Order<X>
data class Asc<X>(override val order: Order<X>) : Ord<X>()
data class Desc<X>(override val order: Order<X>) : Ord<X>()
}

interface OrderBy<F> {

fun foldable(): Foldable<F>

private data class DelegatingComparator<A>(val ord: Ord<A>) : Comparator<A> {
override fun compare(a: A, other: A): Int = ord.order.run {
val comp = a.compareTo(other)
when (ord) {
is Ord.Asc<*> -> comp
is Ord.Desc<*> -> -comp
}
}
}

infix fun <A, Z> Query<F, A, Z>.orderBy(ord: Ord<Z>): Query<ForId, List<Z>, List<Z>> =
foldable().run {
Query(
select = ::identity,
from = from.foldLeft(emptyList<Z>()) { list, a ->
list + select(a)
}.sortedWith(DelegatingComparator(ord)).just()
)
}

infix fun <X, Z> Query<ForId, Map<X, List<Z>>, Map<X, List<Z>>>.orderMap(ord: Ord<X>): Query<ForId, Map<X, List<Z>>, Map<X, List<Z>>> {
val sortedMap = from.value().toSortedMap(kotlin.Comparator { o1, o2 ->
val result = ord.order.run { o1.compareTo(o2) }
when (ord) {
is Ord.Asc<*> -> -result
is Ord.Desc<*> -> result
}
})
return Query(
select = ::identity,
from = sortedMap.toMap().just()
)
}

fun <Z> Query<ForId, List<Z>, List<Z>>.value(): List<Z> =
this@value.from.value()

fun <Z, X> Query<ForId, Map<X, List<Z>>, Map<X, List<Z>>>.value(dummy: Unit = Unit): Map<X, List<Z>> =
this@value.from.value()

}
@@ -0,0 +1,6 @@
package arrow.aql

import arrow.Kind

typealias Source<F, A> = Kind<F, A>
typealias Selection<A, Z> = A.() -> Z
@@ -0,0 +1,6 @@
package arrow.aql

data class Query<out F, A, out Z>(
val select: Selection<A, Z>,
val from: Source<F, A>
)
@@ -0,0 +1,29 @@
package arrow.aql

import arrow.typeclasses.Functor

/**
* SELECT * FROM Student
*
* listOf(1, 2, 3)
* .select { it * 10 }
* .value()
* //listOf(10, 20, 30)
*/
interface Select<F> {

fun functor(): Functor<F>

infix fun <A, Z> Source<F, A>.query(f: Source<F, A>.() -> Z): Z =
f(this)

infix fun <A, Z> Source<F, A>.select(f: Selection<A, Z>): Query<F, A, Z> =
Query(f, this)

fun <A> Source<F, A>.selectAll(): Query<F, A, A> =
Query({ this }, this)

fun <A, Z> Query<F, A, Z>.value(): Source<F, Z> =
functor().run { from.map(select) }

}
25 changes: 25 additions & 0 deletions modules/aql/arrow-query-language/src/main/kotlin/arrow/aql/Sum.kt
@@ -0,0 +1,25 @@
package arrow.aql

import arrow.core.*
import arrow.typeclasses.Foldable

interface Sum<F> {

fun foldable(): Foldable<F>

infix fun <A, Z> Query<F, A, Z>.sum(f: A.() -> Long): Query<ForId, Long, Long> =
foldable().run {
Query(
select = ::identity,
from = Id(from.foldLeft(0L) { acc, a ->
acc + f(a)
})
)
}

fun Query<ForId, Long, Long>.value(): Long =
foldable().run {
this@value.from.value()
}

}
@@ -0,0 +1,23 @@
package arrow.aql

import arrow.core.identity
import arrow.data.ForListK
import arrow.data.ListK
import arrow.data.k
import arrow.instances.list.semigroupK.combineK
import arrow.instances.listk.monoid.monoid
import arrow.typeclasses.Foldable

interface Union<F> {

fun foldable(): Foldable<F>

infix fun <A, B, Z> Query<F, A, Z>.union(query: Query<F, B, Z>): Query<ForListK, Z, Z> =
foldable().run {
val la: ListK<Z> = from.foldMap(ListK.monoid()) { listOf(select(it)).k() }
val lb: ListK<Z> = query.from.foldMap(ListK.monoid()) { listOf(query.select(it)).k() }
val result: ListK<Z> = la.combineK(lb).k()
Query(select = ::identity, from = result)
}

}
@@ -0,0 +1,35 @@
package arrow.aql

import arrow.core.None
import arrow.core.some
import arrow.mtl.typeclasses.FunctorFilter

interface Where<F> {

fun functorFilter(): FunctorFilter<F>

infix fun <A, Z> Query<F, A, Z>.where(predicate: A.() -> Boolean): Query<F, A, Z> =
functorFilter().run {
copy(from = from.mapFilter {
if (predicate(it)) it.some()
else None
})
}

infix fun <A, Z> Query<F, A, Z>.whereSelection(predicate: Z.() -> Boolean): Query<F, A, Z> =
functorFilter().run {
copy(from = from.mapFilter {
if (predicate(select(it))) it.some()
else None
})
}

infix fun <A, Z> Query<F, A, Z>.having(predicate: A.() -> Boolean): Query<F, A, Z> =
functorFilter().run {
copy(from = from.mapFilter {
if (predicate(it)) it.some()
else None
})
}

}
@@ -0,0 +1,47 @@
package arrow.aql.instances

import arrow.aql.*
import arrow.core.Either
import arrow.core.EitherPartialOf
import arrow.extension
import arrow.instances.either.applicative.applicative
import arrow.instances.either.foldable.foldable
import arrow.instances.either.functor.functor
import arrow.instances.listk.foldable.foldable
import arrow.typeclasses.Applicative
import arrow.typeclasses.Foldable
import arrow.typeclasses.Functor

@extension interface EitherSelect<L> : Select<EitherPartialOf<L>> {
override fun functor(): Functor<EitherPartialOf<L>> = Either.functor()
}

@extension
interface EitherFrom<L> : From<EitherPartialOf<L>> {
override fun applicative(): Applicative<EitherPartialOf<L>> = Either.applicative()
}

@extension
interface EitherGroupBy<L> : GroupBy<EitherPartialOf<L>> {
override fun foldable(): Foldable<EitherPartialOf<L>> = Either.foldable()
}

@extension
interface EitherCount<L> : Count<EitherPartialOf<L>> {
override fun foldable(): Foldable<EitherPartialOf<L>> = Either.foldable()
}

@extension
interface EitherSum<L> : Sum<EitherPartialOf<L>> {
override fun foldable(): Foldable<EitherPartialOf<L>> = Either.foldable()
}

@extension
interface EitherOrderBy<L> : OrderBy<EitherPartialOf<L>> {
override fun foldable(): Foldable<EitherPartialOf<L>> = Either.foldable()
}

@extension
interface EitherUnion<L> : Union<EitherPartialOf<L>> {
override fun foldable(): Foldable<EitherPartialOf<L>> = Either.foldable()
}
@@ -0,0 +1,21 @@
package arrow.aql.instances

import arrow.aql.From
import arrow.aql.Select
import arrow.core.Eval
import arrow.core.ForEval
import arrow.extension
import arrow.instances.eval.applicative.applicative
import arrow.instances.eval.functor.functor
import arrow.typeclasses.Applicative
import arrow.typeclasses.Functor

@extension
interface EvalSelect : Select<ForEval> {
override fun functor(): Functor<ForEval> = Eval.functor()
}

@extension
interface EvalFrom : From<ForEval> {
override fun applicative(): Applicative<ForEval> = Eval.applicative()
}
@@ -0,0 +1,20 @@
package arrow.aql.instances

import arrow.aql.From
import arrow.aql.Select
import arrow.core.ForFunction0
import arrow.core.Function0
import arrow.extension
import arrow.instances.function0.applicative.applicative
import arrow.instances.function0.functor.functor
import arrow.typeclasses.Applicative
import arrow.typeclasses.Functor

@extension interface Function0Select : Select<ForFunction0> {
override fun functor(): Functor<ForFunction0> = Function0.functor()
}

@extension
interface Function0From : From<ForFunction0> {
override fun applicative(): Applicative<ForFunction0> = Function0.applicative()
}

0 comments on commit 6192fb2

Please sign in to comment.