🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: pl.comp.objects
Article #15549

Jeszcze o C++ templates i ich zwiazku z.... ML i Lispem

#15549
From: ethouris
Date: Fri, 06 Oct 2006 16:35
238 lines
10851 bytes
Postanowilem odswiezyc nieco temat, skoro i tak ruch jest niewielki.

Z nudów, przeczytalem sobie ten artykul

http://people.cs.uchicago.edu/~jacobm/pubs/templates.html

i musze przyznac: tak, jest smieszny.

Wlasciwie najbardziej rozbawil mnie ponizszy fragment:

> For one thing, templates hurt the C++ compiler's ability to generate
> efficient code. It might surprise you to hear that, considering that
> C++ is "efficient" while functional languages like ML and Scheme are
> "inefficient," but it's true. C++ templates hide your intentions
> from the compiler: did you mean type abstraction? Did you mean a
> syntax extension? Did you mean constant-folding or compile-time code
> generation? The compiler doesn't know, so it has to just blindly
> apply the copy-and-paste strategy and then see what happens. In ML
> or Scheme, though, aside from the individual benefits described
> above, the simple fact that you're telling the compiler what you
> want to achieve lets it optimize for you much more effectively.

Tu autor zdaje sie zapominac o kilku sprawach.

Jezyk ML jest jezykiem nieco wyzszego poziomu, niz C++. To
oznacza np., ze ML pozwala na symboliczny zapis algorytmu,
a kod z niego wygenerowany to nie jest translacja podanych
wyrazen na kod maszynowy, tylko generowanie kodu maszynowego,
który ma robic to samo najtanszym kosztem. W C++ po prostu
tak sie nie pisze - co nie zmienia równiez faktu, ze optymalizacje
w kompilatorach C++ sa równiez wykonywane na kilku poziomach.
Skoro te same optymalizacje sa stosowane dla funkcji z ustalonymi
typami, to dlaczego mialoby to wygladac inaczej w przypadku funkcji
"wygenerowanych" z wzorca?

Na dodatek, nie rozumiem dlaczego tak czesto jest podnoszony
argument o "generowaniu" funkcji z wzorców funkcji w C++. Przeciez
dokladnie taka sama "generacja" nastepuje w przypadku funkcji
inline, a tym bardziej wzorca funkcji inline. Czym sie te dwie ostatnie
rzeczy róznia? A trzeba tu tez wziac pod uwage, ze w przewazajacej
wiekszosci wzorce funkcji robi sie jako inline, z uwagi na nie
rozwiazany
w C++ problem dostepu do wzorców funkcji pomiedzy jednostkami
kompilacji.

> But from a broader perspective, realizing that templates are really
> just a C++ version of Lisp macros geared towards generating type
> declarations rather than extending C++'s syntax helps you understand
> the wider history of programming languages rather than just knowing
> the flavor of the month (which is rapidly becoming the flavor of
> last month!)...

Slusznie. Ale co z tego?

Autor podnosi równiez kwestie bezpieczenstwa typów, czyli:

fun f(a,b) = a + 2*b

powoduje, ze funkcja przyjmuje i zwraca typy, powiedzmy, liczbowe
(a niech tam, zalózmy nawet, ze calkowite). To powoduje, ze uzycie
tej funkcji z typem innym, niz calkowity, daje blad kompilacji. Blad
polega tutaj na tym, ze uzyto typu innego, niz calkowity, chociaz
w deklaracji nikt jawnie nie zazadal, ze typ ma byc calkowity...

i to ma byc rozwiazanie lepsze, niz w C++, gdzie taka funkcje moge
zastosowac sobie dla dowolnych typów, które moga miec "najlepszy
wspólny" i zawierac definicje tych operatorów?

Dalej:

fun f(a,b) = a + 2*b
fun g(x) = x
fun h(a,b) = g(f(a,b))

Tu w funkcji 'g' mamy tzw. "typ polimorficzny". Czym to sie zatem
rózni
od napisania w C++:

int f( int a, int b ) { return a + 2*b; }
template<class T> T g( T x ) { return x; }
int h( int a, int b ) { return g( f( a, b ) ); }

Chyba tylko tym, ze naszym zadaniem jest dostarczyc deklaracje funkcji,
a jakie ona bedzie miala typy argumentów i typ wartosci zwracanej
dowiemy
sie podczas kompilacji. Dlaczego tak jest lepiej, tez sie nie
dowiedzialem
z tego artykulu, jesli mam byc szczery.

Przyklad z sygnatura, który podal, doskonale z kolei wpasowuje sie
w reguly wzorców w C++. Stosujac skladnie koncepcji, które maja
byc w przyszlym standardzie C++, mozemy nawet zrobic niemal
wierna kopie ponizszego przykladu:

signature PRINTABLE = sig
  type t
  val printT : t -> unit
end

functor List(T : PRINTABLE) =
struct
  datatype List = ListNode of T.t * List | Empty
  exception EmptyList
  (* ... other functions as before ... *)

  fun print (ListNode (i,_)) = T.printT i
end

Czyli:

concept PRINTABLE {    typename t;    static void printT( t ); }

template<PRINTABLE T>
struct List
{
  vector<ListNode<T>> list; // pominmy, bo to akurat inna sprawa :)
  void print( const ListNode<T> & node ) { return T::printT( node.i );
}
};


Odnoszac to teraz wszystko do Javowych generyków, wyglada na to, ze
zarówno C++, jak i Java wziely wiele koncepcji programowania
generycznego
z ML, z ta tylko róznica, ze wyciagnely z ML zupelnie inne czesci i
rozwinely
na swój wlasny sposób.

Zwracam tu uwage na to, ze w przypadku Javy wszelkie sprawdzanie, czy
okreslony typ posiada pewne wlasciwosci, opiera sie na klasach i
interfejsach.
Co w praktyce oznacza, ze nie da sie wykorzystac okreslonej
funkcjonalnosci
generycznej dla typów, które byly stworzone wczesniej, niz koncepcje
wymagane
przez owa funkcjonalnosc - trzeba do tego celu robic rózne dodatkowe
opakowania, nawet jesli te opakowania beda polegaly na tym, ze po
prostu
wywolamy metody o dokladnie tych samych nazwach przekazujac jej te
same argumenty.

C# zreszta stosuje dokladnie ta sama metode (fragment wywiadu z
Andersem
Hejlsbergiem na Artimie):

Anders Hejlsberg: What you are really asking is, what can you say in
terms of constraints? Constraints, like any other feature, can become
arbitrarily complex if taken to their ultimate extreme. When you think
about it, constraints are a pattern matching mechanism. You want to be
able to say, "This type parameter must have a constructor that takes
two arguments, implement operator+, have this static method, has these
two instance methods, etc." The question is, how complicated do you
want this pattern matching mechanism to be?
[...]

Bill Venners: You did the constraints by type, not by signature.

Anders Hejlsberg: Yes.

Bill Venners: So the type must either extend a class or implement
interfaces.

Anders Hejlsberg: Yes.
[...]

Bruce Eckel: Well, yes, I started seeing C++ templates as sort of a
weak typing mechanism. And when you start adding constraints on top of
that, you're going from the weak typing to the strong typing. It always
gets more complex when you add strong typing. It's a spectrum.

Anders Hejlsberg: The thing you realize about typing is that it's a
dial. The higher you place the dial, the more painful the programmer's
life becomes, but the safer it becomes too. But you can turn that dial
too far in either direction.


Interesujace w tej calej dyskusji jest to, ze w zasadzie wszyscy
dyskutanci,
niezaleznie od pogladów, zdaja sie wyglaszac kuriozalne stwierdzenie,
ze
"strong typing" oznacza bezpieczenstwo. Jednoczesnie stwierdzajac, ze
wzorce w C++ maja "weak typing", co odbieralbym raczej jako pejoratywne
okreslenie, bo w terminologii C++ jako przeciwienstwo "strong" (w
róznych
kontekstach) uzywano raczej "vague", a nie "weak". Jest to wierutna
bzdura - silne typowanie jest po prostu typowaniem bardziej scislym,
w którym, niezaleznie czy celowo, czy nie, ograniczamy mozliwosci
uzytkownika pod wzgledem tego, co on moze z tym zrobic. Nie ma to
nic wspólnego z jakimkolwiek bezpieczenstwem: typowanie po
sygnaturze jest bezpieczne w tych ramach, w jakich wymaga tego
sygnatura. Nikomu wiecej nie jest potrzebne, bo nie zostanie w zaden
sposób wykorzystane.

W tej dyskusji z kolei pominieto równiez fakt, ze po czesci ten
sposób
uprawiania generyzmu jest dostepny w ML-u i rozwiazanie tam wlasnie
zastosowane (konkretnie chodzi o sygnatury) rózni sie wyraznie od
tego,
co jest w Javie i C#.

Co mozna zauwazyc wspólnego dla tych obu dyskusji? Obie przy opisach
spraw techniczno-projektowych dotyczacych implementacji generyzmu
skupiaja sie na jednym konkretnym stwierdzeniu: jak bardzo chujowy jest
C++ (choc oczywiscie nikt z dyskutantów nie pokusil sie o podanie
jakichs
przykladów z praktyki, które moglyby powodowac problemy).

Jacob Matthews wspomnial w swojej dyskusji o Javie dokladnie w jednym
miejscu: zeby pokazac, jak wyglada róznica miedzy sygnaturami i
funktorami
w ML-u i Javowymi interfejsami w kwestii programowania generycznego.
Niby
wszystko ok, tyle ze na koniec rzuca kuriozalnym stwierdzeniem:

"This system gives you more abstraction power than C++ templates or
Java interfaces while providing type-safety."

Po pierwsze, co maja wspólnego "Java interfaces" i "C++ templates", w
istocie
blizsze ML-owej metodzie opartej na sygnaturach, niz Javowe generyki?

Po drugie - gdzie tu jest w C++ problem z "type safety"? Na dokladnie
tej
samej zasadzie moge stwierdzic, ze w ML-u jest problem z type safety
w kwestii tej pierwszej funkcji, bo NIE MA JAWNIE NAPISANEGO TYPU.
Dokladnie tak samo w C++ nie ma jawnie okreslonej koncepcji (w biezacym
standardziej).
Co, skadinad, nie wiem, czym sie rózni od funkcji przyjmujacej "typ
polimorficzny" w ML-u.

Wszystko to wyglada w ten sposób, ze jezyki maja swoje jakies-tam
okreslone wlasciwosci zwiazane z implementacja generyzmu. Ale jak
na razie nie zauwazylem, zeby ktos prowadzil rzeczowa dyskusje o tym,
jak wyglada porównanie tych wlasciwosci i jak wygladaja praktyczne
konsekwencje ich realizacji. Za to wszyscy z luboscia uprawiaja tani
marketing, w którym wielce merytoryczne stwierdzenie p. Lewandowskiego
"Szablony w C++ to straszne badziewie" wcale nie odbiega poziomem od
tego, co prezentuja panowie Jacob Matthews i Anders Hejlsberg.
Technical Details
Message-ID: <1160152506.689352.31240@c28g2000cwb.googlegroups.com>
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.gazeta.pl!newsfeed.pionier.net.pl!news.glorb.com!postnews.google.com!c28g2000cwb.googlegroups.com!not-for-mail

Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.gazeta.pl!newsfeed.pionier.net.pl!news.glorb.com!postnews.google.com!c28g2000cwb.googlegroups.com!not-for-mail
From: "ethouris" <ethouris@gmail.com>
Newsgroups: pl.comp.objects
Subject: Jeszcze o C++ templates i ich zwiazku z....  ML i Lispem
Date: 6 Oct 2006 09:35:06 -0700
Organization: http://groups.google.com
Lines: 239
Message-ID: <1160152506.689352.31240@c28g2000cwb.googlegroups.com>
NNTP-Posting-Host: 151.193.220.27
Mime-Version: 1.0
X-Trace: posting.google.com 1160152512 31924 127.0.0.1 (6 Oct 2006 16:35:12 GMT)
X-Complaints-To: groups-abuse@google.com
NNTP-Posting-Date: Fri, 6 Oct 2006 16:35:12 +0000 (UTC)
User-Agent: G2/1.0
X-HTTP-UserAgent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7,gzip(gfe),gzip(gfe)
X-HTTP-Via: 1.1 sgtulpx03 (NetCache NetApp/6.0.1P1D1)
Complaints-To: groups-abuse@google.com
Injection-Info: c28g2000cwb.googlegroups.com; posting-host=151.193.220.27;   posting-account=W6788A0AAADjfLYufIDScQB28tLT5-6D
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit