piątek, 21 marca 2008

Darmowe rzeczy są dobre, bo są dobre i darmowe

Nie ma to jak dostać coś wartościowego za darmo. Wiele osób twierdzi, że na tym polega ruch otwartego oprogramowania. To nie do końca jest prawdą, bo można zarabiać na oprogramowaniu open source, a wręcz je sprzedawać. Faktem jednak jest, że coraz więcej materiałów w Internecie jest (legalnie) dostępna za darmo. Dzisiaj chciałem zwrócić uwagę na trzy książki, które warte są poznania, kiedy zabieramy się do pracy z językami programowania Haskell i Ruby.

Naukę języka Haskell można zacząć, korzystając z Wikibooks. Dostępna tam jest książka o tym języku. Obejmuje treścią niemal wszystko co z Haskellem się wiąże, chociaż nie każdy fragment książki został dopracowany, jak to często z Wiki bywa. To, co jest już napisane, jest jednak naprawdę porządną porcją wiedzy, zarówno dla zaczynających poznawanie języka jak i dla chcących poznać go lepiej.

Drugą pozycją o języku Haskell, bardzo obiecującą ale jeszcze nie skończoną, jest książka "Real world Haskell", która stanowi przystępnie napisane wprowadzenie do języka. Powstaje ona dosłownie na oczach jej czytelników, ponieważ kolejne fragmenty pojawiają się na stronie w miarę, jak autorzy książki je tworzą. Dodatkowo, każdy akapit można komentować, sugerując zmiany lub błędy (ten sposób pisania książki można by nazwać zgodnym z Web 2.0).

Wiele osób poznających język Ruby z kolei na pewno zna już ten adres: "Programming Ruby - The Pragmatic Programmer's Guide". Są tacy, którzy uważają Pickaxe (jak popularnie zwana jest też ta książka) za angielskojęzyczną biblię programistów Ruby. Dla wszystkich tych jednak, którzy nie trafili jeszcze pod wyżej wymieniony adres mam informację - to podstawowy zbiór informacji o języku Ruby. Nie jest to niestety książka do nauki stylu programowania w tym języku, ale jako baza wiedzy o składni i bibliotece standardowej (do wersji 1.6) jest niezastąpiona.

Życzę wszystkim dobrej lektury na święta!

wtorek, 19 lutego 2008

Z archiwum X

Każdy kto na poważnie zajmuje się programowaniem w Javie zetknął się z przełącznikami, które można ustawiać przy wywołaniu wirtualnej maszyny. Naturalnie w większości przypadków wystarczają trzy:
  • -jar do wskazywania archiwum Jar zamiast pojedynczego pliku klasy
  • -cp (lub -classpath) do ustawiania ścieżki klas
  • -Xmx do ustawiania maksymalnej wielkości stosu (przydatne zwłaszcza przy pracy ze środowiskami typu Eclipse czy Netbeans)
Ten ostatni to tak naprawdę tylko jeden skromny przedstawiciel bardzo płodnej rodziny przełączników X. JVM od Suna posiada ich mnóstwo, a implementacje od IBM czy Bea również posiadają swoje zestawy. Jak się w tym połapać? Na szczęście jest strona która zbiera wszystkie przełączniki w jednym miejscu (chociaż tylko z implementacji Sunowskiej) - "A Collection of JVM Options". Podane są przełączników nazwy, wartości, wersje JVM które je obsługują, a spora część z nich jest całkiem dobrze opisana (chociaż nie znalazłem tam np. niekiedy cennej informacji że przełącznik -client jest zawsze domyślnie włączony w JVM dla systemów Windows).

Od teraz będę już wiedzieć jak sprawić żeby JVM zauważył że jest w środowisku 64-bitowym (-d64), włączyć zapisywanie do pliku logu akcji odśmiecania pamięci (-Xloggc), wymusić użycie maksymalnej dostępnej pamięci (-XX:+AggressiveHeap) czy wyświetlić ilość użytej pamięci stosu (-XX:+PrintHeapUsageOverTime). Masa innych trików czeka jednak na swoich odkrywców, a więc do dzieła!

środa, 6 lutego 2008

Unikanie błędów

Tytuł tego posta jest dość chwytliwy. Kto nie chciałby unikać błędów w życiu? Wiedzieć z góry, że kupno tamtego mieszkania będzie oznaczało nieustanną walkę z grzybkami na suficie albo że autobus, do którego ma się zamiar wsiąść, ma zamiar się w połowie drogi zepsuć?

Tutaj chciałbym jednak zwrócić uwagę na jeden specyficzny sposób unikania błędów podczas tworzenia oprogramowania. Sposób jest bardzo prosty - pisać mniej kodu. Po chwili zastanowienia można zauważyć, że pomysł jest wręcz oczywisty, bo nie da się zrobić błędu w linii kodu, która... nie istnieje.

Kolejna chwila zastanowienia nad powyższym sposobem unikania błędów prowadzi do wniosku, że idealnie byłoby nie pisać kodu w ogóle. Zero kodu = zero błędów! Nirwana! Chociaż może nie do końca, bo tworzone oprogramowanie miało mieć jakąś funkcjonalność, a skoro nic nie napisaliśmy, to próżno szukać tej funkcjonalności.

Jak w takim razie pisać mniejsze ilości kodu, a mimo to z powodzeniem dostarczać gotowe oprogramowanie? Jest parę sposobów:
  • używanie generatorów kodu - to rozwiązanie wykorzystujące fakt, że większość programów IDE ma funkcje wstawiania gotowych fragmentów kodu (np. klasyczne getters / setters dla pól obiektów w Javie); ten sposób oznacza tylko, że programista sam nie musi pisać kodu, który jednak i tak powstaje, więc nie tędy droga
  • stosowanie zasady DRY (ang. "don't repeat yourself" - "nie powtarzaj się") - z czystym sumieniem mogę ten sposób polecić; trzymanie się głównej wytycznej DRY będzie powodować zapalenie się ostrzegawczej lampki za każdym razem, gdy spróbujemy po raz kolejny napisać to samo
  • używanie języków wyższego poziomu - to najbardziej kontrowersyjny sposób i naturalnie nie zawsze możliwy do zastosowania; pamiętając o tym, należy jednak mieć świadomość korzyści, które niesie ze sobą język wysokiego poziomu - możliwość wyrażania myśli bardziej bezpośrednio w kodzie; jako ilustracja niech posłuży króciutki kod w języku Haskell obliczający pierwiastek liczby metodą Newtona:
    -- funkcja "sqrt" zwraca nieskończony ciąg kolejnych
    -- przybliżeń wartości pierwiastka, z którego możemy
    -- wybrać np. piąte jako wystarczająco dobre
    sqrt x = sqrtNewton x 1

    sqrtNewton x guess = average : sqrtNewton x average
    where
    average = (y + (x / guess)) / 2
Pozostaje tylko problem, co zrobić z kodem, który już został napisany i przypomina objętością miłośnika Big Mac'ów? Odpowiedź wykracza już poza tego posta, ale w największym skrócie - używając refaktoryzacji stosować się do zasady DRY.

A więc - miłego (nie)pisania kodu!

czwartek, 24 stycznia 2008

Zamienił stryjek sIEkierkę na kIEk

Sporo mojej pracy dotyczy aplikacji WWW (web applications). Z tego powodu należę do dużej grupy ludzi niezadowolonych - to najłagodniejsze określenie - z powodu konieczności użerania się z produktem Internet Explorer firmy MS, a konkretnie z jego silnikiem renderującym strony HTML. Powstało mnóstwo stron opisujących błędy tego silnika oraz sposobów ich obejścia, nie zawsze niestety skutecznych. Wiele napisano również o tym, jak bardzo zacofana jest w IE obsługa standardów (szczątkowa obsługa CSS 2 - pochodzącego z 1998 roku, brak obsługi XHTML 1.1 - to rok 2001 się kłania, brak wsparcia dla SVG 1.1 - zatwierdzonego w 2003 roku). Jednak miesiąc temu na blogu IE pojawiła się pierwsza jaskółka nadziei: kolejna, 8. już wersja IE, ma być zgodna z CSS2! Niedługo potem z piekła zaczęły docierać sygnały o wyraźnym ochłodzeniu.

Minął miesiąc i oto na tym samym blogu pojawia się wpis "Compatibility and IE8". I wszystko wraca do normy. Owszem, IE8 będzie poprawnie(*) obsługiwał CSS2, ale... mało kto się o tym dowie, bo ten tryb "super-zgodności-a-nie-jak-do-tej-pory-takiej-zwykłej-zgodności" będzie się włączał tylko na specjalne życzenie wyświetlanej strony (po dodaniu do niej odpowiedniego tagu <meta>). Czyli po raz kolejny (po sztuczce z odpowiednio spreparowanym DOCTYPE i komentarzami-instrukcjami "if IE") pojawi się hak / przełącznik / obejście dla poprzednio zepsutych implementacji. To już przestało być śmieszne, chociaż co bardziej wytrwali w komentarzach do wpisu na blogu dowcipnie i z sarkazmem sugerują że w IE9 będzie tryb "hiper-zgodności-tym-razem-to-już-naprawdę" włączany np. głośnym klaśnięciem w dłonie...

(*) oczywiście może się okazać, że będzie to "poprawnie", ale tylko w microsoftowym znaczeniu tego słowa; pożyjemy zobaczymy

poniedziałek, 17 grudnia 2007

A mój wizual basic jest szybszy!!11!

Przyznaję, sam lubię poczytać dobrze rozpalone technologiczne wojny dyskusyjne. Najczęściej spotykane są oczywiście wojny pomiędzy zwolennikami systemów operacyjnych, ale w dziedzinie programowania równie chwytliwe są wojny o wyższości jednego języka nad innymi. Ta wyższość może się objawiać w najróżniejszy sposób, przez "lepszą" składnię, "lepszą" funkcjonalność, "lepszą" popularność, "lepszą" historię powstania, czy wreszcie "lepszą" wydajność. I do tematu wydajności ostatnim chciałbym tu parę słów komentarza od siebie dodać.

Na wypadek, gdyby jakimś cudem ktoś nie spotkał się dotąd z podobnymi wypowiedziami, oto parę przykładów:
  • "PHP skaluje się dużo lepiej niż Rails"
  • "Java jest za wolna"
  • "Mogę to samo napisać w C i będzie działać 600 razy szybciej"
...i można by tak wymieniać w nieskończoność. A czy te stwierdzenia są prawdziwe? Oczywiście że... tak. W konkretnym przypadku, środowisku, sprzęcie, problemie. Dla doświadczonego programisty PHP napisanie skalowalnej aplikacji CRUD będzie tylko wykorzystaniem jego wiedzy w praktyce; napisanie podobnej aplikacji w Rails - bez odpowiedniej wiedzy - będzie niemal niewykonalne. Program napisany w Javie z wykorzystaniem biblioteki Swing, naruszający podstawowe zasady modelu jednowątkowego dostępu do zasobów graficznych, będzie działał fatalnie i przywieszał się co chwilę. Zoptymalizowany program napisany w C będzie działał szybciej od większości odpowiedników np. w Ruby.

Czyżby więc rację mieli ci wszyscy którzy z uporem godnym lepszej sprawy wciskają podobne wypowiedzi na forach, blogach itp.? Niestety nie, ponieważ upraszczają sytuację do maksimum, zestawiając ze sobą języki i "porównując" suche fakty lub wręcz stwierdzając tylko swoją opinię. Tymczasem nie istnieje coś takiego jak "szybszy" język (pomijając już tu fakt, że porównywać można co najwyżej konkretne implementacje języka a nie specyfikacje):
  1. można szybciej coś napisać w tym języku niż w innym
  2. gotowy program napisany w tamtym języku może się wykonywać szybciej (na identycznej maszynie) niż napisany w jeszcze innym
  3. nową wersję aplikacji napisanej w języku X można wdrożyć szybciej niż w języku Y.
Ale jeden język programowania nie może być "szybszy" od drugiego!

Jest jeszcze jedna sprawa - dla niektórych stwierdzenie o "szybszym" języku jest jednoznaczne z pkt. 2 wyżej. To błąd. Rozpatrując tylko czasy wykonania analogicznych programów napisanych w różnych językach, nie należy patrzeć tylko na liczby bezwzględne. Nakład pracy poświęconej na stworzenie takiego programu i przewidywany czas pracy z kodem źródłowym w przyszłości mogą być (i często są) ważniejsze od czasu działania gotowej aplikacji. Przykładowo - piszemy prosty analizator logów serwera, generujący raport w pliku XML z gotowym arkuszem XSL. Napisanie takiego programu w C zajmie nam 4 godziny. Napisanie takiego samego programu w Ruby zajmie 2 godziny (zakładam znajomość Ruby i C). Uruchomienie programu napisanego w C dla danego pliku danych wejściowych zajmie np. 10 sekund. Dla programu w Ruby i tych samych danych program będzie działał 30 sekund. Analiza logów będzie przeprowadzana raz na miesiąc. Jakie w tym momencie ma znaczenie fakt, że implementacja w C jest 300% szybsza od tej w Ruby? Żadne, bo ta w Ruby jest wystarczająco szybka w tej konkretnej sytuacji.

środa, 28 listopada 2007

Odpowiednie narzędzia do pracy to połowa sukcesu

Esencją tego bloga jest informowanie o narzędziach programistycznych, jakich można użyć do rozwiązania problemów codziennej pracy. Do tej pory jednak skupiałem się na językach programowania, a przecież kod, który piszemy musi w jakiś sposób powstać. I nie zamierzam tu zaczynać kolejnej wojny o wyższości Vim nad Emacs, Eclipse nad Netbeans, Scite nad Textpad itd. Chciałbym się podzielić swoim (może ciągle niewielkim) doświadczeniem z programowania w Ruby oraz Haskellu.

Bez wielkiego wstępu i rozwodzenia się, uważam że do tworzenia oprogramowania w Ruby najlepszym środowiskiem jest Netbeans 6. Dzięki pracy autorów "Ruby plugin" możemy cieszyć się komfortem pisania z podpowiadaniem składni, dokumentacją API, debuggerem, refaktoryzacją oraz pełną integracją z samym IDE. Na obecną chwilę, Netbeans (w wersji 6.0) nie ma godnej konkurencji.

Opisując Netbeans nieprzypadkowo użyłem określenia "najlepsze środowisko", ponieważ do szybkiego edytowania kodu w Ruby (oraz Haskell) używam jEdit.

Jest to bardzo mocno rozszerzalny i konfigurowalny edytor z podświetlaniem składni, zaawansowanym formatowaniem tekstu i obsługą wielu plików na raz. Jednak dopiero po uzupełnieniu podstawowej funkcjonalność o wtyczki (np. wspaniałą SuperAbbrevs) zamienia się w prawdziwy kombajn. Polecam - wypróbujcie jEdit! A - czy wspomniałem już że jest całkowicie darmowy? :-)

wtorek, 20 listopada 2007

Różne podejścia do wielowątkowości

Pisałem ostatnio o języku Haskell. Uważam go za jeden z lepszych w dziedzinie programowania wielowątkowego w modelu "tradycyjnym", czyli z współdzieloną pamięcią (dzięki połączeniu czysto języka funkcyjnego i technologii STM - bardzo przystępnie przedstawionej w "Beautiful concurrency"). Nie jest jednak jedyne możliwe podejście do problemu. Oto krótkie zestawienie stosowanych rozwiązań wraz z komentarzem:
  • brak jakiejkolwiek obsługi wielowątkowości (Javascript, PHP) - wbrew pozorom to też jest jakieś rozwiązanie, w końcu nie zawsze potrzeba takiej funkcjonalności
  • pseudo-wątki obsługiwane przez runtime języka (Python, Ruby) - w językach z pseudo-wątkami (określanymi często w j. angielskim jako "green threads") to środowisko uruchomieniowe języka samo zajmuje się obsługą współbieżności w najprostszej formie, czyli przełączania kontekstu pomiędzy wątkami. Stosując takie rozwiązanie nie da się w pełni wykorzystać potencjału współczesnych procesorów wielordzeniowych czy maszyn wieloprocesorowych
  • wątki natywne operujące na współdzielonej pamięci (C, Java, Haskell) - języki te udostępniają interfejs umożliwiający operacje na wątkach sterowanych przez system operacyjny (ang. "native threads"). Rozwiązanie to cechuje z jednej strony możliwość tworzenia bardzo wydajnych aplikacji, ale z drugiej duży stopień trudności związany z zapewnieniem poprawności działania. To tu pojawiają się zagadnienia synchronizacji dostępu, blokad, semaforów itp.
  • niezależne, natywne lub kontrolowane przez runtime wątki z systemem komunikatów (Erlang) - według wielu znawców tematu tylko to rozwiązanie ma przyszłość w świecie, w którym -dziesiąt czy -set rdzeni lub procesorów przestaje być fantazją. Atutem jest tu niemal nieograniczona skalowalność takiego podejścia, wadą - konieczność zaadoptowania diametralnie innej koncepcji budowy oprogramowania.
Jeszcze słowo komentarza o współdzielonej pamięci. Moim skromnym zdaniem model ten nie jest skazany na wymarcie (a jeśli nawet, to nie w najbliższej przyszłości). Zbyt wiele jest aplikacji, które o ile skorzystają na wprowadzeniu współbieżnego działania, to nie są narażone na wielowątkowość w skali większej niż kilka równoległych wątków. Tak czy inaczej, zawsze znajdzie się odpowiednie narzędzie do rozwiązania problemu.