2013-10-27 14 views
9

Co masz na myśli mnie było dlaczego następującyDlaczego dopasowanie w Scali zostało zaimplementowane jako słowo kluczowe, a nie jako metoda?

<something> match { ... } 

nie może być przepisany jako

<something>.match({ ... }) # error: identifier expected but 'match' found. 

Myślę, że to dlatego, że nie byłby możliwy do wdrożenia match jako zwykły sposób, ale nie jestem pewien. A może to ze względu na wydajność.

Co więcej, skoro są dostępne makra, czy możliwe byłoby zaimplementowanie match przy użyciu makra? (nie należy tego robić, ale tylko hipotetycznie)

EDYCJA: Wydaje się to związane np. z What is scala's experimental virtual pattern matcher?; dzięki @ om-nom-nom za wskazanie tego.

+0

Czy są jakieś korzyści z wprowadzenia dopasowania zaimplementowanego za pomocą makr? Zwłaszcza, że ​​istnieje * zwirtualizowany wzorzec dopasowania *? –

+0

@ om-nom-nom: Nie wiem - zadałem to pytanie do nauki/zrozumienia; ale kto wie - usuwanie specjalnych przypadków czasami ma początkowo nieoczywiste korzyści. Ponadto, nie wiedziałem nic o * zwirtualizowanym dopasowywaniu wzorców *, ale teraz sprawdzam to teraz. –

+0

@ om-nom-nom: na podstawie tego, czego właśnie dowiedziałem się z http://stackoverflow.com/questions/8533826/what-is-scalas-experimental-virtual-pattern-matcher, odpowiedziałbym "tak" na druga część twojego pytania. –

Odpowiedz

2

Dzięki temu, że jest słowem kluczowym, nie musi być skojarzone z typem Dowolny, więc kompilator może swobodnie wywnioskować typ wejściowy funkcji (częściowej). Gdyby była to metoda Any, musiałaby przyjąć funkcję [A, B] jako argument.

praktyczne implikacje to, że

3 match { case "foo" => "bar" } 

powoduje błądkompilacji 'niedopasowanie' typu

lecz (typ paramerterless) matchMethod;

3.matchMethod { case "foo" => "bar" } 

powoduje wykonania wyjątku „scala.MatchError”

wtedy nawet jeśli --long wyraźnie paramerterized typy nadal nie pojawia się błąd kompilacji dla następującej sytuacji:

"foo".matchMethod[Int, String] { case 3 => "bar" } 

raczej uzyskalibyśmy wyjątek runtime "java.lang.ClassCastException" jako pod maską, którą musielibyśmy użyć .asInstanceOf.

Innym bonusem jest podświetlanie składni, dopasowania wyskakują w kodzie więcej niż kolejna metoda, co moim zdaniem zasługuje na to, że dopasowywanie wzorców jest tak kluczową częścią Scali, że zasługuje na szczególną uwagę.

DODAJ: Z podobnych powodów należy chcieć, aby catch był konstrukcją słów kluczowych, a nie funkcją, która przyjmuje dwie funkcje jako parametry. Dopasowanie jest wtedy zgodne z haczykiem, co jest również zgodne z Javą.

Ta odpowiedź jest ekspansją na Martin Odersky, który został po raz pierwszy wskazał TravisBrown

+1

Nie zgadzam się z podanym limitem typu wnioskowania. Łatwo jest stworzyć niejawną klasę, która pozwala na bezpieczne porównywanie wzorców: 'niejawna klasa MatchOps [A] (prywatna wartość a: A) powiększa wartość AnyVal { def m1 [B] (pf: PartialFunction [A, B]): B = pf.applyOrElse (a, throw new MatchError (a)) def m2 [B] (pf: PartialFunction [A, B]): Opcja [B] = pf.lift.apply (a) } ' Tutaj m1 działa jako bieżące słowo kluczowe' match', podczas gdy m2 byłoby zbliżone do 'matchOption' – megri

1

odpowiedź samthebest jest prawdopodobnie prawdziwy powód (nie wiem, to nie jest coś, co znam) ale jest inny sposób patrzenia na to, który bardziej ogólnie odnosi się do programowania funkcjonalnego.

Powszechnie wiadomo, że mecze w programowania funkcyjnego można zrobić bez żadnych języków specjalnych funkcji (Church Encoding)

trait List[+T] { 
    def mmatch[R](nil: => R, cons: (T, List[T]) => R): R 
} 
object Nil extends List[Nothing] { 
    def mmatch[R](nil: => R, cons: (Nothing, List[Nothing]) => R) = nil 
} 
class Cons[+T](head: T, tail: List[T]) extends List[T] { 
    def mmatch[R](nil: => R, cons: (T, List[T]) => R) = cons(head, tail) 
} 

def sum(l: List[Int]): Int = l mmatch (
    nil = 0, 
    cons = (x, xs) => x + sum(xs) 
) 

val list = new Cons(1, new Cons(2, Nil)) 

println(sum(list)) 

W tej interpretacji, kiedy piszesz

sealed trait List[+T] 
case object Nil extends List[Nothing] 
case class Cons[+T](head: T, tail: List[T]) extends List[T] 

słowo sealed jest wartość/termin, który zapewnia funkcję match.

Jednym ze sposobów na przeczytanie twojego pytania jest: dlaczego tego nie zrobić? Dlaczego nie budować dopasowań z innych podstawowych funkcji językowych, zamiast dostarczać dopasowanie syntaktyczne?

Powodem jest to, że składniowym match zapewnia trochę cukru składniowej, że ludzie jak:

  • dublujących się struktur mecz:

    sealed trait A 
    sealed trait B 
    case object X extends A 
    case object Y extends A with B 
    case object Z extends B 
    
  • zagnieżdżonych funkcji mecz:

    (1 :: 2 :: Nil) match { 
        case x :: y :: Nil => ??? 
    } 
    

    To jest bardzo niezręcznie pisać bez cukru syntaktycznego. Możesz to zrobić; Próbowałem zbadać możliwość, gdy trying to implement monadic extractors; ale na pewno jest mniej ładnie.

  • Automatyczny wybór funkcji dopasowania otwartego do zamkniętego.

    Oznacza to, że ekstraktory w Scali są jak otwarte funkcje dopasowania, ponieważ każda z nich może zawieść, zwracając None; kompilator nie sprawdzi kompletnego match, ale możesz połączyć dowolną liczbę razem, a Scala wybierze pierwszą. Z drugiej strony, cechy sealed zapewniają zamknięte funkcje dopasowania z korzyścią sprawdzania kompletności. Musiałyby one być dostarczane przez oddzielne funkcje, ale Scala pozwala używać tej samej składni match dla obu.

Osobiście mam podejrzenie, że powyższe wymagania nie wymagają, w ostateczności, specjalnego wsparcia syntaktycznego na mecze. Podejrzewam, że inne, bardziej ogólne funkcje językowe mogą przynieść taką samą korzyść, szczególnie w zakresie zagnieżdżonych dopasowań. Jednak na razie bardziej sensowne jest bezpośrednie rozwiązanie problemu za pomocą specjalnej składni match.

Powiązane problemy