"The Grid. A digital frontier. I tried to picture clusters of information as they move through the computer. What did they look like? Ships? Motorcycles? Were the circuits like freeways? I kept dreaming of a world I thought I’d never see. And then, one day, I got in." — Tron: Legacy
Pokazywanie postów oznaczonych etykietą Smalltalk. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą Smalltalk. Pokaż wszystkie posty

2013-01-26

Smalltalk and Seaside part 4.

(c) Robert Tinney
This time this blog post is in English. I decided to change language when I saw link to my blog at Seaside.st. The other reason is that Google's translator just sucks. I hope to suck less.

Post 4 is the last post from the series about HTML parsing and presenting results in form of a web page using Smalltalk and Seaside. All these posts about Smalltalk were my attempt to find out for myself what this language is really about. A year ago I didn't even know about it's existence. My programming world till that time was wrapped mainly in C/C++ and Java. So I thought I'll try some of it in action and that the best way to grab a bit of it is to try it in practice and to solidify that experience on a blog. Well, actually Smalltalk was the main reason for this blog to show up. At the same time I also found Clojure. Both of these as languages just blown my C/Java mind away. Smalltalk is impossibly simple language and has a very helpful IDE (a REAL IDE not just some editor with syntax highlighter and fancy keyboard shortcuts that spit out code or strictly said: vomit code at you). Seaside is also one of the most brilliant ideas I've seen. To efficiently build apps with Seaside actually all you need is a basic knowledge of HTML and CSS and surprisingly no JavaScript - I didn't need it through the whole creation process. Less is better. ;) I find the idea about Smalltalk application image far superior than Java libraries and whats more, easier way to share applications than using jars. For example, I don't have to do anything with my application if I want run it on Android - just copy and run ( thanks to this project: http://code.google.com/p/squeakvm-tablet/ ). Can you do it in Java? You can't!

So lets get back to play. Last time I left, my blog comments reader was in the state where i had to manually add entries to collection, comments were looking ugly, there was no way to remove entry form collection, etc...

This time I wont show any code here (as a text). I decided to share whole one-click Pharo image where you can experiment with. Ill just describe what my objects do.

First, I'll show you the final look:

Well, at least it is not as scary looking as last one...

The minus sign removes entry , + button displays DPDialog, and U button updates comments.
DPDialog looks like this:

Some code:

A you can see its a jQuery dialog, simple, nothing fancy. Code under submitButton checks if user entered a valid URL (its valid if it can be read from). If its OK then DPRoot class adds blog entry to its internal list and then tries to parse comments from given URL.

CSS code is kept within object called DPCSSLibrary. Adding library is simple, just follow this: http://book.seaside.st/book/in-action/serving-files/filelibraries/creating .

Here is the graphical description of how tabs bar is created:


Updated DPComment rendering method:
This method checks if its instance variable (DPComments object sets it when creating new DPComment) is false or true naming div element accordingly, so its later possible to apply a proper CSS code.

Whole application as One-click archive: https://dl.dropbox.com/u/71388328/DP-OneClick.zip

2012-12-11

Smalltalk : Seaside z użyciem Pharo, cz.3

(c) Robert Tinney

Mam już schemat jak wyciągnąć komentarze z dowolnego bloga, to teraz czas na część wizualną. Tak jak poprzednio, najpierw z grubsza opiszę poszczególne kroki. Film z podsumowaniem dam na końcu. Jako, że film byłby dość długi gdyby pokazywać tworzenie kodu to pokażę w nim tylko jak wygląda struktura pakietu i kod. Z poprzedniej części mam już w zasadzie napisaną podstawę pojedynczej funkcji, która odczyta komentarze z podanego adresu URL i zwróci tablicę komentarzy. Najpierw jednak opiszę jak utworzyć komponent w Seaside.
Otwieram System Browser i zaczynam od utworzenia kategorii (paczki obiektów). Prawy myszki na liście kategorii i klikam "Add category...". Jako, że tworzę pod portal dobreprogramy.pl, utworzę kategorię DobreprogramyAPI.

Tworzenie komponentu głównego

Ten komponent będzie odpowiadać za zawartość strony głównej. To wszystko o czym będę pisać jest także dostępne dostępne w dokumentacji Seaside.. Kasą główną komponentów jest klasa WAComponent. Po niej odziedziczę funkcjonalność na potrzeby mojej nowej klasy "DPRoot":

WAComponent subclass: #DPRoot
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'DobreprogramyAPI'

Pierwsze co trzeba zrobić, to utworzyć metodę "initialize":

initialize
   super initialize.

Metoda "initialize" jest metodą wywoływaną zaraz po utworzeniu obiektu, m.in. po wywołaniu metody "new". Dobrą praktyką jest wywołać metodę "initialize" obiektu nadrzędnego za pomocą metody "super". Zielona strzałka do góry, która się pokaże obok nazwy metody mówi, że moja metoda nadpisuje metodę z klasy nadrzędnej.
Kolejnym krokiem będzie utworzenie metody, która wyświetli coś na stronie. Na chwilę obecną dam jakiś prosty tekst byle zobaczyć, czy działa. Metoda ta posiada określona nazwę, bo Seaside przeszukując obiekty i wyświetlając zawartość szuka właśnie jej:

renderContentOn: html
   html paragraph: 'Alibaba'.

Jako, że obiekt DPRoot jest obiektem głównym, trzeba zaznaczyć to tworząc metodę, która poinformuje framework Seaside. W części klasowej trzeba utworzyć metodę:

canBeRoot
   ^ true.

Dzięki niej Seaside będzie widzieć obiekt w przeglądarce aplikacji.

Rejestracja komponentu jako aplikacji

W przeglądarce przechodzę pod adres http://localhost:8080/config . Klikam w Add na pasku menu. W pole wpisuję 'dp' i wybieram typ jako Application. OK. W sekcji General na kolejnej stronie jako 'Root class' wybieram DPRoot. Klik w Apply. Teraz przechodząc pod adres localhost:8080/dp powinien się wyświetlić Alibaba... Jest. ;)
To samo można zrobić wywołując w Workspace polecenie:

WAAdmin register: DPRoot asApplicationAt: 'dp'.

2012-09-30

Smalltalk : Seaside z użyciem Pharo, cz.2

(c) Robert Tinney

Dzisiaj postanowiłem pokazać jak w prosty sposób odczytać komentarze z bloga na portalu dobreprogramy.pl, który nie posiada usługi odczytu wiadomości za pomocą XML/JSON przez co trzeba parsować HTML. Dla niecierpliwych na końcu tekstu jest krótki film podsumowujący temat tego wpisu.

Odczyt zawartości strony internetowej robi się dość banalnie. Wystarczy w Workspace Pharo wpisać :

'http://www.google.com' asUrl retrieveContents contents.
 
Potem umieścić kursor klawiatury za kropką (bądź zaznaczyć myszką całą linijkę kodu) i nacisnąć [Ctrl+p] ("Print it"). Powinna pojawić się zawartość z podanego adresu. Tekst, który się pojawia jest już zaznaczony, więc można nacisnąć klawisz Del by go usunąć.

Jako, że tekst strony już mamy, to potrzebny będzie parser HTML. Będzie potrzebny, bo wbudowany parser XML tonie w zupie tagów luźno zapodanego HTMLa, gdzie zdarzają się niedomknięte tagi, ew. tagi zapisane niezgodnie z dokumentacją i wtrącenia w postaci JavaScriptu czy CSS i inne śmieci.

Dodatkowe biblioteki Smalltalka są przechowywane w repozytoriach. Z takiego dziś skorzystam. Jest to repozytorium Squeaka (inna odmiana Smalltalka zgodna z Pharo).

Aby zainstalować bibliotekę trzeba wejść do przeglądarki Monticello (dostępne z menu myszki). Następnie trzeba kliknąć w "+Package" nadać dowolną nazwę, np. "Soup". Potem klik w "+Repository" i wybieram opcję HTTP. Modyfikuję adres, który się pojawi na szablonie na:

location: 'http://www.squeaksource.com/Soup'

Klikam OK, potem w "Open". Załaduje się lista wersji pakietów. Trzeba teraz wybrać ConfigurationOfSoup - najnowszą wersję , u mnie jest 37 i kliknąć Load. ConfigurationOfSoup to pakiet obiektów, które pobiorą najnowszą wersję zupki. Aby się dowiedzieć jak go uruchomić trzeba otworzyć System Browser i znaleźć ConfigurationOfSoup. Można to zrobić poprzez menu z Find, górną belkę, lub przewinąć listę na sam koniec. Najnowsze pakiety są widoczne zawsze na końcu. W drugim okienku, które jest zwane klasowym lub instancji trzeba kliknąć jeszcze raz nazwę ConfigurationOfSoup, a potem pytajnik. Pod pytajnikiem zwykle jest dokumentacja obiektu.

Widać tam dwie linijki. Jako, że kod Smalltaka można wykonać z każdego okienka, zaznaczam linijkę z wersją #stable i wykonuję "Do it". skrypt pobierze i skompiluje resztę biblioteki.

Teraz można pobrać zupę do obiektu już za pomocą samej biblioteki (oczywiście wszystko to wpisuję w Workspace):

zupa := Soup fromUrl: 'http://*dobreprog***.pl/aeroflyluby/Domowy-sposob-na-Diode,36474.html'.

Tym razem użyłem prawdziwego adresu z bloga. Mam więc zupę z tagów i w tym momencie zaczyna się zabawa. Trzeba dowiedzieć się pomiędzy jakimi tagami trzymany jest blok komentarzy. Potem znaleźć pojedyncze wpisy, a następnie rozbić na:
  •     adres logo: #img
  •     nazwę komentującego: #nick
  •     treść komentarza: #text
Zrobiłem małe rozeznanie i znalazłem id tagu, w którym te komentarze siedzą:

ctl00_phContentLeft_panUpdateComment
 
Niestety Soup nie ma możliwości wyciągania tagów po identyfikatorze. Trzeba sobie napisać swoją funkcję. Najlepiej to zrobić zrzynając już z istniejącej:

findTagByClass: aString
    ^ self findTagByClass: aString ifAbsent: [nil]


Oczywiście tworzymy ją w instancji klasy SoupTag w pakiecie Soup-Core. Widać w kodzie, że funkcja ta wywołuje jeszcze inną, z parametrem ifAbsent, która to jest główną (rozszerzoną funkcją) do wyciągania tagu. Jej nazwę też trzeba podmienić. W sumie tworzymy dwie funkcje:

findTagByID: aString
    ^ self findTagByID: aString ifAbsent: [nil]


i

findTagByID: aString ifAbsent: aBlock
    self findTag: [:aTag | (aTag attributeAt: 'id') = aString]
        ifPresent: [:aTag | ^ aTag].
    ^ aBlock value


Mając te dwie funkcje mogę już wyciągnąć tag, w którym siedzą komentarze:

root:= aSoup findTagByID: 'ctl00_phContentLeft_panUpdateComment'.

Można użyć "Print it", które wyświetli treść tagu, lub dać "Do it" i podejrzeć obiekt root przez zaznaczenie go i użycie "Inspect it".

Teraz czas wyciągnąć pojedyncze pola komentarzy. Tutaj będzie trochę trudniej, bo id komentarzy zmienia się. To jest, id komentarzy jest w postaci: komentarz_124234. Niestety, Soup nie umożliwia wyciągania tagów z użyciem wyrażeń regularnych. Trzeba będzie sobie poradzić. I znów trzeba zmodyfikować nasze nowe funkcje:


findAllTagsByIDregX: aString
    ^ Array streamContents: [ :aStream |
                self findAllTagsByIDregX: aString
                    stream: aStream ]

i

findAllTagsByIDregX: aString stream: aStream
    self childTagsDo:
        [ :aTag | ((aTag attributeAt: 'id') isNil ) ifFalse:
            [((aTag id) matchesRegex: aString) ifTrue: [aStream nextPut: aTag ]].
        aTag findAllTagsByID: aString stream: aStream ]

oraz:

findAllTagsByID: aString
   
^ Array streamContents:  [ :aStream |
               
self findAllTagsByID: aString stream: aStream ]i

findAllTagsByID: aString stream: aStream
    self childTagsDo:
        [ :
aTag | (aTag attributeAt: 'id') = aString
            ifTrue: [
aStream nextPut: aTag ].
       
aTag findAllTagsByID: aString stream: aStream ]

Mając te funkcje teraz mogę wyciągnąć komentarze do tablicy:


komentIDregX
:= 'komentarz_[0-9]+'.
komentSoups := root findAllTagsByIDregX: komentIDregX.


Super. ;) Teraz utworzę obiekty na tagi komentarzy, nicków i logo komentujących:


komentClass := 'text-h75 tresc'. 
imgClass := 'border small float-left'.
nickClass := 'text-h65 font-heading display-inl_blk nick'.
nixesTags := root findAllTagsByClass: nickClass.
imgsTags := root findAllTagsByClass: imgClass.
komentTags := root findAllTagsByClass: komentClass.

OK. Teraz wyciągamy tekst i inne ciekawe rzeczy:


nixes := OrderedCollection new.imgs := OrderedCollection new. 
koments := OrderedCollection new.nixesTags do: [:tag | nixes add: (tag text) ].
imgsTags do: [:tag | imgs add: (tag src) ].
komentTags do: [:tag | koments add: (tag text) ].


Oto rezultat:


Jak ktoś chce sprawdzić czy adresy obrazków są dobre, to może wyświetlić np. pierwszy poleceniem:

(ImageMorph fromStream: ((imgs at: 1) asUrl retrieveContents contentStream)) openInWorld.


Aby pozbyć się obrazka trzeba wcisnąć Shift i kliknąć na obrazku środkowym przyciskiem myszki.

Oczywiście poprzedni kod można zapisać krócej używając słownika i listy. Słownik (Dictionary) to odpowiednik mapy w Javie. Ostateczna postać:

zupa := Soup fromUrl: 'http://www.d***y.pl/aeroflyluby/Domowy-sposob-na-Diode,36474.html'.
root:= zupa findTagByID: 'ctl00_phContentLeft_panUpdateComment'.

komentIDregX := 'komentarz_[0-9]+'.
komentClass := 'text-h75 tresc'.
imgClass := 'border small float-left'.
nickClass := 'text-h65 font-heading display-inl_blk nick'.

komentSoups := root findAllTagsByIDregX: komentIDregX.

komenty := OrderedCollection new.
komentSoups do: [:ks |
      koment := Dictionary new.
        koment at: #nick put: (ks findTagByClass: nickClass) text.
        koment at: #img put: (ks findTagByClass: imgClass) src.
        koment at: #text put: (ks findTagByClass: komentClass) text.
        komenty add: koment.
    ].

(ImageMorph fromStream: (((komenty at: 1) at: #img) asUrl retrieveContents contentStream)) openInWorld.


Podsumowanie na video w jeszcze innym stylu:


Na filmie można zauważyć, że nie tworzę funkcji przed wykonaniem kodu. Środowisko Smalltalka informuje mnie, że takowej nie ma i pozwala utworzyć brakującą. Po utworzeniu funkcji można kliknąć "Proceed" i środowisko zachowa się jakby nic nie było. W ten sposób także bardzo łatwo i szybko pisze się testy w konwencji TDD - test first. To nie żadna nowość. Smalltalk był pierwszym szeroko dostępnym środowiskiem, które było wyposażone w bibliotekę do testów jednostkowych xUnit (jUnit - wersja na Javę). To, czym dziś ludzie się zachwycają i biorą za nowość, było używane grubo ponad 30 lat temu.

W kolejnej części o tym jak to wszystko wyświetlić na stronie WWW.

2012-09-22

Smalltalk: Seaside z użyciem Pharo



Chciałbym zaprezentować dwie rzeczy:

(c) Robert Tinney
  • i webowy framework zbudowany na jego podstawie: Seaside. W oparciu o nowoczesną, darmową implementację Pharo
Aby lepiej zrozumieć wpis najlepiej zacząć od ściągnięcia Seaside 3.0.7 w wersji One-Click. W katalogu Seaside.app, po rozpakowaniu będą skróty do uruchomienia na trzech systemach: Linux, Mac i Windows.


Krótko o Smalltaku

dla tych, co im się nie chce czytać Wiki + moje własne obserwacje:
 Smalltalk to dynamiczny język zorientowany obiektowo, zaprojektowany z myślą o zintegrowanym środowisku programowania. Smalltalk nie posiada struktury plików z jakich korzystają inne języki programowania. Wszystko żyje w obrazie, który jest obsługiwany przez maszynę wirtualną. Rozwiązanie bardzo podobne do systemu operacyjnego uruchomionego w Virtual Box. Daje to m. in. możliwość zapisania stanu w jakim jest w danej chwili - tzw. snapshot. Język jest tak prosty, że wdraża się go w OLPC jako pomoc edukacyjną dla dziesięciolatków.

Na dzień dzisiejszy popularne i darmowe odmiany Smalltalka : Pharo, Squeak (OLPC), GNU Smalltalk, działają tylko na jednym rdzeniu. To jest spuścizna po latach '80 i '90, gdzie nikt nie przejmował się skalowalnością horyzontalną na jednej maszynie, bo procesory były wtedy jednordzeniowe. Mogę powiedzieć, że i dziś nie jest to żadną wadą. System webowy w oparciu o Seaside tworzy się najczęściej w oparciu o Apache, który zajmuje się rozsyłaniem żądań do maszyn wirtualnych, które działają na różnych portach i różnych procesorach. W systemie Linux łatwo wskazać na jakim rdzeniu ma się uruchomić dany program. W przypadku awarii maszyny po prostu podnosi się kolejną na tym samym porcie. Wszystko to w zwykłym skrypcie powłoki systemowej.

Smalltalk to język, w którym nie wywołuje się funkcji tak jak w językach imperatywnych, a jedynie przekazuje wiadomości do obiektów (wysyła). Dość ciekawa sprawa, bo kompilator wysyła wiadomość do obiektu, a w momencie, gdy obiekt i klasy macierzyste nie obsługują komunikatu pokazuje się wyjątek 'doesNotUnderstand' ('nie rozumiem'). Nie jest to wyjątek taki jak w Javie, czy C, że program podnosi ręce do góry i często kończy działanie. Tutaj pokazuje się okienko z ostrzeżeniem i możliwością dodania obsługi komunikatu do obiektu. Po edycji dajemy restart i program działa dalej jakby nic. Ta wbrew pozorom błaha funkcja pozwala na pisanie testów w konwencji TDD - 'test first'.
Smalltalk posiada jedną z najczystszych składni językowych. Nie najkrótszą, a najczystszą. Jest bardzo zbliżony w wyglądzie do zwykłego angielskiego. 


myArray at: 1 put: 'tekst'.

Sentencję kończy się kropką (w C średnikiem). Komentarz umieszcza się w "podwójnym cudzysłowie". Łańcuchy znaków łączy się przecinkiem: napi := 'aaaa','bddd'.
W języku występują tzw. bloki. Są to kawałki kodu ujęte w nawiasy kwadratowe. Służą do przekazywania wykonania kodu w argumencie wiadomości lub mogą być zwracane. Posiadają także argumenty, np.:


elements := 5.
tab := Array new: elements.
1 to: elements do: [ :a | tab at: a put: (a * a)]

Jak to działa:
- utwórz obiekt SmallInteger (32 bit) z wartością 5
- utwórz tabelę tab jako wektor 5 obiektów (wszystkie mają wartość nil)
- dla każdego elementu w tablicy przypisz kwadrat wartości jego indeksu



Jak widać na powyższym obrazku obiekt można podejrzeć zaznaczając go i klikając w menu Inspect it. Okienko inspektora posiada na dole wygodne miejsce, w którym można wpisywać kod i np. w ten sposób modyfikować obiekt.


Przydatne skróty klawiszowe:
  • Ctrl + s, - (Accept) zapis zmian
  • Ctrl + d, - (Do it) wykonanie kodu
  • Ctrl + p, - (Print it) wykonanie kodu i pokazanie wyniku na końcu zaznaczonego bloku
  • Ctrl + i, - (Inspect it) - podgląd obiektu
Niektórzy zastanawiają się w tym momencie co to są te pola z dwukropkiem np. 'put:'.
Są to tzw. settery - funkcje, które odpowiadają za odebranie i przeróbkę obiektu podanego jako parametr. Setter obiektu może przyjąć tylko jeden parametr. Bez dwukropka to getter, czyli funkcja, która zwraca wartość i/lub wykonuje działanie na obiekcie. Zmienne instancji obiektu (są także zmienne klasowe - odpowiednik static w Javie) nie są widoczne z zewnątrz, stąd użycie getterów i setterów. Obiekty są dzielone w IDE na dwie strony: część instancji (instance) i część klasową (class). Strona instancji jest miejscem, gdzie przechowuje się zmienne i funkcje, które mogą się zmieniać z każdą kopią obiektu. Część klasowa jest wspólna dla wszystkich kopii, a także jest dostępna bez potrzeby tworzenia nowego obiektu.


Klasa w Smalltalku ma postać:

Object subclass: #MojaKlasa
    instanceVariableNames: 'zmienna_prywatna1 zmienna_prywatna_2'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'Moja-Kategoria'


Zwykle setter ma postać:


zmienna_prywatna1: param
 zmienna_prywatna1 := param.

a getter:


zmienna_prywatna1
 ^ zmienna_prywatna1
 
Pierwsza linijka to jednocześnie nazwa funkcji i parametry. Zamiast pisać znane z C: 
typ getA(void) , pisze się tylko a . Zamiast pisać void setA(int a) pisze się a: aInt.

Znaczek ^ oznacza zwrócenie obiektu - słowo return z Javy/C. Średnika używa się do powtórzeń operacji na tym samym obiekcie. Zamiast pisać tak:


tablica_do_liczenia_sensu_zycia = Array new: 3.
tablica_do_liczenia_sensu_zycia at: 1 put: 'a'.
tablica_do_liczenia_sensu_zycia at: 2 put: 'y'.
tablica_do_liczenia_sensu_zycia at: 3 put: 'z'.

można pisać tak:


tablica_do_liczenia_sensu_zycia at: 1 put: 'a';
                                at: 2 put: 'y';
                                at: 3 put: 'z'.

Każdy obiekt można edytować. I to w sposób jaki jest to nieosiągalny dla innych języków. Mam tu na myśli przede wszystkim popularną rodzinę języków C. Przykładowo mogę dodać brakującą funkcjonalność do klasy String - nie do pomyślenia w Javie. Jako, że mamy do czynienia tylko z obiektami i przesyłaniem komunikatów w tym języku nie ma operatorów. Przykład:


wynik_działania := 1 + 2 * 3.
wynik_działania -> 9

Działa to w ten sposób: do obiektu '1' prześlij komunikat '+' i obiekt '2'. W tym momencie obiekt '1' zwraca obiekt : '3' do którego przesyłany jest komunikat '*' i obiekt '3'.
Aby dostać prawidłowy wynik trzeba iloczyn wziąć w nawias. Kod w nawiasach jest wykonywany jako pierwszy. 




Podstawowe obiekty liczbowe

Podstawowym obiektem, z którego wywodzą się inne (poprzez dziedziczenie) jest obiekt Number. Zawiera on podstawową obsługę wiadomości typu '+' '-' '/' 'negated' 'abs', 'even', 'odd',konwersję typów, testowanie,  itp.

Kolejnym obiektem jest typ całkowity Integer. Nie jest jednak bezpośrednio używany, a posłużył do utworzenia dwóch klas pochodnych: SmallInteger i LargeInteger. SmallInteger posiada rozmiar 32-bit z wartościami od -2^30 do 2^30-1. LargeInteger przejmuje na siebie odpowiedzialność w przypadku sytuacji gdy jego wcześniejszy kolega próbuje 'przekręcić licznik'. LargeInteger jest ograniczony ilością dostępnej pamięci. Przykład:

a := 400000000  "-> SmallInteger"
a := a * 400000000 "-> zamiana typów"
a -> 80000000000000000 "-> LargeInteger"

Kolejny typ to typ ułamkowy Fraction. Powstaje gdy w czasie dzielenia biorą udział wartości całkowite. Użycie typu ułamkowego nie powoduje utraty precyzji związanej z zaokrągleniem.

a := 1 / 2
a -> (1/2)

a asFloat
a -> 0.5

Float to typ zmiennoprzecinkowy zgodny ze standardem  IEEE-754 . Zakres do +/- 10^307
Więcej  można zobaczyć przeglądając paczkę Kernel-Numbers.


Instrukcje warunkowe i pętle


W Smalltalku nie ma instrukcji warunkowych i pętli. Są tylko komunikaty i operacje na blokach. Np. znana z C instrukcja: 

if ( a > 0 ) a = a * 5; 

w Smalltalku przyjmuje postać: 

( a > 0 ) ifTrue: [ a := a * 5 ].  

- do zmiennej a przesyłana jest wiadomość '>' z parametrem 0. Obiekt a następnie wysyła nowy obiekt Boolean z ustawioną wartością true lub false. Do tego obiektu wysyłana jest wiadomość ifTrue: z blokiem [ a := a * 5 ] jako parametr. Jeżeli warunek jest spełniony obiekt Boolean wykona blok.
Pętle są realizowane w podobny sposób, np.: 
[ a > 0 ] whileTrue: [ a := a - 1 ].

 Tym razem to blok [ a > 0 ] otrzymuje komunikat whileTrue: z blokiem 
 [ a := a - 1 ] jako parametr. Trochę więcej o blokach:


Bloki

 

Blokiem jest kod zapakowany w nawiasy kwadratowe. Np.:


[ 1 + 2 ]

Blok zawsze zwraca sam siebie, chyba, że poprosimy o wartość (value):


blok := [ 1 + 2 ].
blok -> [ clojure ] in UndefinedObject
blok value. -> 3

Blok może przyjmować parametry:


sqrt := [ :a | ( a >= 0) ifTrue: [a sqrt]
                         ifFalse: [Complex new real: 0 imaginary: (a abs sqrt)]].

sqrt value: 4. -> 2
sqrt value: -4. -> 0 + 2i 

suma2arg := [:a :b | a + b ].
suma2arg value: 1 value: 2. -> 3

[:a :b | a + b ] value: 1 value: 2. -> 3

Kolejny szczegół: obiekt może mieć tylko jedną klasę bazową. Nie ma wielokrotnego dziedziczenia. Interpreter i kompilator Smalltaka są napisane w Smalltalku. Pozwala to uruchomić maszynę wirtualną wewnątrz maszyny wirtualnej. To tak jakby się dało uruchomić Javę w Javie.




Praca w Smalltalku jest pracą z całym systemem. Nie ma tu podziałów na programy. Programem, jest wynik modyfikacji systemu. 

Smalltalk nie potrzebuje systemu operacyjnego by działać. Są wersje edukacyjne dla dzieci, które bootują do Smalltalka. Jako ciekawostkę podam, że w innych, bardziej rozwiniętych krajach naukę programowania dla dzieci zaczyna się od Smalltalka. U nas zaczyna się od Logo. Nie wiem czyj to wymysł, ale znając ten kraj można się było tego spodziewać.

Tworzenie nowej klasy w Smalltalku to wysłanie komunikatu do obiektu, po którym chcemy dziedziczyć lub do podstawowego obiektu 'Object'. Nie tworzy się żadnego pliku tak jak w Javie. Oczywiście utworzony obiekt można zapisać do pliku, np w celu archiwizacji.
I ostatnia dość ciekawa cecha środowiska, której nie spotkałem w żadnym języku, czy IDE, czyli: wykonywanie kodu gdziekolwiek i kiedykolwiek. To cecha, która pozwala bardzo szybko testować kod. Słabą namiastką tego są tak zwane REPL w innych językach, czyli interaktywne konsole, gdzie kawałek kodu się kopiuje bądź wysyła i on tam jest wykonywany. Przykład: (Transcript to odpowiednik stdout).





Gdy chcę zobaczyć wynik operacji w okienku, którym piszę, używam opcji 'Print it'. Np.:

 

Teraz trochę o Seaside

Małe intro na początek:

Widać trochę machania myszką, ale to efekt usunięcia ścieżki dźwiękowej, która nie nadawała się do publikacji. 


Seaside jest oparty o model komponentowy. Obiektowość Smalltalka bardzo ułatwia ich tworzenie i edycję. Seaside pozbywa się także lub stara się pozbyć instrukcji GOTO. Znanej i pogardzanej instrukcji, która została wypędzona w programowaniu klasycznym dzięki licznym egzorcyzmom, a która w programowaniu webowym manifestuje się w tagu HREF. W Seaside kliknięcie w odnośnik na stronie powoduje wysłanie wiadomości do obiektu, który decyduje jak odpowiedzieć. Seaside nie wykorzystuje szablonów. Tutaj wszystko tworzy się w jednym języku. Chodzi o to, by jak najbardziej zbliżyć się w stylu tworzenia do aplikacji desktopowych.

Seaside posiada wbudowane i przezroczyste wsparcie dla jQuery i jQuery UI. Ajax jest bardziej bezbolesny niż w Ruby on Rails. Tutaj w ogóle można nie dotykać JavaScriptu.
Ogólne podejście z tego, co zauważyłem zamyka się w tworzeniu rdzenia aplikacji w Seaside, a reszta treści: multimedia, css i inny statyczny kontent serwuje się oddzielnie. Oczywiście można wciągnąć CSS i obrazki do obrazu aplikacji, ale o ile nie jest to pojedyncza aplikacja, to będzie tylko zajmować niepotrzebnie RAM.
W Seaside dzięki użyciu kontynuacji użycie przycisku przeglądarki 'Wstecz' i 'Następny' nie powoduje zaburzenia w działaniu aplikacji. 

Jako ciekawostkę dodam, że jest już maszyna wirtualna, która działa na Androidzie. Działa, bo sprawdziłem. ;) Polecam tablet 10 cali, bo będzie ciężko z rozdzielczością. Najlepiej ściągnąć i zainstalować samą maszynę wirtualną, a później skopiować plik .image i .changes na kartę SD. Aplikacja sama znajdzie pliki i wyświetli listę, z której można uruchomić obraz Seaside.

To rozwiązanie z Androidem jest o tyle dobre, że ten sam obraz można uruchomić na wielu systemach bez kompilacji. Nawet Java i inne języki tak nie potrafią. Przede wszystkim to zaleta Open Source, które to dzięki dostępności kodu źródłowego pozwoliło skompilować maszynę na Androida. BTW. Ktoś czeka aż Java od Oracle pojawi się na Androidzie? ;)