2015-09-04 1 views
1

Я пишу тип для преобразования между типами, и я замечаю что-то необычное с методом unapply на неявных объектах. Конкретноscala StackOverflow на неявном объекте unapply

object IntString extends PartialFunction[String, Int] { 
    def isDefinedAt(x: String) = Try(x.toInt).isSuccess 
    def apply(v1: String) = v1.toInt 
    def unapply(a:String):Option[Int] = if(this.isDefinedAt(a)) Some(this.apply(a)) else None 
} 

val s = "1000" 
val IntString(i) = s 

работает отлично, но

implicit object IntString extends PartialFunction[String, Int] { 
    def isDefinedAt(x: String) = Try(x.toInt).isSuccess 
    def apply(v1: String) = v1.toInt 

    def unapply(a:String):Option[Int] = if(this.isDefinedAt(a)) Some(this.apply(a)) else None 
} 

val s = "1000" 
val IntString(i) = s 

дает StackOverflow по методу apply. Я хотел бы быть в состоянии иметь объект быть неявным, так что я могу сделать что-то вроде

def parse[A,B](a:A)(implicit ev:PartialFunction[A,B]) = ev(a) 

в дополнение к явно применяются/исключить его.

ответ

3

Все пошло не так в apply, в частности с v1.toInt. java.lang.String не имеет метода toInt. Он предоставляется неявно StringLike. Но Intтакже имеет метод toInt, и вы предоставляете неявное преобразование от String => Int.

Компилятор видит, что вы хотите позвонить toInt по телефону String. Вместо того, чтобы выбирать обогащенный метод из StringLike, компилятор знает, что Int имеет метод toInt, и вы предоставляете неявный String => Int в области видимости, поэтому он использует это. Но с помощью ваших конверсионных звонков apply снова, и он бесконечно повторяет процесс.

простое решение было бы использовать фактический класс типов, а не PartialFunction:

trait Conv[A, B] { 
    def isDefinedAt(x: A): Boolean 
    def apply(v1: A): B 
    def unapply(a: A): Option[B] 
} 

implicit object IntString extends Conv[String, Int] { 

    def isDefinedAt(x: String) = Try(x.toInt).isSuccess 

    def apply(v1: String): Int = v1.toInt 

    def unapply(a: String): Option[Int] = 
    if(this.isDefinedAt(a)) Some(this.apply(a)) else None 
} 

scala> val IntString(i) = s 
i: Int = 1000 

И вы бы изменить parse к:

def parse[A, B](a: A)(implicit ev: Conv[A,B]) = ev(a) 
+0

Спасибо! Это очень четкое объяснение происходящего. Я думал, что произошла какая-то неожиданная неявная хитрость, но я не был уверен, что. Я действительно хочу продолжать расширять 'PartialFunction' (у моей фактической реализации есть класс, который расширяет ее), чтобы я мог делать такие вещи, как' orElse' и 'andThen', но на основании вашего ответа я изменил' v1.toInt' на 'Integer.parseInt (v1)' и он сработал. –