Open kubukoz opened 3 weeks ago
Hey! Thanks for the report - yeah I'd fully expect it to work... Maybe I went a .dealias
too far somewhere down the line, I'll investigate ASAP
So, I'm not able to repro this on Scala 3.3.3 (running right from the ducktape
repo), i.e. this:
case class From(name: String)
object scoped {
opaque type Ident = String
case class To(name: String, id: Ident)
def f(
f: From,
theId: scoped.Ident
): scoped.To = f.into[scoped.To].transform(Field.const(_.id, theId))
}
object demo {
def f(
f: From,
theId: scoped.Ident
): scoped.To = f.into[scoped.To].transform(Field.const(_.id, theId))
}
@main def main = {
println(scoped.f(From("a"), "ident".asInstanceOf[scoped.Ident]))
println(demo.f(From("a"), "ident".asInstanceOf[scoped.Ident]))
}
compiles and works exactly as I'd expect:
[info] running io.github.arainko.ducktape.main
To(a,ident)
To(a,ident)
[success] Total time: 0 s, completed Aug 22, 2024, 9:02:12 PM
I'll try running from a project that pulls the dep in and across different Scalas, maybe that's a factor for some reason...
Hah, pulling the dep in from a separate project reproduces it, weird
full repro:
//> using scala 3.3.3
//> using repositories sonatype-s01:snapshots
//> using dep io.github.arainko::ducktape::0.2.4-16-8bf2679-20240822T175302Z-SNAPSHOT
import io.github.arainko.ducktape.*
case class From(name: String)
object scoped {
opaque type Ident = String
case class To(name: String, id: Ident)
// def f(
// f: From,
// theId: scoped.Ident
// ): scoped.To = f.into[scoped.To].transform(Field.const(_.id, theId))
}
object demo {
def f(
f: From,
theId: scoped.Ident
): scoped.To =
f
.into[scoped.To]
.transform(Field.const(_.id, theId))
}
@main def main = {
// println(scoped.f(From("a"), "ident".asInstanceOf[scoped.Ident]))
println(demo.f(From("a"), "ident".asInstanceOf[scoped.Ident]))
}
Logs:
aleksander@pop-os:~/repos/repro-196$ scala-cli compile . --server=false
-- [E008] Not Found Error: /home/aleksander/repos/repro-196/repro.scala:34:17 --
34 | println(scoped.f(From("a"), "ident".asInstanceOf[scoped.Ident]))
| ^^^^^^^^
| value f is not a member of object scoped
1 error found
Compilation failed
aleksander@pop-os:~/repos/repro-196$ scala-cli compile . --server=false
[INFO] [repro.scala:17:24] Structure: Product(
tpe = Type.of[From],
path = From,
fields = Map(Entry(key = "name", value = Lazy(tpe = Type.of[String], path = From.name)))
)
[INFO] [repro.scala:17:24] Structure: Product(
tpe = Type.of[To],
path = To,
fields = Map(
Entry(key = "name", value = Lazy(tpe = Type.of[String], path = To.name)),
Entry(key = "id", value = Lazy(tpe = Type.of[String], path = To.id))
)
)
[INFO] [repro.scala:17:24] Structure: Ordinary(tpe = Type.of[String], path = From.name)
[INFO] [repro.scala:17:24] Structure: Ordinary(tpe = Type.of[String], path = To.name)
[INFO] [repro.scala:17:24] Structure: Ordinary(tpe = Type.of[Nothing], path = From)
[INFO] [repro.scala:17:24] Parsed path: To.id
[INFO] [repro.scala:17:24] Original plan: BetweenProducts(
source = Product(
tpe = Type.of[From],
path = From,
fields = Map(Entry(key = "name", value = Lazy(tpe = Type.of[String], path = From.name)))
),
dest = Product(
tpe = Type.of[To],
path = To,
fields = Map(
Entry(key = "name", value = Lazy(tpe = Type.of[String], path = To.name)),
Entry(key = "id", value = Lazy(tpe = Type.of[String], path = To.id))
)
),
fieldPlans = Map(
Entry(
key = "name",
value = Upcast(
source = Ordinary(tpe = Type.of[String], path = From.name),
dest = Ordinary(tpe = Type.of[String], path = To.name)
)
),
Entry(
key = "id",
value = Error(
source = Ordinary(tpe = Type.of[Nothing], path = From),
dest = Lazy(tpe = Type.of[String], path = To.id),
message = NoFieldFound(fieldName = "id", fieldTpe = Type.of[String], sourceTpe = Type.of[From]),
suppressed = None
)
)
)
)
[INFO] [repro.scala:17:24] Config: List(
Static(
path = To.id,
side = Dest(),
config = Const(value = Expr[theId], tpe = Type.of[Ident]),
span = Span(start = 569, end = 593)
)
)
[INFO] [repro.scala:17:24] Reconfigured plan: Reconfigured(
errors = List(
Error(
source = Ordinary(tpe = Type.of[Nothing], path = From),
dest = Lazy(tpe = Type.of[String], path = To.id),
message = InvalidConfiguration(
configTpe = Type.of[Ident],
expectedTpe = Type.of[String],
side = Dest(),
span = Span(start = 569, end = 593)
),
suppressed = None
)
),
result = BetweenProducts(
source = Product(
tpe = Type.of[From],
path = From,
fields = Map(Entry(key = "name", value = Lazy(tpe = Type.of[String], path = From.name)))
),
dest = Product(
tpe = Type.of[To],
path = To,
fields = Map(
Entry(key = "name", value = Lazy(tpe = Type.of[String], path = To.name)),
Entry(key = "id", value = Lazy(tpe = Type.of[String], path = To.id))
)
),
fieldPlans = Map(
Entry(
key = "name",
value = Upcast(
source = Ordinary(tpe = Type.of[String], path = From.name),
dest = Ordinary(tpe = Type.of[String], path = To.name)
)
),
Entry(
key = "id",
value = Error(
source = Ordinary(tpe = Type.of[Nothing], path = From),
dest = Lazy(tpe = Type.of[String], path = To.id),
message = InvalidConfiguration(
configTpe = Type.of[Ident],
expectedTpe = Type.of[String],
side = Dest(),
span = Span(start = 569, end = 593)
),
suppressed = None
)
)
)
)
)
-- Error: /home/aleksander/repos/repro-196/repro.scala:27:4 --------------------
27 | f
| ^
| No field 'id' found in From @ To.id
|----------------------------------------------------------------------------
|Inline stack trace
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from repro.scala:27
----------------------------------------------------------------------------
-- Error: /home/aleksander/repos/repro-196/repro.scala:29:17 -------------------
29 | .transform(Field.const(_.id, theId))
| ^^^^^^^^^^^^^^^^^^^^^^^^
|Configuration is not valid since the provided type (scoped.Ident) is not a subtype of java.lang.String @ To.id
|----------------------------------------------------------------------------
|Inline stack trace
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from repro.scala:29
----------------------------------------------------------------------------
2 errors found
Compilation failed
I'm pretty sure that this is some kind of an incremental compilation bug, it randomly disappears and reappears with no actual code changes (i.e. it fails after adding a newline but disappears after a clean;compile)...
Reported this under https://github.com/scala/scala3/issues/21430
Hi! I was trying to use the library for a work project but found an apparent shortcoming that makes it a little bit tricky.
Consider this example:
Everything is alright here. Now, move
opaque type Ident
one level up so thatTo
actually sees that it's a string:Now,
f
fails to compile:Originally, my code looked like the broken version, I can probably work around this by moving / wrapping the opaque types, but this seems like something that the library could improve upon. :)