Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

How to construct a structural type via metaprogramming? #14056

Discussion options

If I have class Foo[T] extends Selectable, how do I construct the type

Foo[T] {
 val bar : Int
 val baz : String
}

?

And this is just an example. Obviously I want to be able to control how many fields, their names and types.
It seems that the quotes API does not provide a way to construct a RefinedType.

You must be logged in to vote

You need to use the Refined type wich does have a Refined.apply factory method.

Here is a prototype macro that converts a tuple ("name1" -> T1, "name2" -> T2, ...) into Record { val name1: T1; val name2: T2; ... }:

Replies: 2 comments 12 replies

Comment options

You need to use the Refined type wich does have a Refined.apply factory method.

Here is a prototype macro that converts a tuple ("name1" -> T1, "name2" -> T2, ...) into Record { val name1: T1; val name2: T2; ... }:

You must be logged in to vote
7 replies
Comment options

@AugustNagro I hope this example helps https://gist.github.com/prolativ/0fca0f4c1a36abca291837007ff80dd0
makeSel will create an instance of

Sel {
 val field: String
 def parameterless: String
 def emptyParamList(): String
 def singleParam(arg1: Int): String
 def curried(arg1: Int)(arg2: Boolean): String
}
Comment options

@prolativ Thanks, that works great and I was able to figure out the same last night.

One problem is with creating a refinement methodType that returns this.type. For example, something like

Sel { def thisMethod(arg1: Int): this.type }

In my example github repo I am doing something like

 val refinement = RecursiveType: parent =>
 val mt =
 MethodType(List("arg1"))(
 _ => List(TypeRepr.of[Int]),
 _ => parent.recThis
 )
 Refinement(getterRefinement, setter, mt)

However, as Main.scala shows, scalac stack overflows when compiling an invalid method (like mySql.wrongName(22)). Would this be a dotty bug or is there a better way to set this.type?

Comment options

Unfortunately it looks like what you're trying to do is kind of illegal even without a macro. This snippet

trait Sel extends Selectable
val sel = (new Sel {}).asInstanceOf[{def thisMethod(arg1: Int): this.type}]

causes a warning:

-- Warning: --------------------------------------------------------------------
2 |val sel = (new Sel {}).asInstanceOf[{def thisMethod(arg1: Int): this.type}]
 | ^^^^
 | self reference in refinement is deprecated
Comment options

@prolativ Right. However this issue suggests that it's fine in a macro?

#17409

Comment options

The macro supports the pattern, but it also warns about the deprecation of this feature.

The example in #17409 shows

8 | def makeThing: { def me: this.type } = ???
 | ^^^^
 | self reference in refinement is deprecated
Answer selected by bishabosha
Comment options

Is it possible to create a Refinement method that has an implicit parameter? Ala

Foo[T] {
 def bar(i: Int)(using MyImplict): String
}
You must be logged in to vote
5 replies
Comment options

This compiles

trait MyImplict
trait Sel extends Selectable
val sel = (new Sel {}).asInstanceOf[Sel { def bar(i: Int)(using MyImplict): String }]

so I assume it is.
The only limitations that I can think of right now is that refinement methods cannot:

  • have a type parameter
  • be extension methods
Comment options

From a Macro I mean :)

From your (awesome) gist I see we can generate curried methods:

val tpe5 = Refinement(tpe4, "curried", MethodType(List("arg1"))(_ => List(TypeRepr.of[Int]), _ => MethodType(List("arg2"))(_ => List(TypeRepr.of[Boolean]), _ => TypeRepr.of[String])))

But I don't see a way to make an argument implicit.

Comment options

Hmm, interesting... It looks like the compiler internally uses ContextualMethodType rather than just MethodType to express a method with (using ...) parameters clause but currently there seems no way to create contextual methods from the quotes' reflect API. @nicolasstucki are there any plans to add this functionality in the nearest future?

Comment options

Here's a workaround. Lets say we have

case class MyImplicit[A](x: A)
class Foo extends Selectable:
 def applyDynamic(name: String)(a: Int, b: Int, c: MyImplicit[Int]): Int =
 a + b + c.x

And want to create this refinement:

Foo {
 def bar(a: Int, b: Int)(using MyImplicit[Int]): Int
}

Such that the following runs:

@main def fooMain(): Unit =
 // Foo { def bar(a: Int, b: Int)(using MyImplicit[Int]): Int
 val myFoo = fooMacro[Int]
 given MyImplicit[Int] = MyImplicit(3)
 println(myFoo.bar(1, 2)) // 6

We can do it via the following macro. The idea is that we write a dummy Expr with the desired using clause by hand. Then we extract the ContextualMethodType, and apply it to our own refinement.

trait Dummy[A] {
 def dummy(using myImplicit: MyImplicit[A]): Unit
}
transparent inline def fooMacro[A] = ${ fooMacroImpl[A] }
private def fooMacroImpl[A: Type](using q: Quotes): Expr[Any] =
 import q.reflect.*
 val dummyExpr = '{
 class D extends Dummy[A]:
 def dummy(using myImplicit: MyImplicit[A]): A = ???
 }
 val Inlined(_, _, Block(List(ClassDef(_, _, _, _, List(defdef))), _)) =
 dummyExpr.asTerm: @unchecked
 val methodType = defdef
 .asInstanceOf[DefDef]
 .symbol
 .typeRef
 .translucentSuperType
 .asInstanceOf[MethodType]
 println(methodType)
 val refinement =
 Refinement(
 TypeRepr.of[Foo],
 "bar",
 MethodType(List("a", "b"))(
 _ => List(TypeRepr.of[Int], TypeRepr.of[Int]),
 _ => methodType
 )
 )
 refinement.asType match
 case '[tpe] =>
 '{
 new Foo().asInstanceOf[tpe]
 }
end fooMacroImpl
Comment options

We do not have the ability to create ContextMethodTypes. We should be able to make them. I opened #18477 to track this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

AltStyle によって変換されたページ (->オリジナル) /