Struggling with types in an extension

I’m struggling to write this extension:

extension [A, C <: Seq[A]](seq: C) def deleted(i: Int): C = seq.patch(i, Seq.empty[A], 1)

or

extension [A, CC[t] <: Seq[t]](seq: CC[A]) def deleted(i: Int): CC[A] = seq.patch(i, Seq.empty[A], 1)

In both variants, the compiler insists on Seq[A] instead of C or CC[A] for the type of the patch expression.

What’s the correct type parametrization to achieve what I want?

1 Like

What happens if you change:

to:

extension [A, CC <: Seq](seq: CC[A]) def deleted(i: Int): CC[A] = seq.patch(i, Seq.empty[A], 1)

Ah the collections hierarchy, always a joy to debug type errors related to it.

The problem here is not the Seq.empty[A], that is actually fine and works as expected since patch is able to receive any IterableOnce.
The problem is actually that patch here is returning a Seq[A] rather than a CC[A] as you want.

I fixed it by mixing the SeqOps to the type signature:

extension [A, CC[t] <: Seq[t]](seq: CC[A] & SeqOps[A, CC, CC[A]])
  def deleted(i: Int): CC[A] =
    seq.patch(i, Seq.empty[A], 1)

Code running here: Scastie - An interactive playground for Scala.

But I am unsure if that is the best way to do it.
You may want to check: Adding Custom Collection Operations (Scala 2.13) | Scala Documentation

5 Likes

I like it better written as:

extension [A, CC[t] <: Seq[t] & SeqOps[t, CC, CC[t]]](seq: CC[A])
   def deleted(i: Int): CC[A] = seq.patch(i, Seq.empty[A], 1)

but it’s still rather ugly.

Maybe deleted should just be part of the standard interface? :wink:

Thanks for the help.

Consider making a request here: GitHub - scala/scala-library-next: backwards-binary-compatible Scala standard library additions

1 Like

I’ll have a look. It’s interesting that Map has an updatedWith that is exactly what I need here (update or remove based on an option). I was surprised not to find it on IndexedSeq.

Because you’re essentially extending SeqOps, you could write it simply as

extension [A, CC[_]](self: SeqOps[A, CC, CC[A]])
  def deleted(i: Int): CC[A] =
    self.patch(i, Seq.empty, 1)
3 Likes

Yes, it looks a lot better that way!

I think your first attempt was closer, because deletion does not introduce a new element type, so it should return a C like (checks notes) diff does.

The doc explains that CC is for when the element type changes (widens), and C is what you started with.

But in an extension, you can’t call fromSpecific because that’s why we can’t have nice things.

For example, this is weirdly natural:

scala> "1234".diff("3")
val res0: String = 124

Thanks to SethTisue and BalmungSan, I added scala to my triple backtick, and it seems to render better. I’m not sure what the colors mean, but there is no extravagant boldface.

1 Like

Yes, but it didn’t work. And to use SeqOps explicitly, I need a CC[_].

That’s funny. I started to remove scala because it was giving me weird colors. I’ll put it back.