scare-quoted "type-safe" contains method for covariant collections

63 views
Skip to first unread message

Paul Phillips

unread,
Feb 10, 2012, 5:07:08 PM2/10/12
to scala-l...@googlegroups.com
class MyCovariantCollection[+T, +CC[X] <: Seq[X]](xs: CC[T]) {
  // Oops, our "evidence" seems to be useless, as what we want
  // to know is that T1 <:< T, but of course we can't do that because
  // of the variance position.
  def contains[T1](x: T1)(implicit ev: T <:< T1): Boolean = xs contains x
}

// But wait! Unspecified implementation vagaries to the rescue!
object Test extends App {
  val xs = new MyCovariantCollection(List(1, 2, 3))
  assert(xs contains "abc", "I'm sure it's in there somewhere")
}

// Thank-you type inference algorithm! Pick the most specific
// type while resolving the first parameter list without regard
// for the fact that we are doomed once you see the second
// parameter list.  That's what we want today.
% scalac ./covariance-contains.scala 
./covariance-contains.scala:11: error: Cannot prove that Int <:< java.lang.String.
  assert(xs contains "abc", "I'm sure it's in there somewhere")
            ^
one error found

/*
Yes, of course this works:

  xs.contains[Any]("abc")

I don't know about you guys but people going that far out of their way
to look for Strings in a list of Ints is not my most pressing concern.
*/

Daniel Sobral

unread,
Feb 10, 2012, 6:49:45 PM2/10/12
to scala-l...@googlegroups.com
There's a lot of merit on this just in the decreased noise level from
the detractors. Granted, they'll pick up on something else if they are
bull-headed enough, but _some_ of them are bound not to be bull-headed
enough! :-)

--
Daniel C. Sobral

I travel to the future all the time.

Miles Sabin

unread,
Feb 11, 2012, 4:28:01 AM2/11/12
to scala-l...@googlegroups.com

Maybe I'm being a bit dim, but why is this preferable to just pimping
away the variance altogether?

scala> :paste
// Entering paste mode (ctrl-D to finish)

class InvariantContains[T, CC[X] <: Seq[X]](xs: CC[T]) {
def invarContains(x : T) : Boolean = xs contains x
}

// Exiting paste mode, now interpreting.

defined class InvariantContains

scala> implicit def invar[T, CC[X] <: Seq[X]](xs: CC[T]) = new
InvariantContains(xs)
invar: [T, CC[X] <: Seq[X]](xs: CC[T])InvariantContains[T,CC]

scala> List(1, 2, 3) invarContains "foo"
<console>:10: error: type mismatch;
found : java.lang.String("foo")
required: Int
List(1, 2, 3) invarContains "foo"
^
Cheers,


Miles

--
Miles Sabin
tel: +44 7813 944 528
gtalk: mi...@milessabin.com
skype: milessabin
g+: http://www.milessabin.com
http://twitter.com/milessabin
http://www.chuusai.com/

Paul Phillips

unread,
Feb 11, 2012, 9:52:15 AM2/11/12
to scala-l...@googlegroups.com


On Sat, Feb 11, 2012 at 1:28 AM, Miles Sabin <mi...@milessabin.com> wrote:
Maybe I'm being a bit dim, but why is this preferable to just pimping
away the variance altogether?

It's interesting because it's possible to do it without an intermediate structure (which is surprising) not because it's appealing.

That said, it might be cheaper (but I'm not sure.)

Miles Sabin

unread,
Feb 11, 2012, 10:38:59 AM2/11/12
to scala-l...@googlegroups.com
On Sat, Feb 11, 2012 at 2:52 PM, Paul Phillips <pa...@improving.org> wrote:
> On Sat, Feb 11, 2012 at 1:28 AM, Miles Sabin <mi...@milessabin.com> wrote:
>>
>> Maybe I'm being a bit dim, but why is this preferable to just pimping
>> away the variance altogether?
>
>
> It's interesting because it's possible to do it without an intermediate
> structure (which is surprising) not because it's appealing.

Oh, so the idea would be to replace the existing definition of
contains with your one with the type constraint, rather than use that
MyConvariantCollection type (which looks a lot like an intermediate
structure to me)? In which case my only quibble would be that using
=:= rather than <:< would have the same effect and be a bit more
transparent.

Paul Phillips

unread,
Feb 11, 2012, 11:35:13 AM2/11/12
to scala-l...@googlegroups.com


On Sat, Feb 11, 2012 at 7:38 AM, Miles Sabin <mi...@milessabin.com> wrote:
In which case my only quibble would be that using
=:= rather than <:< would have the same effect and be a bit more
transparent.

You can't, for the same reason you can't reverse the operands.  (Except instead of "covariant type in contravariant position" you have "covariant type in invariant position".)

Paul Phillips

unread,
Feb 11, 2012, 11:36:42 AM2/11/12
to scala-l...@googlegroups.com


On Sat, Feb 11, 2012 at 7:38 AM, Miles Sabin <mi...@milessabin.com> wrote:
rather than use that
MyConvariantCollection type (which looks a lot like an intermediate
structure to me)?

Yes, "MyCovariantCollection" was supposed to be read as "List".

Chris Marshall

unread,
Feb 11, 2012, 1:21:54 PM2/11/12
to scala-l...@googlegroups.com, scala-l...@googlegroups.com
What will the value of the implicit parameter be? Won't it be a new instance of an identity function?

Chris

Paul Phillips

unread,
Feb 11, 2012, 1:28:18 PM2/11/12
to scala-l...@googlegroups.com


On Sat, Feb 11, 2012 at 10:21 AM, Chris Marshall <oxbow...@gmail.com> wrote:
What will the value of the implicit parameter be? Won't it be a new instance of an identity function?

There is only one instance.

  private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
  implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]

Reply all
Reply to author
Forward
0 new messages