Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Oct 19 23:13
    djneades edited #10042
  • Oct 19 23:12
    djneades edited #10042
  • Oct 19 23:03
    djneades labeled #10042
  • Oct 19 23:03
    djneades opened #10042
  • Oct 19 21:40
    griggt opened #10041
  • Oct 19 21:10
    smarter commented #7960
  • Oct 19 21:06
    griggt commented #7960
  • Oct 19 19:46
    griggt commented #7738
  • Oct 19 15:30
    bishabosha opened #10040
  • Oct 19 15:30
    bishabosha labeled #10040
  • Oct 19 15:04
    smarter edited #10039
  • Oct 19 14:56
    bishabosha labeled #10039
  • Oct 19 14:55
    bishabosha labeled #10039
  • Oct 19 14:55
    bishabosha opened #10039
  • Oct 19 14:39
    smarter commented #10005
  • Oct 19 14:38
    smarter closed #10005
  • Oct 19 14:35
    odersky assigned #9992
  • Oct 19 14:32
    odersky assigned #10016
  • Oct 19 14:26
    bishabosha commented #9970
  • Oct 19 14:25
    sjrd assigned #9970
Ólafur Páll Geirsson
@olafurpg
(using 27.0.0-RC1)
Sébastien Doeraene
@sjrd
Probably. Cooperative equality means that Ints and Chars can be compared.
Ólafur Páll Geirsson
@olafurpg
Makes sense, thanks!

I was surprised to see this behavior

scala> Option(1) == Option(2)                                                                                                        
1 |Option(1) == Option(2)
  |^^^^^^^^^^^^^^^^^^^^^^
  |Values of types Option[Int] and Option[Int] cannot be compared with == or !=

scala> Vector(1) == List(2)                                                                                                        
val res3: Boolean = false

Why is there a default value for collection.Seq[T] but not Option[T]?

Ólafur Páll Geirsson
@olafurpg
Back to Eql[Int, Char], I would suspect that users who enable -language:strictEquality expect 'a' == 'a'.toInt to fail at compile-time, even if the values are equal at runtime.
Stéphane Micheloud
@michelou
@sjrd I have a question about the execution of the workflow file ci.yaml on the Windows runner : does it matter if I split the runner command sbt ";sjsJUnitTests/test ;sjsCompilerTests/test" in two steps, i.e. sbt ";sjsJUnitTests/test"and then "sbt ;sjsCompilerTests/test" ? PS: I have memory issues when running my Actions on my GitHub-hosted Windows runner (2 cpus + 8 GB versus 4 cpus + 24 GB at EPFL)
Guillaume Martres
@smarter
starting sbt multiple times is slower
@olafurpg like all language flags, strictEquality is barely tested and probably not really usable, I wouldn't recommend using it.
Ólafur Páll Geirsson
@olafurpg
@smarter fair enough. I was trying it out following your suggestion in https://github.com/scalameta/munit/issues/189#issuecomment-702292672 :p
Do you think it's worth opening a "bug report" with the example above?
I'm planning to use Eql in MUnit scalameta/munit#225 I think it would be really nice to be able to use multiversal equality for cases like this
Guillaume Martres
@smarter
one can use multiversal equality without turning on strictEquality and still get some benefits
it sounds like you disagree on what should and should not be equal (Int and Char, ...), so https://contributors.scala-lang.org/ would be more appropriate for open-ended discussions
we could add Option to the instances in https://github.com/lampepfl/dotty/blob/master/library/src/scala/Eql.scala, but that doesn't really scale, so I assume you'd want different rules to handle this, so you'd need to come up with rules and propose them
Sébastien Doeraene
@sjrd
@michelou There's no consequence in terms of correctness of execution, if that's what you're asking. The tests are independent. As @smarter mentioned, they're bundled to reduce execution time.
Ólafur Páll Geirsson
@olafurpg

@smarter Fair enough, I opened https://contributors.scala-lang.org/t/should-multiversal-equality-provide-default-eql-instances/4574

I agree it would not scale to add Option[T] instances to theEql object, a better approach would be to remove all of the default instances to give users 100% control over what types are safe to compare with each other. I am concerned this new language feature won't gain widespread adoption in its current form.

Stéphane Micheloud
@michelou
@sjrd Thanks. That's exactly what I wanted to make sure. I prefer not to comment the execution time of 's'+'b'+'t' :speak_no_evil:
Anton Sviridov
@keynmol
sbtn thin client would make this go away, but I don't think people would be open to upgrading dotty to 1.4.0 this late in the game :D
oscar-broman
@oscar-broman
  final case class Universe() {
  case class Needle()
    def findNeedle(n: Needle) = n
  }

  // Doesn't compile
  def find1[U <: Universe with Singleton](u: U, n: U#Needle) = { u.findNeedle(n) }
  // Compiles
  def find2[U <: Universe with Singleton](u: U)(n: u.Needle) = { u.findNeedle(n) }
Why does find1 not compile?
Guillaume Martres
@smarter
if you have a U#Needle you don't necesarily have an u.Needle
also you don't need two parameter lists for dependent params in dotty
(also type projections on abstract types are unsound in general and should be avoided)
oscar-broman
@oscar-broman
This compiles in Scala 2.13 but not in Dotty, how should I go about doing similar things?
final case class Universe() {
  val needle = Needle()
  case class Needle() {
    def combine(other: Needle): Needle = ???
  }
  def findNeedle(n: Needle) = n
}

def combine[U <: Universe with Singleton](n1: U#Needle, n2: U#Needle) = { n1.combine(n2) }
Guillaume Martres
@smarter
write n2: n1.Needle ?
oscar-broman
@oscar-broman
Note: I've read this blog post
n1 doesn't have Needle since it is Needle
Guillaume Martres
@smarter
ah yeah
def combine[U <: Universe, N <: U#Needle](n1: N, n2: N) maybe
or even N <: Universe#Needle
oscar-broman
@oscar-broman
That doesn't work either:
1 |  def combine[N <: Universe#Needle](n1: N, n2: N) = { n1.combine(n2) }
  |                                                                 ^^
  |    Found:    (n2 : N)
  |    Required: Nothing
  |
  |    where:    N is a type in method combine with bounds <: Universe#Needle
Hanns Holger Rutz
@Sciss
projection types have basically been removed, except for rare cases where they still work. You may need a value of universe:
trait Universe {
  trait Needle {
    def combine(other: Needle): Needle
  }
}

def combine(u: Universe)(n1: u.Needle, n2: u.Needle) = n1 combine n2
unless you want to permit needles beyond one single universe
oscar-broman
@oscar-broman
It just seems so unnecessary having to pass u just for the compiler to know that the types are the same
And while my example is very simplified, I have some rather messy code because of this
Guillaume Martres
@smarter
yeah we don't have a good way to express that
but if you have a combine method on Needle, you can write n1.combine(n2) directly, no?
oscar-broman
@oscar-broman
Yeah I did simplify a bit too much
Main point being I have multiple types belonging to a single Universe and I'd like a method aware of that fact, able to operate on these types and return U#Something without needing to give it U.
Guillaume Martres
@smarter
can you define those methods inside Universe itself?
oscar-broman
@oscar-broman
Obviously I could resort to type refinements and type parameters connected to those, but that quickly blows up as well
In our case, the universe is a trait with multiple subtraits and in the end singleton objects inheriting from those. On the top level, all types are abstract and they become more specific along the way. There's no natural place within the universe to place many methods, unfortunately. And having the code spread throughout multiple packages also make things worse (e.g. one place where the methods are replaced by mocked ones).
I'll try to come up with a better test case demonstrating my issues more specifically
Michał Pałka
@prolativ

Is there any particular reason why this compiles

object ChainedConversions1 {
  given implicitLength as Conversion[String, Int] = _.length
  given implicitLengths[A](using ev: Conversion[A, Int]) as Conversion[Seq[A], Seq[Int]] = _.map(ev)
  val strings: Seq[String] = Seq("", "a", "xyz")
  val lengths: Seq[Int] = strings
}

but this doesn't

object ChainedConversions2 {
  implicit val implicitLength: Conversion[String, Int] = _.length
  implicit def implicitLengths[A](implicit ev: Conversion[A, Int]): Conversion[Seq[A], Seq[Int]] = _.map(ev)
  val strings: Seq[String] = Seq("", "a", "xyz")
  val lengths: Seq[Int] = strings
}

?

Hanns Holger Rutz
@Sciss

Main point being I have multiple types belonging to a single Universe and I'd like a method aware of that fact, able to operate on these types and return U#Something without needing to give it U.

There is no good approach in Dotty if you need to bundle multiple type members. You can only resort to restating multiple type constructor parameters, like

// Scala 2
trait Space[D <: Space[D]] {
  type Point <: PointLike
  type HyperCube <: HyperCubeLike
}

def foo[D <: Space[D]](p: D#Point, c: D#HyperCube) = ???
// Scala 3
trait Space[P, H]

def foo[P <: PointLike, H <: HyperCubeLike](p: P, c: H) = ???

but as soon as these types need to be matched to each other, you know need an instance of Space:

def foo[P <: PointLike, H <: HyperCubeLike](p: P, c: H)(implicit space: Space[P, H]) = ???
in my experience, if you have been using type projections, you need to apply a major reworking of your project to make it elegant again in Scala 3 (even more work if you want it still to cross-build against Scala 2), although it's doable.
oscar-broman
@oscar-broman
Does the implicit parameter list in your last example affect how the type parameters are inferred? Or is that behavior same as Scala 2?
Hanns Holger Rutz
@Sciss
implicit resolution should still work, but of course you can no longer write foo[TwoDim] but now you'll have to write foo[Point2D, Rectangle2D]
oscar-broman
@oscar-broman
Right, cheers
Hanns Holger Rutz
@Sciss
that's why you may want to stick to value dependent type u.Needle