Can Be Matched As

58 views
Skip to first unread message

Seyed H. HAERI (Hossein)

unread,
Apr 25, 2017, 8:07:30 PM4/25/17
to scala-user
Dear all,

How can

trait t {
type T
}

enforce that T has an extractor which takes say an Int? I tried

trait t {
type T <: {def isEmpty: Boolean; def get}
val T: {def unapply(i: Int): T}

def test(a: Any): Unit = a match {case T(i) => println("")}
}

object o extends t {
case class C(i: Int)

type T = C//(*)
val T = C//(**)
}

but, get the following two errors at (*) and (**), respectively:

(*) overriding type T in trait t with bounds <: AnyRef{def isEmpty:
Boolean; def get: Unit}; type T has incompatible type

(**) overriding value T in trait t of type AnyRef{def unapply(i: Int):
o.T}; value T has incompatible type

Why is that?

TIA,
--Hossein

--------------------------------------------------------------------------------------------------------------

Seyed H. HAERI (Hossein), Dr.

Post-Doctoral Research Fellow
Department of Computing Science and Engineering
Catholic University of Louvain
Louvain-la-Neuve, Belgium

ACCU - Professionalism in programming - http://www.accu.org/
--------------------------------------------------------------------------------------------------------------

Naftoli Gugenheim

unread,
Apr 25, 2017, 8:54:46 PM4/25/17
to Seyed H. HAERI (Hossein), scala-user

First of all, a few of your types are wrong.

As you can see from the error message, def get with no type is assumed to return Unit.

type C does not have isEmpty and get methods
unapply (an extractor) goes from an existing value and returns its parameters in an Option (remember, we’re deconstructing), which is the opposite of you have it.

However, after fixing it to this:

trait t {
  type T
  val T: {def unapply(t: T): Option[Int]}

  
def test(a: Any): Unit = a match {case T(i) => println("")}
}

object o extends t {
  case class C(i: Int)

  type T = C

  val T = C//(**)
}

The compiler complains,

/tmp/test.scala:5: warning: abstract type pattern t.this.T is unchecked since it is eliminated by erasure
  def test(a: Any): Unit = a match {case T(i) => println("")}
                                          ^
/tmp/test.scala:3: error: Parameter type in structural refinement may not refer to an abstract type defined outside that refinement
  val T: {def unapply(t: T): Option[Int]}
                      ^
one warning found
one error found

(The first warning sounds like a compiler bug.)

However, similar to your other question, we can achieve the bigger-picture goal by passing functions around. This works:

trait t {
  type T
  def extractT: T => Option[Int]

  object T {
    def unapply(t: T) = extractT(t)
  }

  def test(a: Any): Unit = a match {case T(i) => println(i)}
}

object o extends t {
  case class C(i: Int)

  type T = C

  def extractT = C.unapply
}

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Seyed H. HAERI (Hossein)

unread,
Apr 26, 2017, 7:58:29 AM4/26/17
to Naftoli Gugenheim, scala-user
Thank you Naftoli. That was very helpful. :)
--

Seyed H. HAERI (Hossein)

unread,
Apr 26, 2017, 8:10:08 AM4/26/17
to scala-user
Hi Naftoli,

One more thing: When I try

object T {
def unapply = extractT(_)
}

I get

an unapply result must have a member `def isEmpty: Boolean

under "T(i)" in

def test(a: Any): Unit = a match {case T(i) => println(i)}

Is that a bug?

TIA,
--Hossein

P.S. How do you get to embed Scala so beautifully in your postings?


On 26 April 2017 at 13:58, Seyed H. HAERI (Hossein)

Naftoli Gugenheim

unread,
Apr 26, 2017, 1:12:52 PM4/26/17
to Seyed H. HAERI (Hossein), scala-user


On Wed, Apr 26, 2017, 8:09 AM Seyed H. HAERI (Hossein) <hossei...@gmail.com> wrote:
Hi Naftoli,

One more thing: When I try

object T {
  def unapply = extractT(_)
}

I get

an unapply result must have a member `def isEmpty: Boolean

under "T(i)" in

def test(a: Any): Unit = a match {case T(i) => println(i)}

Is that a bug?

Unapply usually returns Option. (As the message indicates it's more flexible, however unless you have a performance reason, Option is the easiest way.)


TIA,
--Hossein

P.S. How do you get to embed Scala so beautifully in your postings?

Markdown Here browser extension. I write the blocks like this:

```scala
// code
```

And press ctrl-alt-m before sending.

Seyed H. HAERI (Hossein)

unread,
Apr 26, 2017, 1:31:14 PM4/26/17
to scala-user
Hi Naftoli,

>> object T {
>> def unapply = extractT(_)
>> }
>>
>> I get
>>
>> an unapply result must have a member `def isEmpty: Boolean
>>
>> under "T(i)" in
>>
>> def test(a: Any): Unit = a match {case T(i) => println(i)}
>>
>> Is that a bug?
>
> Unapply usually returns Option.

But, so does also extractT. You wrote:

def extractT: T => Option[Int]

> (As the message indicates it's more flexible, however unless you have a performance reason, Option is the easiest way.)

Sorry, I'm not following. How is that related?

> Markdown Here browser extension.

Cool. It unfortunately is not available on the old version of my
browser which I insist on using. Thank you anyway. :)

Cheers,
--Hossein

Naftoli Gugenheim

unread,
Apr 26, 2017, 2:06:29 PM4/26/17
to Seyed H. HAERI (Hossein), scala-user


On Wed, Apr 26, 2017, 1:30 PM Seyed H. HAERI (Hossein) <hossei...@gmail.com> wrote:
Hi Naftoli,

>> object T {
>>   def unapply = extractT(_)
>> }
>>
>> I get
>>
>> an unapply result must have a member `def isEmpty: Boolean
>>
>> under "T(i)" in
>>
>> def test(a: Any): Unit = a match {case T(i) => println(i)}
>>
>> Is that a bug?
>
> Unapply usually returns Option.

But, so does also extractT. You wrote:

def extractT: T => Option[Int]

I guess it's because you're returning a function rather than defining it like a regular method the way I showed above


> (As the message indicates it's more flexible, however unless you have a performance reason, Option is the easiest way.)

Sorry, I'm not following. How is that related?

It doesn't say unapply result must be Option, because technically it could instead be anything that has isEmpty and get. But for normal usage, we can pretend it said result must be Option.


> Markdown Here browser extension.

Cool. It unfortunately is not available on the old version of my
browser which I insist on using. Thank you anyway. :)

Cheers,
--Hossein

Seyed H. HAERI (Hossein)

unread,
Apr 26, 2017, 3:54:12 PM4/26/17
to scala-user
Thank you Naftoli.

Seyed H. HAERI (Hossein)

unread,
May 16, 2017, 7:41:17 PM5/16/17
to scala-user
Hi Naftoli (and all others),

> The compiler complains,
>
> /tmp/test.scala:5: warning: abstract type pattern t.this.T is unchecked
> since it is eliminated by erasure
> def test(a: Any): Unit = a match {case T(i) => println("")}
<snip>
> (The first warning sounds like a compiler bug.)

It looks like you're right. I say so because for

trait t {
type B
type T1 <: B
type T2 <: B

def t1_extr: T1 => Option[Int]
def t2_extr: T2 => Option[(B, B)]

object T1 {def unapply(t1: T1) = t1_extr(t1)}
object T2 {def unapply(t2: T2) = t2_extr(t2)}

def fun_with_three: B => Int = {
case T1(i) => i * 3
case T2(b1, b2) => (fun_with_three(b1) - 3) * (fun_with_three(b2) + 3)
}
}

I get the same erasure warning message. But, then, given

object o extends t {
class Base
case class C1(i: Int) extends Base
case class C2(l: Base, r: Base) extends Base

type B = Base
type T1 = C1
type T2 = C2
def t1_extr = C1.unapply
def t2_extr = C2.unapply

val c2 = C2(C1(10), C1(2))
def run = fun_with_three(c2)
}

I get the following exception for "o.run":

java.lang.ClassCastException: test.o$C2 cannot be cast to test.o$C1
at test.o$$anonfun$t1_extr$1.apply(tester.scala:28)
at test.t$T1$.unapply(tester.scala:11)
at test.t$$anonfun$fun_with_three$1.apply(tester.scala:15)
at test.t$$anonfun$fun_with_three$1.apply(tester.scala:14)
at test.o$.run(tester.scala:32)
... 32 elided

It looks like the compiler (Scala 2.11.8 (Java HotSpot(TM) 64-Bit
Server VM, Java 1.8.0_121)) is mistakenly trying to shoehorn C2
instances into C1. And, the source of that seems to be the first match
statement of fun_with_three. Am I right in that that's a bug? How do I
overcome the problem anyway?

Cheers,
--Hossein
Reply all
Reply to author
Forward
0 new messages