28 września 2010

Zapytany o Clojure na gtalku - Jaki on jest? Warty naszego czasu?

1 komentarzy
Coraz częściej wokół mnie pojawia się pytanie o sens nauki Clojure. Tym razem padło na gtalku, więc po odpowiedzi, postanowiłem ujawnić ją (niedługą!) na blogu pro publico bono. Komentarze mile widziane.

ON: Hej Jacku. Pozwolę sobie tak zadać jedno konkretne pytanie. Bazując na Twojej wiedzy i doświadczeniu. Clojure jest: fajne, ciężkie, przereklamowane, rewolucyjne, tego mi było trzeba, daje duże możliwości (nie potrzebne skreślić) :-)

JA: Gdybym miał wybierać z podanego zbioru, wybrałbym na pewno: fajne i rewolucyjne. Do dwóch ostatnich muszę się przekonać, ale tak jest reklamowane. Ktoś powiedział, że "jeśli język programowania nie wymaga zrewolucjonizowania myślenia, to nie warto się go uczyć". Można to przypisać do każdej z naszych działalności, nie tylko nauki nowego języka programowania, czy technologii. Dla mnie Clojure jest po prostu boski (dla wielu takim jest Ruby czy Scala, ale są one z mojego punktu widzenia jedynie ewolucyjne - Clojure jest Rewolucyjne).

ON: No może jeszcze jedno. Czy obecnie Clojure i biblioteki może być stosowane do "prawdziwych' web aplikacji? Bo mi się kojarzy to tylko i wyłącznie do bibliotek server side

JA: Zacznij od compojure, któremu się przyglądam. Możesz również skorzystać z sandbar. To są dedykowane biblioteki/rozwiązania na potrzeby aplikacji webowych. Jeśli do tego dodać, że Clojure to Java, a raczej swego rodzaju DSL do Javy (bo ostatecznie i tak mamy bajtkod i jesteśmy nim ograniczeni), to czy Clojure czy Java nie ma wielkiego znaczenia w kontekście gotowości szkieletów webowych. Każdy się nadaje. Warto jednak skorzystać z możliwości Clojure i nie łatać, a łączyć, czyli...compojure albo sandbar.

ON: Hmm, w zasadzie skoro Clojure operuje na JVM to po co dedykowany web framework do niego? Czy nie można po prostu skorzystać z HTML, JS i w jakiś sposób (mam nadzieję, ze jakoś się da) wystawić REST serwisy? W zasadzie powinno dać się mieszać Clojure ze Scalą i Groovy :-) 

JA: Nie czuję się w pełni przygotowany do odpowiedzi na to pytanie, bo pewnie, gdybym znał odpowiedź, stworzyłbym jej realizację - nowy szkielet webowy bazując na dotychczasowych doświadczeniach w JEE. Z mojej perspektywy funkcyjnej wygląda jednak, że celem dowolnego "web framework" jest uproszczenie tworzenia aplikacji i konstrukcje języka mogą w tym bardzo pomóc, np. w Clojure mamy makra, które operują na danych podczas kompilacji, zamiast funkcjach, które operują podczas uruchomienia. Łącząc obie konstrukcje języka możemy stworzyć rozwiązanie - szkielet webowy, w którym zadeklarowanie obsługi żądania sprowadzi się do 2-3 zrozumiałych linii zamiast 20-30 niekoniecznie zrozumiałych (chociażby przez swoją objętość). W końcu, w programowaniu funkcyjnym nie chodzi o to jak, ale co.

Problem z dotychczasowymi szkieletami webowymi to, że bierzesz wszystko, albo masz kłopoty. Trudno skorzystać z części i wyskoczenie poza ramy może być bolesne, a już połączenie z innymi rozwiązaniami może przyprawić o ból głowy. Stąd też różnica między rozwiązaniami typu szkielet (framework) a rusztowanie (scaffolding). Pierwsze zostanie na wieki, drugie wyrzucasz, kiedy nabierzesz wprawy i doświadczenia, albo kiedy zorientujesz się, że są prostsze rozwiązania.

W Clojure forowane podejście polega na łączeniu mniejszych cegiełek do zrealizowania tworzenia aplikacji webowych. Tu nie ma pytania: Seam vs Struts2 vs Wicket vs JSF, ale składa się rozwiązania, aby obsłużyć bardziej skomplikowany problem - w końcu wszystko w Clojure jest funkcją, albo idąc dalej, strukturą danych, np. listą. Jeśli składanie funkcji matematycznych jest powszechnie stosowane i zrozumiane, to dlaczego tworzenie oprogramowania takim nie może być?!

Odnośnie REST Web Services i łączenia ze Scalą i Groovy, uchylam się od odpowiedzi. Za mało mam jeszcze zrozumienia, gdzie i jak mógłbym zastosować Clojure, aby łączyć go z Javą i wykorzystania z JEE, a gdzie tu jeszcze dokładać do tego Scalę i Groovy.

ON:...dopiero do niego wysłałem odpowiedź, więc ciąg dalszy nastąpi...kiedyś.

24 września 2010

Teoretycznie długo o monadach (Clojure gdzieś w tle)

2 komentarzy
Trochę trwało zanim poczułem się w stanie chociażby nakreślić, czym są monady i przestrzegam, że ich zrozumienie opieram na własnych lekturach bez możliwości ich skonfrontowania z istotą żywą. Zabrałem się za ten temat przez pryzmat teorii teorii kategorii (podwojenie 'teorii' zamierzone), czyli matematycznie oraz przez pryzmat zastosowań programistycznych, czyli informatycznie. W żaden sposób nie twierdzę, że zawarte w tekście tezy są w jaki sposób właściwe dla zrozumienia pojęcia monad, a mogą wręcz doprowdzić do aktów samobójczych znawców tematu :) Niech mi to wybaczą.

Zabrałem się za temat zrozumienia monad przez serię artykułów, które zebrałem w serwisie delicious w kategorii (tym razem nie w dosłownym sensie matematycznym, a jedynie jako zgrupowanie) monads. Tam też można znaleźć mój pogląd, na każdy z przeczytanych artykułów i tam też będę odkładał kolejne.

Zrozumienie działania monad chciałbym wykorzystać do wzbogacenia swojego warsztatu programistycznego w narzędzia wykorzystywane w programowaniu funkcyjnym z praktyczną realizacją z użyciem języka Clojure. Jest wirtualna maszyna Javy z całym jej dobrodziejstwem inwentarza, jest język funkcyjny, więc nic więcej mi nie trzeba. Bazując na znanym (JVM i Java) poznaję nieznane (programowanie funkcyjne z Clojure). Tyle się mówi o poznawaniu innych paradygmatów programowania, aby wzbogacić swój warsztat, więc postawiłem na zrozumienie programowania funkcyjnego i monady wydały mi się wystarczającym wyzwaniem.

Poza w/w artykułami na delicious, dzięki uprzejmości Michała Januszewskiego, miałem przyjemność przeczytać 3 rozdziały o monadach w wydaniu Haskella w książce Real World Haskell wydawnictwa O'Reilly, które nie pomogło jednak wiele, bo przykłady były w języku, którego wcale nie znam. Dodatkowo, w przeciwieństwie do Clojure, Haskell ma leniwe (ja jednak wolę określać to jako opóźnione) wykonywanie funkcji (a może nawet w ogóle nie dojść do ich wykonania!) oraz wyłącznie funkcje czyste (bez efektów ubocznych, gdzie jedynym środowiskiem działania funkcji są dane wejściowe, a wynik przekazywany w postaci zwracanej struktury). To znaczna różnica nie tylko między Javą a Clojure czy Haskell, ale nawet między reprezentantami tej samej kategorii programowania funkcyjnego - Clojure vs Haskell.

Najbardziej przysłużyło mi się nagranie Introduction to Monads - Adam Smyczek. Przesłuchałem nagrania bodaj 10 razy do 20 minuty, z czego trzykrotnie doszedłem do samego końca, co okazało się być już jednak niewielkiej wartości merytorycznej, bo właśnie pierwsze 20 minut jest kluczowe (jakimś cudem po tych 20 minutach...zasypiałem :)). Kiedy tak wsłuchiwałem się w każde słowo wypowiadane przez niego i porównywałem z już znaną mi teorią, pojęcie monady stawało się bardziej zrozumiałe. Dzisiaj kolejny raz zajrzałem do Wikipedii, w której pod pojęciem Monad (functional programming) znajduje się wyjaśnienie informatyczne monady. Mogę powiedzieć, że wysiłki ostatniego miesiąca w obszarze monad zakończyły się co najmniej zrozumieniem wstępu na Wikipedii. Mimo swojej niewielkiej ilości pod względem treści, twierdzę, że można nazwać to niewielkim, acz znaczącym sukcesem (nawet, jeśli trwało to miesiąc i po drodze nic nie szło, co możnaby nazwać porażkami, teraz suma porażek staje się sukcesem :)). I teraz, wyjaśnienie na Wikipedii ma sens i jest faktycznie kwintesencją wprowadzenia do monad dla programistów (ale tylko tych, którym dane jest myśleć poza ramami aktualnie wykorzystywanego języka programowania, co dla wielu wśród moich czytelników sprowdzi się do Javy - obym się mylił!).

Gdyby zacząć wprowadzenie do monad matematycznie, to należałoby wskazać na dobre wprowadzenie do teorii kategorii na MIMUWowym ważniaku - Teoria kategorii dla informatyków. Proponuję zacząć od wykładu Wykład 1: Teoria kategorii jako abstrakcyjna teoria funkcji, aby po nim przejść do Wykład 11: Monady. Ech, gdyby jeszcze opublikowano nagrania z wykładów, byłoby cudnie i znacznie uprościłoby zrozumienie informatyki teoretycznej (co wierzę, że miałoby ostatecznie przełożenie na nasze projekty).

Zatem, czym są monady i czy jest się czym ekscytować?

Założenia naszego rozumowania są takie, że funkcja nie ma skutków ubocznych (całe jej środowisko jest jej przekazywane w postaci parametrów wejściowych) oraz nie zawsze musi być wykonana w miejscu jej wystąpienia w kodzie źródłowym (bo i po co, kiedy nikt nie oczekuje na zwracany przez nią wynik). To może się nie mieścić w głowie osobom z praktycznym programowaniem imperatywnym, a szczególnie obiektowym, w którym o skutki uboczne się właśnie gra. Nie raz, nie dwa, działanie funkcji to zmiana parametrów wejściowych przekazywanych przez referencję, a nie wartość, więc poza zwracanymi wartościami może dojść do zmiany przekazywanych. Innymi słowy, wielokrotne wywołanie danej funkcji z tymi samymi parametrami nie musi zwracać tego samego wyniku, mimo, że pamiętamy z lekcji matematyki, że funkcja dla danej wartości zawsze tak robi i dla tych samych danych wejściowych zwraca tę samą wartość. Zysk z takiego założenia jest prosty - można zapamiętać wynik długotrwającej funkcji i raz ją wykonując dla pewnych danych wejściowych zapomnieć o niej dla nich. Wtedy i zrównoleglanie obliczeń jest łatwiejsze, bo nie trzeba niczego synchronizować, albo niewiele. I nie piszę o wsparciu przez programistę, ale przez samo środowisko uruchomieniowe!

Przy takich założeniach można postawić sobie pytanie, jak w czystym i leniwym języku można zrealizować funkcjonalność wypisywania na ekran, albo przechowywania stanu, coś na wzór stanu globalnego programu, kiedy i ekran i stan są zabronione w programowaniu funkcyjnym? Wszystko za sprawą ich nieczystości - ekran może w danej chwili prezentować coś innego niż chwilę wcześniej, więc jako funkcja jest nieczysta. Idąc tym tropem, można właśnie potraktować ekran jako funkcję, której przekazuje się, co ma być wyświetlane. To mi przypomina rekurencję za pomocą akumulatora, gdzie zapobiega się zbyt zagnieżdżonym wywołaniom przez odnotowanie aktualnego wyniku w akumulatorze i zamianę (zamiast odłożenia) aktualnego wywołania na stosie wykonywania programu.

Matematycznie monada jest trójką z funktorem i dwoma morfizmami, które spełniają pewne założenia (patrz Definicja 11.1 [monada] w Wykład 11: Monady). To wciąż dla mnie niewiele mówiąca definicja, aczkolwiek jeśli się nie mylę, chodzi o takie przekształcenie obiektu (w sensie teorii kategorii nie programowania obiektowego) na drugi (przez morfizmy), aby nie zaburzyć jej struktury, tj. praw w nim zachodzących. Innymi słowy, mamy obiekt i wzbogacamy go, ale jego właściwości się nie zmieniają (tutaj nie jestem tego dokładnie pewien, czy dobrze się wyrażam - wybaczcie puryści językowi).

Teoretyczna informatyka przedstawia monady jako...tak, tak...trójkę składającą się z abstrakcyjnego typu opisującego akcje/działania (zamiast danych jak w warstwie danych w modelu obiektowym) oraz dwóch funkcji m-return i m-bind (w notacji Clojure). Pierwsza z nich - m-return - służy do wstrzykiwania/opakowania typu wejściowego do typu monadycznego, a m-bind rozpakowuje typ monadyczny i przekazuje wynik akcji do kolejnej (wciąż będąc w ramach przestrzeni obiektów wzbogaconych). W ten sposób możemy dostarczać dodatkowych funkcjonalności do już istniejących funkcji bez wpływu na ich implementację. Widzę tutaj analogię do interceptorów czy dekoratorów, aczkolwiek pamiętam, że wielu przestrzegało mnie przed takim (imperatywnym!) myśleniem. Cóż, na razie po prostu widzę ich analogie - przywołując pierwsze zdanie we wspomnianym już wykładzie Teoria kategorii dla informatyków "dobry matematyk to taki, który potrafi znaleźć analogie pomiędzy twierdzeniami, zaś wybitny matematyk to ten, który widzi analogie pomiędzy analogiami". Niestety nie należę do żadnej z tych kategorii :]

Jeśli wyobrazimy sobie stan zewnętrzny jako dodatkowy i ukryty parametr przekazywany między funkcjami to stan programu może być odczytany w każdej chwili właśnie z owej ukrytej struktury. Czyż nie przypomina nam to w Javie dodatkowego parametru this przekazywanego do każdej funkcji instancji (niestatycznej)?! Mam wrażenie, że to bardzo podobny mechanizm, gdzie programista rozpoczynający swoje życie programistyczne w Javie wie, że ma this, ale nie wie dlaczego. Jeszcze kolejny przykład z naszego podwórka javowego, aczkolwiek niewiele mającym związku z monadami, ale z ukrywaniem informacji już tak - generowanie domyślnego, bezparametrowego konstruktora. To też nam jest dane, bo programujemy w Javie. Język programowania to środowisko, w którym poruszamy się, aby wyrazić/zamodelować pewne czynności z życia realnego i Clojure, czy programowania funkcyjne w ogólności, możemy porównać do tego. Monady są pewną konstrukcją programistyczną, którą wielu nazywa kontenerem, jak kontener EJB w serwerze aplikacyjnym. Jeśli dopasujemy swoje modelowanie do zasad rządzących w kontenerze będzie łatwiej, ale najpierw musimy zrozumieć jego zachowanie, co już łatwym zwykle nie jest. Podobnie z monadami. To kontener, gdzie mając 2 obowiązkowe funkcje m-return i m-bind z naszą implementacją wpasowujemy się w kontrakt monadyczny. Jeśli tylko tak się stanie, możemy wyczyniać znacznie więcej, mniejszym kosztem (długofalowo, bo moje miesięczne boje wcale o tym nie świadczą). To jest cel mojej podróży w świat monad i programowania funkcyjnego - dobra zabawa, która ostatecznie ma przynieść korzyści mentalne (a jeśli jeszcze i finansowe, to tym lepiej).

Dla wytrwałych, dodatkowa porcja wiedzy nt. monad z przeczytanych artykułów.

Monada jest reprezentowana przez strukturę danych, która z kolei reprezentuje wynik działania lub samo działanie. Strukturę danych będącą podwaliną monady nazywamy monadyczną strukturą danych. W językach czystofunkcyjnych jak Haskell lub mniej restrykcyjnych jak Clojure, monady wspierają tworzenie aplikacji z jawnie zmiennym stanem, którego zmiana jest jednak niemożliwa (symuluje się ją przez kopiowanie struktur). Sam algorytm mógłby wskazywać na zachodzące zmiany w programie, a dzięki monadom są one symulowane (zaczynam przedstawiać monady jak inni, jakimś bliżej niezrozumiałym językiem - przyznaję, że sam nie wiem, o czym jeszcze piszę i wychodzi jakiś bełkot :))

Monada (chociażby w Clojure) opisana jest przez 2 obowiązkowe funkcje - m-result i m-bind oraz 2 dodatkowe - m-zero i m-plus.

m-result zamienia dowolną wartość na jej reprezentację monadyczną, wyrażoną konstrukcjami owej struktury monadycznej.
m-bind przekazuje wartość monadyczną (wyrażoną strukturą monadyczną) do funkcji jednoargumentowej, która wykonuje obliczenia (po co, wyjaśni się w przykładach, które kiedyś się w końcu pojawią - przypominam, że wciąż zgłębiam tę tajemną wiedzę i liczę na wyrozumiałość z niemałą dawką cierpliwości).

Innymi, bardziej matematycznymi słowy, acz wciąż jedynie bardzo delikarnie, trójka określa monadę.

Nieobowiązkowe funkcje to:
- m-zero reprezentująca specjalną wartość monadyczną, która odpowiada działaniu bez wyniku, np. zakończonego wyjątkiem lub po prostu zwróceniem nil.
- m-plus jest złożeniem wyników dwóch lub więcej obliczeń/działań.

Zwykle istnienie jednej implikuje (prowokuje?) istnienie drugiej, ale nie jest to obowiązkowe.

Wynika stąd, że m-result, m-bind, m-zero i m-plus operują na pewnej strukturze danych, którą wszystkie muszą respektować.

W kolejnej odsłonie zaplanowałem sobie bardziej praktyczne przedstawienie monad, przez przykłady.

20 września 2010

Compojure w akcji - trasowanie i obsługa parametrów

0 komentarzy
W artykule Clojure w aplikacji webowej z Compojure oraz Ring i Hiccup przedstawiłem szkielet aplikacji webowych Compojure zbudowany na języku Clojure. Compojure jest w tak aktywnym rozwoju, że mimo wersji 0.4.1 już dorobił się kilku bibliotek wspierających - hiccup oraz clout, a sam jest nakładką dla innej biblioteki ring. Nie stanowią one bardzo obszernych rozwiązań - szkieletów aplikacyjnych, do których przyzwyczaiłem się w Java EE i zrozumienie sensu istnienia wspomnianych stanowi dla mnie nie lada wyzwanie. A gdzie tu jeszcze umiejętne użycie.

W tym odcinku-artykule w serii o roboczej nazwie "Poznajemy Clojure praktycznie" przedstawię sposób, w jaki Compojure obsługuje trasowanie (wyznaczanie tras, tj. routing) oraz parametry z formularzy (aczkolwiek w tym odcinku formularza nie będzie i będzie jedynie przekazywanie parametrów przez adres URL).

Zainteresowanych zapraszam do lektury artykułu Compojure w akcji - trasowanie i obsługa parametrów.

Jest to kolejny krok ku zrozumieniu narzędzi budowania aplikacji webowych w Clojure i praktycznego jego wykorzystania - budowania aplikacji webowych (co oczywiście nie oznacza, że jest to dobrze dobrane narzędzie do problemu). Siła Clojure płynie przede wszystkim z użycia makr, które tłumaczone są na właściwe wywołania funkcji (w postaci bajtkodu) podczas kompilacji. Clojure nie jest językiem interpretowanym, więc tym samym Compojure staje się DSLem do tworzenia aplikacji webowych. DSL w założeniu ma być językiem dziedzinowym, a skoro mowa o aplikacjach webowych, to dobrze jest korzystać z języka, które takie aplikacje wspiera wprost. Java zdecydowanie takim językiem nie jest - jest zbyt ogólna i możnaby powiedzieć, że jest zbyt niskopoziomowa, podobnie jak C czy assembler.

17 września 2010

Clojure w aplikacji webowej z Compojure oraz Ring i Hiccup

0 komentarzy
Logo ClojureMiało być filmowo, a wyszło literacko - zamiast skrinkastu pojawił się artykuł Clojure w aplikacji webowej z Compojure oraz Ring i Hiccup, w którym (cytując samego siebie z artykułu) zaprezentuję alternatywne podejście do tworzenia aplikacji webowych z Clojure i rozwiązaniami wspierającymi opartymi na nim - Compojure, Ring oraz Hiccup. W skrócie, Compojure to szkielet webowy, Ring to mechanizm uruchamiający środowisko kontenera webowego (w naszym przypadku będzie to Jetty) oraz Hiccup do tworzenia szablonów stron webowych. Po więcej informacji zapraszam na odpowiednie strony domowe projektów (które nota bene są we wczesnej fazie rozwojowej i większość z nich to projekty na GitHubie). Swoje miejsce znalazły również Leiningen, NetBeans IDE i enclojure!

Chciałbym tym samym podziękować Danielowi Janusowi, który zapoczątkował moje poznawanie Compojure i który niestrudzenie pomaga mi zrozumieć tajniki samego Clojure w postaci komentarzy na tym blogu. Jestem zobowiązany. Dzięki.

Gdyby tego było mało, właśnie pojawiła się inicjatywa poprowadzenia zainteresowanych poznaniem Clojure przez zakamarki języka i rozwiązań wspierających w postaci nowego bloga Clojure Blog. Na mój apel odnośnie pomocy w prowadzeniu takowego, napisał do mnie Marcin Rzewucki i tak oto, po kilku dniach, mamy owoc jego pracy. Nie daj mu zapomnieć o swoim krytycznym (w pozytywnym tego słowa znaczeniu) podejściu do publikowanych tam treści i daj się zauważyć, np. w postaci komentarza, albo pomysłu na kolejne wpisy. Marcin wykazuje niespożytą energię, którą zamierza przeznaczyć na przybliżanie nam Clojure, ale świat jest pełen pokus i może zdryfować :-)

Wracając do artykułu...Co jest niezwykle odświeżającym doświadczeniem, które pozostawia niezapomniane wrażenia po przeczytaniu tego artykułu, to fakt, że stworzenie aplikacji webowej w Clojure nie wymaga bardzo rozległej wiedzy. Szkielety webowe w Clojure składa się z interesujących i potrzebnych funkcjonalności, co stoi w przeciwieństwie do bardzo rozbudowanych (i wielokrotnie "spłycanych" do wykorzystania nielicznych funkcjonalności) szkieletów javowych. To może być cenione i ganione jednocześnie. Dla mnie, początkowo, taki sposób budowania aplikacji webowych przypominał programowanie w assemblerze, ale nie trwało długo, aż zdałem sobie sprawę, że może doświadczam niepotrzebnego zwątpienia, bo być może tylko tyle mi akurat potrzeba i szczęśliwie dobrałem właściwe narzędzie do problemu (a nie próbowałem dopasowywać problem do narzędzi). Ocenę pozostawiam Tobie.

Zapraszam do lektury Clojure w aplikacji webowej z Compojure oraz Ring i Hiccup. Nie powinien zająć więcej niż 10 minut. Uwagi i komentarze mile widziane, a wręcz oczekiwane.

14 września 2010

Alfabet w Clojure - zagadka z kanału @learnclojure

4 komentarzy
Logo ClojureDzisiaj będzie niezwykle krótko, ale bardzo intensywnie mentalnie. Nauka Clojure sprawia, że coraz bardziej "żałuję", że się za ten język zabrałem - coraz więcej problemów przypominających zagadki algorytmiczne, w których nigdy nie byłem orłem (albo nie odkryłem w sobie powołania), albo wręcz zejście na tematy bardzo abstrakcyjne matematycznie. Możnaby rzec, że programowania "produkcyjnego" zostało niewiele (zresztą nigdy w moim wykonaniu nie było), a "teoretyczne" wyraźnie zwyżkuje.

Od dzisiejszego wieczoru, kanał @learnclojure na twitterze przeszedł pod opiekę autora książki "The Joy of Clojure". Książka niebawem pojawi się w sprzedaży, a dzięki uprzejmości wydawnictwa Manning, mam ją przyjemność czytać od kilku tygodni i szczerze cierpię z powodu nadmiaru informacji, których nie mogę w żaden sposób strawić w tempie, jakim bym sobie życzył - powiedzmy, zamiast tradycyjnego 5 x 45 minut tygodniowo nauki Clojure (jak to miało miejsce za czasów szkolnych z chociażby matematyką), zamarzyło mi się podwoić to. I boli, bo zintensyfikowanie wysiłków przy nauce Clojure (przez m.in. zwiększenie czasu dla niego), wcale nie przekłada się na zwiększenie przyswajania wiedzy o nim w strawnej postaci. Czuję, że zaczynam wykorzystywać te miejsca w moim umyśle, które do tej pory były rezerwowane na pogrywanie sobie w Civilization, albo Traviana :-)

Jeśli ktoś nie wierzy, że nauka języka programowania może boleć i chciałby poczuć klimaty, w których przychodzi zmagać się z "oczywistościami" dla osób, które pojęły już te tajniki (jak ma miejsce często przy rozmowach o Javie w moim przypadku, kiedy to nie mogę zrozumieć, że Java może być i jest dla wielu trudna), niech przyjrzy się poniższej konstrukcji (zaczerpnięte z dzisiejszego statusu na kanale @learnclojure) i odpowie na pytanie: "Co będzie wynikiem wykonania?".
user=> (->> (iterate inc (int \a)) (take 26) (map char))
;...jaki będzie wynik?
user=> (map char (range (int \a) (int \{)))
;...wynik jak wyżej i pytanie to samo
Szacunek dla tego, komu tego typu cudeńka nie sprawiają problemu. Ja, kiedykolwiek widzę podobne konstrukcje, zwijam się z bólu, a głowa eksploduje. Pora trawić.

W ramach uzupełnienia, trochę dokumentacji, która może pomóc w zrozumieniu powyższego.
user=> (doc ->>)
-------------------------
clojure.core/->>
([x form] [x form & more])
Macro
  Threads the expr through the forms. Inserts x as the
  last item in the first form, making a list of it if it is not a
  list already. If there are more forms, inserts the first form as the
  last item in second form, etc.
nil
user=> (doc iterate)
-------------------------
clojure.core/iterate
([f x])
  Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects
nil
user=> (doc inc)
-------------------------
clojure.core/inc
([x])
  Returns a number one greater than num.
nil
user=> (doc int)
-------------------------
clojure.core/int
([x])
  Coerce to int
nil
user=> (doc take)
-------------------------
clojure.core/take
([n coll])
  Returns a lazy sequence of the first n items in coll, or all items if
  there are fewer than n.
nil
user=> (doc map)
-------------------------
clojure.core/map
([f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls])
  Returns a lazy sequence consisting of the result of applying f to the
  set of first items of each coll, followed by applying f to the set
  of second items in each coll, until any one of the colls is
  exhausted.  Any remaining items in other colls are ignored. Function
  f should accept number-of-colls arguments.
nil
user=> (doc char)
-------------------------
clojure.core/char
([x])
  Coerce to char
nil
user=> (doc range)
-------------------------
clojure.core/range
([] [end] [start end] [start end step])
  Returns a lazy seq of nums from start (inclusive) to end
  (exclusive), by step, where start defaults to 0, step to 1, and end
  to infinity.
nil

09 września 2010

Zagadka z Clojure - funkcyjna niezmienność struktur danych

5 komentarzy
Logo ClojureKontynuuję moją przygodę z Clojure i zgodnie z planem wczytuję się w różnego rodzaju publikacje na ten temat - grupa dyskusyjna clojure na Google Groups monitorowana na bieżąco, na swoim koncie na twitterze podłączyłem się do co bardziej znaczących osób w światku Clojure i się wdrażam. Stopniowo i bez szaleństw, ale konsekwentnie. Ach i bym zapomniał - książkę "The Joy of Clojure" również czytam. Trochę wolniej niż zwykle - 1 rozdział na dzień podczas, gdy zwykle przychodzi mi przejść przez kilka rozdziałów, ale biorąc pod uwagę inne aktywności wokół Clojure, to i tak uważam, że utrzymuję dosyć wysokie tempo (wysoka samoocena wie, kiedy się ujawnić).

W trakcie lektury A monad tutorial for Clojure programmers (part 3) o monadach trafiłem na przykład, w którym zamiast idiomatycznego ('ma' w tym słowie ma niebagatelne znaczenie! :)) pobrania wartości dla danego klucza w mapie (klucz mapa) zastosowano (prawie) równoważną funkcję (get mapa klucz).
user=> (doc get)
-------------------------
clojure.core/get
([map key] [map key not-found])
  Returns the value mapped to key, not-found or nil if key not present.
nil
user=> (def m {:a \a})
#'user/m
user=> (class m)
clojure.lang.PersistentArrayMap
user=> (:a m)
\a
user=> (get m :a)
\a
user=> (= (get m :a) (:a m))
true
Różnica jest niewielka, acz istotna - obsługa sytuacji braku istnienia klucza w mapie. Warto pamiętać, że różnica istnieje, ale w przywołanym przykładzie użycie funkcji (get) sprowadza się do idiomatycznego użycia klucza do poszukiwania jego wartości w mapie. Innymi słowy, klucz w Clojure jest jednocześnie funkcją, która wyszukuje wartość dla niego samego. W przypadku (get) jest możliwość podania trzeciego parametru, który zostanie zwrócony, jeśli nie zostanie odszukany klucz.
user=> m
{:a \a}
user=> (:b m)
nil
user=> (get m :b \b)
\b
Jasne? Na pewno! I nie przyjmuję odpowiedzi, że nie :]

Tak przy okazji, właśnie znalazłem przykład do zobrazowania monady, która zareaguje na sytuację braku klucza! Poszukiwałem go przez dobrych kilka dni i właśnie znalazłem. Kolejny istotny powód dla publikowania swoich doświadczeń na blogu. Właśnie, gdyby ktoś rozważał bloga, ale brakowało mu inspiracji lub przewodnika, zgłaszam się na ochotnika, aby wskazać zainteresowanemu tematy. Najlepiej niech to będzie zapaleniec programowania funkcyjnego w dowolnej postaci - Haskell, Scala, Python, Lisp, Ruby, itp.

Wracając do tematu dzisiejszego wpisu, mam dla Ciebie zagadkę związaną z Clojure, aczkolwiek więcej tu logicznego myślenia niż samej znajomości języka. Warto jednak pamiętać, że wyróżnikiem programowania funkcyjnego jest przede wszystkim czystość funkcji - im większa tym lepiej. Zero skutków ubocznych i funkcja jedynie operuje na danych wejściowych zawsze zwracając pewną wartość, w szczególności nil. Pamiętaj, że do rozwiązania zagadki potrzebne jest zapamiętanie, że...patrz na tytuł wpisu. Pora na zagadkę, która mnie osobiście na zauważalny moment "zawiesiła": Dlaczego ostatnia linia zwróci "Nie ma elementu?!"? Nagrodą jest doświadczenie błogiego stanu nirwany zrozumienia podstawowej cechy programowania funkcyjnego.
user=> (def m {:a \a})
#'user/m
user=> m
{:a \a}
user=> (get :a a)
nil
user=> (get a :a)
\a
user=> (:b m)
nil
user=> (get m :b)
nil
user=> (get m :b "Nie ma elementu?!")
"Nie ma elementu?!"
user=> (doc assoc)
-------------------------
clojure.core/assoc
([map key val] [map key val & kvs])
  assoc[iate]. When applied to a map, returns a new map of the
    same (hashed/sorted) type, that contains the mapping of key(s) to
    val(s). When applied to a vector, returns a new vector that
    contains val at index. Note - index must be <= (count vector).
nil
user=> (assoc m :b \b)
{:b \b, :a \a}
user=> (get m :b "Nie ma elementu?!")
"Nie ma elementu?!"
Dlaczego ostatnia linia zwróci "Nie ma elementu?!"? Odpowiedzi dozwolone w komentarzach. Kto pierwszy, ten lepszy.

p.s. Jeśli komuś przypadła zagadka do gustu, proszony jest o wyrażenie swojego zachwytu w komentarzu, abym wiedział, czy kontynuować tego typu wpisy.

08 września 2010

PIT-36 dla dochodów z zagranicy - zadowolony z rozmowy z US Ursynów

12 komentarzy
Dzisiejszy ranek postanowiłem przeznaczyć na małe dochodzenie odnośnie przychodów z zagranicy i ich rozliczania przez osoby nieprowadzące działalności gospodarczej. Nie pierwszy raz przychodzi mi dotknąć tego tematu i nigdy nie wiem, jak należałoby wystawić fakturę/rachunek (ang. invoice) za moją potencjalną działalność na rzecz firmy amerykańskiej.

Trzeba było widzieć moją minę, kiedy po kwadransie byłem niezwykle usatysfakcjonowany z rozmowy z Urzędem Skarbowym Warszawa Ursynów, ul. Wynalazek 3, 02-677 Warszawa.

Zaczęło się od telefonu na centralę 22 548-68-00, gdzie wybrałem tonowo 1 (mieszkaniec Ursynowa), a później 9 (operator, wcześniejsze brzmiały dosyć egzotycznie). I zaczyna się przedstawienie sprawy - firma amerykańska płaci obywatelowi polskiemu, który nie prowadzi działalności gospodarczej (aż trudno uwierzyć, że potrafiłem złożyć takie zdanie), a chciałby zgodnie z polskim prawem gospodarczym rozliczyć się jak na praworządnego obywatela przystało (końcówkę zmieniłem na potrzeby tego wpisu, bo aż tak wyniośle to nie artykułowałem sprawy). Pan przekierował mnie na Dział Rachunkowości pod numer 5486894, gdzie dowiedziałem się (po przedstawieniu sprawy), że powinienem "udać się" do Działu Podatków - i tu pojawiają się 4 możliwe numery: 5486712, 5486717, 5486708, 5486709. Zaczynam od pierwszego i po minucie rezygnuję. Drugi to samo, aby powiodło się z trzecim - 5486708. Tam dowiaduję się o kolejnym numerze 5486711. Super, dzwonię pod niego, aby dowiedzieć się, że faktura to nie sprawa dla mnie, a co najwyżej rachunek (co wydawało mi się, że jest po prostu fakturą uproszczoną, czy nieVATowską) i warto dowiedzieć się szczegółów pod kolejnym numerem 5486833 - Dział Podatku Dochodowego. Dzwonię, a tam kolejne przekierowanie na numery 5486821 lub -856 - Dział Podatku Dochodowego od Osób Fizycznych Nieprowadzących Działalności Gospodarczej. Odbiera pierwszy, aby miła pani poinformowała mnie (po konsultacjach w tle z innymi osobami, co przywołało mi na myśl, że osoba, z którą rozmawiam, to jedynie pośrednik - z naszego "proxy", albo "interceptor" z zerową implementacją poza wywołaniem docelowej metody :)), że rozwiązaniem jest PIT-36, gdzie wypisuję wszystkie dochody zagraniczne.

Trwało pewnie z kwadrans, wybrałem 10 numerów, ale cel osiągnięty. Jestem nad wyraz usatysfakcjonowany. Teraz wystarczy wystawić rachunek z wymogami płacącego i będzie cacy.

p.s. Zastanawiam się, czy byłoby łatwiej/prościej/przyjemniej, gdybym realizował to jako jednoosobowa działalność gospodarcza? Byłbym wdzięczny za wszelkie (p)odpowiedzi.