This blog is no longer being maintained, please go to The missing link of Agile instead

Saturday, July 19, 2008

Path-Dependent Types w Scali



Nie jestem specjalistą w dziedzinie języków, teorii typów etc. Zawarte w poniższym poście sformułowania mogą być nieprecyzyjne a czasem nawet mijać się z prawdą. Najważniejszym dla mnie celem jest pokazanie pragmatycznych problemów.



Cały czas poznaję Scalę i muszę powiedzieć, że niektóre konstrukcje/rozwiązania mogą przyprawić czasem o ból głowy.

Ciemna strona mocy - czyli typy zależne od ścieżki (PDT - path-dependent types). Całość wynika z możliwości definiowania struktur wewnętrznych praktycznie bez ograniczeń. Można więc napisać:

trait A {
type X
class B {
trait C {
type Y
}
case class D {
}
}
}

"So far, so good" - można bowiem znaleźć analogię do podobnych konstrukcji z Javy, gdzie mamy 'member classes'. Nie dajmy się jednak zwieść pozorom - w Javie 'member class' jest określona w definicji typu i nie interesuje nas, przez jaką zmienną tam się dostaniemy. Raz zdefiniowana klasa wewnętrzna zawsze "pozostanie sobą". Nie ma więc problemu żeby napisać coś takiego:

abstract class A {
abstract class B {
abstract void and(B b);
}

abstract B foo();
}

abstract class C extends A {
A a;
{
a.foo().and(foo());
}
}

Wystarczy jednak, że zmienimy nieco składnię (i język programowania ;)), żeby uzyskać coś takiego...

abstract class A {
abstract class B {
def and(b : B)
}

def foo : B
}

abstract class C extends A {
val a : A

{
a.foo and foo
}
}

... plus piękny Syntax error:

type mismatch;
found : C.this.B
required: C.this.a.B

Niestety moja mała wiedza na temat Scali nie pozwala mi na ominięcie tego problemu inaczej niż przez zastosowanie brzydkiego hacka: "a.foo.asInstanceOf[B] and foo". Powyższa kwestia niestety nie jest ani czysto abstrakcyjna, ani wydumana. Wystarczy, że zaczniemy stosować w Scali pewne idomatyczne rozwiązania (zwłaszcza pewien rodzaj agregacji typów w bibliotekach - p. Parsers). Okazuje się, że będziemy wtedy niemal zmuszeni przy bardziej skomplikowanych strukturach do zastosowania dziedziczenia zamiast delegacji. Wynik nie jest ani ładny ani składny.

Monday, July 07, 2008

Krytycznie o Selenium

Na blogu Jaya Fieldsa pojawił się świetny krytyczny wpis dotyczący Selenium:
The Immaturity of In Browser Testing
Chciałoby się rzec "Amen to this brother !"

Saturday, July 05, 2008

Polyglot programming - słowa kluczowe vs API

Ponieważ ostatnio dużo się bawię Scalą przy rozpoznawaniu technologii często używam jej obok Javy. Z dwóch najnowszych (kilim i Neodatis) ciekawe wnioski - nazwy metod użyte w API są słowami kluczowymi w Scali (odpowiednio: yield oraz match). W pierwszym przypadku problem łatwo rozwiązać poprzez wprowadzenie prostego dekoratora po stronie Javy. W Neodatis jednak dość niefortunnie wykorzystano refleksję (getDeclaredMethods) przez co nie udało mi się w ogóle tego obejść. O ile można mieć wątpliwości co do poprawności stosowanego przez Neodatis rozwiązania - problem pozostaje, i choć może nie jest krytyczny sądzę że musimy brać go pod uwagę.

Znaczenie języków ponoć zmniejsza się na rzecz VM'ów - podejrzewam że napotkany problem słów kluczowych nie jest jedynym z jakim musimy sobie poradzić jeśli dalej będziemy podążać tą scieżką.

Thursday, June 26, 2008

Scala bez kontraktów...

Pisząc w Scali mam wrażenie, że ciężko byłoby mi w tym języku znaleźć konstrukcji czy rozwiązań, które mi znacząco przeszkadzają - nawet jeśli sam język przeraża po pierwszym czytaniu specyfikacji. W pewnym stopniu zgadzam się z poniższą opinią Steva Yegge:
"The... the the the... the language spec... oh, my god. I've gotta blog about this. It's, like, ninety percent [about the type system]. It's the biggest type system you've ever seen in your life, by 5x. Not by an order of magnitude, but man! There are type types, and type type types; there's complexity..."

Jestem jednak chyba większym od niego optymistą i uważam, że o ile można uznać Scalę za "onieśmielającą" to spójność języka pozwala na sukcesywne jego opanowywanie. Nie sądzę też, że programy stworzone za jego pomocą będą magiczne i niezrozumiałe.. Przede wszystkim uwzględnić trzeba w tym równaniu ekspresywność języka, która pozwoli zmniejszyć LOC tworzonych rozwiązań conajmniej dwukrotnie ("The Scala Experiment", system obsługi konferencji).

Mogę jednak z całą pewnością stwierdzić, że jest element, który został przy implementacji języka pominięty - a ja jestem bardzo niemile zaskoczony, że Odersky nie zdecydował się na jego zawarcie. Mowa o znanym z Eiffel'a i lubianym DBC (Design by Contract).

Brak zainteresowania DBC przez twórców języków dziwi mnie niesamowicie od czasu przeczytania "Object Oriented Software Construction" 2nd edition, Bertranda Meyera. Pamiętam, że rozdział o powyższym mechanizmie czytałem niemalże z wypiekami na twarzy.. Na początku, kiedy uczyłem się programowania obiektowego - interfejsów, klas abstrakcyjnych, kontrakt oparty o te konstrukcje wydawał mi się nadzwyczaj abstrakcyjny. Meyer pokazał, że moje obawy były jak najbardziej uzasadnione - bo przecież ile wart jest kontrakt bez opisu semantyki?! (Tyle ile waży ;])

Po latach pomysł wprowadzenia DBC pojawił się w Javie - okrojony i brzydszy - jako JSR 305 Annotations for Software Defect Detection. Mogę ostatecznie zaakceptować, że Java kontynuując strategię wstecznej zgodności przyjęła takie rozwiązanie. Nie potrafię jednak zrozumieć dlaczego DBC nie jest wbudowanym mechanizmem językowym w Scali. Scala już jest językiem przepełnionym konstrukcjami - ale co ważne spójnym! I tak długo jak spójność będzie zachowana warto dodać przełomowe według mnie rozwiązanie, jakim jest DBC. Niech stoją za mną słowa Meyera:
"This comment is relevant because Ada, although undoubtedly a “big language”, differs from others in that category by clearly showing (even to its critics) that it was designed and has little gratuitous featurism. As with other serious languages, the whole design is driven by a few powerful ideas, and every feature has a rational justification. You may disagree with some of these ideas, contest some of the justifications, and dislike some of the features, but it would be unfair to deny the consistency of the edifice. Consistency is indeed the key here: size, however defined, is a measure, but consistency is the goal."

Należy wspomnieć, że istnieje biblioteka umożliwiająca DBC dla Scali. Jest to jednak według mnie absolutnie niewystarczające (1). Przede wszystkim DBC powinno być wreszcie porządnie rozreklamowane.. choćby na przykładzie Ruby'ego widać, że bez dobrego marketingu (jakim dla niego był Ruby on Rails) nawet bardzo dobra technologia nie ma dużych na szans na popularyzację. Druga rzecz, że DBC można zbudować nawet na asercjach... to co jednak zaoferował w Eiffel'u Meyer - czyli silne wyabstrahowanie koncepcji poprzez wyróżnienie warunków pre i post oraz inwariantów, łącznie z ich dziedziczeniem (!!) jest ważnym krokiem w zapewnianiu jakości przy użyciu kontraktów.

DBC ma swoje wady i zalety (co nie ma ...) jednak czas wreszcie zauważyć, że w kolejce na listę popularnych konstrukcji językowych stoi już długo i jak najbardziej zasługuje żeby się tam dostać.

P.S. Tak sobie jeszcze pomyślałem - czy BDD nie ma więcej wspólnego z DBC niż by się nam zdawało....

Thursday, June 12, 2008

XML considered harmful

Albo udziela mi się nastrój sesji, albo po prostu od czasu do czasu muszę na coś ponarzekać.. mam jednak nadzieję, że do powstania poniższego 'ranta' przyczynił się raczej 'przewlekłe uprzedzenie' do formatu XML.

Kilka dni temu napisałem w Scali małą aplikację do generowania danych testowych i wypełniania nimi bazy danych. Wybór Scali także jako formatu specyfikacji danych bynajmniej nie był spowodowany niechęcią do XML'a. Powodem mojej decyzji był najzwyklejszy w świecie pragmatyzm. Potrzebowałem języka ekspresywnego, zwięzłego i elastycznego (1) - piwo dla osoby, która udowodni obecność w XML'u choćby jednej z wymienionych cech. Najwyraźniej mówi się o tym zbyt rzadko, bo tęże technologię wybrał autor rozwiązania na którym w dużej mierze się oparłem (2). Podczas gdy specyfikacja w Scali:

  • zajmuje 2x mniej linii kodu (3)

  • jest o niebo czytelniejsza

  • udostępnia dodatkową funkcjonalność (niemożliwe do zapisu w XML !)


Wydaje się, że jedynym powodem dla którego wybieramy XML'a w każdym-kolejnym-projekcie jest jego popularność. Tej "zalety" z całą pewnością nie można mu zarzucić: Ant, Maven, Spring, Web-Service'y itd., etc... lista z pewnością zajęłaby więcej niż drugie wydanie "Object-oriented software construction" Meyer'a. Nie zdziwił bym się nawet gdyby od jutra tabelki wartości odżywczych na opakowaniach zaczętoby zapisywać w XML'u...

Z czasem okazuje się, że zrozumienie builda ant'owego dla większego projektu graniczy z cudem (jego maintenance wymaga wielkiego męstwa i odwagi), a edycja xml'i spring'owych bez plugin'a zasługuje na medal. Żeby XML wciąż spełniał nasze oczekiwania zaczyna się go wypełniać dodatkowymi elementami - np. OpenLaszlo i JavaScript - po czym okazuje się, że stosowany przez nas format danych to Java z duża ilością operatorów relacyjnych i jeszcze bardziej rozwlekłą składnią (a jednak się da !).

To zabrzmi jak truizm, ale: XML nie jest najlepszym rozwiązaniem dla wszystkich problemów.. powiem więcej, sądzę, że w 80% przypadków (liczba dobrana celowo) mógłby być z ogromną korzyścią dla projektów zastąpiony inną technologią. Pomyśleć tylko o projektach na użytek własny, specyfikacjach (do generacji kodu, translacji), projektach wymagających dużej ekspresywności (Ant vs Gant !!).

Życzę wszystkim programistom, by przy wyborze technologii towarzyszyło Wam hasło: "Wszędzie gdzie można wykorzystać XML'a, użyć można n+1 technologii bardziej wydajnych/wygodnych/seksownych (niepotrzebne skreślić)."

1. Przyjmuję, że w ocenie elastyczności uwzględnia się także proces parsowania języka.
2. Porównanie wyłącznie w celach dramatycznych - nie ma na celu krytyki autora aplikacji.
3. Zastosowałem standardowe wcięcia - priotytem dla obu formatów była czytelność.

Thursday, June 05, 2008

Scala Frenzy

W nocy z wtorku na środę miałem plan, aby wreszcie podgonić zaległości z projektu (Projektowanie Systemów Informatycznych). Niestety.. o 18 'na chwilkę' włączyłem projekt w Scali i na tym skończyły się moje ambitne plany :). W rezultacie tejże nocy powstała mała biblioteka do wypełniania danymi testowymi bazy danych, z której stworzeniem nosiłem się od pewnego czasu.

Jest to mój pierwszy projekt w Scali i muszę powiedzieć, że tkwi w tym języku niewiarygodny potencjał. Całość powstała bez ani jednej linijki kodu testowego - co samo w sobie nie jest powodem do dumy :P - ale dzięki temu udało mi się docenić łatwość z jaką można tworzyć w Scali bezbłędny (!) wysokiej jakości kod. Jest coś w tym języku (mój wykładowca dr Spławski twierdził, że to cecha funkcyjnych językow w ogóle), że jeśli już uda się program skompilować to z dużym prawdopodobieństwem będzie on działał poprawnie. Co jak co, ale trudno mi taką opinię wyrazić o Javie ;)

Poniżej zamieszczam bodaj najciekawszy fragment biblioteki, pokazujący piękno Scali.

def newTuple = { table.columns.transform { (k, v) => v.generateValue } }
var tuple = newTuple;

while (
constraintFunctions.
map ( f => f(list, tuple) ).
reduceLeft ( _ || _ ))
{ tuple = newTuple; }

Celem powyższego jest generowanie krotek tak długo, aż wszystkie podane przez użytkownika funkcje sprawdzające czy naruszono ograniczenie zwrócą 'fałsz'. Może być w tym jakieś szaleństwo (czy narcyzm ;] ), ale nie mogę się napatrzeć na powyższy fragment kodu, który niemalże zabija prostotą.

Po całej nocy kodowania udało mi się stworzyć stosunkowo spójną całość. Przerzuciłem istniejący kod generujący dane testowe z wyżej wspomnianego projektu do własnego rozwiązania, dodając kilka funkcjonalności których nie mogłem uzyskać we wcześniej używanym narzędziu. Efekt okazał się na tyle zadowolający, że postanowiłem udostępnić narzędzie jako Open Source i dalej rozbudowywać jego możliwości :)

Projekt jest dostępny na SourceForge:
http://sourceforge.net/projects/dage/.

Sunday, May 25, 2008

Psucie XML'a

Reader discretion is advised. (1)

Na wiki u Jacka Laskowskiego pojawił się artykuł o JAXB, co pozwoliło mi po raz drugi w życiu na szybkie przyjrzenie się tej technologii. Kwestią, która przyciągnęła moja uwagę jest nazewnictwo elementów przy marshallingu kolekcji. Nazwa elementów XML odpowiada nazwie kolekcji w Javie, co daje dość śmieszny efekt w postaci:

<opakowanie>
<listaElementow>
..
</listaElementow>

<listaElementow>
..
</listaElementow>
</opakowanie>


Wydaje się, że po raz kolejny czkawką odbija się sposób implementacji generyków w Javie. Zdecydowanie lepiej, gdyby nazwy elementów były wnioskowane na podstawie typu generycznego - jeśli byłoby to możliwe :P. W jednym z projektów, w których uczestniczyłem, używano właśnie JAXB i pamiętam, że taki sposób implementacji nazw powodował niejednokrotnie zakłopotanie programistów (klientów) korzystających z tworzonego XML'a.

Posunę się do stwierdzenia, że takie traktowanie formatu XML jest niszczeniem intencji jego istnienia, a przynajmniej ważnej części jakim jest semantyka XML. Ponoć XML jest formatem samo-opisującym. Jeśli jednak będziemy go "wykrzywiać" i dopasowywać do ułomności technologicznych nie jestem pewien czy po pewnym czasie XML wciąż będzie zdatny do użytku..

1) Wyolbrzymienie problemu jest celowe.

Wednesday, May 14, 2008

Facet to świnia ?!

Podczas przygotowywania się do dzisiejszego seminarium - jak każdy porządny student zacząłem 6h przed wydarzeniem ;) - potrzebowałem dokładnie sprawdzić jak brzmi poprawna nazwa zasady Liskova. Mnie osobiście najbardziej podoba się nazwa 'zasada substytucji', ale jak to w nauce nie liczy się to co się komu podoba ale jak jest :]. Jakież było moje zdziwienie gdy natrafiłem na ten post: http://www.3jane.pl/2006/10/11/jestem-niezyciowa/.

Otóż okazuje się, że 'Zasada substytucji' nie jest Liskova, ale Liskovej ! Autorem elementarnej dla programowania obiektowego zasady jest bowiem pani Barbara Liskov, dla potwierdzenia: http://en.wikipedia.org/wiki/Barbara_Liskov. Muszę się w tym momencie uderzyć w pierś, ponieważ i ja niejednokrotnie zawiniłem cytując źródła, zawsze zakładając że autorem jest jakiś Pan.

A potem dziwią się wszyscy, że wśród inżynierów informatyków tak mało kobiet. Wstyd panowie, wstyd !

Friday, May 02, 2008

Blog - reaktywacja

Ostatni post ukazał się w listopadzie zeszłego roku, tak więc trudno mówić o jakiejkolwiek ciągłości :P Tamten okres był jednak stosunkowo pracowity, więc mam nadzieję że kilka miesięcy po jego zakończeniu będę w stanie "zmusić się" do regularnego pisania :) Co więcej odkryłem w tym czasie, że ostatnio zaprezentowany pomysł okazał się reifikacją (hehe Holub rox ;) ) wzorca projektowego State (z domieszką Special Case'a).

A co natchnęło mnie do powrotu na bloga - otóż znalazłem buga w Javie :), a dokładniej w implementacji introspekcji (java.beans.*). Na problem natknąłem się rozwijając aplikację webową, co tylko utrudniło wyodrębnienie go, ale w końcu się udało ! Na początek trochę kodu:

public interface ITest<T> {
public T getDelta();
}

public class Foo implements ITest<String> {
private List<String> alfa;
private String beta;
private String gamma;

public List<String> getAlfa() {
return alfa;
}

public void setAlfa(final List<String> alfa) {
this.alfa = alfa;
}

public String getBeta() {
return beta;
}

public void setBeta(final String beta) {
this.beta = beta;
}

public String getDelta() {
return gamma;
}

public void setDelta(final String gamma) {
this.gamma = gamma;
}
}

public class Bar implements ITest<String> {
private List<String> alfa;
private String beta;
private String gamma;

public List<String> getAlfa() {
return alfa;
}

public void setAlfa(final List<String> alfa) {
this.alfa = alfa;
}

public String getGamma() {
return gamma;
}

public void setGamma(final String gamma) {
this.gamma = gamma;
}

public String getBeta() {
return beta;
}

public void setBeta(final String beta) {
this.beta = beta;
}

public String getDelta() {
return gamma;
}

public void setDelta(String gamma) {
this.gamma = gamma;
}
}


I najważniejsza część - kod testujący. Pobiera deskryptory pól dla każdej z klas, a następnie wypisuje metody zapisu dla poszczególnych deskryptorów. Mowiąc prościej kod wyświetla settery zgodne ze specyfikacją JavaBeans (jeśli nie ma - wypisze 'null'):

Dla ułatwienia będę podawał wartości tylko dla property: "delta".

Po wywołaniu kodu powyżej otrzymamy wynik:

"delta null" (dla Foo)
"delta null" (dla Bar)

Po zamianie wywołań dostaniemy:

"delta public void pl.test.Bar.setDelta(java.lang.String)"
"delta null"

Po zakomentowaniu części "Dla Foo" wynik będzie:

"delta public void pl.test.Bar.setDelta(java.lang.String)"

Natomiast po zakomentowaniu części "Dla Bar" na konsoli pojawi się znowu:

"delta null"

Szczerze mówiąc powyższe wyniki są dla mnie kompletnie niedeterministyczne ;D. Mogę jedynie domniemywać, że ma to jakiś związek z typami generycznymi i cache'owaniem przy introspekcji. Strzelałbym nawet, że w tym przypadku łączy się więcej niż jeden bug, stąd dość "ciekawe" rezultaty". Biorąc nawet pod uwagę, że są znane problemy na styku JavaBeans i generyków1 powyższe wyniki wydają się definitywnie zbyt "pseudo-losowe" ;).

Wszystko będzie ok jeśli:
  • zarówno 'getter' i 'setter' jest generyczny i dziedziczony,
  • i co ciekawe ... 'setter' jest generyczny i dziedziczony
Potencjalny work-around - unikać rozwiązania jak powyżej :P.

1. Warto popatrzeć na następujące zgłoszenia:
http://bugs.sun.com/view_bug.do?bug_id=6422403
http://bugs.sun.com/view_bug.do?bug_id=6473468
http://bugs.sun.com/view_bug.do?bug_id=5098163