Domanda

Perché questo WTF di stampa? Ha pattern matching non funziona su tipologie strutturali?

  "hello" match {
    case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?")
    case _ => println("okie dokie")
  }
È stato utile?

Soluzione

L'esecuzione di questo esempio, l'interprete Scala con avvisi deselezionata (scala -unchecked) produce il seguente avviso: warning: refinement AnyRef{def doesNotExist(Int,List[_]): Double} in type pattern is unchecked since it is eliminated by erasure. Purtroppo, un tipo generico come questo non può essere controllato in fase di esecuzione, come la JVM non dispone reificata farmaci generici.

Tutto ciò che la JVM vede in questa partita modello è:

"hello" match {
  case s: Object => ... 
  case annon: Object => ...
}

Modifica In risposta ai vostri commenti, ho pensato a una soluzione, ma non ho avuto il tempo di postare ieri. Purtroppo, anche se dovrebbe di lavoro, il compilatore non riesce a iniettare il corretto Manifest.

Il problema si vuole risolvere è quello di confrontare se un oggetto è di un certo tipo strutturale. Ecco un po 'di codice che ho pensato di (Scala 2,8-r20019, come Scala 2.7.6.final si è schiantato su di me un paio di volte durante il gioco con idee simili)

type Foo = AnyRef { def doesNotExist(i: Int, x: List[_]): Double }

def getManifest[T](implicit m: Manifest[T]) = m

def isFoo[T](x: T)(implicit mt: Manifest[T]) = 
  mt == getManifest[Foo]

Metodo isFoo confronta essenzialmente i manifesti delle x classe di Foo. In un mondo ideale, il manifesto di tipo strutturale dovrebbe essere pari al manifestarsi di qualsiasi tipo contenente i metodi richiesti. Almeno questa è la mia linea di pensiero. Purtroppo questo non riesce a compilare, come il compilatore inietta un Manifest[AnyRef] invece di un Manifest[Foo] quando si chiama getManifest[Foo]. È interessante notare che, se non si utilizza un tipo strutturale (ad esempio, type Foo = String), questo codice si compila e funziona come previsto. Vi posto una domanda a un certo punto per vedere il motivo per cui questo non funziona con i tipi strutturali -. Si tratta di una decisione di progettazione, o è solo un problema delle API di riflessione sperimentale

In mancanza di questo, si può sempre usare Java riflessione per vedere se un oggetto contiene un metodo.

def containsMethod(x: AnyRef, name: String, params: java.lang.Class[_]*) = {
  try { 
    x.getClass.getMethod(name, params: _*)
    true
    }
  catch {
    case _ =>  false
  }
}

che funziona come previsto:

containsMethod("foo", "concat", classOf[String]) // true
containsMethod("foo", "bar", classOf[List[Int]]) // false

... ma non è molto bello.

Si noti inoltre che la struttura di tipo strutturale non è disponibile in fase di esecuzione. Se si dispone di un metodo def foo(x: {def foo: Int}) = x.foo, dopo la cancellazione si ottiene def foo(x: Object) = [some reflection invoking foo on x], le informazioni sul tipo perdersi. Ecco perché riflessione viene utilizzato in primo luogo, come si deve invocare un metodo su un Object e la JVM non sa se il Object ha quel metodo.

Altri suggerimenti

Se si sta andando ad avere per utilizzare la riflessione, si può almeno far sembrare più bello con un estrattore:

object WithFoo {
    def foo(){
        println("foo was called")
    }
}

object HasFoo {
    def containsMethod(x: AnyRef, name: String, params: Array[java.lang.Class[_]]) : Boolean = {
        try { 
            x.getClass.getMethod(name, params: _*)
            true
        } catch {
            case _ => false
        }
    }

    def unapply(foo:AnyRef):Option[{def foo():Unit}] = {
        if (containsMethod(foo, "foo", new Array[Class[_]](0))) {
            Some(foo.asInstanceOf[{def foo():Unit}])
        } else None
    }
}


WithFoo.asInstanceOf[AnyRef] match {
    case HasFoo(foo) => foo.foo()
    case _ => println("no foo")
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top