21 kwietnia 2012

scala> (-48 to Euro2012)


Lektura Functional Scala: Introduction pozwoliła mi ułożyć sobie w głowie koncepcję programowania funkcyjnego. Mimo ukierunkowania autora na Scalę, prawie żadna z części nie wskazywała na jakikolwiek związek z tym językiem. Wystarczy 30 minut skupienia i zrozumienie cech paradygmatu funkcyjnego powinno być jaśniejsze. Mi pomogło znacznie.

Najbardziej płodną sekcją artykułu była końcówka Stateless vs. Stateful, w której dotarło do mnie, że:

Skutek uboczny jest sposobem na przechowanie tymczasowego wyniku obliczeń na potrzeby innych części aplikacji. W programowaniu funkcyjnym zmienne nie istnieją, więc jedynym sposobem na przekazanie wyniku obliczeń jest użycie rekurencji (przekazywanie wyniku funkcji samej sobie) lub zwrócenie go (przekazanie wyniku innym funkcjom).

Podczas lektury miałem uruchomioną konsolę scala i bawiłem się poznanymi konstrukcjami. Nie zagłębiam się w lekturę składni Scali, więc idzie mi jak po grudzie i próbuję odgadywać, jak mogłoby dane wywołanie wyglądać. Najbardziej zaskakujące jest, że bezwiednie wpisuję wywołania w składni Clojure - zaczynam nawiasem! Stało się to dla mnie naturalne! Całkowicie przestawiłem swoje myślenie na "wszechobecne funkcje" w Clojure na "metody są przypisane do obiektów" w Scali. Scala wymusza u mnie przestawienie się na myślenie obiektowe z dodatkiem funkcyjnym.

Poniżej wycinek moich wyczynów scalowych. Samo środowisko często samo z siebie podpowiada co mogłoby być właściwe w danym kontekście. Bajka!
$ scala
Welcome to Scala version 2.10.0-M2 (OpenJDK 64-Bit Server VM, Java 1.8.0-jdk8-b29).
Type in expressions to have them evaluated.
Type :help for more information.

scala> (1 to 9)
res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> Range(1, 4)
res1: scala.collection.immutable.Range = Range(1, 2, 3)
Zwróć uwagę na sufix Inclusive przy nazwie klasy, która obsługuje pierwszy przykład vs drugi.
scala> (0 to 10).foldLeft(0)
<console>:8: error: missing arguments for method foldLeft in trait TraversableOnce;
follow this method with `_' if you want to treat it as a partially applied function
              (0 to 10).foldLeft(0)
                                ^

scala> (0 to 10).foldLeft(0)_
res3: ((Int, Int) => Int) => Int = <function1>

scala> val fl = (0 to 10).foldLeft(0)_
fl: ((Int, Int) => Int) => Int = <function1>

scala> fl(_+_)
res4: Int = 55

scala> val fl = (0 to 10).reduceLeft_
<console>:7: error: value reduceLeft_ is not a member of scala.collection.immutable.Range.Inclusive
       val fl = (0 to 10).reduceLeft_
                          ^

scala> val fl = (0 to 10).reduceLeft _
fl: ((Int, Int) => Int) => Int = <function1>

scala> fl(_+_)
res5: Int = 55

8 komentarzy:

  1. Hej Jacku,
    skoro wpadłeś na "to" to jeszcze warto wspomnieć obok niego o "until", który właśnie produkuje (exclusive) Range. Czyli: val range = 1 until 4

    Cheers i powodzenia w nauce - uważam, że warto ;-)

    OdpowiedzUsuń
    Odpowiedzi
    1. Dzięki za to "until" :-)

      Możesz jeszcze napisać, dlaczego warto nauczyć się Scali? Mnie jedynie przyciągnęła jej strona funkcyjna, na której zamierzam pogłębiać znajomość tego paradygmatu i samego Clojure (!)

      Usuń
  2. Poza wymienianymi ostatnio na WJUGu (Akka rocks) to killerem tutaj jest myślę type system który w otóle pozwala tym wszystkim świetnym bibliotekom zaistnieć.
    Jest to co prawda zawiła bestia chwilami, ale pozwala na przepiękne i typesafe biblioteki - z prostych a bardzo ładnych to ScalaTest jest bardzo przyjemny dla oka. Druga strona medalu to potwory jak scala dispatch który ciperpi na metody "krzaczkowe" (których ma trochę za dużo...). No i dochodzą makra... crazy times ;-)

    OdpowiedzUsuń
    Odpowiedzi
    1. To właśnie do mnie nie dociera i martwię się tym, że wszystkich porywa "Akka rocks!" i "type system". Co się kryje pod tymi terminami? Co jest w nich takiego szczególnego? Gdzie ich można użyć? Gdzie powinno mi brakować tego w Javie i/lub Clojure? I w końcu, gdzie Ty używasz ich - Akka + system typów?

      Gdybyś zechciał zaprezentować mi ich zalety, na pewno zdecydowanie wpłynęłoby to na moje zrozumienie sprawy. Zechciałbyś? Proszę.

      Usuń
    2. Tak krótko i ogólnie, to język z dobrym system typów, daje Ci pewne rzeczy "za darmo" w dziedzinie testów i dokumentacji. Kompilator sam sprawdza pewne własności programu (i to przed jego uruchomieniem) i gwarantuje, że są spełnione, więc nie trzeba ich testować dodatkowo i nie trzeba ich dodatkowo opisywać słownie. W językach dynamicznych, jak Clojure, nie dostajesz tych gwarancji. Czyli na przykład możesz skompilować program:
      (ns ex1 (:gen-class)) (defn -main [] (println (- "x" 1)))
      Ale przy próbie wykonania dostajesz wyjątek. W Scali podobnego programu nie uda się skompilować:
      object ex1 extends App { println("x" - 1) }

      Usuń
  3. (0 to 10).foldLeft(0)_ - Co robi ten podkreślnik na końcu? Po co on? Jakie jeste jego znaczenie? Zawsze jak widzę ten podkreślnik w Scali to się dłuuuugo zastanawiam o co chodzi...

    OdpowiedzUsuń
  4. Podkreślnik ma różne funkcje w Scali. A jeśli chodzi o jego znaczenie konkretnie w tym przypadku, to tworzona jest w tym wyrażeniu funkcja. Bez niego funkcja by nie została utworzona - co zresztą zostało pokazane. Grzegorz Balcerek

    OdpowiedzUsuń
  5. Czyli jest to przekształcenie metody na funkcję? To co jest tutaj: metody-i-funkcje opisane?

    OdpowiedzUsuń