[WIP] change KClass to KType

This commit is contained in:
Alexander Nozik 2021-02-02 17:48:25 +03:00
parent 6a0bfae931
commit fcd99b1ca8
13 changed files with 123 additions and 117 deletions

View File

@ -12,10 +12,6 @@ kotlin {
commonMain{ commonMain{
dependencies { dependencies {
api(project(":dataforge-meta")) api(project(":dataforge-meta"))
}
}
jvmMain{
dependencies{
api(kotlin("reflect")) api(kotlin("reflect"))
} }
} }

View File

@ -7,7 +7,8 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.reflect.KClass import kotlin.reflect.KType
import kotlin.reflect.typeOf
/** /**
* Action environment includes data name, data meta and action configuration meta * Action environment includes data name, data meta and action configuration meta
@ -34,8 +35,9 @@ public class MapActionBuilder<T, R>(public var name: Name, public var meta: Meta
} }
public class MapAction<in T : Any, out R : Any>( @PublishedApi
public val outputType: KClass<out R>, internal class MapAction<in T : Any, out R : Any>(
private val outputType: KType,
private val block: MapActionBuilder<T, R>.() -> Unit, private val block: MapActionBuilder<T, R>.() -> Unit,
) : Action<T, R> { ) : Action<T, R> {
@ -84,8 +86,8 @@ public class MapAction<in T : Any, out R : Any>(
@Suppress("FunctionName") @Suppress("FunctionName")
public inline fun <T : Any, reified R : Any> MapAction( public inline fun <T : Any, reified R : Any> Action.Companion.map(
noinline builder: MapActionBuilder<T, R>.() -> Unit, noinline builder: MapActionBuilder<T, R>.() -> Unit,
): MapAction<T, R> = MapAction(R::class, builder) ): Action<T, R> = MapAction(typeOf<R>(), builder)

View File

@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.fold import kotlinx.coroutines.flow.fold
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KType
@DFExperimental @DFExperimental
@ -75,7 +76,7 @@ public class ReduceGroupBuilder<T : Any, R : Any>(
@DFExperimental @DFExperimental
public class ReduceAction<T : Any, R : Any>( public class ReduceAction<T : Any, R : Any>(
private val inputType: KClass<out T>, private val inputType: KClass<out T>,
outputType: KClass<out R>, outputType: KType,
private val action: ReduceGroupBuilder<T, R>.() -> Unit, private val action: ReduceGroupBuilder<T, R>.() -> Unit,
) : CachingAction<T, R>(outputType) { ) : CachingAction<T, R>(outputType) {
//TODO optimize reduction. Currently the whole action recalculates on push //TODO optimize reduction. Currently the whole action recalculates on push

View File

@ -11,7 +11,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.collections.set import kotlin.collections.set
import kotlin.reflect.KClass import kotlin.reflect.KType
public class SplitBuilder<T : Any, R : Any>(public val name: Name, public val meta: Meta) { public class SplitBuilder<T : Any, R : Any>(public val name: Name, public val meta: Meta) {
@ -39,8 +39,8 @@ public class SplitBuilder<T : Any, R : Any>(public val name: Name, public val me
/** /**
* Action that splits each incoming element into a number of fragments defined in builder * Action that splits each incoming element into a number of fragments defined in builder
*/ */
public class SplitAction<T : Any, R : Any>( internal class SplitAction<T : Any, R : Any>(
private val outputType: KClass<out R>, private val outputType: KType,
private val action: SplitBuilder<T, R>.() -> Unit, private val action: SplitBuilder<T, R>.() -> Unit,
) : Action<T, R> { ) : Action<T, R> {
@ -59,11 +59,11 @@ public class SplitAction<T : Any, R : Any>(
// apply individual fragment rules to result // apply individual fragment rules to result
return split.fragments.entries.asFlow().map { (fragmentName, rule) -> return split.fragments.entries.asFlow().map { (fragmentName, rule) ->
val env = SplitBuilder.FragmentRule<T, R>(fragmentName, laminate.toMutableMeta()).apply(rule) val env = SplitBuilder.FragmentRule<T, R>(fragmentName, laminate.toMutableMeta()).apply(rule)
data.map(outputType, meta = env.meta) { env.result(it) }.named(fragmentName) data.map<R>(outputType, meta = env.meta) { env.result(it) }.named(fragmentName)
} }
} }
return ActiveDataTree(outputType) { return ActiveDataTree<R>(outputType) {
populate(dataSet.flow().flatMapConcat(transform = ::splitOne)) populate(dataSet.flow().flatMapConcat(transform = ::splitOne))
scope?.launch { scope?.launch {
dataSet.updates.collect { name -> dataSet.updates.collect { name ->

View File

@ -8,13 +8,14 @@ import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlin.reflect.KClass import kotlin.reflect.KType
import kotlin.reflect.typeOf
/** /**
* A mutable [DataTree.Companion.active]. It * A mutable [DataTree.Companion.active]. It
*/ */
public class ActiveDataTree<T : Any>( public class ActiveDataTree<T : Any>(
override val dataType: KClass<out T>, override val dataType: KType,
) : DataTree<T>, DataSetBuilder<T>, ActiveDataSet<T> { ) : DataTree<T>, DataSetBuilder<T>, ActiveDataSet<T> {
private val mutex = Mutex() private val mutex = Mutex()
private val treeItems = HashMap<NameToken, DataTreeItem<T>>() private val treeItems = HashMap<NameToken, DataTreeItem<T>>()
@ -49,7 +50,7 @@ public class ActiveDataTree<T : Any>(
private suspend fun getOrCreateNode(token: NameToken): ActiveDataTree<T> = private suspend fun getOrCreateNode(token: NameToken): ActiveDataTree<T> =
(treeItems[token] as? DataTreeItem.Node<T>)?.tree as? ActiveDataTree<T> (treeItems[token] as? DataTreeItem.Node<T>)?.tree as? ActiveDataTree<T>
?: ActiveDataTree(dataType).also { ?: ActiveDataTree<T>(dataType).also {
mutex.withLock { mutex.withLock {
treeItems[token] = DataTreeItem.Node(it) treeItems[token] = DataTreeItem.Node(it)
} }
@ -92,10 +93,10 @@ public class ActiveDataTree<T : Any>(
*/ */
@Suppress("FunctionName") @Suppress("FunctionName")
public suspend fun <T : Any> ActiveDataTree( public suspend fun <T : Any> ActiveDataTree(
type: KClass<out T>, type: KType,
block: suspend ActiveDataTree<T>.() -> Unit, block: suspend ActiveDataTree<T>.() -> Unit,
): ActiveDataTree<T> { ): ActiveDataTree<T> {
val tree = ActiveDataTree(type) val tree = ActiveDataTree<T>(type)
tree.block() tree.block()
return tree return tree
} }
@ -103,15 +104,15 @@ public suspend fun <T : Any> ActiveDataTree(
@Suppress("FunctionName") @Suppress("FunctionName")
public suspend inline fun <reified T : Any> ActiveDataTree( public suspend inline fun <reified T : Any> ActiveDataTree(
crossinline block: suspend ActiveDataTree<T>.() -> Unit, crossinline block: suspend ActiveDataTree<T>.() -> Unit,
): ActiveDataTree<T> = ActiveDataTree(T::class).apply { block() } ): ActiveDataTree<T> = ActiveDataTree<T>(typeOf<T>()).apply { block() }
public suspend inline fun <reified T : Any> ActiveDataTree<T>.emit( public suspend inline fun <reified T : Any> ActiveDataTree<T>.emit(
name: Name, name: Name,
noinline block: suspend ActiveDataTree<T>.() -> Unit, noinline block: suspend ActiveDataTree<T>.() -> Unit,
): Unit = emit(name, ActiveDataTree(T::class, block)) ): Unit = emit(name, ActiveDataTree(typeOf<T>(), block))
public suspend inline fun <reified T : Any> ActiveDataTree<T>.emit( public suspend inline fun <reified T : Any> ActiveDataTree<T>.emit(
name: String, name: String,
noinline block: suspend ActiveDataTree<T>.() -> Unit, noinline block: suspend ActiveDataTree<T>.() -> Unit,
): Unit = emit(name.toName(), ActiveDataTree(T::class, block)) ): Unit = emit(name.toName(), ActiveDataTree(typeOf<T>(), block))

View File

@ -9,7 +9,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlin.reflect.KClass import kotlin.reflect.KType
/** /**
* Remove all values with keys starting with [name] * Remove all values with keys starting with [name]
@ -23,7 +23,7 @@ internal fun MutableMap<Name, *>.removeWhatStartsWith(name: Name) {
* An action that caches results on-demand and recalculates them on source push * An action that caches results on-demand and recalculates them on source push
*/ */
public abstract class CachingAction<in T : Any, out R : Any>( public abstract class CachingAction<in T : Any, out R : Any>(
public val outputType: KClass<out R>, public val outputType: KType,
) : Action<T, R> { ) : Action<T, R> {
protected abstract fun CoroutineScope.transform( protected abstract fun CoroutineScope.transform(
@ -36,7 +36,7 @@ public abstract class CachingAction<in T : Any, out R : Any>(
dataSet: DataSet<T>, dataSet: DataSet<T>,
meta: Meta, meta: Meta,
scope: CoroutineScope?, scope: CoroutineScope?,
): DataSet<R> = ActiveDataTree(outputType) { ): DataSet<R> = ActiveDataTree<R>(outputType) {
coroutineScope { coroutineScope {
populate(transform(dataSet, meta)) populate(transform(dataSet, meta))
} }

View File

@ -7,7 +7,8 @@ import hep.dataforge.misc.Type
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
import kotlin.reflect.KClass import kotlin.reflect.KType
import kotlin.reflect.typeOf
/** /**
* A data element characterized by its meta * A data element characterized by its meta
@ -17,7 +18,7 @@ public interface Data<out T : Any> : Goal<T>, MetaRepr {
/** /**
* Type marker for the data. The type is known before the calculation takes place so it could be checked. * Type marker for the data. The type is known before the calculation takes place so it could be checked.
*/ */
public val type: KClass<out T> public val type: KType
/** /**
* Meta for the data * Meta for the data
@ -25,7 +26,7 @@ public interface Data<out T : Any> : Goal<T>, MetaRepr {
public val meta: Meta public val meta: Meta
override fun toMeta(): Meta = Meta { override fun toMeta(): Meta = Meta {
"type" put (type.simpleName ?: "undefined") "type" put (type.toString())
if (!meta.isEmpty()) { if (!meta.isEmpty()) {
"meta" put meta "meta" put meta
} }
@ -34,16 +35,17 @@ public interface Data<out T : Any> : Goal<T>, MetaRepr {
public companion object { public companion object {
public const val TYPE: String = "data" public const val TYPE: String = "data"
public fun <T : Any> static( public inline fun <reified T : Any> static(
value: T, value: T,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
): Data<T> = StaticData(value, meta) ): Data<T> = StaticData(typeOf<T>(),value, meta)
/** /**
* An empty data containing only meta * An empty data containing only meta
*/ */
public fun empty(meta: Meta): Data<Nothing> = object : Data<Nothing> { public fun empty(meta: Meta): Data<Nothing> = object : Data<Nothing> {
override val type: KClass<out Nothing> = Nothing::class private val nothing: Nothing get() = error("this is nothing")
override val type: KType = this::nothing.returnType
override val meta: Meta = meta override val meta: Meta = meta
override val dependencies: Collection<Goal<*>> = emptyList() override val dependencies: Collection<Goal<*>> = emptyList()
override val deferred: Deferred<Nothing> override val deferred: Deferred<Nothing>
@ -58,7 +60,7 @@ public interface Data<out T : Any> : Goal<T>, MetaRepr {
} }
public class LazyData<T : Any>( public class LazyData<T : Any>(
override val type: KClass<out T>, override val type: KType,
override val meta: Meta = Meta.EMPTY, override val meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext, context: CoroutineContext = EmptyCoroutineContext,
dependencies: Collection<Data<*>> = emptyList(), dependencies: Collection<Data<*>> = emptyList(),
@ -66,15 +68,14 @@ public class LazyData<T : Any>(
) : Data<T>, LazyGoal<T>(context, dependencies, block) ) : Data<T>, LazyGoal<T>(context, dependencies, block)
public class StaticData<T : Any>( public class StaticData<T : Any>(
override val type: KType,
value: T, value: T,
override val meta: Meta = Meta.EMPTY, override val meta: Meta = Meta.EMPTY,
) : Data<T>, StaticGoal<T>(value) { ) : Data<T>, StaticGoal<T>(value)
override val type: KClass<out T> get() = value::class
}
@Suppress("FunctionName") @Suppress("FunctionName")
public fun <T : Any> Data( public fun <T : Any> Data(
type: KClass<out T>, type: KType,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext, context: CoroutineContext = EmptyCoroutineContext,
dependencies: Collection<Data<*>> = emptyList(), dependencies: Collection<Data<*>> = emptyList(),
@ -87,4 +88,4 @@ public inline fun <reified T : Any> Data(
context: CoroutineContext = EmptyCoroutineContext, context: CoroutineContext = EmptyCoroutineContext,
dependencies: Collection<Data<*>> = emptyList(), dependencies: Collection<Data<*>> = emptyList(),
noinline block: suspend () -> T, noinline block: suspend () -> T,
): Data<T> = Data(T::class, meta, context, dependencies, block) ): Data<T> = Data(typeOf<T>(), meta, context, dependencies, block)

View File

@ -7,14 +7,14 @@ import hep.dataforge.meta.set
import hep.dataforge.names.* import hep.dataforge.names.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlin.reflect.KClass import kotlin.reflect.KType
public interface DataSet<out T : Any> { public interface DataSet<out T : Any> {
/** /**
* The minimal common ancestor to all data in the node * The minimal common ancestor to all data in the node
*/ */
public val dataType: KClass<out T> public val dataType: KType
/** /**
* Traverse this provider or its child. The order is not guaranteed. * Traverse this provider or its child. The order is not guaranteed.
@ -43,7 +43,9 @@ public interface DataSet<out T : Any> {
* An empty [DataSet] that suits all types * An empty [DataSet] that suits all types
*/ */
public val EMPTY: DataSet<Nothing> = object : DataSet<Nothing> { public val EMPTY: DataSet<Nothing> = object : DataSet<Nothing> {
override val dataType: KClass<out Nothing> = Nothing::class override val dataType: KType = this::nothing.returnType
private val nothing: Nothing get() = error("this is nothing")
override fun flow(): Flow<NamedData<Nothing>> = emptyFlow() override fun flow(): Flow<NamedData<Nothing>> = emptyFlow()
@ -88,7 +90,7 @@ public suspend fun DataSet<*>.toMeta(): Meta = Meta {
set(it.name, it.meta) set(it.name, it.meta)
} else { } else {
it.name put { it.name put {
"type" put it.type.simpleName "type" put it.type.toString()
"meta" put it.meta "meta" put it.meta
} }
} }

View File

@ -3,11 +3,12 @@ package hep.dataforge.data
import hep.dataforge.names.* import hep.dataforge.names.*
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlin.reflect.KClass import kotlin.reflect.KType
import kotlin.reflect.typeOf
@PublishedApi @PublishedApi
internal class StaticDataTree<T : Any>( internal class StaticDataTree<T : Any>(
override val dataType: KClass<out T>, override val dataType: KType,
) : DataSetBuilder<T>, DataTree<T> { ) : DataSetBuilder<T>, DataTree<T> {
private val items: MutableMap<NameToken, DataTreeItem<T>> = HashMap() private val items: MutableMap<NameToken, DataTreeItem<T>> = HashMap()
@ -26,7 +27,7 @@ internal class StaticDataTree<T : Any>(
0 -> this 0 -> this
1 -> { 1 -> {
val itemName = name.firstOrNull()!! val itemName = name.firstOrNull()!!
(items[itemName].tree as? StaticDataTree<T>) ?: StaticDataTree(dataType).also { (items[itemName].tree as? StaticDataTree<T>) ?: StaticDataTree<T>(dataType).also {
items[itemName] = DataTreeItem.Node(it) items[itemName] = DataTreeItem.Node(it)
} }
} }
@ -61,14 +62,14 @@ internal class StaticDataTree<T : Any>(
@Suppress("FunctionName") @Suppress("FunctionName")
public suspend fun <T : Any> DataTree( public suspend fun <T : Any> DataTree(
dataType: KClass<out T>, dataType: KType,
block: suspend DataSetBuilder<T>.() -> Unit, block: suspend DataSetBuilder<T>.() -> Unit,
): DataTree<T> = StaticDataTree(dataType).apply { block() } ): DataTree<T> = StaticDataTree<T>(dataType).apply { block() }
@Suppress("FunctionName") @Suppress("FunctionName")
public suspend inline fun <reified T : Any> DataTree( public suspend inline fun <reified T : Any> DataTree(
noinline block: suspend DataSetBuilder<T>.() -> Unit, noinline block: suspend DataSetBuilder<T>.() -> Unit,
): DataTree<T> = DataTree(T::class, block) ): DataTree<T> = DataTree(typeOf<T>(), block)
public suspend fun <T : Any> DataSet<T>.seal(): DataTree<T> = DataTree(dataType){ public suspend fun <T : Any> DataSet<T>.seal(): DataTree<T> = DataTree(dataType){
populate(this@seal) populate(this@seal)

View File

@ -11,7 +11,8 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
import kotlin.reflect.KClass import kotlin.reflect.KType
import kotlin.reflect.typeOf
/** /**
* Lazily transform this data to another data. By convention [block] should not use external data (be pure). * Lazily transform this data to another data. By convention [block] should not use external data (be pure).
@ -19,8 +20,8 @@ import kotlin.reflect.KClass
* @param meta for the resulting data. By default equals input data. * @param meta for the resulting data. By default equals input data.
* @param block the transformation itself * @param block the transformation itself
*/ */
public fun <T : Any, R : Any> Data<T>.map( private fun <T : Any, R : Any> Data<T>.map(
outputType: KClass<out R>, outputType: KType,
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta = this.meta, meta: Meta = this.meta,
block: suspend (T) -> R, block: suspend (T) -> R,
@ -35,7 +36,7 @@ public inline fun <T : Any, reified R : Any> Data<T>.map(
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta = this.meta, meta: Meta = this.meta,
crossinline block: suspend (T) -> R, crossinline block: suspend (T) -> R,
): LazyData<R> = LazyData(R::class, meta, coroutineContext, listOf(this)) { ): LazyData<R> = LazyData(typeOf<R>(), meta, coroutineContext, listOf(this)) {
block(await()) block(await())
} }
@ -47,7 +48,7 @@ public inline fun <T1 : Any, T2 : Any, reified R : Any> Data<T1>.combine(
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta = this.meta, meta: Meta = this.meta,
crossinline block: suspend (left: T1, right: T2) -> R, crossinline block: suspend (left: T1, right: T2) -> R,
): LazyData<R> = LazyData(R::class, meta, coroutineContext, listOf(this, other)) { ): LazyData<R> = LazyData(typeOf<R>(), meta, coroutineContext, listOf(this, other)) {
block(await(), other.await()) block(await(), other.await())
} }
@ -62,7 +63,7 @@ public inline fun <T : Any, reified R : Any> Collection<Data<T>>.reduceToData(
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
crossinline block: suspend (Collection<T>) -> R, crossinline block: suspend (Collection<T>) -> R,
): LazyData<R> = LazyData( ): LazyData<R> = LazyData(
R::class, typeOf<R>(),
meta, meta,
coroutineContext, coroutineContext,
this this
@ -71,7 +72,7 @@ public inline fun <T : Any, reified R : Any> Collection<Data<T>>.reduceToData(
} }
public fun <K, T : Any, R : Any> Map<K, Data<T>>.reduceToData( public fun <K, T : Any, R : Any> Map<K, Data<T>>.reduceToData(
outputType: KClass<out R>, outputType: KType,
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
block: suspend (Map<K, T>) -> R, block: suspend (Map<K, T>) -> R,
@ -96,7 +97,7 @@ public inline fun <K, T : Any, reified R : Any> Map<K, Data<T>>.reduceToData(
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
noinline block: suspend (Map<K, T>) -> R, noinline block: suspend (Map<K, T>) -> R,
): LazyData<R> = LazyData( ): LazyData<R> = LazyData(
R::class, typeOf<R>(),
meta, meta,
coroutineContext, coroutineContext,
this.values this.values
@ -110,7 +111,7 @@ public inline fun <K, T : Any, reified R : Any> Map<K, Data<T>>.reduceToData(
* Transform a [Flow] of [NamedData] to a single [Data]. * Transform a [Flow] of [NamedData] to a single [Data].
*/ */
public suspend fun <T : Any, R : Any> Flow<NamedData<T>>.reduceToData( public suspend fun <T : Any, R : Any> Flow<NamedData<T>>.reduceToData(
outputType: KClass<out R>, outputType: KType,
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
transformation: suspend (Flow<NamedData<T>>) -> R, transformation: suspend (Flow<NamedData<T>>) -> R,
@ -127,7 +128,7 @@ public suspend inline fun <T : Any, reified R : Any> Flow<NamedData<T>>.reduceTo
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
noinline transformation: suspend (Flow<NamedData<T>>) -> R, noinline transformation: suspend (Flow<NamedData<T>>) -> R,
): LazyData<R> = reduceToData(R::class, coroutineContext, meta) { ): LazyData<R> = reduceToData(typeOf<R>(), coroutineContext, meta) {
transformation(it) transformation(it)
} }
@ -148,11 +149,11 @@ public suspend inline fun <T : Any, reified R : Any> Flow<NamedData<T>>.foldToDa
//DataSet operations //DataSet operations
public suspend fun <T : Any, R : Any> DataSet<T>.map( public suspend fun <T : Any, R : Any> DataSet<T>.map(
outputType: KClass<out R>, outputType: KType,
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
metaTransform: MetaBuilder.() -> Unit = {}, metaTransform: MetaBuilder.() -> Unit = {},
block: suspend (T) -> R, block: suspend (T) -> R,
): DataTree<R> = DataTree(outputType) { ): DataTree<R> = DataTree<R>(outputType) {
populate( populate(
flow().map { flow().map {
val newMeta = it.meta.toMutableMeta().apply(metaTransform).seal() val newMeta = it.meta.toMutableMeta().apply(metaTransform).seal()
@ -165,7 +166,7 @@ public suspend inline fun <T : Any, reified R : Any> DataSet<T>.map(
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
noinline metaTransform: MetaBuilder.() -> Unit = {}, noinline metaTransform: MetaBuilder.() -> Unit = {},
noinline block: suspend (T) -> R, noinline block: suspend (T) -> R,
): DataTree<R> = map(R::class, coroutineContext, metaTransform, block) ): DataTree<R> = map(typeOf<R>(), coroutineContext, metaTransform, block)
public suspend fun <T : Any> DataSet<T>.forEach(block: suspend (NamedData<T>) -> Unit) { public suspend fun <T : Any> DataSet<T>.forEach(block: suspend (NamedData<T>) -> Unit) {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }

View File

@ -1,42 +0,0 @@
package hep.dataforge.data
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
/**
* Check if data could be safely cast to given class
*/
internal fun <R : Any> Data<*>.canCast(type: KClass<out R>): Boolean = this.type.isSubclassOf(type)
/**
* Cast the node to given type if the cast is possible or return null
*/
@Suppress("UNCHECKED_CAST")
public fun <R : Any> Data<*>.castOrNull(type: KClass<out R>): Data<R>? =
if (!canCast(type)) null else object : Data<R> by (this as Data<R>) {
override val type: KClass<out R> = type
}
/**
* Unsafe cast of data node
*/
public fun <R : Any> Data<*>.cast(type: KClass<out R>): Data<R> =
castOrNull(type) ?: error("Can't cast ${this.type} to $type")
public inline fun <reified R : Any> Data<*>.cast(): Data<R> = cast(R::class)
@Suppress("UNCHECKED_CAST")
public fun <R : Any> DataSet<*>.castOrNull(type: KClass<out R>): DataSet<R>? =
if (!canCast(type)) null else object : DataSet<R> by (this as DataSet<R>) {
override val dataType: KClass<out R> = type
}
public fun <R : Any> DataSet<*>.cast(type: KClass<out R>): DataSet<R> =
castOrNull(type) ?: error("Can't cast ${this.dataType} to $type")
/**
* Check that node is compatible with given type meaning that each element could be cast to the type
*/
internal fun <R : Any> DataSet<*>.canCast(type: KClass<out R>): Boolean =
type.isSubclassOf(this.dataType)

View File

@ -3,26 +3,69 @@ package hep.dataforge.data
import hep.dataforge.actions.NamedData import hep.dataforge.actions.NamedData
import hep.dataforge.actions.named import hep.dataforge.actions.named
import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.DFExperimental
import hep.dataforge.names.* import hep.dataforge.names.Name
import hep.dataforge.names.matches
import hep.dataforge.names.toName
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlin.reflect.KClass import kotlin.reflect.KType
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.typeOf
/**
* Check if data could be safely cast to given class
*/
private fun <R : Any> Data<*>.canCast(type: KType): Boolean = this.type.isSubtypeOf(type)
/**
* Cast the node to given type if the cast is possible or return null
*/
@Suppress("UNCHECKED_CAST")
private fun <R : Any> Data<*>.castOrNull(type: KType): Data<R>? =
if (!canCast<R>(type)) null else object : Data<R> by (this as Data<R>) {
override val type: KType = type
}
/**
* Unsafe cast of data node
*/
private fun <R : Any> Data<*>.cast(type: KType): Data<R> =
castOrNull(type) ?: error("Can't cast ${this.type} to $type")
private inline fun <reified R : Any> Data<*>.cast(): Data<R> = cast(typeOf<R>())
@Suppress("UNCHECKED_CAST")
private fun <R : Any> DataSet<*>.castOrNull(type: KType): DataSet<R>? =
if (!canCast<R>(type)) null else object : DataSet<R> by (this as DataSet<R>) {
override val dataType: KType = type
}
private fun <R : Any> DataSet<*>.cast(type: KType): DataSet<R> =
castOrNull(type) ?: error("Can't cast ${this.dataType} to $type")
/**
* Check that node is compatible with given type meaning that each element could be cast to the type
*/
private fun <R : Any> DataSet<*>.canCast(type: KType): Boolean =
type.isSubtypeOf(this.dataType)
/** /**
* Select all data matching given type and filters. Does not modify paths * Select all data matching given type and filters. Does not modify paths
*/ */
@OptIn(DFExperimental::class) @OptIn(DFExperimental::class)
public fun <R : Any> DataSet<*>.select( @PublishedApi
type: KClass<out R>, internal fun <R : Any> DataSet<*>.select(
type: KType,
namePattern: Name? = null, namePattern: Name? = null,
): ActiveDataSet<R> = object : ActiveDataSet<R> { ): ActiveDataSet<R> = object : ActiveDataSet<R> {
override val dataType: KClass<out R> = type override val dataType = type
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun flow(): Flow<NamedData<R>> = this@select.flow().filter { override fun flow(): Flow<NamedData<R>> = this@select.flow().filter {
it.canCast(type) && (namePattern == null || it.name.matches(namePattern)) it.type.isSubtypeOf(type) && (namePattern == null || it.name.matches(namePattern))
}.map { }.map {
it as NamedData<R> it as NamedData<R>
} }
@ -31,7 +74,7 @@ public fun <R : Any> DataSet<*>.select(
override val updates: Flow<Name> = this@select.updates.filter { override val updates: Flow<Name> = this@select.updates.filter {
val datum = this@select.getData(it) val datum = this@select.getData(it)
datum?.canCast(type) ?: false datum?.canCast<R>(type) ?: false
} }
} }
@ -40,12 +83,12 @@ public fun <R : Any> DataSet<*>.select(
* Select a single datum of the appropriate type * Select a single datum of the appropriate type
*/ */
public inline fun <reified R : Any> DataSet<*>.select(namePattern: Name? = null): DataSet<R> = public inline fun <reified R : Any> DataSet<*>.select(namePattern: Name? = null): DataSet<R> =
select(R::class, namePattern) select(typeOf<R>(), namePattern)
public suspend fun <R : Any> DataSet<*>.selectOne(type: KClass<out R>, name: Name): NamedData<R>? = public suspend fun <R : Any> DataSet<*>.selectOne(type: KType, name: Name): NamedData<R>? =
getData(name)?.castOrNull(type)?.named(name) getData(name)?.castOrNull<R>(type)?.named(name)
public suspend inline fun <reified R : Any> DataSet<*>.selectOne(name: Name): NamedData<R>? = selectOne(R::class, name) public suspend inline fun <reified R : Any> DataSet<*>.selectOne(name: Name): NamedData<R>? = selectOne(typeOf<R>(), name)
public suspend inline fun <reified R : Any> DataSet<*>.selectOne(name: String): NamedData<R>? = public suspend inline fun <reified R : Any> DataSet<*>.selectOne(name: String): NamedData<R>? =
selectOne(R::class, name.toName()) selectOne(typeOf<R>(), name.toName())

View File

@ -4,8 +4,8 @@ import hep.dataforge.meta.Meta
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.full.cast
import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.safeCast
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
public fun <T : Any> Column<*>.cast(type: KClass<out T>): Column<T> { public fun <T : Any> Column<*>.cast(type: KClass<out T>): Column<T> {
@ -22,7 +22,7 @@ public class CastColumn<T : Any>(private val origin: Column<*>, override val typ
override val size: Int get() = origin.size override val size: Int get() = origin.size
override fun get(index: Int): T? = type.cast(origin[index]) override fun get(index: Int): T? = type.safeCast(origin[index])
} }
public class ColumnProperty<C: Any, T : C>(public val table: Table<C>, public val type: KClass<T>) : ReadOnlyProperty<Any?, Column<T>> { public class ColumnProperty<C: Any, T : C>(public val table: Table<C>, public val type: KClass<T>) : ReadOnlyProperty<Any?, Column<T>> {