Język Scala dla programistów PHP – cz.4 case classes i pattern matching

Witajcie ! Jakiś czas minął już od ostatniego wpisu o Scali (3 miesiące), czas więc odświeżyć swoje zainteresowanie i wiedzę na temat tego języka. Dzisiejszym daniem głównym są tzw. case classes. Jest to pewnego rodzaju nowość dla osób przyzwyczajonych do Javopodobnych obiektowych systemów typów.

Czym jest więc case class ? Poprzez analogie do innych języków, możemy ją traktować jako typowany kontener na dane – czyli znaną z C/C++ (oraz w troche innym wydaniu w C#) strukturę (struct). Jest to specjalny typ klasy, w przypadku której w konstruktorze deklarujemy listę pól, natomiast kompilator automatycznie generuje nam gettery i settery. Jest to oczywiście wymarzona sytuacja dla wszystkich fanów tego typu „enkapsulacji” (pozdrowienia dla anonimowego „P.” ;P).

Deklaracja:

case class Foo(baz:String, bar:Number)
val myFoo = Foo("test",5)

println(myFoo.baz, myFoo.bar)

Jak można zauważyć w przykładzie nie jest wymagany operator „new” przy tworzeniu nowych instancji case class. Przy okazji pragnę zwrócić uwagę na jeden istotny szczegół – case classa zadeklarowana w sposób pokazany powyżej będzie miała tylko automatycznie wygenerowane gettery. By również settery zostały wygenerowane, należy uzupełnić deklaracje o słowa kluczowe var dla zmiennych, dla których ma być to uczynione.

Przykład:

case class Foo(var baz:String, var baz:Number)
val myFoo = Foo("test",5)

myFoo.baz = "World"

Skoro wiemy już co nieco o case classes, pora powiedzieć kilka słów o pattern matching, by na koniec zgrabnie połączyć oba koncepty. Czym jest więc pattern maching ?

Pattern matching jest to rodzaj uniwersalnego switcha dla języka statycznie typowanego. W Scali używany jest również między innymi w obsłudze wyjątków.

Przykład:

val baz = 10

baz match {
    case 1 => println("raz")
    case 2 => println("dwa")
    case _ => println("default")
}

Jeżeli zadeklarujemy baz jako dowolny typ (Any), mamy możliwość elastycznego matchowania, behold:

val baz:Any = 10

baz match {
    case 1 => println("raz")
    case "2" => println("dwa")
    case _ => println("default")
}

Wyjątki:

def testWyjątków():Unit = {     
      throw new Exception("houston mamy problem")
}

try{
    testWyjątków()
} catch {
      case e:Exception => println(e.getMessage)
}

Sytuacja z matchowaniem wyjątków jest podobna jak dla poprzednich przykładów. Smaczkiem jest to, że w nazwach metod (i zapewne klas) możemy używać znaków narodowych – świetna sprawa, przy tworzeniu różnego rodzaju DSL-i.

Połączmy na koniec wiedzę dotyczącą case classes oraz pattern matching. Jeżeli śledzicie mojego bloga, pewnie zauważyliście ostatnio wpis tyczących się dekoratorów w Zend_Form, był tam fragment kodu podobny do:

foreach($this->_elements as $element){
    if($element instanceOf Zend_Form_Element_Text){
        $element->setAttrib('class', 'iText required');
    }
    if($element instanceOf Zend_Form_Element_Select){
        //inny kod
    }
}

Poniżej równoważny kod w Scali(bez pętli) pokazuje połączenie obu koncepcji (case class i pattern matching):

abstract class Zend_Form_Element;
    
case class Zend_Form_Element_Text(name:String,var classes:String = "") extends Zend_Form_Element
case class Zend_Form_Element_Select(name:String, var classes: String ="") extends Zend_Form_Element
    
val myInput:Zend_Form_Element = Zend_Form_Element_Select("role")
    
myInput match {
  case myInput:Zend_Form_Element_Text => {
    myInput.classes = "iText required"
  }
  case myInput:Zend_Form_Element_Select => {
    myInput.classes = "iSelect required"
  }
}
    
println(myInput)

Mam nadzieje, że podobał Wam się ten odcinek cyklu. Ciekaw jestem, co sądzicie o obu koncepcjach i czy w jakiejś formie pasowały by do języka PHP ?

  1. wookieb pisze:

    Zamierzasz wdrażać produkcyjnie jakieś narzędzie na Scali?

  2. @wookieb: jak na razie niestety nie, w tej chwili uczę się tego języka i docelowo chciałbym znaleźć jakąś pracę gdzie mógłbym w nim kodować full time, ale jeszcze do tego długa droga…

  3. wookieb pisze:

    Ja niestety mam tak samo. Obawiam się, że w Polsce to ze Scalą ciężko (jedynie wawa).
    Chyba, że się mylę?

  4. Z tego co obserwuje, to w tej chwili na rynek wchodzi trochę zagranicznych firm, które tworzą oprogramowanie w Scali. Nie wymagają koniecznie jej znajomości, natomiast chcą ludzi zaprawionych w Javie i/lub .net(C#). Wiele Javowych firm polskich również zaczyna wdrażać Scalę (np. Comarch). Dlatego też w sumie mój kierunek docelowy to Scala, natomiast obawiam się, że będzie trzeba dobrze poznać Javowe frameworki nim się coś konkretnego złapie…

  5. wookieb pisze:

    „natomiast obawiam się, że będzie trzeba dobrze poznać Javowe frameworki nim się coś konkretnego złapie…” Niestety masz rację …

  6. Takie życie, cóż poradzić. Jesteś może z Krakowa lub okolic ? Jeżeli tak to można by założyć jakieś koło wielbicieli Scali, żeby wzbudzić zainteresowanie pracodawców lokalnych tym językiem. Może to się przełoży potem na jakieś oferty zatrudnienia, lub przynajmniej zrobi się jakiś ciekawy projekt btw.

  7. wookieb pisze:

    Jestem z Trójmiasta (stety).
    Osobiście raczej nastawiam się na realizację ciekawego projektu w Scali. Jeżeli w przyszłości będzie okazja pracowania jako Programista Scali nie będę zastanawiał się ani chwili. Aczkolwiek jeżeli poznam język na tyle dobrze, że będę mógł o nim ewangelizować to na pewno coś zorganizujemy (już nawet wiem jak).

  8. Chętnie się dowiem o twoim pomyśle, napisz może na mojego maila, żeby nie robić tutaj masakry komentarzowej :)

  9. Theq pisze:

    Zawsze można się zatrudnić jako programista Java/PHP i silną ręką wprowadzać Scalę gdzie się da 😉

  10. No w teorii to łatwe, ale w praktyce już gorzej 😉 Btw. czemu nikt nie komentuje treści ;P ?

  11. Theq pisze:

    Bo czytają to tylko osoby, które znają Scale?

  12. Hm to nie dobrze, bo jest raczej skierowana ta seria do osób, które nie znają 😉

  13. Theq pisze:

    Ok, przejrzałem wpis więc mogę się do czegoś przyczepić. Z twojego opisu ciężko wywnioskować różnicę między case class, a zwykła klasą. Jak dodasz var/val do konstruktora zwykłej klasy to też ci wygeneruje settery i gettery. Czyli pomijając to, że można tworzyć instancję bez new, wynika, że najważniejszą cechą case class jest to, że można ją używać w pattern matchingu.

    Po drugie, przydałyby się jakieś lepsze przykłady wykorzystania pattern matchingu, żeby pokazać w czym lepsze to jest od zwykłego switcha. Na przykład jakiś przykład z ekstraktorami. Nie musi to być nie wiadomo co z guardami, ale chociażby skorzystanie z Option. Można też wspomnieć, że tak jak większość rzeczy w Scali, wyrażenie pattern matchingu zwraca wartość. No chyba, że zostawiłeś to na następny wpis :)

  14. Słuszne uwagi Theq, dopisze jeszcze kilka ciekawszych przykładów :)

  1. There are no trackbacks for this post yet.

Leave a Reply

Informuj mnie o odpowiedziach poprzez e-mail. Możesz również subskrybować wpis bez zostawiania komentarza.