Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: python vs php - potrzebne argumenty

6 views
Skip to first unread message

Piotr Sawicki

unread,
Dec 2, 2009, 11:03:14 AM12/2/09
to
Sebastian Kaliszewski powiada:

> Zobacz jak wygląda standardowa callcc (uups, w 1.9 jest w pewnym sensie
> "deprecated", trzeba ją specjalnie aktywować, bo poważnie spowalnia
> interpreter). "Kultura języka" zachęca do zapisania kontynuacji w
> zmiennej globalnej.

Używanie zmiennych globalnych to nie jest problem nad wykryciem
którego trzeba ślęczeć godzinami, widać od razu po kodzie.

>> W Lispie czy Smalltalku jakoś ma sens.
>
> Mowa o wartości dodanej w postaci statycznej kontroli obsłużenia błędów.
> Po co naglke statyczna kontrola w języku dynamicznym.

To akurat proste: żeby zapobiec problemom zanim wyjdą w praniu.

> Lisp jest stary jak progtramowanie wysokopoziomowe.
> W Smalltalku -- kolejnym minimalistycznym języku --
> kontynuacje służą do paru innych rzeczy,

Służą m.in. do obsługi błędów właśnie. Nie bardzo rozumiem co masz
na myśli - kontrprzykłady są niewłaściwe z powodu wieku i minimalizmu?

>>>> Stosowanie wyjątków, przy wszystkich zaletach, powoduje że nietrywialne
>>>> staje się statyczne sprawdzenie, czy obsłużono wszystkie rzucane błędy.
>>> Eee.. jest tak samo trywialne/nietrywialne jak w przypadku monad /
>>> kontynuacji.
>>
>> No właśnie nie - pod tym względem wyjątki nie są łatwiejsze w pilnowaniu
>> nawet od obsługi błędów przez zwracanie funkcją kodu błędu i sprawdzaniu
>> tego ifami. Tzn. w najlepszym przypadku można każdy call obstawić własnym
>> try-catchem (co podpada już pod brzytwę Ockhama).
>
> Po pierwsze właśnie są przez to, że nie trzeba każdego calla obstawiać
> własnym try-except (przypominam, że to grupa pythonowa a nie
> C-cokolwiek) jest łatwiej.

Łatwiej jest napisać badziewiasty kod, po którym kompletnie nie wiadomo
czy obsługę sytuacji wyjątkowych już zaimplementowano i czy prawidłowo.

> Po drugie nadal pytam o wartość dodaną kontynuacji pod tym względem?

Proste pilnowanie obsługi błędów.

>>> try:
>>> whole_program()
>>> except BaseException:
>>> wszystko_obsłużone()
>>>
>>> Mamy wszystko obsłużone ale do prawdy niewiele z tego
>>> "wszystko-obsłużenia" wynika.
>>
>> Przecież to nie jest obsługa błędów.
>
> Przecież tyle samo daje zapisanie sobie kontynuacji "gdzieś globlanie".

Podałeś przypadek w którym każdy wariant jest równie zły,
to nie stanowi argumentu w żadną stronę.

> nicym nie różni się od statycznej kontroli specyfikacji wyjątków
> (włączenia specyfikacji wyjątków do statycznie sprawdzanej sygnatury
> funkcji) w Javie. To co jest w Javie jest dość powszechnie krytykowane,
> bo poza wymuszaniem dodatkowego klepania sprowadza się do konstrukcji
> jak powyżej,

Dlatego napisałem że korzyści widzę w zapewnianiu jakości (a Ty ich
wypatrujesz w kodowaniu). Zawsze można źle zaprogramować, tylko że
w przypadku niejawnego przepływu sterowania bardzo trudno to wychwycić
i jest masa roboty z poprawianiem, wymaga to analizy całego kodu.

>> Widziałem np. narzekania na szkodliwy wpływ nauki Pythona i Haskella
>> na wydajność pisania w C# - uzasadniany tym, że C# co prawda jest
>> całkiem niezły i ma dużo zaawansowanych cech, ale sabotażują ich
>> wykorzystanie imperatywnie zaprojektowane biblioteki.
>
> To samo masz w światku popularnych języków dynamicznych. W Rubim
> wycofują się rakiem, do Pythona nie dodali (IMHO słusznie, bo ciągle nie
> mogę dojrzeć tej wartości dodanej).

To akurat było o funkcyjności i braku efektów ubocznych, a chodziło
nie o wykorzystanie komercyjne tylko dydaktyczne (że pobawienie się
dobrym językiem powinno uczyć dobrych nawyków w czym tam musisz klepać).

> Cóż, Haskell to taki język którego czasem ktoś sobie dla zabawy użyje,
> ale niezabawowe użycia praktycznie poza akademię nie wychodzą. Od lat.

Jeśli powodu upatrujesz w cechach języka, to jak w ramach tej
teorii wyjaśniasz niezabawowe praktyczne użycie Erlanga?

>> D nie znam, Adę inaczej kwalifikuję, pozostałe są zrealizowane
>> (AFAIK) właśnie metodą ceplusplusową, alias obleśną.
>
> Co rozumiesz przez metodę Ceplusplusową? Składnię, semantykę?

Dla mnie to jest pełen bajzel na każdym poziomie (sorry, NTG żeby
się rozpisywać - Qrczak kiedyś wyliczył bzdury i wyszła cała epopeja).

> polimorfizm statyczny w statycznym języku jako taki sens ma.

Zgoda, ale jakoś np. w OCamlu dało się to zrobić normalnie
w języku, a nie na poziomie niemal preprocesora.

>>> Teraz (czytaj od nastu lat) są Turing complete (modulo dopuszczane przez
>>> standard ograniczenia głębokości rekursji do baardzo płytkiego poziomu)
>>> z całkowicie porąbaną drogą uzyskania tego Turing completeness.
>>
>> Tak jakby nie wystarczył do tego system typów.
>
> Przecież to jest część systemu typów :)

Mam inne zdanie, ale teoria typów jest na takim poziomie
ezoteryki że nie ma się co spierać o etykietki. W każdym razie:
wolę język od początku dobrze zaprojektowany od sztucznie
połatanego metodą doktora Frankensteina.

>>> Innymi słowy -- teraz jest to bardzo silny mechanizm stricte statyczny,
>>> tylko składnię ma kompletnie, absolutnie do bani. Wynikło to z tego, że
>>> nikt go nie zaprojektował pod kątem tej siły -- ot wstawiono kilka cech
>>> które różnym ludziom gdzies tam były przydatne czy gdzie indziej
>>> wygodnie się implementowały, ale ich suma okazała się istotnie silniejsza.
>>
>> Fajnie, ale taka siła to jest potrzebna do metaprogramowania - a tego
>> się w praktyce nie da zrobić w c++.
>
> W praktyce to robią.

Tzn. jaki konkretny software tak napisano, oprócz tej biblioteki
protezującej ułomność języka?

>> Całe to przerośnięte monstrum szablonowe jest używane głównie
>> do uzyskania polimorfizmu, a jak ktoś chce skorzystać
>> z metaprogramowania to pisze w czymś lispowatym z makrami.
>
> No właśnie coraz więcej różnych kolesi metaprogramuje w C++.
> Zobacz sobie boost::lambda [...] a zwłaszcza boost::mpl

Zaraz zaraz, używanie lambd czy foralli to nie jest metaprogramowanie.
W innych językach masz takie cechy z buta, a tu do języka kulawego
dodano łatki i tylko one są zametaprogramowane. Żeby nie było -
przekonałeś mnie, że szablony do czegoś w ogóle się przydały.

>> W obronę Javy (jako języka a nie platformy) mnie nie wciągniesz :).
>
> Platforma też jest kiepska :)

Musisz przemyśleć swój argument z liczbą programistów :).


Piotr Sawicki

Sebastian Kaliszewski

unread,
Dec 2, 2009, 1:52:53 PM12/2/09
to
Piotr Sawicki wrote:
> Sebastian Kaliszewski powiada:
>
>> Zobacz jak wygląda standardowa callcc (uups, w 1.9 jest w pewnym sensie
>> "deprecated", trzeba ją specjalnie aktywować, bo poważnie spowalnia
>> interpreter). "Kultura języka" zachęca do zapisania kontynuacji w
>> zmiennej globalnej.
>
> Używanie zmiennych globalnych to nie jest problem nad wykryciem
> którego trzeba ślęczeć godzinami, widać od razu po kodzie.

W miejscu użycia (mówimy o typowych przedstawicielach języków
dynamicznych -- w każdym z nich możesz sobie zmienną globalną łatwo
utworzyć lokalnie). Brak try/except widać tak samo.

>>> W Lispie czy Smalltalku jakoś ma sens.
>> Mowa o wartości dodanej w postaci statycznej kontroli obsłużenia błędów.
>> Po co naglke statyczna kontrola w języku dynamicznym.
>
> To akurat proste: żeby zapobiec problemom zanim wyjdą w praniu.

To po co w ogóle język dynamiczny?

Chcesz mieć ciastko i zjeść ciastko...


>> Lisp jest stary jak progtramowanie wysokopoziomowe.
>> W Smalltalku -- kolejnym minimalistycznym języku --
>> kontynuacje służą do paru innych rzeczy,
>
> Służą m.in. do obsługi błędów właśnie.

Służą do wielu innych rzeczy, w tym paru pierwszorzędnych (jak
podstawowe konstrukcje sterujące, błędy to idą "przy okazji")

> Nie bardzo rozumiem co masz
> na myśli - kontrprzykłady są niewłaściwe z powodu wieku i minimalizmu?

To mam na myśli, że pełnią rolę konstrukcji podstawowej -- w jednych
językach może ją pełnić if i goto, w innych wywołanie (rekurencyjne)
funkcji oraz pattern matching, w innych while/if/for, w innych
SLD-rezolucja, itd...

>
>>>>> Stosowanie wyjątków, przy wszystkich zaletach, powoduje że nietrywialne
>>>>> staje się statyczne sprawdzenie, czy obsłużono wszystkie rzucane błędy.
>>>> Eee.. jest tak samo trywialne/nietrywialne jak w przypadku monad /
>>>> kontynuacji.
>>> No właśnie nie - pod tym względem wyjątki nie są łatwiejsze w pilnowaniu
>>> nawet od obsługi błędów przez zwracanie funkcją kodu błędu i sprawdzaniu
>>> tego ifami. Tzn. w najlepszym przypadku można każdy call obstawić własnym
>>> try-catchem (co podpada już pod brzytwę Ockhama).
>> Po pierwsze właśnie są przez to, że nie trzeba każdego calla obstawiać
>> własnym try-except (przypominam, że to grupa pythonowa a nie
>> C-cokolwiek) jest łatwiej.
>
> Łatwiej jest napisać badziewiasty kod, po którym kompletnie nie wiadomo
> czy obsługę sytuacji wyjątkowych już zaimplementowano i czy prawidłowo.

To samo masz w pozostałych przypadkach. Od kilku postów nie mogę się
doprosić pokazania wartości dodanej.

>
>> Po drugie nadal pytam o wartość dodaną kontynuacji pod tym względem?
>
> Proste pilnowanie obsługi błędów.

Przecież ci pokazałem jak to (nie) działa. Tzn. rółnie łatwo jest to
zrobić zupełnie do bani, choć formalnie poprawnie.

>
>>>> try:
>>>> whole_program()
>>>> except BaseException:
>>>> wszystko_obsłużone()
>>>>
>>>> Mamy wszystko obsłużone ale do prawdy niewiele z tego
>>>> "wszystko-obsłużenia" wynika.
>>> Przecież to nie jest obsługa błędów.
>> Przecież tyle samo daje zapisanie sobie kontynuacji "gdzieś globlanie".
>
> Podałeś przypadek w którym każdy wariant jest równie zły,
> to nie stanowi argumentu w żadną stronę.

To pokaż przypadek w którym jeden jest istotnie lepszy.

>
>> nicym nie różni się od statycznej kontroli specyfikacji wyjątków
>> (włączenia specyfikacji wyjątków do statycznie sprawdzanej sygnatury
>> funkcji) w Javie. To co jest w Javie jest dość powszechnie krytykowane,
>> bo poza wymuszaniem dodatkowego klepania sprowadza się do konstrukcji
>> jak powyżej,
>
> Dlatego napisałem że korzyści widzę w zapewnianiu jakości (a Ty ich
> wypatrujesz w kodowaniu). Zawsze można źle zaprogramować, tylko że
> w przypadku niejawnego przepływu sterowania bardzo trudno to wychwycić
> i jest masa roboty z poprawianiem, wymaga to analizy całego kodu.

W Javie masz jawny przepływ sterowania -- wyjątki są częścią sygnatur
funkcji. I jak widać to marnie działa i sprowadzane jest bez
najmniejszego trudu do przypadku try { whole_program(); } catch
(everything).


>>> Widziałem np. narzekania na szkodliwy wpływ nauki Pythona i Haskella
>>> na wydajność pisania w C# - uzasadniany tym, że C# co prawda jest
>>> całkiem niezły i ma dużo zaawansowanych cech, ale sabotażują ich
>>> wykorzystanie imperatywnie zaprojektowane biblioteki.
>> To samo masz w światku popularnych języków dynamicznych. W Rubim
>> wycofują się rakiem, do Pythona nie dodali (IMHO słusznie, bo ciągle nie
>> mogę dojrzeć tej wartości dodanej).
>
> To akurat było o funkcyjności i braku efektów ubocznych, a chodziło
> nie o wykorzystanie komercyjne tylko dydaktyczne (że pobawienie się
> dobrym językiem powinno uczyć dobrych nawyków w czym tam musisz klepać).
>
>> Cóż, Haskell to taki język którego czasem ktoś sobie dla zabawy użyje,
>> ale niezabawowe użycia praktycznie poza akademię nie wychodzą. Od lat.
>
> Jeśli powodu upatrujesz w cechach języka, to jak w ramach tej
> teorii wyjaśniasz niezabawowe praktyczne użycie Erlanga?

Erlang jest nieco bardziej "praktyczny" w użyciu. Choć i tak to użycie
bardzo niszowe jest(było).


>>> D nie znam, Adę inaczej kwalifikuję, pozostałe są zrealizowane
>>> (AFAIK) właśnie metodą ceplusplusową, alias obleśną.
>> Co rozumiesz przez metodę Ceplusplusową? Składnię, semantykę?
>
> Dla mnie to jest pełen bajzel na każdym poziomie (sorry, NTG żeby
> się rozpisywać - Qrczak kiedyś wyliczył bzdury i wyszła cała epopeja).

Jaka jest wspólnota rozwiazania z Javy z rozwiązaniem z C++? To
rozwiązanie z Javy to poza składnią ma niewiele wspólnego z tym z C++ --
jest też do bani, ale jest do banie w zupełnie inny sposób.

>
>> polimorfizm statyczny w statycznym języku jako taki sens ma.
>
> Zgoda, ale jakoś np. w OCamlu dało się to zrobić normalnie
> w języku, a nie na poziomie niemal preprocesora.

Nnno, ten preprocesor to juz się taki prawie jak w Lispie zrobił (tam
też są makra :) ). Tylko jeszcze trudniejszy w użyciu jest.


>>>> Teraz (czytaj od nastu lat) są Turing complete (modulo dopuszczane przez
>>>> standard ograniczenia głębokości rekursji do baardzo płytkiego poziomu)
>>>> z całkowicie porąbaną drogą uzyskania tego Turing completeness.
>>> Tak jakby nie wystarczył do tego system typów.
>> Przecież to jest część systemu typów :)
>
> Mam inne zdanie, ale teoria typów jest na takim poziomie
> ezoteryki że nie ma się co spierać o etykietki. W każdym razie:
> wolę język od początku dobrze zaprojektowany od sztucznie
> połatanego metodą doktora Frankensteina.

Też wolę. C++ bronić nie zamierzam. Oprócz stwierdzenia faktu, że toto
jest popularne (chyba głównie przez przewidywalną i bardzo przyzwoitą
wydajność połączoną z dostępnością wszelakich bibliotek). I oprócz
stwierdzenia faktu, że templaty to już za preprocesor daleko wyszły.


>>>> Innymi słowy -- teraz jest to bardzo silny mechanizm stricte statyczny,
>>>> tylko składnię ma kompletnie, absolutnie do bani. Wynikło to z tego, że
>>>> nikt go nie zaprojektował pod kątem tej siły -- ot wstawiono kilka cech
>>>> które różnym ludziom gdzies tam były przydatne czy gdzie indziej
>>>> wygodnie się implementowały, ale ich suma okazała się istotnie silniejsza.
>>> Fajnie, ale taka siła to jest potrzebna do metaprogramowania - a tego
>>> się w praktyce nie da zrobić w c++.
>> W praktyce to robią.
>
> Tzn. jaki konkretny software tak napisano, oprócz tej biblioteki
> protezującej ułomność języka?

A owszem. Całkiem dużo softu tych bibliotek używa.

Jeden gościu (całkiem rozsądny człowiek) opisywał jak użył tego w jakiś
cudach z okolic klasyfikacji, dataminingu, itp. w bardzo poważnym sofcie
(jakieś zarządzanie ruchem na autostradach czy coś), który to soft miał
przy okazji ostre wymaganie real time.

Choć tego nie lubię, bo z uwagi na ciężkie porąbanie jest wredne w
utrzymaniu, też w paru miejscach użyć musiałem (pisanie kodu na około
byłoby jeszcze gorsze)

>
>>> Całe to przerośnięte monstrum szablonowe jest używane głównie
>>> do uzyskania polimorfizmu, a jak ktoś chce skorzystać
>>> z metaprogramowania to pisze w czymś lispowatym z makrami.
>> No właśnie coraz więcej różnych kolesi metaprogramuje w C++.
>> Zobacz sobie boost::lambda [...] a zwłaszcza boost::mpl
>
> Zaraz zaraz, używanie lambd czy foralli to nie jest metaprogramowanie.

Ale ich napisanie w języku w których ich nie ma już jest :)

Poza tym mpl to ma mało wspólnego z forallem (poza tym, że
metaprogramowaego foralla implementuje).

> W innych językach masz takie cechy z buta, a tu do języka kulawego
> dodano łatki i tylko one są zametaprogramowane. Żeby nie było -
> przekonałeś mnie, że szablony do czegoś w ogóle się przydały.

boost::mpl istotnie zawiera takie rzeczy jak metakonstrukcje sterujące,
jakieś nawet biedne metafunkcje itp. Wszystko to brzydkie w użyciu,
kompiluje się niezwykle wolno i pamięciożernie a jak poleci błąd
kompilacji to nie wiadomo gdzie się chować. Ale jest i ma paru
zagorzałych zwolenników (niedawno był na pl.comp.lang.c spory flejm na
temat wspaniałości lub wręcz przeciwnie (jak twierdziłem, i nadal
twierdzę, że wręcz przeciwnie :) ) boost::mpl).


>>> W obronę Javy (jako języka a nie platformy) mnie nie wciągniesz :).
>> Platforma też jest kiepska :)
>
> Musisz przemyśleć swój argument z liczbą programistów :).

Cóż, ma jedną zaletę: programistyczną małpę można tego nauczyć. A jak
jest zbyt głupia, to można dać jej jakiś super framework wktóry wyklika
wszystko do jakiegoś zapisu XMLowego który to zapis XMLowy java wykona.
To że jak przyjdzie co do czego to soft z jakimś (skąd inąd) paskudnym
perlu działa istotnie szybciej i mniej pamięci źre to inna sprawa :)

Innymi słowy -- łatwo w tym zrobić coś co jakoś działa (podkreślając
"jakoś").

Tu też upatruję braku popularności języków funkcyjnych -- małpy tego nie
nauczysz. Języki funkcyjne są mało kompatybilne z mózgami sporej grupy
programujących. W tym wszystkim zadziwia mnie nieco popularność C++,
choć z drugiej strony można tego użyć jako tzw. C z klasami i to jakoś
tam się w mózgi imperatywnie mapuje.


pzdr
\SK
--
"Never underestimate the power of human stupidity" -- L. Lang
--
http://www.tajga.org -- (some photos from my travels)

Piotr Sawicki

unread,
Dec 3, 2009, 8:06:12 PM12/3/09
to
Sebastian Kaliszewski powiada:

>> Używanie zmiennych globalnych to nie jest problem nad wykryciem
>> którego trzeba ślęczeć godzinami, widać od razu po kodzie.
>
> W miejscu użycia (mówimy o typowych przedstawicielach języków
> dynamicznych -- w każdym z nich możesz sobie zmienną globalną łatwo
> utworzyć lokalnie). Brak try/except widać tak samo.

Tylko że taki kod może poprawnie obsługiwać błędy mimo braku
w danym miejscu try/except. Można oczywiście się umówić, że
nie korzystamy z takiej możliwości i każdy call jest otoczony
sprawdzaniem wyjątków, ale wtedy stają się one zbędną koncepcją.

>>> Po co naglke statyczna kontrola w języku dynamicznym.
>>
>> To akurat proste: żeby zapobiec problemom zanim wyjdą w praniu.
>
> To po co w ogóle język dynamiczny?
>
> Chcesz mieć ciastko i zjeść ciastko...

Osłabienie możliwości przewidywania błędów to przykry efekt uboczny
dynamizmu a nie jego cel. Chcę zjeść ciastko i nie nakruszyć.

>> kontrprzykłady są niewłaściwe z powodu wieku i minimalizmu?
>
> To mam na myśli, że pełnią rolę konstrukcji podstawowej

Nadal nie wyczajam argumentu, musiałbyś skonkretyzować
tok rozumowania (są bez sensu - kontrprzykłady że z sensem
- ale tam są podstawowe - omgwtf).



>> Łatwiej jest napisać badziewiasty kod, po którym kompletnie nie wiadomo
>> czy obsługę sytuacji wyjątkowych już zaimplementowano i czy prawidłowo.
>
> To samo masz w pozostałych przypadkach.

No właśnie nie - w przypadku obsługi błędów zwykłymi if-errcode
widać na pierwszy rzut oka czy kod był napisany już z obsługą
błędów czy jeszcze bez (i czy wszystkie są pokryte, w jaki sposób).
Przy continuation-passing-style kod z pominiętą obsługą błędów
się nawet nie skompiluje/nie wykona nawet w poprawnym przypadku.
A jak ktoś to spróbuje odbębnić, to przynajmniej od razu widać.

> Od kilku postów nie mogę się doprosić pokazania wartości dodanej.

Pisałem: w miejscu wywołania funkcji wiem co się stanie po powrocie.

>>> Po drugie nadal pytam o wartość dodaną kontynuacji pod tym względem?
>>
>> Proste pilnowanie obsługi błędów.
>
> Przecież ci pokazałem jak to (nie) działa. Tzn. rółnie łatwo
> jest to zrobić zupełnie do bani, choć formalnie poprawnie.

Chyba rozumiem - myślałeś że chodzi mi o metodę javową sprawdzania
wyjątków automatycznie. No więc nie, o czytelność kodu.

>> Podałeś przypadek w którym każdy wariant jest równie zły,
>> to nie stanowi argumentu w żadną stronę.
>
> To pokaż przypadek w którym jeden jest istotnie lepszy.

Dowolny fragment kodu z licznymi wywołaniami procedur/funkcji.
Jeśli to jest napisane z wyjątkami to mam trzy możliwości:
1) sprawdzanie wyjątku jest przy każdym wywołaniu,
2) przy żadnym,
3) przy niektórych.
W 1) wyjątki są redundante z ifami; w 2) nie wiem czy obsługa
błędów była przewidziana czy nie, a w dodatku przebieg sterowania
robi się zupełnie nieoczywisty (bo każdy call jest potencjalnym
exit pointem); w 3) sprawdzenie poprawności jest pracochłonne
(trzeba rekursywnie sprawdzać kto ten kod wywołuje i które wyjątki
łapie). Istotnie lepsze jest przelecenie kodu pagedownem i patrzenie
czy każdy call ma jawnie wpisaną obsługę błędów, mając pewność
że albo sterowanie przejdzie linijkę dalej, albo (w razie sytuacji
wyjątkowej) przesunie się do wskazanego miejsca - bo z wyjątkami
to nie bardzo wiadomo gdzie to przeskoczy, okaże się w runtimie.

>> Dlatego napisałem że korzyści widzę w zapewnianiu jakości (a Ty ich
>> wypatrujesz w kodowaniu). Zawsze można źle zaprogramować, tylko że
>> w przypadku niejawnego przepływu sterowania bardzo trudno to wychwycić
>> i jest masa roboty z poprawianiem, wymaga to analizy całego kodu.
>
> W Javie masz jawny przepływ sterowania -- wyjątki są częścią sygnatur

No pliz, to jest tylko wstępna deklaracja jakie w ogóle wyjątki mogą
zostać rzucone do góry przez cały hektar kodu z niejawnymi wyskokami.

>> Jeśli powodu upatrujesz w cechach języka, to jak w ramach tej
>> teorii wyjaśniasz niezabawowe praktyczne użycie Erlanga?
>
> Erlang jest nieco bardziej "praktyczny" w użyciu.

,,Praktyczny'' to nie jest ficzer ani składni ani semantyki.
Po prostu przyczyny leżą poza językiem, choćby wsparcie
silnego koncernu - gdzie byłaby java bez suna.

> Jaka jest wspólnota rozwiazania z Javy z rozwiązaniem z C++?

Chyba nie ma, przegapiłem
(ale genericsy od początku odróżniałem od templates).



>>> No właśnie coraz więcej różnych kolesi metaprogramuje w C++.
>>> Zobacz sobie boost::lambda [...] a zwłaszcza boost::mpl
>>
>> Zaraz zaraz, używanie lambd czy foralli to nie jest metaprogramowanie.
>
> Ale ich napisanie w języku w których ich nie ma już jest :)

E tam, to jest użycie biblioteki, co za różnica jak zaimplementowanej.

> Języki funkcyjne są mało kompatybilne z mózgami
> sporej grupy programujących.

To prawda, ale właśnie dlatego w Ocamlu jest obiektowość,
a w Haskellu można pisać prawie-jak-imperatywnie (co prawda
używając monad, ale można je traktować na zasadzie copy-paste
i nawet nie wiedzieć że mówi się prozą). Za to nie ma żadnej
gotowej dystrybucji całego środowiska, a dostępnych programistów
jest za mało żeby ryzykować powodzenie projektu od ich dyspozycji.


Piotr Sawicki

Sebastian Kaliszewski

unread,
Dec 4, 2009, 5:35:38 AM12/4/09
to
Piotr Sawicki wrote:
>>> Używanie zmiennych globalnych to nie jest problem nad wykryciem
>>> którego trzeba ślęczeć godzinami, widać od razu po kodzie.
>> W miejscu użycia (mówimy o typowych przedstawicielach języków
>> dynamicznych -- w każdym z nich możesz sobie zmienną globalną łatwo
>> utworzyć lokalnie). Brak try/except widać tak samo.
>
> Tylko że taki kod może poprawnie obsługiwać błędy mimo braku
> w danym miejscu try/except.

Tak samo kontynuacja zapisana gdzieś globalnie może być poprawną obsługą
błędu, mimo braku w danym miejscu jej jawnego przekazania. A nawet przy
jej jawnym przekazaniu wcale nie musi być jasne gdzie w końcu konkretne
błedy obsłużone zostaną (więcej w dalszej części postu).

> Można oczywiście się umówić, że
> nie korzystamy z takiej możliwości i każdy call jest otoczony
> sprawdzaniem wyjątków, ale wtedy stają się one zbędną koncepcją.
>
>>>> Po co naglke statyczna kontrola w języku dynamicznym.
>>> To akurat proste: żeby zapobiec problemom zanim wyjdą w praniu.
>> To po co w ogóle język dynamiczny?
>>
>> Chcesz mieć ciastko i zjeść ciastko...
>
> Osłabienie możliwości przewidywania błędów to przykry efekt uboczny
> dynamizmu a nie jego cel. Chcę zjeść ciastko i nie nakruszyć.
>
>>> kontrprzykłady są niewłaściwe z powodu wieku i minimalizmu?
>> To mam na myśli, że pełnią rolę konstrukcji podstawowej
>
> Nadal nie wyczajam argumentu, musiałbyś skonkretyzować
> tok rozumowania (są bez sensu - kontrprzykłady że z sensem
> - ale tam są podstawowe - omgwtf).

One mają sens gdy stosowane są do uzyskania rzeczy które w pozostałych
językach są kluczowe, tj podstawowe konstrukcje sterujące. Gdy język te
podstawowe konstrukcje implementuje inaczej to stają się one
(kontynuacje) dodatkiem.

>>> Łatwiej jest napisać badziewiasty kod, po którym kompletnie nie wiadomo
>>> czy obsługę sytuacji wyjątkowych już zaimplementowano i czy prawidłowo.
>> To samo masz w pozostałych przypadkach.
>
> No właśnie nie - w przypadku obsługi błędów zwykłymi if-errcode
> widać na pierwszy rzut oka czy kod był napisany już z obsługą
> błędów czy jeszcze bez (i czy wszystkie są pokryte, w jaki sposób).

Tego ostatniego nie widać bez znajomości semantyki wołanej funkcji.

> Przy continuation-passing-style kod z pominiętą obsługą błędów
> się nawet nie skompiluje/nie wykona nawet w poprawnym przypadku.
> A jak ktoś to spróbuje odbębnić, to przynajmniej od razu widać.

Jw. -- nie tak od razu.

>
>> Od kilku postów nie mogę się doprosić pokazania wartości dodanej.
>
> Pisałem: w miejscu wywołania funkcji wiem co się stanie po powrocie.

Tzn albo pójdzie normalnie, albo poleci w stornę sprawdzenia błedu --
tak samo jest przy wyjątkach.

>
>>>> Po drugie nadal pytam o wartość dodaną kontynuacji pod tym względem?
>>> Proste pilnowanie obsługi błędów.
>> Przecież ci pokazałem jak to (nie) działa. Tzn. rółnie łatwo
>> jest to zrobić zupełnie do bani, choć formalnie poprawnie.
>
> Chyba rozumiem - myślałeś że chodzi mi o metodę javową sprawdzania
> wyjątków automatycznie. No więc nie, o czytelność kodu.

Co do czytelności kodu, to i tak nie widzisz lokalnie, jakie błędy dana
funkcja rzuca tylko jakie są obsługiwane -- jedno nie musi pasować (i
często nie pasuje) do drugiego.

BTW. zrobiono w swoim czasie badania co wpływa na liczbę błędów w kodzie
programów pisanych w różnych językach. I czy jakieś języki są bardziej
czy mniej błędogenne. Znaleziono tylko jedną korelację -- liczba błędów
zależała od długości programu, najmniej błędogennymi (w danych
zadaniach) okazały się tę języki w których dane zadanie dawało się
zapisać najbardziej zwięźle.

>
>>> Podałeś przypadek w którym każdy wariant jest równie zły,
>>> to nie stanowi argumentu w żadną stronę.
>> To pokaż przypadek w którym jeden jest istotnie lepszy.
>
> Dowolny fragment kodu z licznymi wywołaniami procedur/funkcji.
> Jeśli to jest napisane z wyjątkami to mam trzy możliwości:
> 1) sprawdzanie wyjątku jest przy każdym wywołaniu,
> 2) przy żadnym,
> 3) przy niektórych.
> W 1) wyjątki są redundante z ifami; w 2) nie wiem czy obsługa
> błędów była przewidziana czy nie, a w dodatku przebieg sterowania
> robi się zupełnie nieoczywisty (bo każdy call jest potencjalnym
> exit pointem);

Zupełnie jak przy kontynuacjach. Jeśli masz statyczne specyfikacje
wyjątków, to albo obsługa jest na końcu funkcji (w catch / except czy
jak tam autorzy języka nazwali) albo w sygnaturze funkcji jest jawnie
napisane, że leci dalej. W przypadku kontynuacji albo masz ogólny skok
do miejsca gdzieś w ciele funkcji z której wołasz, albo kontynuacja była
parametrem tej funkcji i wiesz tyle że leci dalej. W Rumym i podobnych
masz jeszcze opcję, że kontynuacja była zapisana gdzieś i jawnie do
wołanej funkcji nigdy przekazywana nie była a funkcja sobie i tak
wyskoczyła.

W przypadku wyjątków Każda wołana funkcja też ma swoją sygnaturę --
wystarczy znać tę sygnaturę by wiedzieć jakie wyjątki wypuszcza.

> w 3) sprawdzenie poprawności jest pracochłonne
> (trzeba rekursywnie sprawdzać kto ten kod wywołuje i które wyjątki
> łapie).

Przy statycznej kontroli jak w Javie nie trzeba, bo jak nie jest
obsłużone to się nie skompiluje. Podobnie jak z jawnie przekazywanymi
kontynuacjami.

Tzn. tyle mówi teoria -- i tu i tu można zrobić obsługę wszystkiego w
jednym punkcie. W przypadku wyjątkiem jedno catch / except w "programie
głównym" w przypadku kontynuacji -- jedna kontynuacja wzięta też z
programu głównego. I jak mówi praktyka tak się dzieje dużo dużo częściej
niż byłoby to uzasadnione (dopuszczam przypadki, że to jest
uzasadnione). To, że coś takiego w kółko widać w kodzie napisanym w
Javie a nie widać w napisanym w językach z kontynuacjami wynika, wg
mnie, tylko z tego, że przeciętna programistyczna małpa nie używa tych
drugich. Gdyby używała to robiłaby to samo.

> Istotnie lepsze jest przelecenie kodu pagedownem i patrzenie
> czy każdy call ma jawnie wpisaną obsługę błędów, mając pewność
> że albo sterowanie przejdzie linijkę dalej, albo (w razie sytuacji
> wyjątkowej) przesunie się do wskazanego miejsca - bo z wyjątkami
> to nie bardzo wiadomo gdzie to przeskoczy, okaże się w runtimie.

A we wskazanym miejscu będzie dispatch do kolejnej kontynuacji
przekazanej z wyższego wołania. Efekt ten sam -- i tak lokalnie nie
wiesz, gdzie program w końcu wyląduje -- okaże się w runtime.

>
>>> Dlatego napisałem że korzyści widzę w zapewnianiu jakości (a Ty ich
>>> wypatrujesz w kodowaniu). Zawsze można źle zaprogramować, tylko że
>>> w przypadku niejawnego przepływu sterowania bardzo trudno to wychwycić
>>> i jest masa roboty z poprawianiem, wymaga to analizy całego kodu.
>> W Javie masz jawny przepływ sterowania -- wyjątki są częścią sygnatur
>
> No pliz, to jest tylko wstępna deklaracja jakie w ogóle wyjątki mogą
> zostać rzucone do góry przez cały hektar kodu z niejawnymi wyskokami.

Hektar? Jak ktoś napisał funkcję na 1000 wierszy to sam sobie jest
winien. Ta wstępna deklaracja musi pasować do wszystkich wywołań w
połączeniu z obsługą wyjątków w kodzie tą wstępną deklaracją objętym.


>>> Jeśli powodu upatrujesz w cechach języka, to jak w ramach tej
>>> teorii wyjaśniasz niezabawowe praktyczne użycie Erlanga?
>> Erlang jest nieco bardziej "praktyczny" w użyciu.
>
> ,,Praktyczny'' to nie jest ficzer ani składni ani semantyki.
> Po prostu przyczyny leżą poza językiem, choćby wsparcie
> silnego koncernu - gdzie byłaby java bez suna.

Nie porównujmy Javy z Erlangiem.

Właśnie uważam że istotne znaczenie ma tu semantyka, w połączeniu z
występowaniem w skłądni i w standardowej bibliotece tego, co potrzeba.
Cały ten język zaprojektowano do określonego całkiem konkretnego
praktycznego celu i ten cel realizuje dobrze (nawet bardzo dobrze). Ale
poza tą wąską dość dziedziną jego użycie pozostało znikome.

Zaś Java to miał być język do programowania "inteligentnmych" kuchenek
mikrofalowych. Do zadań do których został zaprzęgnięty przez Suna
nadawał się słabo. Na tyle słabo że się kiepsko przyjął -- postawiono mu
więc, po konkretnej rozbudowie (głównie) bibliotek inne zadania, w
których wreszcie się przyjął, częściowo, zresztą, wracając do
oryginalnego pomysłu (J2ME). Bez potężnego popychania przez Suna to by
wszystko się rozlazło.

>
>> Jaka jest wspólnota rozwiazania z Javy z rozwiązaniem z C++?
>
> Chyba nie ma, przegapiłem
> (ale genericsy od początku odróżniałem od templates).

Poza składnią faktycznie nie ma.


>>>> No właśnie coraz więcej różnych kolesi metaprogramuje w C++.
>>>> Zobacz sobie boost::lambda [...] a zwłaszcza boost::mpl
>>> Zaraz zaraz, używanie lambd czy foralli to nie jest metaprogramowanie.
>> Ale ich napisanie w języku w których ich nie ma już jest :)
>
> E tam, to jest użycie biblioteki, co za różnica jak zaimplementowanej.

No ale użycie mpl już jest -- bo to w zasadzie jedyne zadanie tej
biblioteki.

BTW. Jest jeszcze jedna istotna cecha C++ (zresztą wspólna z Adą i D,
wprowadzająca najpoważniejszą różnicę względem C). W przypadku C++, jak
zwykle idiotycznie, nazwano to RAII (Resource Acquisition Is
Initialization) co w ogóle nie odzwierciedla o co chodzi. Chodzi po
prostu o automatyczną i synchroniczną destruckcję.

W połączeniu z niezbyt skomplikowanym użyciem szablonów można w ten
sposób było np. zaimplementować GC (nie nie zliczanie referencji, choć
to też zaimplementowano, i to dość trywialnie ale GC, żrące cykle).
Implementuje się tym też takie rzeczy, jak muteksy powiązane z blokami
kodu, tranakcje które zawsze się zakończą, itd...

Ma też wady -- np. źre się z domknięciami.

>
>> Języki funkcyjne są mało kompatybilne z mózgami
>> sporej grupy programujących.
>
> To prawda, ale właśnie dlatego w Ocamlu jest obiektowość,
> a w Haskellu można pisać prawie-jak-imperatywnie (co prawda
> używając monad, ale można je traktować na zasadzie copy-paste
> i nawet nie wiedzieć że mówi się prozą).

Prawie robi wielką różnicę.

BTW. nigdy nie podobało mi się użycie monad (plus dziwacznych typów
reprezentujących stan świata) do uzyskiwania prawie-imperatywizmu. Dla
mnie to jest intelektualny hack.

Usiłowanie (niby) czysto funkcyjnej realizacji zadań które nie dają się
sprowadzić do podania argumentów przy wywołaniu i poczekania na wynik
budzi u mnie pewien rodzaj intelektualnego obrzydzenia.

W moim odczuciu (częściowo estetycznym), trzeba się pogodzić z faktem,
że interaktywność nie wpisuje się wewnątrz funkcyjności, choć w drugą
stronę to idzie -- funkcyjność można wpisać (użyć, wywołać) z
interaktywnego otoczenia. Funcyjność nie jest interaktywna, może być
interaktywnie użyta niejako z zewnątrz.

> Za to nie ma żadnej
> gotowej dystrybucji całego środowiska, a dostępnych programistów
> jest za mało żeby ryzykować powodzenie projektu od ich dyspozycji.

Niby tak. Ale wiele innych języków też zaczynało od zera i nie miało
wsparcia wielkiej korporacji. A mimo to trafiło do pierwszej 10.
najczęściej używanych i znanych przez prgramistów języku -- wśród nich
jest zarówno język którego dotyczy ta grupa jak i język którego dotyczy
spora część pyskówki w niniejszym wątku. Przy czym o ile PHP rozwiązywał
bardzo konkretne zadanie (każda małpa zrobi interaktywną stronę WWW), to
w przypadku Pythona nie jest to takie jasne. Moim zdaniem zadecydowała
właśnie "wysoka kompatybilność z mózgami".

Piotr Sawicki

unread,
Dec 8, 2009, 8:01:02 PM12/8/09
to
Sebastian Kaliszewski powiada:

>> Tylko że taki kod może poprawnie obsługiwać błędy mimo braku
>> w danym miejscu try/except.
>
> Tak samo kontynuacja zapisana gdzieś globalnie może być poprawną obsługą
> błędu, mimo braku w danym miejscu jej jawnego przekazania.

Przyznam że kompletnie nie chwytam o co Ci chodzi. Jakiś przykład kodu?

>> Nadal nie wyczajam argumentu, musiałbyś skonkretyzować
>> tok rozumowania (są bez sensu - kontrprzykłady że z sensem
>> - ale tam są podstawowe - omgwtf).
>
> One mają sens gdy stosowane są do uzyskania rzeczy które w pozostałych
> językach są kluczowe, tj podstawowe konstrukcje sterujące.

No ale są tam stosowane również przy obsłudze sytuacji wyjątkowych.
Bezsensownie?

>> No właśnie nie - w przypadku obsługi błędów zwykłymi if-errcode
>> widać na pierwszy rzut oka czy kod był napisany już z obsługą
>> błędów czy jeszcze bez (i czy wszystkie są pokryte, w jaki sposób).
>
> Tego ostatniego nie widać bez znajomości semantyki wołanej funkcji.

To czy wszystkie (kwantyfikatorowo) to bez znajomości: errcode<>success.

>> w miejscu wywołania funkcji wiem co się stanie po powrocie.
>
> Tzn albo pójdzie normalnie, albo poleci w stornę sprawdzenia
> błedu -- tak samo jest przy wyjątkach.

Nie tak samo, bo jest napisane czarno na białym jaka to strona.
A z exceptionami hgw gdzie poleci, trzeba sprawdzać cały program.

>> Chyba rozumiem - myślałeś że chodzi mi o metodę javową sprawdzania
>> wyjątków automatycznie. No więc nie, o czytelność kodu.
>
> Co do czytelności kodu, to i tak nie widzisz lokalnie,
> jakie błędy dana funkcja rzuca tylko jakie są obsługiwane

A to akurat jest najmniejszy problem, żeby to sprawdzić
wystarczy zajrzeć w jeden wskazany nazwą procedury fragment
kodu - to można mieć w ide pod klikiem. Żeby sprawdzić jaka
będzie obsługa wyjątku trzeba przeczytać zylion miejsc
kodu które mogą wywoływać aktualnie czytany kawałek.

> BTW. zrobiono w swoim czasie badania co wpływa na liczbę
> błędów w kodzie programów pisanych w różnych językach.

Jak znam życie to nie robili eksperymentów tylko pomiary.
W szczególności niewykrycie innych korelacji mogło wynikać
nie z ich braku, a z przyjęcia złych hipotez badawczych.

>> Dowolny fragment kodu z licznymi wywołaniami procedur/funkcji.
>> Jeśli to jest napisane z wyjątkami to mam trzy możliwości:
>> 1) sprawdzanie wyjątku jest przy każdym wywołaniu,
>> 2) przy żadnym,
>> 3) przy niektórych.
>> W 1) wyjątki są redundante z ifami; w 2) nie wiem czy obsługa
>> błędów była przewidziana czy nie, a w dodatku przebieg sterowania
>> robi się zupełnie nieoczywisty (bo każdy call jest potencjalnym
>> exit pointem);
>
> Zupełnie jak przy kontynuacjach.

Niezupełnie, bo wiem że może skoczyć i gdzie. I to każdy
poszczególny call, a nie cały obleczony try-catchem blok.

> Jeśli masz statyczne specyfikacje wyjątków, to albo obsługa
> jest na końcu funkcji (w catch / except czy jak tam autorzy
> języka nazwali) albo w sygnaturze funkcji jest jawnie napisane,
> że leci dalej.

To akurat nie jest złe - ale to słabsza cecha (w sensie matematycznym).

> W przypadku kontynuacji albo masz ogólny skok
> do miejsca gdzieś w ciele funkcji z której wołasz,
> albo kontynuacja była parametrem

Albo wskazujesz inny fragment kodu - continuation passing style da się
stosować nawet przy użyciu zwykłych procedur (tyle że to mało wygodne).

> tej funkcji i wiesz tyle że leci dalej. W Rumym i podobnych
> masz jeszcze opcję, że kontynuacja była zapisana gdzieś
> i jawnie do wołanej funkcji nigdy przekazywana nie była
> a funkcja sobie i tak wyskoczyła.

Nie wiem jak wygląda implementacja kontynuacji w Rubym,
więc mogę się zgodzić że ją spartaczono skoro tak twierdzisz.

> W przypadku wyjątków Każda wołana funkcja też ma swoją sygnaturę --
> wystarczy znać tę sygnaturę by wiedzieć jakie wyjątki wypuszcza.

Ale nie kto je łapie.

>> w 3) sprawdzenie poprawności jest pracochłonne
>> (trzeba rekursywnie sprawdzać kto ten kod wywołuje i które wyjątki
>> łapie).
>
> Przy statycznej kontroli jak w Javie nie trzeba, bo jak nie jest
> obsłużone to się nie skompiluje. Podobnie jak z jawnie przekazywanymi
> kontynuacjami.

Jeśli sygnatura mówi o przerzucaniu wyjątku wyżej to nadal
nie wiem, czy to właściwa obsługa błędów, czy wpisana na odwal
się, aby skompilowało - trzeba sprawdzać gdzie indziej.
Całe to rozwiązanie zachęca do robienia stubów które zbiorczo
łapią wszystko jak popadnie, kontynuacji nie ma sensu tak
odbębniać (bo trzeba je wpisać w każdym miejscu).



> Tzn. tyle mówi teoria -- i tu i tu można zrobić obsługę wszystkiego w
> jednym punkcie. W przypadku wyjątkiem jedno catch / except w "programie
> głównym" w przypadku kontynuacji -- jedna kontynuacja wzięta też z
> programu głównego. I jak mówi praktyka tak się dzieje dużo dużo częściej
> niż byłoby to uzasadnione (dopuszczam przypadki, że to jest
> uzasadnione).

Zgoda, programowanie to nie smażenie hamburgerów
- zawsze można spartaczyć i nie ma co szukać silver bullet.
Za to różnica jest w łatwości wykrycia takiego partactwa:
jak trzeba je jawnie wpisywać to widać, a jak nie trzeba
(i dobry kod może wyglądać tak samo jak zły) to nie.

>> Istotnie lepsze jest przelecenie kodu pagedownem i patrzenie
>> czy każdy call ma jawnie wpisaną obsługę błędów, mając pewność
>> że albo sterowanie przejdzie linijkę dalej, albo (w razie sytuacji
>> wyjątkowej) przesunie się do wskazanego miejsca - bo z wyjątkami
>> to nie bardzo wiadomo gdzie to przeskoczy, okaże się w runtimie.
>
> A we wskazanym miejscu będzie dispatch do kolejnej kontynuacji
> przekazanej z wyższego wołania.

Tego się nie da nie zauważyć - bo jest jawne.



>>> W Javie masz jawny przepływ sterowania -- wyjątki są częścią sygnatur
>>
>> No pliz, to jest tylko wstępna deklaracja jakie w ogóle wyjątki mogą
>> zostać rzucone do góry przez cały hektar kodu z niejawnymi wyskokami.
>
> Hektar? Jak ktoś napisał funkcję na 1000 wierszy to sam sobie jest
> winien. Ta wstępna deklaracja musi pasować do wszystkich wywołań w
> połączeniu z obsługą wyjątków w kodzie tą wstępną deklaracją objętym.

No właśnie musi pasować do wszystkich razem a nie każdego oddzielnie.

>>> Erlang jest nieco bardziej "praktyczny" w użyciu.
>>
>> ,,Praktyczny'' to nie jest ficzer ani składni ani semantyki.
>> Po prostu przyczyny leżą poza językiem, choćby wsparcie
>> silnego koncernu - gdzie byłaby java bez suna.
>
> Nie porównujmy Javy z Erlangiem.
>
> Właśnie uważam że istotne znaczenie ma tu semantyka, w połączeniu z
> występowaniem w skłądni i w standardowej bibliotece tego, co potrzeba.

I które to są te kluczowe różnice w składni i semantyce między
Erlangiem a innymi funkcyjnymi? Bo dla mnie jest przede wszystkim mniej
czytelny, a plusy widzę głównie w maszynie wirtualnej (czy jak to zwać).

> Zaś Java to miał być język do programowania "inteligentnmych" kuchenek
> mikrofalowych. Do zadań do których został zaprzęgnięty przez Suna
> nadawał się słabo. Na tyle słabo że się kiepsko przyjął -- postawiono mu
> więc, po konkretnej rozbudowie (głównie) bibliotek inne zadania, w
> których wreszcie się przyjął, częściowo, zresztą, wracając do
> oryginalnego pomysłu (J2ME). Bez potężnego popychania przez Suna to by
> wszystko się rozlazło.

To właśnie mój punkt (przyczyny powodzenia są pozalingwistyczne).

>>>>> No właśnie coraz więcej różnych kolesi metaprogramuje w C++.
>>>>> Zobacz sobie boost::lambda [...] a zwłaszcza boost::mpl
>>>> Zaraz zaraz, używanie lambd czy foralli to nie jest metaprogramowanie.
>>> Ale ich napisanie w języku w których ich nie ma już jest :)
>>
>> E tam, to jest użycie biblioteki, co za różnica jak zaimplementowanej.
>
> No ale użycie mpl już jest -- bo to w zasadzie jedyne zadanie tej
> biblioteki.

Lambdy, funkcje wyższych rzędów, częściowa ewaluacja i leniwość
to cechy w dobrych językach dostępne out of the box, bez żadnego
metaprogramowania. Po prostu C++ jest tak biedne, że inaczej niż
metaprogramowaniem nie dało się tego doimplementować. Co jeszcze
nie znaczy, że używając lambdy pisze się program tworzący programy
(fakt że to kwestia definicji MP, ja wysoko stawiam poprzeczkę).

> BTW. nigdy nie podobało mi się użycie monad (plus dziwacznych typów
> reprezentujących stan świata) do uzyskiwania prawie-imperatywizmu.
> Dla mnie to jest intelektualny hack.

Zamyka efekty uboczne w piaskownicy, niezły wynik.

> Usiłowanie (niby) czysto funkcyjnej realizacji zadań które
> nie dają się sprowadzić do podania argumentów przy wywołaniu
> i poczekania na wynik

Wszystkie się dają, kwestia czytelności i łatwości napisania.

> Funcyjność nie jest interaktywna, może być
> interaktywnie użyta niejako z zewnątrz.

Nie mam pojęcia, co masz na myśli.

>> Za to nie ma żadnej
>> gotowej dystrybucji całego środowiska, a dostępnych programistów
>> jest za mało żeby ryzykować powodzenie projektu od ich dyspozycji.
>
> Niby tak. Ale wiele innych języków też zaczynało od zera i nie miało
> wsparcia wielkiej korporacji. A mimo to trafiło do pierwszej 10.
> najczęściej używanych i znanych przez prgramistów języku -- wśród nich
> jest zarówno język którego dotyczy ta grupa jak i język którego dotyczy
> spora część pyskówki w niniejszym wątku.

Batteries included; learning curve.


Piotr Sawicki

Sebastian Kaliszewski

unread,
Dec 9, 2009, 1:54:00 PM12/9/09
to
Piotr Sawicki wrote:
> Sebastian Kaliszewski powiada:
>
>>> Tylko że taki kod może poprawnie obsługiwać błędy mimo braku
>>> w danym miejscu try/except.
>> Tak samo kontynuacja zapisana gdzieś globalnie może być poprawną obsługą
>> błędu, mimo braku w danym miejscu jej jawnego przekazania.
>
> Przyznam że kompletnie nie chwytam o co Ci chodzi. Jakiś przykład kodu?

Użyję pseudopythona (czyli jakby zapewne wyglądały kontynuacje
zrealizowane w Pythonie gdyby je ktoś zrealizwoał:


def z_bledem(err_cont, a, b):
if not a:
err_cont.call(ErrorA())
elif not b:
err_cont.call(ErrorB())


def wolanie(err_cont):
callcc cont:
for i in range(10):
z_bledem(random.choice(True, False), random.choice(True, False))
continue error:
if error.type is ErrorA():
obsługa_na_miejscu()
else:
# pozostałe błędy ma obsłużyć wolajacy wolanie
err_cont.call(error)


VS


def z_bledem(a, b):
if not a:
raise ErrorA()
elif not b:
raise ErrorB()

def wolanie()
try:
for i in range(10):
z_bledem(random.choice(True, False), random.choice(True, False))
except ErrorA as error:
obsługa_na_miejscu()
# pozostałe błędy ma obsłużyć wolajacy wolanie


Co zabawne w powyższym w wersji kontynuacyjnej jeśli pominiemy ostatnie
else to błąd zostanie po cichu zignorowany.


>>> Nadal nie wyczajam argumentu, musiałbyś skonkretyzować
>>> tok rozumowania (są bez sensu - kontrprzykłady że z sensem
>>> - ale tam są podstawowe - omgwtf).
>> One mają sens gdy stosowane są do uzyskania rzeczy które w pozostałych
>> językach są kluczowe, tj podstawowe konstrukcje sterujące.
>
> No ale są tam stosowane również przy obsłudze sytuacji wyjątkowych.
> Bezsensownie?

Kluczowe jest *również*.

>
>>> No właśnie nie - w przypadku obsługi błędów zwykłymi if-errcode
>>> widać na pierwszy rzut oka czy kod był napisany już z obsługą
>>> błędów czy jeszcze bez (i czy wszystkie są pokryte, w jaki sposób).
>> Tego ostatniego nie widać bez znajomości semantyki wołanej funkcji.
>
> To czy wszystkie (kwantyfikatorowo) to bez znajomości: errcode<>success.

Tyle samo mi daje catch(...) -- w obu wypadkach niewiele.


>>> w miejscu wywołania funkcji wiem co się stanie po powrocie.
>> Tzn albo pójdzie normalnie, albo poleci w stornę sprawdzenia
>> błedu -- tak samo jest przy wyjątkach.
>
> Nie tak samo, bo jest napisane czarno na białym jaka to strona.

Jeśli mamy cokolwiek więcej niż jeden ogólny błąd to dokładnie tak samo
nie wiadomo. W dodatku łatwiej zrobić źle i niezauważyć (niezłapany
wyjątek wyleci w sposób zwykle widoczny, dziurawa obsługa błędu w
kontynuacji poprostu błąd zgubi).

> A z exceptionami hgw gdzie poleci, trzeba sprawdzać cały program.

I tak trzeba sprawdzać cały program. I tu i tu.


>>> Chyba rozumiem - myślałeś że chodzi mi o metodę javową sprawdzania
>>> wyjątków automatycznie. No więc nie, o czytelność kodu.
>> Co do czytelności kodu, to i tak nie widzisz lokalnie,
>> jakie błędy dana funkcja rzuca tylko jakie są obsługiwane
>
> A to akurat jest najmniejszy problem, żeby to sprawdzić
> wystarczy zajrzeć w jeden wskazany nazwą procedury fragment
> kodu - to można mieć w ide pod klikiem. Żeby sprawdzić jaka
> będzie obsługa wyjątku trzeba przeczytać zylion miejsc
> kodu które mogą wywoływać aktualnie czytany kawałek.

To samo przecież, w oczywisty sposób dotyczy użycie kontynuacji. Nawet
jal kontynuację dostaniesz jako parametr, przy każdym wołaniu może to
być kontynuacjia utworzona zupełnie gdzie indziej więc gdzie indziej
prowadząca. Bez sprawdzenia wszysktkuch miejsc wołania i tak nie wiesz.


>> BTW. zrobiono w swoim czasie badania co wpływa na liczbę
>> błędów w kodzie programów pisanych w różnych językach.
>
> Jak znam życie to nie robili eksperymentów tylko pomiary.

Robili eksperyment. Ludzie dostali do napisania programy (w języku który
znali), potem te programy starannie sprawdzono patrząc ile jest błędów.

> W szczególności niewykrycie innych korelacji mogło wynikać
> nie z ich braku, a z przyjęcia złych hipotez badawczych.

Hipotezą było, że są języki mniej i bardziej błędogenne. Sprawdziĸła się
tylko w takim stopniu w jakim dany język pozwalał na napisanie kodu
składającego się z mniejszej liczby konstrukcji. Koerlacja była
zachowana również pomiędzy różnymi programami w tym samym języku --
programy dłuższe miały proporcjonalnie więcej błędów.


>>> Dowolny fragment kodu z licznymi wywołaniami procedur/funkcji.
>>> Jeśli to jest napisane z wyjątkami to mam trzy możliwości:
>>> 1) sprawdzanie wyjątku jest przy każdym wywołaniu,
>>> 2) przy żadnym,
>>> 3) przy niektórych.
>>> W 1) wyjątki są redundante z ifami; w 2) nie wiem czy obsługa
>>> błędów była przewidziana czy nie, a w dodatku przebieg sterowania
>>> robi się zupełnie nieoczywisty (bo każdy call jest potencjalnym
>>> exit pointem);
>> Zupełnie jak przy kontynuacjach.
>
> Niezupełnie, bo wiem że może skoczyć i gdzie.

Nie wiesz jeśli tylko gdziekolwiek przekazujesz dalej kontynuację
otrzymaną "z zewnątrz" (w parametrach aktyualnie wykonywanej funkcji,
lub co gorsza globalnie).

> I to każdy
> poszczególny call, a nie cały obleczony try-catchem blok.

Jak ci bardzo na tej wiedzy zależy, to na końcu możesz mieć catch(...)
-- wtedy wiesz, że tędy przeleci.


>> Jeśli masz statyczne specyfikacje wyjątków, to albo obsługa
>> jest na końcu funkcji (w catch / except czy jak tam autorzy
>> języka nazwali) albo w sygnaturze funkcji jest jawnie napisane,
>> że leci dalej.
>
> To akurat nie jest złe - ale to słabsza cecha (w sensie matematycznym).

No właśnie nie widzę tej "słabszości".


>> W przypadku kontynuacji albo masz ogólny skok
>> do miejsca gdzieś w ciele funkcji z której wołasz,
>> albo kontynuacja była parametrem
>
> Albo wskazujesz inny fragment kodu - continuation passing style da się
> stosować nawet przy użyciu zwykłych procedur (tyle że to mało wygodne).

Ale ten fragment kodu jest skądś dany -- albo zrobiłem go sobie lokalnie
(czyli tak jak catch / except) albo przyszedł z zewnątrz (czyli lokalnei
nie wiem gdzie to dalej poleci).

>
>> tej funkcji i wiesz tyle że leci dalej. W Rumym i podobnych
>> masz jeszcze opcję, że kontynuacja była zapisana gdzieś
>> i jawnie do wołanej funkcji nigdy przekazywana nie była
>> a funkcja sobie i tak wyskoczyła.
>
> Nie wiem jak wygląda implementacja kontynuacji w Rubym,
> więc mogę się zgodzić że ją spartaczono skoro tak twierdzisz.

To nie jest kwestia spartaczenia kontynuacji tylko istnienia globalnych
zmiennych. I (złego) nauczenia ludzi by ich (nad)używali do trzymania
kontynuacji.


>> W przypadku wyjątków Każda wołana funkcja też ma swoją sygnaturę --
>> wystarczy znać tę sygnaturę by wiedzieć jakie wyjątki wypuszcza.
>
> Ale nie kto je łapie.

Tak samo jest z kontynuacjami. Skaczesz do podanej z zewnątrz
kontynuacji, ale bez obejrzenia całego kodu nie wiesz skąd się ona
(kontynuacja) wzięła.


>>> w 3) sprawdzenie poprawności jest pracochłonne
>>> (trzeba rekursywnie sprawdzać kto ten kod wywołuje i które wyjątki
>>> łapie).
>> Przy statycznej kontroli jak w Javie nie trzeba, bo jak nie jest
>> obsłużone to się nie skompiluje. Podobnie jak z jawnie przekazywanymi
>> kontynuacjami.
>
> Jeśli sygnatura mówi o przerzucaniu wyjątku wyżej to nadal
> nie wiem, czy to właściwa obsługa błędów, czy wpisana na odwal
> się, aby skompilowało - trzeba sprawdzać gdzie indziej.
> Całe to rozwiązanie zachęca do robienia stubów które zbiorczo
> łapią wszystko jak popadnie, kontynuacji nie ma sensu tak
> odbębniać (bo trzeba je wpisać w każdym miejscu).

To tylko jeden parametr. Co gorsza kontynuacje zachęcają by je stworzyć,
przekazać gdzie są wymagane a następnie po cichu wyrzucić.


>> Tzn. tyle mówi teoria -- i tu i tu można zrobić obsługę wszystkiego w
>> jednym punkcie. W przypadku wyjątkiem jedno catch / except w "programie
>> głównym" w przypadku kontynuacji -- jedna kontynuacja wzięta też z
>> programu głównego. I jak mówi praktyka tak się dzieje dużo dużo częściej
>> niż byłoby to uzasadnione (dopuszczam przypadki, że to jest
>> uzasadnione).
>
> Zgoda, programowanie to nie smażenie hamburgerów
> - zawsze można spartaczyć i nie ma co szukać silver bullet.
> Za to różnica jest w łatwości wykrycia takiego partactwa:
> jak trzeba je jawnie wpisywać to widać, a jak nie trzeba
> (i dobry kod może wyglądać tak samo jak zły) to nie.

Przy specyfikowaniu wyjątków jest nie gorzej niż przy kontynuacjach --
czy dodam do funkcji dodatkowy parametr czy dospecyfikuję rzucane
wyjątki to widać/ nie widać partactwo tak samo.


>>> Istotnie lepsze jest przelecenie kodu pagedownem i patrzenie
>>> czy każdy call ma jawnie wpisaną obsługę błędów, mając pewność
>>> że albo sterowanie przejdzie linijkę dalej, albo (w razie sytuacji
>>> wyjątkowej) przesunie się do wskazanego miejsca - bo z wyjątkami
>>> to nie bardzo wiadomo gdzie to przeskoczy, okaże się w runtimie.
>> A we wskazanym miejscu będzie dispatch do kolejnej kontynuacji
>> przekazanej z wyższego wołania.
>
> Tego się nie da nie zauważyć - bo jest jawne.

Za to nie widać, że jakiś błąd został po cichu zgubiony.

>
>>>> W Javie masz jawny przepływ sterowania -- wyjątki są częścią sygnatur
>>> No pliz, to jest tylko wstępna deklaracja jakie w ogóle wyjątki mogą
>>> zostać rzucone do góry przez cały hektar kodu z niejawnymi wyskokami.
>> Hektar? Jak ktoś napisał funkcję na 1000 wierszy to sam sobie jest
>> winien. Ta wstępna deklaracja musi pasować do wszystkich wywołań w
>> połączeniu z obsługą wyjątków w kodzie tą wstępną deklaracją objętym.
>
> No właśnie musi pasować do wszystkich razem a nie każdego oddzielnie.

A otrzymana z zewnątrz kontynuacja tak samo będzie pasować do wszystkich
razem.


>>>> Erlang jest nieco bardziej "praktyczny" w użyciu.
>>> ,,Praktyczny'' to nie jest ficzer ani składni ani semantyki.
>>> Po prostu przyczyny leżą poza językiem, choćby wsparcie
>>> silnego koncernu - gdzie byłaby java bez suna.
>> Nie porównujmy Javy z Erlangiem.
>>
>> Właśnie uważam że istotne znaczenie ma tu semantyka, w połączeniu z
>> występowaniem w skłądni i w standardowej bibliotece tego, co potrzeba.
>
> I które to są te kluczowe różnice w składni i semantyce między
> Erlangiem a innymi funkcyjnymi? Bo dla mnie jest przede wszystkim mniej
> czytelny, a plusy widzę głównie w maszynie wirtualnej (czy jak to zwać).

Ta wiele rzeczy co ta maszyna wirtualna daje to część semantyki.
Głównie w całej zabawie w przesyłanie komunikatów w około którego
wszystko się kręci. I w tym, że mamy "wbitą w semantykę" współbieżność.


>>>>>> No właśnie coraz więcej różnych kolesi metaprogramuje w C++.
>>>>>> Zobacz sobie boost::lambda [...] a zwłaszcza boost::mpl
>>>>> Zaraz zaraz, używanie lambd czy foralli to nie jest metaprogramowanie.
>>>> Ale ich napisanie w języku w których ich nie ma już jest :)
>>> E tam, to jest użycie biblioteki, co za różnica jak zaimplementowanej.
>> No ale użycie mpl już jest -- bo to w zasadzie jedyne zadanie tej
>> biblioteki.
>
> Lambdy, funkcje wyższych rzędów, częściowa ewaluacja i leniwość
> to cechy w dobrych językach dostępne out of the box, bez żadnego
> metaprogramowania.

Ale ja nie mówię o tym. boost::lambda to co innego niż boost::mpl.
boost::mpl daje metefunkcje (biedne i niewygodne ale są).

> Po prostu C++ jest tak biedne, że inaczej niż
> metaprogramowaniem nie dało się tego doimplementować. Co jeszcze
> nie znaczy, że używając lambdy pisze się program tworzący programy
> (fakt że to kwestia definicji MP, ja wysoko stawiam poprzeczkę).

W mpl pisze się programy piszące programy.


>> BTW. nigdy nie podobało mi się użycie monad (plus dziwacznych typów
>> reprezentujących stan świata) do uzyskiwania prawie-imperatywizmu.
>> Dla mnie to jest intelektualny hack.
>
> Zamyka efekty uboczne w piaskownicy, niezły wynik.

Stawiając płot dookoła sebie zamykam świat za płotem? Intelektualny hack
i tyle :)

W szczególności stan świata jest niezdefiniowany (bo i nie ma jak), jest
ukrywany w pseudowartości która wartością nie jest. Nie bardzo więc
można mówić o zbiorze wartości (skoro nie ma wartości), więc i nie
bardzo można mówić o funkcji.

>
>> Usiłowanie (niby) czysto funkcyjnej realizacji zadań które
>> nie dają się sprowadzić do podania argumentów przy wywołaniu
>> i poczekania na wynik
>
> Wszystkie się dają, kwestia czytelności i łatwości napisania.

Nie dają się. Istnieją zadanie w których całe clue polega na tym, że są
w nich zależności czasowe.


>> Funcyjność nie jest interaktywna, może być
>> interaktywnie użyta niejako z zewnątrz.
>
> Nie mam pojęcia, co masz na myśli.

To, że wykonanie funkcji to przygotowanie argumentów i poczekanie na
wynik. Możesz taką funkcję sobie w różnych miejscach i z różnymi
argumentami wołać i za każdym razem czekać na wynik. Ale robienie z
funkcji czegoś co realizuje złożone zależności czasowe to co najwyżej
intelektualny hack.

Sebastian Kaliszewski

unread,
Dec 9, 2009, 2:09:55 PM12/9/09
to
Sebastian Kaliszewski wrote:

Poprawka...

> def z_bledem(err_cont, a, b):
> if not a:
> err_cont.call(ErrorA())
> elif not b:
> err_cont.call(ErrorB())
>
>
> def wolanie(err_cont):
> callcc cont:
> for i in range(10):
> z_bledem(random.choice(True, False), random.choice(True, False))

z_bledem(cont, random.choice(True, False),
random.choice(True, False))

> continue error:
> if error.type is ErrorA():
> obsługa_na_miejscu()
> else:
> # pozostałe błędy ma obsłużyć wolajacy wolanie
> err_cont.call(error)
>

\SK

Piotr Sawicki

unread,
Dec 11, 2009, 8:50:27 PM12/11/09
to
Sebastian Kaliszewski powiada:

> def z_bledem(err_cont, a, b):
> if not a:
> err_cont.call(ErrorA())
> elif not b:
> err_cont.call(ErrorB())
>
>
> def wolanie(err_cont):
> callcc cont:
> for i in range(10):

> z_bledem(cont, random.choice(True, False),

> random.choice(True, False))
> continue error:
> if error.type is ErrorA():
> obsługa_na_miejscu()
> else:
> # pozostałe błędy ma obsłużyć wolajacy wolanie
> err_cont.call(error)
>

Tak to dziwacznie wymyśliłeś, żeby było kalką wyjątków.
No i nadal nie wiem, gdzie jest ta niejawna globalna kontynuacja.

> VS
>
>
> def z_bledem(a, b):
> if not a:
> raise ErrorA()
> elif not b:
> raise ErrorB()
>
> def wolanie()
> try:
> for i in range(10):
> z_bledem(random.choice(True, False), random.choice(True, False))
> except ErrorA as error:
> obsługa_na_miejscu()
> # pozostałe błędy ma obsłużyć wolajacy wolanie

VS np.

def z_bledem(a,b, contA, contB):
if not a: continue to contA()
elif not b: continue to contB()

def wolanie():
for i in range(10):
z_bledem(true/false, true/false, errorAhandler, errorBhandler)



>>> One mają sens gdy stosowane są do uzyskania rzeczy które w pozostałych
>>> językach są kluczowe, tj podstawowe konstrukcje sterujące.
>>
>> No ale są tam stosowane również przy obsłudze sytuacji wyjątkowych.
>> Bezsensownie?
>
> Kluczowe jest *również*.

Kilka postingów temu postawiłeś tezę, że używanie kontynuacji
nie ma sensu w językach typowanych dynamicznie. Jeśli teraz
doprecyzowujesz, że tylko w tych z nich, w których kontynuacje
nie są częścią języka, to wychodzi banalna tautologia
(i nie wiadomo po co w to w ogóle mieszać dynamizm).

>> To czy wszystkie (kwantyfikatorowo) to bez znajomości: errcode<>success.
>
> Tyle samo mi daje catch(...) -- w obu wypadkach niewiele.

Da, jeśli obstawimy nim każde wołanie. No to po co w ogóle wyjątki.

>>>> w miejscu wywołania funkcji wiem co się stanie po powrocie.
>>> Tzn albo pójdzie normalnie, albo poleci w stornę sprawdzenia
>>> błedu -- tak samo jest przy wyjątkach.
>>
>> Nie tak samo, bo jest napisane czarno na białym jaka to strona.
>
> Jeśli mamy cokolwiek więcej niż jeden ogólny błąd to dokładnie tak samo
> nie wiadomo. W dodatku łatwiej zrobić źle i niezauważyć

Jeśli podasz za mało argumentów do funkcji (tzn. pominiesz którąś
z wyjątkowych kontynuacji), to da się to wyłapać statycznie,
a błąd wykonania pojawi się nawet w prawidłowym przypadku.

>> A z exceptionami hgw gdzie poleci, trzeba sprawdzać cały program.
>
> I tak trzeba sprawdzać cały program. I tu i tu.

W jednym przypadku wystarczy sprawdzić liniowo, jednoprzebiegowo
(czy każdy call uwzględnia błędy) - i to wystarczy do pewności,
że obsługę błędów przewidziano w stu procentach. W drugim przypadku
konieczne jest błądzenie po całym kodzie i analiza wszystkim możliwych
tras i przeplotów wywołań. Czas rzetelnego sprawdzenia poleciałby
w kosmos, więc w rzeczywistości robi się to na oko zostawiając dziury.

>> Żeby sprawdzić jaka
>> będzie obsługa wyjątku trzeba przeczytać zylion miejsc
>> kodu które mogą wywoływać aktualnie czytany kawałek.
>
> To samo przecież, w oczywisty sposób dotyczy użycie kontynuacji. Nawet
> jal kontynuację dostaniesz jako parametr, przy każdym wołaniu może to
> być kontynuacjia utworzona zupełnie gdzie indziej więc gdzie indziej
> prowadząca. Bez sprawdzenia wszysktkuch miejsc wołania i tak nie wiesz.

Jak ktoś się uprze, to zawsze może napisać program nieczytelnie
(ale to widać i można poprawić). W przypadku wyjątków nieczytelność
rozwiązania jest nieodłączną cechą ich głównej zalety, jaką jest
możliwość przeniesienia obsługi błędu poza miejsce wystąpienia.

>>> BTW. zrobiono w swoim czasie badania co wpływa na liczbę
>>> błędów w kodzie programów pisanych w różnych językach.
>>
>> Jak znam życie to nie robili eksperymentów tylko pomiary.
>
> Robili eksperyment. Ludzie dostali do napisania programy
> (w języku który znali), potem te programy starannie sprawdzono
> patrząc ile jest błędów.

No sorry, ale to nie spełnia wymogów naukowego eksperymentu.
Chociażby nie ma mowy o podwójnie ślepej próbie.

>> W szczególności niewykrycie innych korelacji mogło wynikać
>> nie z ich braku, a z przyjęcia złych hipotez badawczych.
>
> Hipotezą było, że są języki mniej i bardziej błędogenne.

To nie jest nawet poprawnie zdefiniowane, nie wspominając
w ogóle o falsyfikowalności. Można sobie policzyć jakieś
korelacje na nielosowej próbce algorytmów, i w zasadzie tyle.
Nie wiadomo jakich korelacji nie stwierdzono, bo ich nie badano.

>> I to każdy
>> poszczególny call, a nie cały obleczony try-catchem blok.
>
> Jak ci bardzo na tej wiedzy zależy, to na końcu możesz
> mieć catch(...) -- wtedy wiesz, że tędy przeleci.

Dlatego pisałem, że wyjątki w najlepszym przypadku dadzą taki sam
efekt jak inne metody (po rezygnacji z ich kluczowego ficzeru
przerzucenia obsługi błędów wzwyż), ale wtedy są zbędną konstrukcją.

>>> Jeśli masz statyczne specyfikacje wyjątków, to albo obsługa
>>> jest na końcu funkcji (w catch / except czy jak tam autorzy
>>> języka nazwali) albo w sygnaturze funkcji jest jawnie napisane,
>>> że leci dalej.
>>
>> To akurat nie jest złe - ale to słabsza cecha
>> (w sensie matematycznym).
>
> No właśnie nie widzę tej "słabszości".

Nie wiesz konkretnie które z wywołań zadeklarowanych blokowo
do łapania/przerzucania wyjątków rzuca które z nich,
trudniej sprawdzić poprawność takiej obsługi błędów.

>>> W przypadku kontynuacji albo masz ogólny skok
>>> do miejsca gdzieś w ciele funkcji z której wołasz,
>>> albo kontynuacja była parametrem
>>

>> Albo wskazujesz inny fragment kodu [...]


>
> Ale ten fragment kodu jest skądś dany

Normalnie palcem można pokazać, nie ma musu
stosować na upartego metodę exceptionową w innej składni.

> albo zrobiłem go sobie lokalnie (czyli tak jak catch / except)
> albo przyszedł z zewnątrz (czyli lokalnei
> nie wiem gdzie to dalej poleci).

Tylko że jak ktoś ma problem z tym, że w programowaniu używa się
zmiennych, to lepiej niech zmieni profil. Przecież nie będziesz
zarzucał instrukcji print, że nie wiesz co zostanie wydrukowane
przez ,,print variableA''. Problem byłby dopiero gdyby bezparametrowy
secretprint drukował nieoczywistą wartość wyciągniętą gdzieś z szafy
(dla mnie tak wygląda perl i jego m@giczne #argumenty domy$lne).

> To nie jest kwestia spartaczenia kontynuacji tylko istnienia
> globalnych zmiennych. I (złego) nauczenia ludzi by ich
> (nad)używali do trzymania kontynuacji.

No dobra, ale to kwestia używania zmiennych globalnych
w ogóle, a nie szczególnego przypadku.

>>> W przypadku wyjątków Każda wołana funkcja też ma swoją sygnaturę --
>>> wystarczy znać tę sygnaturę by wiedzieć jakie wyjątki wypuszcza.
>>
>> Ale nie kto je łapie.
>
> Tak samo jest z kontynuacjami.

Nie tak samo, bo od razu wiem że błąd jest obsłużony, już w tym
miejscu. Samo info o rzucaniu wyżej wyjątku mi jeszcze nie mówi,
czy jest zawsze sensownie złapany, czy ktoś walnął wielki catch
na wszystko żeby zapuścić daily build i w końcu odebrać dziecko
z przedszkola. Wiem że odbębnić zawsze się da, ale możliwość
otoczenia całego kawałka kodu klamrą catch-everything to ułatwia.

> kontynuacje zachęcają by je stworzyć, przekazać
> gdzie są wymagane a następnie po cichu wyrzucić.

Jak na zachęcanie to strasznie dużo roboty na takie obejście.
A sprawdza się je prosto i tylko raz - w definicji wołanej procedury
(sprawdzenie jej jest i tak nie do uniknięcia, więc ta sama robota).

> Przy specyfikowaniu wyjątków jest nie gorzej niż przy kontynuacjach --
> czy dodam do funkcji dodatkowy parametr czy dospecyfikuję rzucane
> wyjątki to widać/ nie widać partactwo tak samo.

Nie zgadzam się, po dopisku throws cośtam jeszcze nie wiadomo czy
tak powinno być czy nie, i gdzie tkwi problem. W drugim przypadku
wszystko co potrzebne do wypatrzenia partactwa jest na miejscu.

> otrzymana z zewnątrz kontynuacja tak samo będzie pasować
> do wszystkich razem.

Taką bzdurę, że każdy błąd ma ten sam handler, to od razu widać.

>> I które to są te kluczowe różnice w składni i semantyce między
>> Erlangiem a innymi funkcyjnymi? Bo dla mnie jest przede wszystkim mniej
>> czytelny, a plusy widzę głównie w maszynie wirtualnej (czy jak to zwać).
>
> Ta wiele rzeczy co ta maszyna wirtualna daje to część semantyki.
> Głównie w całej zabawie w przesyłanie komunikatów w około którego
> wszystko się kręci. I w tym, że mamy "wbitą w semantykę" współbieżność.

Spawnowanie ~procesów~ i przesyłanie komunikatów jest też gdzie
indziej bibliotecznie, tylko bez zapewnienia wszystkich
bajeranckich cech produkcyjnych erlangowego vm-u. Tychże pozbawiony
interpreter erlanga napisany samemu byłby już mocno taki sobie.

>> Lambdy, funkcje wyższych rzędów, częściowa ewaluacja i leniwość
>> to cechy w dobrych językach dostępne out of the box, bez żadnego
>> metaprogramowania.
>
> Ale ja nie mówię o tym. boost::lambda to co innego niż boost::mpl.
> boost::mpl daje metefunkcje (biedne i niewygodne ale są).

To co mpl nazywa metafunkcjami to przecież po prostu funkcje
wyższych rzędów - gdzie indziej nie robi się takiego rozróżnienia,
po prostu typ->typ jest równie dobrym typem jak struktura czy unia.

> W mpl pisze się programy piszące programy.

Hello world.

>>> BTW. nigdy nie podobało mi się użycie monad (plus dziwacznych typów
>>> reprezentujących stan świata) do uzyskiwania prawie-imperatywizmu.
>>> Dla mnie to jest intelektualny hack.
>>
>> Zamyka efekty uboczne w piaskownicy, niezły wynik.
>
> Stawiając płot dookoła sebie zamykam świat za płotem?
> Intelektualny hack i tyle :)

Ja tam lubię sandboksy.

> W szczególności stan świata jest niezdefiniowany (bo i nie ma jak),
> jest ukrywany w pseudowartości która wartością nie jest. Nie bardzo
> więc można mówić o zbiorze wartości (skoro nie ma wartości), więc
> i nie bardzo można mówić o funkcji.

Chyba tego nie przemyślałeś na poziomie epistemologicznym,
niepoznawalność to jeszcze nie jest niezdefiniowanie
(że już o kryteriach bycia prawdziwą wartością nie wspomnę).

>>> Usiłowanie (niby) czysto funkcyjnej realizacji zadań które
>>> nie dają się sprowadzić do podania argumentów przy wywołaniu
>>> i poczekania na wynik
>>
>> Wszystkie się dają, kwestia czytelności i łatwości napisania.
>
> Nie dają się. Istnieją zadanie w których całe clue polega na tym,
> że są w nich zależności czasowe.

Nie bardzo chwytam, masz na myśli zadania real-time?

>>> Funcyjność nie jest interaktywna, może być
>>> interaktywnie użyta niejako z zewnątrz.
>>
>> Nie mam pojęcia, co masz na myśli.
>
> To, że wykonanie funkcji to przygotowanie argumentów i poczekanie na
> wynik. Możesz taką funkcję sobie w różnych miejscach i z różnymi
> argumentami wołać i za każdym razem czekać na wynik.

Prawdę powiedziawszy nie wiem na ile poważnie piszesz o różnej sile
języków turing-kompletnych. Z zastosowań interaktywnych masz przecież
funkcyjnie napisany filesystem, serwery sieciowe czy windowmanager.


Piotr Sawicki

Grzegorz Staniak

unread,
Dec 12, 2009, 3:39:29 AM12/12/09
to
On 12.12.2009, Piotr Sawicki <ps17...@students.mimuw.edu.pl> wroted:

>>>> BTW. zrobiono w swoim czasie badania co wpływa na liczbę
>>>> błędów w kodzie programów pisanych w różnych językach.
>>>
>>> Jak znam życie to nie robili eksperymentów tylko pomiary.
>>
>> Robili eksperyment. Ludzie dostali do napisania programy (w języku
>> który znali), potem te programy starannie sprawdzono patrząc ile
>> jest błędów.
>
> No sorry, ale to nie spełnia wymogów naukowego eksperymentu.
> Chociażby nie ma mowy o podwójnie ślepej próbie.

Jak sobie wyobrażasz podwójnie ślepą próbę w tym kontekście?
Programiści, którzy nie wiedzą w jakim języku piszą?

GS
--
Grzegorz Staniak <gstaniak _at_ wp [dot] pl>
Nocturnal Infiltration and Accurate Killing

Piotr Sawicki

unread,
Dec 12, 2009, 9:14:24 AM12/12/09
to
Grzegorz Staniak powiada:

>>> Robili eksperyment. Ludzie dostali do napisania programy (w języku
>>> który znali), potem te programy starannie sprawdzono patrząc ile
>>> jest błędów.
>>
>> No sorry, ale to nie spełnia wymogów naukowego eksperymentu.
>> Chociażby nie ma mowy o podwójnie ślepej próbie.
>
> Jak sobie wyobrażasz podwójnie ślepą próbę w tym kontekście?

No przecież napisałem że nie ma mowy. W psychologii się chyba
robi jakieś wstawianie ściemy na temat celu eksperymentu,
a prawdziwe wyniki zbiera mimochodem. Zauważ, że nie ma też
oślepienia w drugą stronę - eksperymenty fizyczne na cząstkach
w akceleratorze nie obędą się bez blindness boxu odcinającego
bias obserwatora, a tu za pozory naukowości ma służyć ,,staranne
sprawdzanie'' nie wiadomo jak zdefiniowanej liczby błędów.
Nie wiadomo też, czy takie wyniki są powtarzalne, czy w ogóle
były przeprowadzane innymi metodami i poddane metaanalizie.

> Programiści, którzy nie wiedzą w jakim języku piszą?

Tu akurat ponoć pisali w ~swoim~ języku, bo to nie bardzo da się
oddzielić. Słusznie, ale potem wyniki można zinterpretować zupełnie
inaczej. Tzn. to nie są naukowe fakty na temat języków, tylko np.
najlepsi programiści wybierają język X i takiej szukają pracy,
albo firmy zsyłają największych matołów do rzeźbienia w języku Y.


Piotr Sawicki

Sebastian Kaliszewski

unread,
Dec 14, 2009, 9:31:09 AM12/14/09
to
Piotr Sawicki wrote:
> Sebastian Kaliszewski powiada:
>
>> def z_bledem(err_cont, a, b):
>> if not a:
>> err_cont.call(ErrorA())
>> elif not b:
>> err_cont.call(ErrorB())
>>
>>
>> def wolanie(err_cont):
>> callcc cont:
>> for i in range(10):
>> z_bledem(cont, random.choice(True, False),
>> random.choice(True, False))
>> continue error:
>> if error.type is ErrorA():
>> obsługa_na_miejscu()
>> else:
>> # pozostałe błędy ma obsłużyć wolajacy wolanie
>> err_cont.call(error)
>>
>
> Tak to dziwacznie wymyśliłeś, żeby było kalką wyjątków.
> No i nadal nie wiem, gdzie jest ta niejawna globalna kontynuacja.

Nie piszę tu o globalnej kontynuacji. Przy globalnej byłoby jeszcze gorzej:

def z_bledem(a, b):
if not a:

err_cont.call(ErrorA())
elif not b:
err_cont.call(ErrorB())

itd...
err_cont nie wiadomo skąd pochodzi, bo nie jest parametrem

>
>> VS
>>
>>
>> def z_bledem(a, b):
>> if not a:
>> raise ErrorA()
>> elif not b:
>> raise ErrorB()
>>
>> def wolanie()
>> try:
>> for i in range(10):
>> z_bledem(random.choice(True, False), random.choice(True, False))
>> except ErrorA as error:
>> obsługa_na_miejscu()
>> # pozostałe błędy ma obsłużyć wolajacy wolanie
>
> VS np.
>
> def z_bledem(a,b, contA, contB):
> if not a: continue to contA()
> elif not b: continue to contB()
>
> def wolanie():
> for i in range(10):
> z_bledem(true/false, true/false, errorAhandler, errorBhandler)


Zapisz cały odpowiadający kod a nie kawałek. skąd pochodzą errorAhandler
i errorBhandler? Przecież nie z powietrza?


>>>> One mają sens gdy stosowane są do uzyskania rzeczy które w pozostałych
>>>> językach są kluczowe, tj podstawowe konstrukcje sterujące.
>>> No ale są tam stosowane również przy obsłudze sytuacji wyjątkowych.
>>> Bezsensownie?
>> Kluczowe jest *również*.
>
> Kilka postingów temu postawiłeś tezę, że używanie kontynuacji
> nie ma sensu w językach typowanych dynamicznie.

Nie prawda. Nie postawiłem takiej tezy.

Postawiłem tezę, że w językach dynamicznych w których wszelkie
podstawowe konstrukcje (wykonanie warunkowe, powtarzanie) są
zaimplementowane bez opierania się o kontynuacje, kontynuacje nie mają
sensu do obsługi sytuacji wyjątkowych.

> Jeśli teraz
> doprecyzowujesz, że tylko w tych z nich, w których kontynuacje
> nie są częścią języka,

Wcale tak nie doprecyzowuję. Czytasz to co chcesz przeczytać a nie to co
jest napisane. Piszę, że mają sens jeśli są częścią języka niezbędną dla
zaimplementowania w nim konstrukcji podstawowych takich jak wykonanie
warunkowe.

> to wychodzi banalna tautologia
> (i nie wiadomo po co w to w ogóle mieszać dynamizm).

Wychodzi tylko z tego co Ty chciałeś przeczytać a nie z tego co było
napisane.

>
>>> To czy wszystkie (kwantyfikatorowo) to bez znajomości: errcode<>success.
>> Tyle samo mi daje catch(...) -- w obu wypadkach niewiele.
>
> Da, jeśli obstawimy nim każde wołanie. No to po co w ogóle wyjątki.

Mowa o wiedzy czy coś wypadło z błędem. catch(...) daje równie dobrą
(raczej równie nędzną) wiedzę. Nie widzę specjalnego sensu rozwarzania
przypadków zdegenerowanych.

>>>>> w miejscu wywołania funkcji wiem co się stanie po powrocie.
>>>> Tzn albo pójdzie normalnie, albo poleci w stornę sprawdzenia
>>>> błedu -- tak samo jest przy wyjątkach.
>>> Nie tak samo, bo jest napisane czarno na białym jaka to strona.
>> Jeśli mamy cokolwiek więcej niż jeden ogólny błąd to dokładnie tak samo
>> nie wiadomo. W dodatku łatwiej zrobić źle i niezauważyć
>
> Jeśli podasz za mało argumentów do funkcji (tzn. pominiesz którąś
> z wyjątkowych kontynuacji), to da się to wyłapać statycznie,
> a błąd wykonania pojawi się nawet w prawidłowym przypadku.

Praktycznie to samo masz ze specyfikacjami wyjątków z głupiej Javy. Nie
skompiluje się, jeśli nie ma statycznie zapewnionej (a raczej
zamarkowanej) obsługi wszystkich błędów.

>
>>> A z exceptionami hgw gdzie poleci, trzeba sprawdzać cały program.
>> I tak trzeba sprawdzać cały program. I tu i tu.
>
> W jednym przypadku wystarczy sprawdzić liniowo, jednoprzebiegowo
> (czy każdy call uwzględnia błędy) - i to wystarczy do pewności,
> że obsługę błędów przewidziano w stu procentach.

Po pierwsze nie wystarczy, co zostało pokazane wyżej. Można sprawdzić
czy jest tylko (zamarkowana) intencja sprawdzenia błędu (tzn, czy jest
podpięta jakakolwiek akcja, jakakolwiek to również zupełnie beznsesnowna
lub w ogóle dziurawa. To samo dają zwykłe statyczne specyfikacje
wyjątków rodem z Javy.

Po drugie zaś, to to taki kod niby porządny-kontynuacyjny, się fatalnie
rozwija (do momentu kiedy nie ulegnie degeneracji). Każda zmiana
dodająca w jednej głupiej funkcji wymaga dodania nowego parametru w
każdym miejscu wołania i albo dodania go również do funkcji wołającej (i
zabawę powtarzamy dla każdej funkcji wołającej w której tak zrobimy)
albo w każdym z takich miejsc trzeba tworzyć nową kontynuację. Efekt
dąży (degeneruje się) to analogu tego co się spotyka w kodzie Javowym --
globalnego catch.

> W drugim przypadku
> konieczne jest błądzenie po całym kodzie i analiza wszystkim możliwych
> tras i przeplotów wywołań. Czas rzetelnego sprawdzenia poleciałby
> w kosmos, więc w rzeczywistości robi się to na oko zostawiając dziury.

Jest tak samo jak ze statycznymi specyfikacjami wyjątków. Albo czas
rzetelnego sprawdzenia leci w kosmos albo i tak jest na oko.

>
>>> Żeby sprawdzić jaka
>>> będzie obsługa wyjątku trzeba przeczytać zylion miejsc
>>> kodu które mogą wywoływać aktualnie czytany kawałek.
>> To samo przecież, w oczywisty sposób dotyczy użycie kontynuacji. Nawet
>> jal kontynuację dostaniesz jako parametr, przy każdym wołaniu może to
>> być kontynuacjia utworzona zupełnie gdzie indziej więc gdzie indziej
>> prowadząca. Bez sprawdzenia wszysktkuch miejsc wołania i tak nie wiesz.
>
> Jak ktoś się uprze, to zawsze może napisać program nieczytelnie
> (ale to widać i można poprawić). W przypadku wyjątków nieczytelność
> rozwiązania jest nieodłączną cechą ich głównej zalety, jaką jest
> możliwość przeniesienia obsługi błędu poza miejsce wystąpienia.

Taka sama nieczytelność jest przy kontynuacjach. Lokalnie nie wiesz skąd
kontynuację dostałeś. Tu tak samo masz "nieodłączną cechę ich głównej

zalety, jaką jest możliwość przeniesienia obsługi błędu poza miejsce
wystąpienia."


>>>> BTW. zrobiono w swoim czasie badania co wpływa na liczbę
>>>> błędów w kodzie programów pisanych w różnych językach.
>>> Jak znam życie to nie robili eksperymentów tylko pomiary.
>> Robili eksperyment. Ludzie dostali do napisania programy
>> (w języku który znali), potem te programy starannie sprawdzono
>> patrząc ile jest błędów.
>
> No sorry, ale to nie spełnia wymogów naukowego eksperymentu.

Bo?

> Chociażby nie ma mowy o podwójnie ślepej próbie.

LOL! Idź powiedz to jakiemukolwiek socjologowi :)

Podwójnie ślepa próba *nie jest* jedyną metodą naukowego eksperymentu.

Poza tym jak chcesz na powójnie ślepo testować języki?


>>> W szczególności niewykrycie innych korelacji mogło wynikać
>>> nie z ich braku, a z przyjęcia złych hipotez badawczych.
>> Hipotezą było, że są języki mniej i bardziej błędogenne.
>
> To nie jest nawet poprawnie zdefiniowane, nie wspominając
> w ogóle o falsyfikowalności. Można sobie policzyć jakieś
> korelacje na nielosowej próbce algorytmów, i w zasadzie tyle.
> Nie wiadomo jakich korelacji nie stwierdzono, bo ich nie badano.

Ach tak, ty nie czytałeś ale wiesz...


>>> I to każdy
>>> poszczególny call, a nie cały obleczony try-catchem blok.
>> Jak ci bardzo na tej wiedzy zależy, to na końcu możesz
>> mieć catch(...) -- wtedy wiesz, że tędy przeleci.
>
> Dlatego pisałem, że wyjątki w najlepszym przypadku dadzą taki sam
> efekt jak inne metody (po rezygnacji z ich kluczowego ficzeru
> przerzucenia obsługi błędów wzwyż), ale wtedy są zbędną konstrukcją.

Co ma to co tu napisałeś do tego co ja napisałem?

Poza tym, tak samo kluczowym ficzerem obsługi błędów przez kontynuację
jest przerzucanie obsługi błędów wzwyż.

>>>> Jeśli masz statyczne specyfikacje wyjątków, to albo obsługa
>>>> jest na końcu funkcji (w catch / except czy jak tam autorzy
>>>> języka nazwali) albo w sygnaturze funkcji jest jawnie napisane,
>>>> że leci dalej.
>>> To akurat nie jest złe - ale to słabsza cecha
>>> (w sensie matematycznym).
>> No właśnie nie widzę tej "słabszości".
>
> Nie wiesz konkretnie które z wywołań zadeklarowanych blokowo
> do łapania/przerzucania wyjątków rzuca które z nich,

??? Podkręć jasność.

except WyjątekA łapie obiekty klasy WyjątekA a except WyjątekB łapie
obikety klasy WyjątekB.

> trudniej sprawdzić poprawność takiej obsługi błędów.

Tzn?


>>>> W przypadku kontynuacji albo masz ogólny skok
>>>> do miejsca gdzieś w ciele funkcji z której wołasz,
>>>> albo kontynuacja była parametrem
>>> Albo wskazujesz inny fragment kodu [...]
>> Ale ten fragment kodu jest skądś dany
>
> Normalnie palcem można pokazać, nie ma musu
> stosować na upartego metodę exceptionową w innej składni.

Co można palcem pokazać? To, że nawet przekażesz osobno kontynuację dla
każdego rodzaju błędu (co samo w sobie w szybkim tempie prowadzi do
zaciemnienia wywołań) to i tak albo utworzyłeś ją sobie w bloku z
którego wołasz (czyli jest to samo co except czy C++owe catch tylko
inaczej zapisane) albo dostałeś z zewnątrz (znów to samo co z wyjątkami).


>> albo zrobiłem go sobie lokalnie (czyli tak jak catch / except)
>> albo przyszedł z zewnątrz (czyli lokalnei
>> nie wiem gdzie to dalej poleci).
>
> Tylko że jak ktoś ma problem z tym, że w programowaniu używa się
> zmiennych, to lepiej niech zmieni profil. Przecież nie będziesz
> zarzucał instrukcji print, że nie wiesz co zostanie wydrukowane
> przez ,,print variableA''. Problem byłby dopiero gdyby bezparametrowy
> secretprint drukował nieoczywistą wartość wyciągniętą gdzieś z szafy
> (dla mnie tak wygląda perl i jego m@giczne #argumenty domy$lne).

Zatem gdzie widzisz różnicę między napisaniem:

funkcja(x, y, z, on_a, on_b)

a funkcja(x, y, z) throws a, b


Pota oczywiście tym, że od razu widać że a, b to błędy powodowane przez
funkcję a w on_a on_b trzeba szukać w dokumentacji (albo oprzeć się na
obyczaju nazewniczym).


>> To nie jest kwestia spartaczenia kontynuacji tylko istnienia
>> globalnych zmiennych. I (złego) nauczenia ludzi by ich
>> (nad)używali do trzymania kontynuacji.
>
> No dobra, ale to kwestia używania zmiennych globalnych
> w ogóle, a nie szczególnego przypadku.

W tym przypadku "kultura języka" promowała takie rozwiązanie. Ale to już
problem "kultury języka".


>>>> W przypadku wyjątków Każda wołana funkcja też ma swoją sygnaturę --
>>>> wystarczy znać tę sygnaturę by wiedzieć jakie wyjątki wypuszcza.
>>> Ale nie kto je łapie.
>> Tak samo jest z kontynuacjami.
>
> Nie tak samo, bo od razu wiem że błąd jest obsłużony,

Jak?

> już w tym
> miejscu. Samo info o rzucaniu wyżej wyjątku mi jeszcze nie mówi,
> czy jest zawsze sensownie złapany, czy ktoś walnął wielki catch
> na wszystko żeby zapuścić daily build i w końcu odebrać dziecko
> z przedszkola. Wiem że odbębnić zawsze się da, ale możliwość
> otoczenia całego kawałka kodu klamrą catch-everything to ułatwia.

W celu odbębnienia wystarczy raz sobie utworzyć kontynuację
on_general_failure i sobie ją przekazać gdzie jej potrzeba.

W języku dynamicznym jest jeszcze gorzej, bo możesz zamiast kontynuacji
przekazać jakikolwiek śmieć (choćby None) i statycznie nic nie wiesz. A
jakiś tam parametr przekazany jako None nie wygląda od razu alarmująco.

M.in. dlatego piszę że obsługa błędów przez kontynuacje w językach
dynamicznych ma sens mocno wątpliwy. I dlatego odpowiednio ograniczona
(choć wystarczająca) forma kontynuacji jaką są wyjątki jest w takiej
sytuacji lepsza, bo trudniej wszystko cwaniacko rozwalić. Podobnie ja
programowanie strukturalne -- if, while, for z break i continue jest
krokiem naprzód wobec łysego goto -- choć za pomocą goto daje się
wyrzebić więcej niż za pomocą powyższych (które są formą ograniczenia
goto i opakowania go w ramy).

>> kontynuacje zachęcają by je stworzyć, przekazać
>> gdzie są wymagane a następnie po cichu wyrzucić.
>
> Jak na zachęcanie to strasznie dużo roboty na takie obejście.
> A sprawdza się je prosto i tylko raz - w definicji wołanej procedury
> (sprawdzenie jej jest i tak nie do uniknięcia, więc ta sama robota).

W językach dynamicznych sprawdzenie jest przy użyciu.

>> Przy specyfikowaniu wyjątków jest nie gorzej niż przy kontynuacjach --
>> czy dodam do funkcji dodatkowy parametr czy dospecyfikuję rzucane
>> wyjątki to widać/ nie widać partactwo tak samo.
>
> Nie zgadzam się, po dopisku throws cośtam jeszcze nie wiadomo czy
> tak powinno być czy nie, i gdzie tkwi problem. W drugim przypadku
> wszystko co potrzebne do wypatrzenia partactwa jest na miejscu.

No właśnie nie widzę tego na miejscu.

To, że w różnych rzadko używanych językach zwykle nie widzisz takiego
kodu wynika moim zdaniem *tylko i wyłącznie* z faktu że są właśnie
rzadko używane, a więc nie są używane przez przeciętną programistyczną
małpę (małpy, zależnie od prowieniencji, swoją wiedzę kończą na (Visul)
Basicu, Javie, PHP, Deplhi (oni nie znają Objective Pascala, oni znają
Delphi), C lub Fortranie).

>
>> otrzymana z zewnątrz kontynuacja tak samo będzie pasować
>> do wszystkich razem.
>
> Taką bzdurę, że każdy błąd ma ten sam handler, to od razu widać.

Tak samo jak brak catchy w kodzie tylko jeden w main.

Jestem praktycznie pewien, że gdyby Haskell zyskał popularność jak C++
(popularności Javy nie zyska po za trudny, porównujmy więc z językiem
też trudnym) to takie kwiatki widywałoby się w kodzie haskellowym na
każdym kroku.

>
>>> I które to są te kluczowe różnice w składni i semantyce między
>>> Erlangiem a innymi funkcyjnymi? Bo dla mnie jest przede wszystkim mniej
>>> czytelny, a plusy widzę głównie w maszynie wirtualnej (czy jak to zwać).
>> Ta wiele rzeczy co ta maszyna wirtualna daje to część semantyki.
>> Głównie w całej zabawie w przesyłanie komunikatów w około którego
>> wszystko się kręci. I w tym, że mamy "wbitą w semantykę" współbieżność.
>
> Spawnowanie ~procesów~ i przesyłanie komunikatów jest też gdzie
> indziej bibliotecznie, tylko bez zapewnienia wszystkich
> bajeranckich cech produkcyjnych erlangowego vm-u. Tychże pozbawiony
> interpreter erlanga napisany samemu byłby już mocno taki sobie.

I owszem i nikt by go nie użył. Ale conajmniej część tych cech
produkcyjnych łapie się w semantykę. No i w ogóle częścią Erlanga są
określone np. założenia wydajnościowe (choć można dyskutoweać czy to
semantyka czy nie -- w każdym razie są języki gdzie np. specyfikuje się
koszt asymptotyczny określonych operacji).
Podobnie określone założenia (choć zupełnie inne) są niejako częścią np.
C (bliskość sprzętu, w wielu dziedzinach warunek konieczny).


>>> Lambdy, funkcje wyższych rzędów, częściowa ewaluacja i leniwość
>>> to cechy w dobrych językach dostępne out of the box, bez żadnego
>>> metaprogramowania.
>> Ale ja nie mówię o tym. boost::lambda to co innego niż boost::mpl.
>> boost::mpl daje metefunkcje (biedne i niewygodne ale są).
>
> To co mpl nazywa metafunkcjami to przecież po prostu funkcje
> wyższych rzędów - gdzie indziej nie robi się takiego rozróżnienia,
> po prostu typ->typ jest równie dobrym typem jak struktura czy unia.

Nnno, pod warunkiem, że typ jest obiektem języka. Metafunkcje też tym
się też wyróżniają, że są wykonywane w czasie kompilacji i semantyka
języka to wymusza.


>> W mpl pisze się programy piszące programy.
>
> Hello world.

Generalnie tak, ale jak już pisałem, spotykam takich którzy w tym piszą
zdecudowanie więcej. A przez samą liczbę piszących w C++ może się okazać
że oni i tak stanowią większość metaprogramujących w ogóle.


>>>> BTW. nigdy nie podobało mi się użycie monad (plus dziwacznych typów
>>>> reprezentujących stan świata) do uzyskiwania prawie-imperatywizmu.
>>>> Dla mnie to jest intelektualny hack.
>>> Zamyka efekty uboczne w piaskownicy, niezły wynik.
>> Stawiając płot dookoła sebie zamykam świat za płotem?
>> Intelektualny hack i tyle :)
>
> Ja tam lubię sandboksy.

Dla mnie to to jest sandbox wywrócony na lewą stronę :)


>> W szczególności stan świata jest niezdefiniowany (bo i nie ma jak),
>> jest ukrywany w pseudowartości która wartością nie jest. Nie bardzo
>> więc można mówić o zbiorze wartości (skoro nie ma wartości), więc
>> i nie bardzo można mówić o funkcji.
>
> Chyba tego nie przemyślałeś na poziomie epistemologicznym,
> niepoznawalność to jeszcze nie jest niezdefiniowanie
> (że już o kryteriach bycia prawdziwą wartością nie wspomnę).

Niepoznawalność to oczywiście nie niezdefiniowanie. Ale tu wg mnie mamy
właśnie niezdefiniowanie. Wartość niepoznawalna przede wszytskim jest i
jest niezmienna. Można się nią normalnie posługiwać poza samym
poznaniem. Świat (stan zewnętrzny) to nie jest wartość w normalnym
rozumieniu tego słowa, jest np. jednorazowy (nie dziwne nie daje się
cofnąć ani zatrzymać), itd...

Tzn. typ w którym reprezentuję niepoznawalną stałą Chatina jest dla mnie
jak najbardziej normalnym typem, jego wartości mogę przypisywać,
przekazywać do wywołań, brać jako wyniki, itd. A tu guzik.

>
>>>> Usiłowanie (niby) czysto funkcyjnej realizacji zadań które
>>>> nie dają się sprowadzić do podania argumentów przy wywołaniu
>>>> i poczekania na wynik
>>> Wszystkie się dają, kwestia czytelności i łatwości napisania.
>> Nie dają się. Istnieją zadanie w których całe clue polega na tym,
>> że są w nich zależności czasowe.
>
> Nie bardzo chwytam, masz na myśli zadania real-time?

Nie tylko, ale też. Ogólnie zadania w których pewne akcje zależą od
wcześniej wykonanych, od wyników (i błędów) występujących wcześniej. W
dodatku mogą też zależeć od pory dnia/tygodnia/miesiąca/roku. Ot,
przeciętny program użytkowy typu Word się tu łapie.

>
>>>> Funcyjność nie jest interaktywna, może być
>>>> interaktywnie użyta niejako z zewnątrz.
>>> Nie mam pojęcia, co masz na myśli.
>> To, że wykonanie funkcji to przygotowanie argumentów i poczekanie na
>> wynik. Możesz taką funkcję sobie w różnych miejscach i z różnymi
>> argumentami wołać i za każdym razem czekać na wynik.
>
> Prawdę powiedziawszy nie wiem na ile poważnie piszesz o różnej sile
> języków turing-kompletnych. Z zastosowań interaktywnych masz przecież
> funkcyjnie napisany filesystem, serwery sieciowe czy windowmanager.

Pamiętaj, że maszyna Turinga to tylko model. Pewnych rzeczy nie modeluje
i tyle -- po prostu go nie dotyczą. Funkcyjnie możesz zapisać stan
wewnętrzny, ale ze stanem zewnętrznym (czas systemowy, zewnętrzne źródło
danych losowych, etc.) jest gorzej. Albo zamiata się pod dywan tworząc
pseudowartości których się nie daje traktować jak wartości i udaje że
wszystko ok, albo po prostu przyznaje się jasno, że coś tam wykracza
poza model i już. Wolę ten drugi wariant, wolę języki gdzie jest
napisane: ten kawałek nie jest funkcyjny i już, używamy tak i tak. Ale
to moja osobista, głównie estetyczna preferencja.

Sebastian Kaliszewski

unread,
Dec 15, 2009, 5:24:56 AM12/15/09
to
Piotr Sawicki wrote:
> Grzegorz Staniak powiada:
>
>>>> Robili eksperyment. Ludzie dostali do napisania programy (w języku
>>>> który znali), potem te programy starannie sprawdzono patrząc ile
>>>> jest błędów.
>>> No sorry, ale to nie spełnia wymogów naukowego eksperymentu.
>>> Chociażby nie ma mowy o podwójnie ślepej próbie.
>> Jak sobie wyobrażasz podwójnie ślepą próbę w tym kontekście?
>
> No przecież napisałem że nie ma mowy.

Co absolutnie nie wpływa na "naukowość". Ślepa próba jest tylko jedną z
metod (tu nie mającą żadnego sensu).

BTW. naukowe to jest wtedy kiedy zostało opublikowane jako publikacja
naukowa z podlegająca recenzji. Kropka.

> W psychologii się chyba
> robi jakieś wstawianie ściemy na temat celu eksperymentu,
> a prawdziwe wyniki zbiera mimochodem.
> Zauważ, że nie ma też oślepienia w drugą stronę - eksperymenty fizyczne
> na cząstkach w akceleratorze nie obędą się bez blindness boxu
odcinającego
> bias obserwatora, a tu za pozory naukowości ma służyć ,,staranne
> sprawdzanie'' nie wiadomo jak zdefiniowanej liczby błędów.

Nie wiesz co to jest błąd?
Liczba owych błędów w pojedynczym programie jest również dobrze określona.

AFAIR udostęponio również napisane w ramach eksperymentu programy, każdy
mógł sobie sprawdzić czy błędy policzono poprawnie.

> Nie wiadomo też, czy takie wyniki są powtarzalne, czy w ogóle
> były przeprowadzane innymi metodami i poddane metaanalizie.

Eksperyment przeprowadzono dwoma metodami, wynik wyszedł tak samo.

>> Programiści, którzy nie wiedzą w jakim języku piszą?
>
> Tu akurat ponoć pisali w ~swoim~ języku, bo to nie bardzo da się
> oddzielić. Słusznie, ale potem wyniki można zinterpretować zupełnie
> inaczej. Tzn. to nie są naukowe fakty na temat języków, tylko np.
> najlepsi programiści wybierają język X i takiej szukają pracy,
> albo firmy zsyłają największych matołów do rzeźbienia w języku Y.

Bardzo "ciekawe" wnioski "wyciągasz" w ogóle nie znając tych badań.
Jedno jest pewne, te wnioski są "zupełnie nie naukowe" :)

Nie chcesz - nie wierz, to Twój nie mój problem.

Piotr Sawicki

unread,
Dec 16, 2009, 10:30:49 AM12/16/09
to
Sebastian Kaliszewski powiada:

[ciach zmienne globalne - nie używać i tyle]

> Zapisz cały odpowiadający kod a nie kawałek. skąd pochodzą
> errorAhandler i errorBhandler? Przecież nie z powietrza?

Nazwy procedur. Nie muszą być albo-albo (argumentowane z góry
albo lokalnie zdefiniowane w tym miejscu) tak samo jak wyjątki.
Można np. użyć biblioteki do obsługi błędów.

>> Kilka postingów temu postawiłeś tezę, że używanie kontynuacji
>> nie ma sensu w językach typowanych dynamicznie.
>
> Nie prawda. Nie postawiłem takiej tezy.

OK, po prostu wyczytałem to z ,,W przypadku języków dynamicznych
i tak nie bardzo widzę zaletę. To co ma sens w językach statycznie
typowanych często nie ma już sensu wdynamicznie typowanych.''
i stąd polemika - ale skoro doprecyzowujesz to no problemo.

> w językach dynamicznych w których wszelkie podstawowe
> konstrukcje (wykonanie warunkowe, powtarzanie) są
> zaimplementowane bez opierania się o kontynuacje,
> kontynuacje nie mają sensu do obsługi sytuacji wyjątkowych.

Ja widzę taką korzyść: funkcje to podstawowa cegiełka programowania,
z którą można prosto i łatwo zrobić mnóstwo rzeczy gotowymi narzędziami
(dowolnie je przekazywać, komponować, transformować, agregować, wrzucać
w mapę itd) - choćby wspominanymi przez ciebie metafunkcjami.
Z tym samym blokiem kodu, ale wrzuconym w specjalną statyczną
składnię tylko-dla-wyjątków da się bez zamotanego mambodżambo już
zrobić bardzo niewiele (pominąć albo skleić tylko w jednej możliwej
kolejności). Dla mnie to jak różnica między programem posługującym
się na stdin i stdout czytelnym formatem tekstowym możliwym do
obróbki narzędziami shellowymi, a takim który dłubie w pliku z blobem
binarnym, z którym da się zrobić tylko to co z góry przewidziano.

> Mowa o wiedzy czy coś wypadło z błędem.

Nie, ja piszę o innym aspekcie - wiedzy czy w danym fragmencie
kodu przewidziano obsługę wszystkich potencjalnych błędów.

> Praktycznie to samo masz ze specyfikacjami wyjątków z głupiej Javy.
> Nie skompiluje się, jeśli nie ma statycznie zapewnionej (a raczej
> zamarkowanej) obsługi wszystkich błędów.

Kompilator sprawdzi tylko poprawność formalną.

> kod niby porządny-kontynuacyjny się fatalnie rozwija

> (do momentu kiedy nie ulegnie degeneracji). Każda zmiana
> dodająca w jednej głupiej funkcji wymaga dodania nowego
> parametru w każdym miejscu wołania

W razie zmiany interfejsu, a nie implementacji. No i bardzo dobrze,
po takiej zmianie naprawdę trzeba skorygować wszystkie przypadki
użycia - przynajmniej widać czarno na białym, które już poprawiono
a które jeszcze nie. Jeśli zmienił się kontrakt, a stare wywołania
są nieodróżnialne od nowych to dopiero jest problem logistyczny
z przeprowadzeniem całości kodu do nowej wersji.

> Jest tak samo jak ze statycznymi specyfikacjami wyjątków. Albo czas
> rzetelnego sprawdzenia leci w kosmos albo i tak jest na oko.

Tu jest różnica z brakiem jawnej specyfikacji czegokolwiek
- pewien rodzaj enkapsulacji, uniezależniający od siebie poprawność
odrębnych kawałków programu. Dzięki temu czas sprawdzania właśnie
nie rośnie wykładniczo, bo można to zrobić dla każdej funkcji
oddzielnie. Więc doceniam wrzucanie wyjątków w sygnaturę (ale to
jest tylko w javie AFAIK), tylko nie wiem po co wprowadzać wtórny
system [quasi]typów dla argumentów ukrytych składniowo (wyjątków).

> kluczowym ficzerem obsługi błędów przez kontynuację
> jest przerzucanie obsługi błędów wzwyż.

Mam wrażenie (na podstawie przykładowego kodu), że masz zafiksowany
w głowie sposób obsługi błędów poprzez wyjątki i każdą alternatywną
metodę wyobrażasz sobie przez takie jej naginanie żeby innymi środkami
programować nadal na modłę wyjątków. Nie jest kluczowym ficzerem,
bo tak się da ale to jednak niekonieczne, można też zupełnie inaczej.

>> Nie wiesz konkretnie które z wywołań zadeklarowanych blokowo
>> do łapania/przerzucania wyjątków rzuca które z nich,
>
> ??? Podkręć jasność.
>
> except WyjątekA łapie obiekty klasy WyjątekA
> a except WyjątekB łapie obikety klasy WyjątekB.

Nie wiadomo z którego calla ten wyjątek wyleciał, nie zawsze
wynika to z klasy wyjątku. Przy czym nie chodzi mi o moment łapania
błędu w runtimie, tylko o czytelność przy uprzednim go sprawdzaniu.

> Zatem gdzie widzisz różnicę między napisaniem:
>
> funkcja(x, y, z, on_a, on_b)
>
> a funkcja(x, y, z) throws a, b

W tym że jedno wywołanie wygląda funkcja(x,y,z,on_a,on_b), a drugie
funkcja(x,y,z) - chyba oczywiste. W pierwszym wypadku czytając
takie wywołanie od razu wiem że obsługę błędów przewidziano.
I ona oczywiście może być niepoprawna, ale to kwestia sprawdzenia
tamtego kodu, tu na razie jest ok. W drugim nie mogę odfajkować
poprawności właśnie oglądanego kodu dopóki nie sprawdzę w wielu
innych miejscach - w szczególności punktach wołających aktualny
kawałek kodu. Które w trakcie rozwoju programu mogą się jeszcze
pojawiać albo zmieniać, więc tak czy siak zostawiam tykającą bombę.

>>> Tak samo jest z kontynuacjami.
>>
>> Nie tak samo, bo od razu wiem że błąd jest obsłużony,
>
> Jak?

Tak że podano kontynuację dla wyjątkowego przypadku (i jeśli jest
to onGeneralFailure albo None to od razu to widzę). Z wyjątkami
mogę to wiedzieć na podstawie tego, że wpisano łapanie wyjątku.
Ale. To jest zupełnie gdzie indziej w kodzie. W wielu miejscach.

[wpisywanie śmieci]


> I dlatego odpowiednio ograniczona (choć wystarczająca)
> forma kontynuacji jaką są wyjątki jest w takiej
> sytuacji lepsza, bo trudniej wszystko cwaniacko rozwalić.

Na tej zasadzie to równie dobrze można dla każdego catcha wpisać
pass. Przecież pisałem od początku o QA, a nie magicznym wzroście
poprawności jednoosobowo pisanego programu dzięki używaniu silverbullet.
Tak naprawdę to największym zagrożeniem nie jest to, że programista
przewidzi wszystkie błędy i do ich obsługi perfidnie wpisze śmieci.
Tylko że normalny programista z definicji (niezależnej od znajomości
języków programowania) uważa że wszystko pójdzie dobrze, i koncentruje
się na przebiegu algorytmu w poprawnych sytuacjach. A przetykanie kodu
możliwościami niepowodzeń na każdym kroku jest dla niego upierdliwe.

>> Jak na zachęcanie to strasznie dużo roboty na takie obejście.
>> A sprawdza się je prosto i tylko raz - w definicji wołanej procedury
>> (sprawdzenie jej jest i tak nie do uniknięcia, więc ta sama robota).
>
> W językach dynamicznych sprawdzenie jest przy użyciu.

Nie piszę o działaniu programu, tylko czytelności.

>> po dopisku throws cośtam jeszcze nie wiadomo czy
>> tak powinno być czy nie, i gdzie tkwi problem. W drugim przypadku
>> wszystko co potrzebne do wypatrzenia partactwa jest na miejscu.
>
> No właśnie nie widzę tego na miejscu.

call funkcja(x,y,z, None, onGeneralFailure) //- ja widzę jak na dłoni.

> To, że w różnych rzadko używanych językach zwykle nie widzisz takiego
> kodu wynika moim zdaniem *tylko i wyłącznie* z faktu że są właśnie
> rzadko używane, a więc nie są używane przez przeciętną programistyczną

Kiedy właśnie o to chodzi że widzę. Błędy są zawsze, tylko w języku
czytelnym je o wiele łatwiej dostrzec niż w kryptycznym. A jeszcze
trudniej z przeoczeniami. Rzeczy opisywane jawnie przy czytaniu widać,
niejawnych trzeba się domyślać i robić reverse-enginnering.

-----

> conajmniej część tych cech produkcyjnych łapie się w semantykę.
> No i w ogóle częścią Erlanga są określone np. założenia wydajnościowe
> (choć można dyskutoweać czy to semantyka czy nie -- w każdym razie
> są języki gdzie np. specyfikuje się koszt asymptotyczny określonych
> operacji).

Dla mnie to raczej kwestia implementacyjna - ale skoro się
zgadzamy co do meritum to nie ma sensu spór o nazewnictwo.

>>>> Zamyka efekty uboczne w piaskownicy, niezły wynik.

[...]


> Dla mnie to to jest sandbox wywrócony na lewą stronę :)

Na prawą: możesz narobić efektów ubocznych, ale tylko wewnątrz
monady, bez zaśmiecenia nimi całego środowiska.

> Wartość niepoznawalna przede wszytskim jest i jest niezmienna.
> Można się nią normalnie posługiwać poza samym poznaniem.

Zmienność się modeluje funkcjami.

> Świat (stan zewnętrzny) to nie jest wartość w normalnym
> rozumieniu tego słowa, jest np. jednorazowy (nie dziwne nie daje się
> cofnąć ani zatrzymać), itd...

Bo masz rozumienie wartości z ubogich języków. Wartością pierwszego
rzędu może być nie tylko stała, ale również bez problemu funkcja
(nawet z funkcji w funkcje) - więc świat może być np. ciągiem
statycznych stopklatek, a ciąg już się bardzo ładnie modeluje.

> Tzn. typ w którym reprezentuję niepoznawalną stałą Chatina jest dla mnie
> jak najbardziej normalnym typem, jego wartości mogę przypisywać,
> przekazywać do wywołań, brać jako wyniki, itd. A tu guzik.

Też możesz, od tego m.in. są monady stanowe. Nawet w zapisie
to wygląda podobnie jak używanie zmiennych w komputacji imperatywnej.

> Ogólnie zadania w których pewne akcje zależą od wcześniej
> wykonanych, od wyników (i błędów) występujących wcześniej.

No to niech zależą, funkcjami się opisuje właśnie zależności
- wystarczy je wpisać w parametr funkcji. Naprawdę takich nie
widziałeś, np. szkolnego przykładu generatorów kolejnych liczb
pierwszych, zależnie od tego jaka była ostatnia policzona?

> Funkcyjnie możesz zapisać stan wewnętrzny, ale ze stanem zewnętrznym
> (czas systemowy, zewnętrzne źródło danych losowych, etc.) jest gorzej.
> Albo zamiata się pod dywan tworząc pseudowartości których się nie daje
> traktować jak wartości i udaje że wszystko ok,

To jest wewnętrznie sprzeczne - właśnie się da tak traktować, i dlatego
marudzisz że to udawanie, bo jednak działa. Wymyśliłeś jakąś ezoteryczną
kategorię pseudowartości (?), ale tam nie ma żadnego zaszytego w środku
kompilatora hacku - to jest w bibliotece, zamodelowane normalnymi
mechanizmami języka, dało się zrobić jak wszystkie inne funkcje.


Piotr Sawicki

Piotr Sawicki

unread,
Dec 16, 2009, 11:42:03 AM12/16/09
to
Sebastian Kaliszewski powiada: [skleiłem]


>>>>> Robili eksperyment. Ludzie dostali do napisania programy (w języku
>>>>> który znali), potem te programy starannie sprawdzono patrząc ile
>>>>> jest błędów.
>>>> No sorry, ale to nie spełnia wymogów naukowego eksperymentu.
> Bo?

Bo zawiera błędy metodologiczne, jak observer bias. To naprawdę
nie jest urojony problem - wychodzi nawet przy twardych ściślackich
eksperymentach z fizyki. Np. chcąc potwierdzić wyrozumowaną regułę
zachowania powłok elektronowych przez zgodność danych obserwacyjnych
z liczbami przewidzianymi wzorem, eksperymentatorzy tak starannie
zliczali rozbłyski w komorze scyntylacyjnej, że sprawdziło im się
kompletnie fałszywe (jak się później okazało dzięki odpowiedniej
metodologii) prawo fizyki. Poleciłbym książkę A.K. Wróblewskiego
o historii nauki, ale wykłady (wolnodostępne) są lepsze.

>>>> Chociażby nie ma mowy o podwójnie ślepej próbie.

>LOL! Idź powiedz to jakiemukolwiek socjologowi :)

Każdy socjolog zna problem styku ankietera z ankietowanym, np.
eksperyment wykazujący, że wynik zwykłego testu czy dla badanych
lepszy smak ma coca-cola czy pepsi zależy od gustu badacza.

>>> Jak sobie wyobrażasz podwójnie ślepą próbę w tym kontekście?
>>
>> No przecież napisałem że nie ma mowy.
>
> Co absolutnie nie wpływa na "naukowość". Ślepa próba
> jest tylko jedną z metod (tu nie mającą żadnego sensu).

Próba nieślepa nie jest alternatywą dającą równie uprawnione wnioski.

> BTW. naukowe to jest wtedy kiedy zostało opublikowane jako
> publikacja naukowa z podlegająca recenzji. Kropka.

Piękna teoria, ale nauka jest teraz tak poszufladkowana,
że za trudno już o niezależnego recenzenta z dokładnie tej
samej dziedziny - nawet było kilka afer z opublikowaniem
w renomowanych czasopismach zupełnych bzdur (nawet artykuł
wygenerowany losowo ze słownika domenowej terminologii)
bez realnej weryfikacji. Uczestnictwo w dyskursie naukowym
opiera się na zgodzie na nieustanne podleganie krytyce,
sprawdzaniu wyników czy reinterpretacji, ogólnie: obalalności.
A nie uzyskaniu imprimatur od namaszczonych autorytetów.


>> nie wiadomo jak zdefiniowanej liczby błędów.
>
> Nie wiesz co to jest błąd?

Nie znam jakiejś jedynie słusznej ścisłej definicji. Są różne
rodzaje błędów różnej skali, o odmiennych konsekwencjach, diametralnie
innym czasie znajdywania czy poprawiania itd. Plus cała szara strefa
między już błędem a jeszcze tylko złą praktyką programistyczną,
której się nie powinno stosować bo albo do błędu w końcu doprowadzi
albo np. znacznie podroży koszt dalszego rozwoju aplikacji.

> Liczba owych błędów w pojedynczym programie jest również dobrze określona.

W ogóle nie jest określona, dopóki się nie zdefiniuje sposobu ich liczenia.

>> Nie wiadomo też, czy takie wyniki są powtarzalne, czy w ogóle
>> były przeprowadzane innymi metodami i poddane metaanalizie.
>
> Eksperyment przeprowadzono dwoma metodami, wynik wyszedł tak samo.

To napisz albo podaj linka.

>>> Hipotezą było, że są języki mniej i bardziej błędogenne.
>> To nie jest nawet poprawnie zdefiniowane, nie wspominając
>> w ogóle o falsyfikowalności. Można sobie policzyć jakieś
>> korelacje na nielosowej próbce algorytmów, i w zasadzie tyle.
>> Nie wiadomo jakich korelacji nie stwierdzono, bo ich nie badano.
>

> Ach tak, ty nie czytałeś ale wiesz...

Wiem co napisałeś. Od siebie dodaję tylko założenie, że rzetelnie
zrelacjonowałeś te badania. Oraz wiem, że jeśli jakiejś hipotezy
nie postawiono, to i jej nie obalono, to akurat stwierdzenie ogólne.

> Bardzo "ciekawe" wnioski "wyciągasz" w ogóle nie znając tych badań.
> Jedno jest pewne, te wnioski są "zupełnie nie naukowe" :)

Właśnie są to równie dobre interpretacje tej samej korelacji
(która przecież nie jest zależnością przyczynowo-skutkową).

> Nie chcesz - nie wierz, to Twój nie mój problem.

No właśnie problem w tym, że trzeba to przyjąć na wiarę, bo nie
widać dowodu z prawdziwego zdarzenia. Więc wybacz, ale dygresję
o tym eksperymencie potraktuję jako anegdotkę a nie twardy argument
w dyskusji o przyczynach powstawania błędów w oprogramowaniu.


Piotr Sawicki

Sebastian Kaliszewski

unread,
Dec 17, 2009, 8:02:50 AM12/17/09
to
Piotr Sawicki wrote:
>> Zapisz cały odpowiadający kod a nie kawałek. skąd pochodzą
>> errorAhandler i errorBhandler? Przecież nie z powietrza?
>
> Nazwy procedur.

Czyżby. Jak chcesz kontynuować do li tylko nazwanej procedury bez
podania jej gdzie dla odmiany ona ma kontynuować. Gdzieś w końcu te
kontynuacje tworzysz -- więc to pokaż, przynjamniej dla tej jednej,
którą w przykłądzie z wyjątkami załatwiam lokalnie.

> Nie muszą być albo-albo (argumentowane z góry
> albo lokalnie zdefiniowane w tym miejscu) tak samo jak wyjątki.
> Można np. użyć biblioteki do obsługi błędów.

Ale ta biblioteka musi wiedzieć gdzie kontynuować, albo błąd oznacza
koniec programu.

W podanym przeze mnie przykładzie jeden błąd jest obsługiwany lokalnie
(w wolanie) po czym program jest kontynuowany, zatem i w przykładzie
kontynuacyjnym oczekuję, że będzie jego obsługa pokazana (wraz z
kontynuowaniem działania programu).


[...]


>> w językach dynamicznych w których wszelkie podstawowe
>> konstrukcje (wykonanie warunkowe, powtarzanie) są
>> zaimplementowane bez opierania się o kontynuacje,
>> kontynuacje nie mają sensu do obsługi sytuacji wyjątkowych.
>
> Ja widzę taką korzyść: funkcje to podstawowa cegiełka programowania,
> z którą można prosto i łatwo zrobić mnóstwo rzeczy gotowymi narzędziami
> (dowolnie je przekazywać, komponować, transformować, agregować, wrzucać
> w mapę itd) - choćby wspominanymi przez ciebie metafunkcjami.
> Z tym samym blokiem kodu, ale wrzuconym w specjalną statyczną
> składnię tylko-dla-wyjątków da się bez zamotanego mambodżambo już
> zrobić bardzo niewiele (pominąć albo skleić tylko w jednej możliwej
> kolejności). Dla mnie to jak różnica między programem posługującym
> się na stdin i stdout czytelnym formatem tekstowym możliwym do
> obróbki narzędziami shellowymi, a takim który dłubie w pliku z blobem
> binarnym, z którym da się zrobić tylko to co z góry przewidziano.

Ale kontynuacje to nie są takie zupełnie zwykłe funkcje -- to w końcu
forma goto -- można je także określić jako metodę dynamicznego składania
funkcji.


[...]


>> Praktycznie to samo masz ze specyfikacjami wyjątków z głupiej Javy.
>> Nie skompiluje się, jeśli nie ma statycznie zapewnionej (a raczej
>> zamarkowanej) obsługi wszystkich błędów.
>
> Kompilator sprawdzi tylko poprawność formalną.
>
>> kod niby porządny-kontynuacyjny się fatalnie rozwija
>> (do momentu kiedy nie ulegnie degeneracji). Każda zmiana
>> dodająca w jednej głupiej funkcji wymaga dodania nowego
>> parametru w każdym miejscu wołania
>
> W razie zmiany interfejsu, a nie implementacji.

Problem taki, że zmiana implementacji zbyt często wymusza zmianę interfejsu.

> No i bardzo dobrze,
> po takiej zmianie naprawdę trzeba skorygować wszystkie przypadki
> użycia - przynajmniej widać czarno na białym, które już poprawiono
> a które jeszcze nie. Jeśli zmienił się kontrakt, a stare wywołania
> są nieodróżnialne od nowych to dopiero jest problem logistyczny
> z przeprowadzeniem całości kodu do nowej wersji.

Problem taki, że kontrakt z punktu widzenia klasycznej poprawności
aplikacji się nie zmienił. Zmieniły się "peryferia".

>
>> Jest tak samo jak ze statycznymi specyfikacjami wyjątków. Albo czas
>> rzetelnego sprawdzenia leci w kosmos albo i tak jest na oko.
>
> Tu jest różnica z brakiem jawnej specyfikacji czegokolwiek
> - pewien rodzaj enkapsulacji, uniezależniający od siebie poprawność
> odrębnych kawałków programu.

No właśnie on uzależnia. I to często niepotrzebnie.

> Dzięki temu czas sprawdzania właśnie
> nie rośnie wykładniczo, bo można to zrobić dla każdej funkcji
> oddzielnie. Więc doceniam wrzucanie wyjątków w sygnaturę (ale to
> jest tylko w javie AFAIK),

Jest nie tylko w Javie. Ale w Javie to jeden z bardziej krytykowanych
ficzerów -- okazał się bardziej "plątać pod nogami" zamiast pomagać.

Powód jest prosty -- w typowym przypadku, wystarczająca obsługa
większości błędów to jeden catch / except gdzieś blisko korzenia
hierarchii wywołań. Tylko niektóre błędy wymagają bardziej
specjalizowanej obsługi. W dodatku ta konieczność specjalnej (a nie
ogólne) obsługi jest wcale często oczywista.

Inną inkarnacją tego samego metasporu jest dyskusja o tym czy GC jest
dobre czy złe -- przeciwnicy GC twierdzą, że kiedy mają synchroniczne
zwolnienie zasobów i destruktor (czyli jak w Adzie czy C++) to nie ma
problemu np. z automatycznym zamknięciem pliku. Zapominają tylko o tym,
że 95% zwalnianych zasobów to pamięć, która zwykle jest wystarczająco
liczna by nie potrzebować jej zwolnienia natychmiast po zaprzestaniu
używania. I zapominają że destruktory tylko plączą się w tych 95%
przypadków. I że lepiej te 5% przypadków rozwiązać inaczej (choćby
Pythonowe with, using z C#, różne rozwiązanie "beansowe" z Javy, itd).

Podobnie argument o konieczności jawnej obsługi każdego błędu ignoruje
fakt, że zdecydowaną większość błędów można obsłużyć ogólnie. Ba,
nietóre nawet należy (poza wyjątkowymi przypadkami typu odpalanie
podaplikacji w sandboksie), np. wyjątgki informujące o błędach w
programie, brak możliwości kontynuacji z powodu braku niezbędnych i
najbardziej podstawowych zasobów (out of stack, out of memory, out of time).

> tylko nie wiem po co wprowadzać wtórny
> system [quasi]typów dla argumentów ukrytych składniowo (wyjątków).
>
>> kluczowym ficzerem obsługi błędów przez kontynuację
>> jest przerzucanie obsługi błędów wzwyż.
>
> Mam wrażenie (na podstawie przykładowego kodu), że masz zafiksowany
> w głowie sposób obsługi błędów poprzez wyjątki i każdą alternatywną
> metodę wyobrażasz sobie przez takie jej naginanie żeby innymi środkami
> programować nadal na modłę wyjątków. Nie jest kluczowym ficzerem,
> bo tak się da ale to jednak niekonieczne, można też zupełnie inaczej.

Błąd albo możesz obsłużyć lokalnie albo "podać dalej" (obsługa "wyżej"
lub inaczej mówiąc "na zewnątrz"). Obsługa na zewnątrz może być jeszcze
podzielona na bez powrotu (kończącą program -- wszelkie funkcje znane z
różnych języków typu sys.exit, abort, terminate) i powracającą do programu.

Nie ma to nic wspólnego z zafiksowaniem czy niezafiksowaniem w głowie
sposobu obsługi błędów.

>
>>> Nie wiesz konkretnie które z wywołań zadeklarowanych blokowo
>>> do łapania/przerzucania wyjątków rzuca które z nich,
>> ??? Podkręć jasność.
>>
>> except WyjątekA łapie obiekty klasy WyjątekA
>> a except WyjątekB łapie obikety klasy WyjątekB.
>
> Nie wiadomo z którego calla ten wyjątek wyleciał, nie zawsze
> wynika to z klasy wyjątku. Przy czym nie chodzi mi o moment łapania
> błędu w runtimie, tylko o czytelność przy uprzednim go sprawdzaniu.

Ale ta wiedza albo jest niepotrzebna (mógł wylecieć z dowolnego, bez
znaczenia) albo jest potrzebna łączenie z weryfikacją semantyki obsługi
tego błędu oraz semantyki zawołanej funkcji. Ale jeśli znasz semantykę
to wiesz czy błąd może polecieć -- i kółko się zamyka.

>
>> Zatem gdzie widzisz różnicę między napisaniem:
>>
>> funkcja(x, y, z, on_a, on_b)
>>
>> a funkcja(x, y, z) throws a, b
>
> W tym że jedno wywołanie wygląda funkcja(x,y,z,on_a,on_b), a drugie
> funkcja(x,y,z) - chyba oczywiste. W pierwszym wypadku czytając
> takie wywołanie od razu wiem że obsługę błędów przewidziano.
> I ona oczywiście może być niepoprawna, ale to kwestia sprawdzenia
> tamtego kodu, tu na razie jest ok. W drugim nie mogę odfajkować
> poprawności właśnie oglądanego kodu dopóki nie sprawdzę w wielu
> innych miejscach - w szczególności punktach wołających aktualny
> kawałek kodu.

Nie. W obu wypadkach lokalnie nie wiesz nic o kodzie wołającym. Nie
wiesz też o jego poprawności.

> Które w trakcie rozwoju programu mogą się jeszcze
> pojawiać albo zmieniać, więc tak czy siak zostawiam tykającą bombę.

Zatem zostawiasz ją (skoro uważasz, że zowstawiasz) i tu i tu.


>>>> Tak samo jest z kontynuacjami.
>>> Nie tak samo, bo od razu wiem że błąd jest obsłużony,
>> Jak?
>
> Tak że podano kontynuację dla wyjątkowego przypadku (i jeśli jest
> to onGeneralFailure albo None to od razu to widzę).


Parametr None w wywołaniu rzadko wygląda podejrzanie.


Choć np wywołanie
funkcja(13, "ala ma kota", None, on_unable)
jest złe a
funkcja(None, "kot nie ma ali", on_io_fail, on_impossible)
dobre - to oba wywołnia rozpatrywane osobno (widziane w różnych
fragmentach kodu) wyglądają same w sobie dobrze.


> Z wyjątkami
> mogę to wiedzieć na podstawie tego, że wpisano łapanie wyjątku.
> Ale. To jest zupełnie gdzie indziej w kodzie. W wielu miejscach.

Jeśli masz specyfikacje wyjątków to nie zupełnie gdzie indziej tylko w
najbliższej okolicy.

> [wpisywanie śmieci]
>> I dlatego odpowiednio ograniczona (choć wystarczająca)
>> forma kontynuacji jaką są wyjątki jest w takiej
>> sytuacji lepsza, bo trudniej wszystko cwaniacko rozwalić.
>
> Na tej zasadzie to równie dobrze można dla każdego catcha wpisać
> pass. Przecież pisałem od początku o QA, a nie magicznym wzroście
> poprawności jednoosobowo pisanego programu dzięki używaniu silverbullet.
> Tak naprawdę to największym zagrożeniem nie jest to, że programista
> przewidzi wszystkie błędy i do ich obsługi perfidnie wpisze śmieci.
> Tylko że normalny programista z definicji (niezależnej od znajomości
> języków programowania) uważa że wszystko pójdzie dobrze, i koncentruje
> się na przebiegu algorytmu w poprawnych sytuacjach. A przetykanie kodu
> możliwościami niepowodzeń na każdym kroku jest dla niego upierdliwe.

No właśnie. I nie tylko upierdliwe ale zaciemnia kod (jest upierdliwe w
czytaniu). A jak Wielcy Ojcowie Pythona ;) zauważyli kod się przede
wszystkim czyta. Dlatego wychodzi na to że wyjątki są dobre.


>>> Jak na zachęcanie to strasznie dużo roboty na takie obejście.
>>> A sprawdza się je prosto i tylko raz - w definicji wołanej procedury
>>> (sprawdzenie jej jest i tak nie do uniknięcia, więc ta sama robota).
>> W językach dynamicznych sprawdzenie jest przy użyciu.
>
> Nie piszę o działaniu programu, tylko czytelności.
>
>>> po dopisku throws cośtam jeszcze nie wiadomo czy
>>> tak powinno być czy nie, i gdzie tkwi problem. W drugim przypadku
>>> wszystko co potrzebne do wypatrzenia partactwa jest na miejscu.
>> No właśnie nie widzę tego na miejscu.
>
> call funkcja(x,y,z, None, onGeneralFailure) //- ja widzę jak na dłoni.

Co widzisz? Że któryś z parametrów jest None. I co z tego? Parametr None
jest nagminny. BTW onGeneralFailure też może być (i w większym projekcie
po prostu byłby) zaakceptowanym rozwiązaniem.

>
>> To, że w różnych rzadko używanych językach zwykle nie widzisz takiego
>> kodu wynika moim zdaniem *tylko i wyłącznie* z faktu że są właśnie
>> rzadko używane, a więc nie są używane przez przeciętną programistyczną
>
> Kiedy właśnie o to chodzi że widzę. Błędy są zawsze, tylko w języku
> czytelnym je o wiele łatwiej dostrzec niż w kryptycznym. A jeszcze
> trudniej z przeoczeniami. Rzeczy opisywane jawnie przy czytaniu widać,
> niejawnych trzeba się domyślać i robić reverse-enginnering.

Całe programowanie wysokopoziomowe opiera się na tym, że gross rzeczy
nie opisuje się jawnie. Wszystko jawnie to masz w assemblerze.

[...]


>>>>> Zamyka efekty uboczne w piaskownicy, niezły wynik.
> [...]
>> Dla mnie to to jest sandbox wywrócony na lewą stronę :)
>
> Na prawą: możesz narobić efektów ubocznych, ale tylko wewnątrz
> monady, bez zaśmiecenia nimi całego środowiska.

To nie ja robię efekty uboczne, to świat ma efekty :)

>
>> Wartość niepoznawalna przede wszytskim jest i jest niezmienna.
>> Można się nią normalnie posługiwać poza samym poznaniem.
>
> Zmienność się modeluje funkcjami.
>
>> Świat (stan zewnętrzny) to nie jest wartość w normalnym
>> rozumieniu tego słowa, jest np. jednorazowy (nie dziwne nie daje się
>> cofnąć ani zatrzymać), itd...
>
> Bo masz rozumienie wartości z ubogich języków.

Ależ skąd.

> Wartością pierwszego
> rzędu może być nie tylko stała, ale również bez problemu funkcja
> (nawet z funkcji w funkcje)

Oczywiście. Każda z nich jest niezmienna i nie ma "początku" ani "końca"
istnienia jak każdy przyzwoity obiekt matematyczny.

> - więc świat może być np. ciągiem
> statycznych stopklatek, a ciąg już się bardzo ładnie modeluje.

Problem w tym, że nie moższ wrócić do poprzedniej.


>> Tzn. typ w którym reprezentuję niepoznawalną stałą Chatina jest dla mnie
>> jak najbardziej normalnym typem, jego wartości mogę przypisywać,
>> przekazywać do wywołań, brać jako wyniki, itd. A tu guzik.
>
> Też możesz, od tego m.in. są monady stanowe. Nawet w zapisie
> to wygląda podobnie jak używanie zmiennych w komputacji imperatywnej.

Wiem jak działają monady stanowe wraz z lukrowym zapisem tego co robią.
Problem taki, że jeśli reprezentują stan zewnętrzny to nie ma możliwości
powrotu, cofnięcia się.

Dla monady np. reprezentującej dodawanie do listy mogę sobie z jej
pośrednimi wartościami różne rzeczy robić. Z monadami reprezentującymi
stan zewnętrzny już nie jest tak pięknie.

Nie da się świata cofnąć i "sforkować".

>
>> Ogólnie zadania w których pewne akcje zależą od wcześniej
>> wykonanych, od wyników (i błędów) występujących wcześniej.
>
> No to niech zależą, funkcjami się opisuje właśnie zależności
> - wystarczy je wpisać w parametr funkcji. Naprawdę takich nie
> widziałeś, np. szkolnego przykładu generatorów kolejnych liczb
> pierwszych, zależnie od tego jaka była ostatnia policzona?

Oczywiście że widziałem. Wszystkie te szkolne przykłądy reprezentują
stan wewnętrzny. Tak na marginesie: to, że funkcjami się opisuje
zależności to rzecz oczywista, każda funkcja jest relacją. Ale nie każdą
zależność da się opisać funkcjami -- nie każda relacja jest funkcją.

Ze stanem zewnętrznym kłopot taki, że zależności nie koniecznie są
znane, często też nie nawet jak są znane nie są funkcyjne (nie ma
jednoznacznego przejścia stan wcześniejszy (argument) -> stan późniejszy
(wynik)).


>> Funkcyjnie możesz zapisać stan wewnętrzny, ale ze stanem zewnętrznym
>> (czas systemowy, zewnętrzne źródło danych losowych, etc.) jest gorzej.
>> Albo zamiata się pod dywan tworząc pseudowartości których się nie daje
>> traktować jak wartości i udaje że wszystko ok,
>
> To jest wewnętrznie sprzeczne - właśnie się da tak traktować, i dlatego
> marudzisz że to udawanie, bo jednak działa. Wymyśliłeś jakąś ezoteryczną
> kategorię pseudowartości (?), ale tam nie ma żadnego zaszytego w środku
> kompilatora hacku

Jest hack, jeśli nie wprost w kompilatorze, to w ściśle z nim związanym
runtime języka. Mamy specjalny "magiczny" typ, którego "wartości" nie
zachowują się jak by od wartości należało oczekiwać.

> - to jest w bibliotece, zamodelowane normalnymi
> mechanizmami języka, dało się zrobić jak wszystkie inne funkcje.

Dopiero po zastosowaniu ww. kompilatorowo-rantajmowej "magii".

Sebastian Kaliszewski

unread,
Dec 22, 2009, 10:53:58 AM12/22/09
to
Piotr Sawicki wrote:
> Sebastian Kaliszewski powiada: [skleiłem]
>
>>>>>> Robili eksperyment. Ludzie dostali do napisania programy (w języku
>>>>>> który znali), potem te programy starannie sprawdzono patrząc ile
>>>>>> jest błędów.
>>>>> No sorry, ale to nie spełnia wymogów naukowego eksperymentu.
>> Bo?
>
> Bo zawiera błędy metodologiczne, jak observer bias.

Skąd wiesz? Żeby to stwierdzić musiałbyś wiedzieć jak sprawdzano poprawność.

[...]


>> BTW. naukowe to jest wtedy kiedy zostało opublikowane jako
>> publikacja naukowa z podlegająca recenzji. Kropka.

[...]


> Uczestnictwo w dyskursie naukowym
> opiera się na zgodzie na nieustanne podleganie krytyce,
> sprawdzaniu wyników czy reinterpretacji, ogólnie: obalalności.

Falsyfikowalność dotyczy teorii a nie jej eksperymantalnego wyjaśnienia.
Oczywiście źle przeprowadzone badanie można obalić. Ale tego nikt jakoś
nie obalił.

[...]


>>> nie wiadomo jak zdefiniowanej liczby błędów.
>> Nie wiesz co to jest błąd?
>
> Nie znam jakiejś jedynie słusznej ścisłej definicji. Są różne
> rodzaje błędów różnej skali, o odmiennych konsekwencjach, diametralnie
> innym czasie znajdywania czy poprawiania itd. Plus cała szara strefa
> między już błędem a jeszcze tylko złą praktyką programistyczną,
> której się nie powinno stosować bo albo do błędu w końcu doprowadzi
> albo np. znacznie podroży koszt dalszego rozwoju aplikacji.

Tu błąd można rozumieć prosto: jest to albo nie spełnienie wymagań
(specyfikacji problemu, w tym niekompletność rozwiązania) albo
wyprowadzenie systemu w stan niedopuszczalny (czyli przekroczenie
specyfikacji systemu).

[...]


>>> Nie wiadomo też, czy takie wyniki są powtarzalne, czy w ogóle
>>> były przeprowadzane innymi metodami i poddane metaanalizie.
>> Eksperyment przeprowadzono dwoma metodami, wynik wyszedł tak samo.
>
> To napisz albo podaj linka.

Czytałem to wiele lat temu (i wiele "komputerów temu") -- podałem to
jako wynik o którym wiem, stwierdzający pewną prawidłowość.

>
>>>> Hipotezą było, że są języki mniej i bardziej błędogenne.
>>> To nie jest nawet poprawnie zdefiniowane, nie wspominając
>>> w ogóle o falsyfikowalności. Można sobie policzyć jakieś
>>> korelacje na nielosowej próbce algorytmów, i w zasadzie tyle.
>>> Nie wiadomo jakich korelacji nie stwierdzono, bo ich nie badano.
>> Ach tak, ty nie czytałeś ale wiesz...
>
> Wiem co napisałeś. Od siebie dodaję tylko założenie, że rzetelnie
> zrelacjonowałeś te badania. Oraz wiem, że jeśli jakiejś hipotezy
> nie postawiono, to i jej nie obalono, to akurat stwierdzenie ogólne.

Usiłujesz przemycić fałszywą alternatywę.

>
>> Bardzo "ciekawe" wnioski "wyciągasz" w ogóle nie znając tych badań.
>> Jedno jest pewne, te wnioski są "zupełnie nie naukowe" :)
>
> Właśnie są to równie dobre interpretacje tej samej korelacji
> (która przecież nie jest zależnością przyczynowo-skutkową).

Nie jest, ale co z tego? Jak napisałem nie pośród różnych poszukiwanych
korelacji (język - liczba błędów, długość kodu - liczba błędów, zadanie
do rozwiązania - liczba błędów) stwierdzono korelację długość kodu -
liczba błędów są wprost proporcjonalne oraz korelację język - długość
kodu -- jak pamiętam C dawało średnio ok 2x dłuższy kod (i 2x więcej
błędów), Java ok 1.3x dłuższy kod, różnice długości kodu w pozostałych
testowanych języki (był w tym Perl i Python i perę innych - nie pamiętam
co jeszcze) nie były statystycznie istotne.

>
>> Nie chcesz - nie wierz, to Twój nie mój problem.
>
> No właśnie problem w tym, że trzeba to przyjąć na wiarę, bo nie
> widać dowodu z prawdziwego zdarzenia.

To nie jest sympozjum naukowe tylko dyskusja na grupie newsowej.

> Więc wybacz, ale dygresję
> o tym eksperymencie potraktuję jako anegdotkę a nie twardy argument
> w dyskusji o przyczynach powstawania błędów w oprogramowaniu.

Proszę bardzo.

0 new messages