13 października 2010

O warsjawie i monadach w Clojure - nauka wspólnie jako sposób własnego rozwoju

Warszawska Grupa Użytkowników Technologii Java (Warszawa JUG)

Warsjawa 2010


Niedługo powinna wystartować rejestracja na bezpłatną konferencję grupy Warszawa JUG - warsjawa 2010, na której będzie możliwość wysłuchać ciekawych tematów o Javie, językach alternatywnych na JVM oraz o sztuce programowania (aka software craftsmanship). To już za niecałe 2 tygodnie, w sobotę, 23.10.2010 w Warszawie (potwierdzamy dostępność sali na MIMUW).

Odzew zainteresowanych udziałem jest, ale przede wszystkim w roli słuchacza, a już samych prezenterów jak na lekarstwo. Zachodzę w głowę skąd taki obrót spraw? Gdyby porównać naszą aktywność rok-do-roku (korzystając z podobnych zestawień wyników finansowych firm), to powiedziałbym, że spadek jest ponad 50%! Bardzo mnie martwi wyraźny trend spadkowy zainteresowania możliwością wystąpienia na konferencji, która jest wspaniałą wizytówką naszej wiedzy (nawet w fazie aktywnego rozwoju), czyli własnej reklamy, czy (odrzucając sprawy przyziemne i szukając czegoś bardziej wzniosłego) po prostu dzielenia się wiedzą ze społecznością javową. Czyżby to wakacje, a może pogoda?! A może już pozakładaliście własne restauracje, czy inne biznesy, aby czym prędzej zapomnieć o tej ciągłej informatycznej gonitwie?! Nawet na naszych blogach jakoś ciszej, a nie zauważyłem, aby rozwój narzędzi, które wykorzystujemy na codzień osłabł, a ich mnogość gwarantuje możliwość rozwoju własnego warsztatu programistycznego na wielu płaszczyznach.

Mimo swojej nazwy - warsjawa - konferencja jest dla wszystkich zainteresowanych programowaniem w Javie i w ogólności bez względu na miejsce stacjonowania. Nie jest to w żaden sposób zawężone do aktywistów warszawskich! To mówię ja, (współ)organizator :) Zainteresowani wystąpieniem na warsjawie proszeni są o kontakt ze mną na priv, albo zgłoszeniu tematu na forum Warszawa JUG w wątku - Zbieranie tematów wystąpień.

Nie przewidujemy wystawnych bankietów, jedzenia, czy innych, mniej informatycznych dodatków podczas warsjawy, ale istnieje możliwość zmiany tego stanu rzeczy - wszystko zależy od hojności naszych patronów (zwanych również sponsorami). Firmy, które są zainteresowane udziałem w roli patrona konferencji warsjawa 2010, proszone są o kontakt ze mną. Na chwilę obecną rozmawiamy z jednym, ale wierzymy, że to się niebawem zmieni.

Tyle jeśli chodzi o warsjawę 2010, na której będzie i Clojure w wykonaniu Marcina Rzewuckiego, który naukę tego języka postanowił wdrożyć w życie prowadząc własnego bloga. Warto zaglądać.

Słówko o Clojure


Jak wieść niesie, "umoczyłem" na dobre w programowaniu funkcyjnym z Clojure i moją uwagę przykuły monady. Wzięło mnie nawet na uczestniczenie w wykładach z teorii kategorii i póki co, mam wiele z tym frajdy (więcej w Monady w Clojure - wstęp do maybe-m - kontynuacja i Teoria kategorii w podstawach informatyki na MIMUWie). Pewnie monad nie będzie na tych wykładach, ale chociaż przypomnę sobie ich podwaliny - na razie wałkowana jest algebra uniwersalna (z której nota bene miałem pisać swoją pierwszą pracę magisterską o minimalnych, lokalnie skończonych rozmaitościach - się jakoś tak nie udało). Czy mi ta wiedza do czegokolwiek potrzebna, jeszcze nie wiem, ale skoro i tak wykorzystujemy jedynie 5-10% całej "mocy obliczeniowej" i pamięci naszego mózgu, więc nie obawiam się, że nie będzie miejsca na inne rzeczy. Ach, "A co z czasem?", ktoś zapyta. Cóż, mimo nowego wydania Civilization V i oczekiwania na nie przez dobre kilka miesięcy, wybrałem...programowanie funkcyjne i monady. Jak wyjdzie na MacOS, może zmienię zdanie.

Słówko o monadach


Dzisiaj udało mi się przebrnąć przez kilka artykułów dot. monad, tyle, że w wykonaniu C#, F# i Haskella (można śledzić moją aktywność na twitterze, albo delicious). Niewiele o samym Clojure - ot, pojedynczy artykuł, który już czytałem i który zamiast wyjaśnić "gdzie", wyjaśnia jedynie "jak". Podobno zrozumienie "jak" pomaga w zrozumieniu "gdzie", ale biorąc pod uwagę niewielką znajomość samego Clojure, trudno mi dostrzec ich zastosowanie. Widzę jednak światełko w tunelu. Postanowiłem rozdystrybuować poznawanie monad na większą populację, aby bardziej zaktywizować społeczność javową i wpadłem na taki pomysł.

Pomysł wspólnej nauki z monadami w tle


Pomysł opiera się na współpracy dwóch stron - strony imperatywno-obiektowo-javowej (to Ty, On i Ona, i Wasi znajomi - programiści Java, ale nie tylko, bo każdy język mile widziany) i strony monadycznej (w tej roli na razie sam ja z Clojure, ale grupa wciąż otwarta dla innych).

Zabawa polega na tworzeniu kodu javowego, który rozwiązuje zadane przeze mnie wcześniej zagadnienie programistyczne o niewielkim skomplikowaniu, aby każdy programista javowy mógł je rozwiązać, a moim zadaniem będzie przedstawienie rozwiązania monadycznego w Clojure. W ten sposób upiekę dwie pieczenie na jednym ogniu - na podstawie przykładów zaczerpniętych z naszego światka javowego poznamy monady, a ja nie będę musiał wymyślać przykładów w Javie, które ostatecznie mogłyby zostać okrzyknięte trywialnymi i stronniczymi na rzecz monad. I nie chodzi o tricki programistyczne, ale rzeczywiste rozwiązania, które używasz, albo byłyby w użyciu podczas Twoich projektów. Ja nie będę choćby sugerował rozwiązania. Wezmę takie, które zostanie okrzyknięte najbardziej racjonalnym.

Do formatowania i kolorowania kawałków kodu można korzystać m.in. z serwisu gist.github.com, ale i pastebin.com czy www.copypastecode.com. Można również zamieszczać je w komentarzach do tego wpisu.

Problem 1: Napisać metodę, która zwraca walutę, dla pracownika z danego departamentu międzynarodowej korporacji.
Pracownik jest przypisany do departamentu (np. poprzez mapę - pracownik-departament), departament do kraju, a kraj do waluty. Funkcja na wejściu dostaje nazwę, identyfikator, lub cokolwiek jednoznacznie reprezentującego pracownika, a na wyjściu symbol waluty, np. dla "Jacek" powinno być "PLN", a dla "John" "USD", a "Tomek" i "Mateusz" dawaliby "CHF".

Problem 2: Znaleźć wszystkie kombinacje liczb, które pomnożone dadzą w wyniku zadaną liczbę.
Na wejściu metoda otrzymuje liczbę, która jest iloczynem poszukiwanych par. Pary (a,b) i (b,a) są sobie równe, ale tak na prawdę nie ma to znaczenia. Dla wejścia 5 byłoby to {(1,5)}, ale dla 10 dostaniemy {(1,10), (2,5)}.

Chciałbym móc skorzystać z tej formy współpracy już podczas mojego, najbliższego wystąpienia o Clojure podczas inauguracyjnego spotkania Warszawa JUG w najbliższy wtorek, 19.10. Więcej w Otwarcie nowego sezonu - 19.10 z "Wstęp do programowania funkcyjnego z Clojure".

Może wcześniej udałoby mi się jeszcze przedstawić kilka wpisów o monadach z Twoim rozwiązaniem na blogu? Pomożecie? Na pewno!

7 komentarzy:

  1. fajny pomysł z problemami do rozwiązania ;) mnie co prawda bardziej ciągnie w stronę Scali, więc pewnie spróbuje rozwiązać w nim te problemy :)

    OdpowiedzUsuń
  2. Monady są konstrukcją funkcyjną i jeśli Clojure potrafi, to pewnie Scala również. Nie zależy mi na prześciganiu się w liczbie linii w jednym czy drugim języku, a raczej o siłę wyrazu. Gdybyś publikował gdzieś rozwiązanie w Scali, pisz do mnie. Chętnie poznam Scalę przez pryzmat monad (co robię w kontekście Haskella, F# i C# ostatnio) :)

    OdpowiedzUsuń
  3. Problem 1:

    ;; mapy/funkcje wejściowe
    (def pracownik->departament {"Jacek" "Clojurowcy", "John" "Javowcy"})
    (def departament->kraj {"Clojurowcy" "Polska", "Javowcy" "USA"})
    (def kraj->waluta {"USA" "USD", "Polska" "PLN"})

    ;; właściwa funkcja
    (defn pracownik->waluta [p] (-> p pracownik->departament departament->kraj kraj->waluta))

    ;; testujemy...
    user> (pracownik->waluta "Jacek")
    "PLN"
    user> (pracownik->waluta "John")
    "USD"
    user> (pracownik->waluta "Daniel")
    nil

    (można by też zapisać to za pomocą comp, ale wtedy trzeba by podać funkcje w odwrotnej kolejności, co chyba jest mniej czytelne)

    Problem 2 (rozwiązanie głupie, ale krótkie):

    (use 'clojure.contrib.math)

    (defn decompositions [x]
    (for [p (range 1 (inc (ceil (sqrt x)))) :let [q (/ x p)] :when (and (zero? (mod x p)) (<= p q))] [p q]))

    Problem 1:

    ;; mapy/funkcje wejściowe
    (def pracownik->departament {"Jacek" "Clojurowcy", "John" "Javowcy"})
    (def departament->kraj {"Clojurowcy" "Polska", "Javowcy" "USA"})
    (def kraj->waluta {"USA" "USD", "Polska" "PLN"})

    ;; właściwa funkcja
    (defn pracownik->waluta [p] (-> p pracownik->departament departament->kraj kraj->waluta))

    ;; testujemy...
    user> (pracownik->waluta "Jacek")
    "PLN"
    user> (pracownik->waluta "John")
    "USD"
    user> (pracownik->waluta "Daniel")
    nil

    (można by też zapisać to za pomocą comp, ale wtedy trzeba by podać funkcje w odwrotnej kolejności, co chyba jest mniej czytelne)

    Problem 2 (rozwiązanie głupie, ale krótkie):

    (use 'clojure.contrib.math)

    (defn decompositions [x]
    (for [p (range 1 (inc (ceil (sqrt x)))) :let [q (/ x p)] :when (and (zero? (mod x p)) (<= p q))] [p q]))

    OdpowiedzUsuń
  4. Czesc Jacek,

    Nie mam pojecia jaki zwiazek maja zaproponowane przez Ciebie problemy z monadami, ale tutaj jest moje rozwiazanie:

    scala> val pracownicy = Map("P1" -> "D1", "P2" -> "D2")
    pracownicy: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map((P1,D1), (P2,D2))

    scala> val departamenty = Map("D1" -> "PL", "D2" -> "USA")
    departamenty: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map((D1,PL), (D2,USA))

    scala> val waluty = Map("PL" -> "PLN", "USA" -> "USD")
    waluty: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map((PL,PLN), (USA,USD))

    scala> def f(p: String) = (pracownicy andThen departamenty andThen waluty)(p)
    f: (p: String)java.lang.String

    scala> f("P1")
    res1: java.lang.String = PLN

    scala> f("P2")
    res2: java.lang.String = USD

    W tym rozwiazaniu korzystam z faktu, ze w Scali Map[A,B] to rowniez PartialFunction[A,B].

    OdpowiedzUsuń
  5. Jesli chodzi o drugie zadanie to nie bylem pewien czy dobrze rozumiem problem wiec przedstawiam oba rozwiazania. Rozwiazania nie sa optymalne, ale to nie byl wymog...

    scala> def pairs(x: Int) = for (i <- 1 to x if x % i == 0) yield (i, x/i)
    pairs: (x: Int)scala.collection.immutable.IndexedSeq[(Int, Int)]

    scala> pairs(5)
    res0: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,5), (5,1))

    scala> pairs(10)
    res1: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,10), (2,5), (5,2), (10,1))

    scala> def pairsUniq(x: Int) = pairs(x) filter { case (x1, x2) => x1 <= x2 }
    pairsUniq: (x: Int)scala.collection.immutable.IndexedSeq[(Int, Int)]

    scala> pairsUniq(10)
    res3: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,10), (2,5))

    OdpowiedzUsuń
  6. Wczoraj zainstalowałem. I chociaż grałem namiętnie we wszystkie wcześniejsze wersje, to piątka mnie nie porwała. Jeszcze w zeszłym tygodniu skończyłem rozgrywkę CivIV o 5tej rano (udało się na przedostatnim poziomie trudności) ale Civ5 odstawiłem po godzinie.
    Może się starzeję? Może sprzęt mam słaby i efekty graficzne nie powalają?

    OdpowiedzUsuń