I'm having a hard time reconciling what is going on with the following file:
interface T {
_t(chunk: Array<string>): void;
_t(chunk: string): void;
}
class TT implements T {
_t(chunk: string): void {}
}
When I try to compile it I get the following error message:
Types of property '_t' of types 'TT' and 'T' are incompatible:
Call signatures of types '(chunk: string) => void' and
'{ (chunk: string[]): void; (chunk: string): void; }' are incompatible:
Type 'String' is missing property 'join' from type 'string[]'.
That's fine so that means I should be able to get around the issue by explicitly specifying the object type of the property right?
interface T {
_t(chunk: Array<string>): void;
_t(chunk: string): void;
}
class TT implements T {
_t: {
(chunk: string): void;
(chunk: Array<string>): void;
} = function(chunk) {};
}
Except the problem is now when I compile with --noImplicitAny I get the following error message:
error TS7012: Parameter 'chunk' of lambda function implicitly has an 'any' type.
It seems like no matter what I do I lose because if I'm careful with the type specifications on the interface and spell out everything then I can't specialize the implementation to only 1 type. The problem is that there are .d.ts files out there that do exactly this and if I want the compiler to find all any types because I want to be as explicit as possible I have to take out specifications from .d.ts files to be able to both implement interfaces and specialize implementations in subclasses. This seems like a bug. How do I resolve this?
Just type it chunk:any:
interface T {
_t(chunk: Array<string>): void;
_t(chunk: string): void;
}
class TT implements T {
_t: {
(chunk: string): void;
(chunk: Array<string>): void;
} = function(chunk:any) {};
}
// Safety:
var t = new TT();
t._t(''); // okay
t._t([]); // okay
t._t(true); // Error
Note it doesn't decrease your type safety ^
Related
I am writing a serialization library using Scala 3 macro programming.
I want to generate the serializer dynamicly. Because this is a library, so I do not know what class to serializer during compile. So I need a runtime compile feature.
Code:
object CodecMacro {
import scala.quoted.staging.*
given Compiler = Compiler.make(getClass.getClassLoader)
// function 1: entry point: a Class object
def buildSerializer(clazz: Class[_]): Serializer = {
given staging.Compiler = staging.Compiler.make(getClass.getClassLoader)
val fn = (qctx: Quotes) ?=> {
given q: Quotes = qctx
buildSerializerAdapter(clazz)
}
staging.run(fn)
}
// function 2: convert clazz to generic T
// My main question is here
private def buildSerializerAdapter(clazz: Class[_])(using quotes: Quotes): Expr[Serializer] = {
import quotes.reflect.*
val tpe: TypeRepr = TypeRepr.typeConstructorOf(clazz)
val t = tpe.asType
type T = t.Underlying
buildSerializerImpl[T] // error: Missing type parameters for T
}
// function 3: Using a generic T to generate a Serializer
private def buildSerializerImpl[T: Type](using quotes: Quotes): Expr[Serializer] = {
val t = summon[Type[T]]
'{
type T = t.Underlying
new Serializer[T] {
override def serialize(writer: Writer, value: T): Unit = {
// Implemetation, not important
writer.writeString("aaa")
}
}
}
Function 1 is the entry point, which needs a Class object.
Function 3 is the final implementation, which needs a generic type T.
Function 2 is to convert from Class object to generic type T and this is my main question.
My solution is
Class -> TypeRepr use: TypeRepr.typeConstructorOf(clazz)
TypeRepr -> Type use: TypeRepr.asType
Type -> type T use: Type.Underlying
I thought the type T under Type will solve my problem, but the compiler gives me an error:
"Missing type parameters for T"
In Function 3:
type T = t.Underlying
value: T
The generic type T is working fine.
Is there any way to convert Class object to generic type T?
Here is the way it should be done:
tpe.asType match
case '[t] =>
buildSerializerImpl[t]
This very strange invocation comes straight from the documentation of asType. It is necessary because asType returns a Type[?] where Type accepts any type kind, but here we want a concrete type kind. The match would fail if the type was of higher kind, such as a Type[List] (the List constructor takes type arguments).
I use Kotlin and Mongo (with KMongo) and I have multiple models as UserEntity, MovieEntity and so on. Each of them use a specific Dao class to do (actually) the same methods. Therefore, I'm trying to avoid any duplication by using a BaseDao which should have these methods instead.
So I pass the specific entity in the generic base as:
class UserDao : BaseDao<UserEntity>() { ... }
This base class implements the generic methods as follows:
open class BaseDao<T: Any>() {
fun get(id: String): T? {
return getCollection().findOneById(id)
}
fun save(entity: T): T {
return getCollection().save(entity)
}
fun delete(id: String) {
getCollection().deleteOneById(id)
}
...
}
However, a problem occurs on getCollection() method:
private inline fun <reified T: Any> getCollection(): MongoCollection<T> {
return MongoDb.getDatabase().getCollection<T>()
}
This gets a compilation error each time I call it:
Type inference failed: Not enough information to infer parameter T in
inline fun <reified T : Any> getCollection(): MongoCollection<T#1 (type parameter of app.api.db.dao.BaseDao.getCollection)>
Please specify it explicitly.
I can't find the right way to do this. I already checked these threads but I didn't make it work: Generic class type usage in Kotlin & Kotlin abstract class with generic param and methods which use type param.
Question:
How can I achieve this generic BaseDao which should get any collection of each child Dao?
the JVM forgets the type of the generic T in BaseDao<T: Any>() at runtime, which is why type inference fails. A solution to this could be to pass the KClass of T in the constructor of BaseDao:
open class BaseDao<T: Any>(val kClass: KClass<T>) {
...
}
After this, give your reified function an argument that accepts a `KClass:
private inline fun <reified T: Any> getCollection(val kClass: KClass<T>): MongoCollection<T> {
return MongoDb.getDatabase().getCollection<T>()
}
I'm unaware of a method to do this without passing the KClass as a argument to the function, but this should work, as the generic T can be derived from the provided kClass.
`
Another way would be to make all methods in BaseDao inline function with reified generics and dropping the generic on the class.
open class BaseDao() {
inline fun <reified T: Any> get(id: String): T? {
return getCollection().findOneById(id)
}
inline fun <reified T: Any> save (entity: T): T {
return getCollection().save(entity)
}
inline fun <reified T: Any> delete(id: String) {
getCollection().deleteOneById(id)
}
...
}
This way the generic T can be derived since the method calling getCollection is also reified.
(For KMongo 4.0.+) no need to use reified generics for the each method, instead this base class can be used as as a starting point:
open class BaseDao<T: Any>(
protected val collection: CoroutineCollection<T>
) {
suspend fun get(id: Id<T>): T? {
return collection.findOneById(id)
}
suspend fun save(entity: T): UpdateResult? {
return collection.save(entity)
}
suspend fun delete(id: Id<T>) {
collection.deleteOneById(id)
}
}
And implemented in the particular DAO, say SessionDao:
class SessionDao(collection: CoroutineCollection<DbSession>)
: BaseDao<DbSession>(collection)
(note: inheritance can be replaced with delegation by using by keyword if one feel better this way
This and other dao can be created via DI or some sort of dao factory:
class DbInstance(mongodbConnectionString: String = "mongodb://localhost:27017/myproject") {
private val connectionInfo = ConnectionString(mongodbConnectionString)
val client = KMongo.createClient().coroutine
val db = client.getDatabase(
connectionInfo.database ?: throw IllegalArgumentException("mongodb connection string must include db name")
)
val sessions = SessionDao(db.getCollection())
}
Notes:
This example is for the coroutine based kmongo, it can be easly converted to blocking kmongo by replacing CoroutineCollection to MongoCollection
I assume documents id's are annotated via Id container which helps to mitigate errors, so documents should be created in this fashion:
data class DbSession(
#BsonId
val id: Id<DbSession>,
val name: String,
)
The solution is to use reflection as Zigzago mentioned by using KMongoUtil:
protected fun getCollection(): MongoCollection<T> =
getDaoEntityClass().let { k ->
MongoDb.getDatabase().getCollection(
KMongoUtil.defaultCollectionName(k), k.java)
}
#Suppress("UNCHECKED_CAST")
private fun getDaoEntityClass(): KClass<T>
= ((this::class.java.genericSuperclass
as ParameterizedType).actualTypeArguments[0] as Class<T>).kotlin
I am attempting to build a wrapper around scalatest's it keyword. However, this solution does not seem to work as intended. Moreover, it does not even compile:
trait MyFunSpec extends FunSpec {
private val _it: FunSpec.this.ItWord = it
protected def it(specText: String, testTags: org.scalatest.Tag*)
// ...do something extra here...
(testFun: => Any /* Assertion */)(implicit pos: Position): Unit = {
_it(specText, testTags: _*)(testFun)(pos)
// ...do something extra here...
}
}
The error message I am getting after compiling this code is as follows:
[error] MyFunSpec.scala: ambiguous reference to overloaded definition,
[error] both method it in trait MyFunSpec of type (specText: String, testTags:
org.scalatest.Tag*)(testFun: => Any)(implicit pos:
org.scalactic.source.Position)Unit
[error] and value it in trait FunSpecLike of type => FunSpecSpec.this.ItWord
[error] match argument types (String)
Please note the main idea is that method's name remains it, so renaming it to something like alternativeIt is not a satisfactory solution here.
Any suggestions, what am I doing wrong here? Any solution would be highly appreciated! Thanks!
Try this:
trait MyFunSpec extends FunSpec {
override protected val it = new ItWord {
override def apply(specText: String,testTags: org.scalatest.Tag*)(testFun: => Any)(implicit pos: org.scalactic.source.Position): Unit = {
println("Before")
super.apply(specText, testTags:_*)(testFun)(pos)
println("After")
}
}
}
I am working on a Lift project with mixed Scala and Java code.
On the Java side, I have the following relevant items:
interface IEntity
interface IDAO<T extends IEntity> {
void persist(T t);
}
On the Scala side, I have the following:
abstract class Binding[T <: IEntity] extends NgModel {
def unbind: T
}
class BasicService[E <: IEntity](serviceName: String, dataAccessObject: IDAO[E]) {
def render = renderIfNotAlreadyDefined(
angular.module("myapp.services")
.factory(serviceName,
jsObjFactory()
.jsonCall("persist", (binding: Binding[E]) => { //<---COMPILATION ERROR
try {
dataAccessObject.persist(binding.unbind)
Empty
} catch {
case e: Exception => Failure(e.getMessage)
}
})
)
)
}
This code will not compile. I get the following error at the point indicated above:
No Manifest available for Binding[E].
It is not clear at all to me why this occurs, but I am guessing it has something to do with this being a nested method invocation. The code compiles fine if I declare a member function with Binding[E] as a parameter, for example:
def someFunction(binding: Binding[E] = { // same code as above }
Why does this happen, and how can I work around it?
Turns out this is relatively easily solved by implicitly passing on the manifest for the type in question, either in the constructor or the method itself:
class BasicService[E <: IEntity](serviceName: String, dataAccessObject: IDAO[E])(implicit m: Manifest[Binding[E]]) {
or
def render(implicit m: Manifest[Binding[E]])
In over my head dealing with a tricky covariant type used in an inherited trait's overridden function. The basic question is, what is the [?] type? I can't find a good definition (it's kinda ungooglable), and so it's unclear why a [T] gets replaced with [?] in the following example:
> sealed trait Bar[+T]
> trait FooT { type Other; def foo[T,V](stuff:Bar[T]*) { stuff.headOption.isDefined } }
> trait TooF extends FooT { override def foo[T,V](stuff:Bar[T]*) { super.foo(stuff) } }
<console>:7: error: type mismatch;
found : Bar[T]*
required: Bar[?]
trait TooF extends FooT { override def foo[T,V](stuff:Bar[T]*) { super.foo(stuff) } }
I'm not sure of the exact reason that it shows Bar[?] but I think it probably something like the type parameter hasn't been resolved yet. The real problem is that your syntax for passing the varargs on to the super method is incorrect. It should be
override def foo[T,V](stuff:Bar[T]*) { super.foo(stuff:_*) }