[nem-pl] Uwagi różne
Marcin 'Qrczak' Kowalczyk
qrczak at knm.org.pl
Fri Feb 20 14:20:46 CET 2004
W liście z pią, 20-02-2004, godz. 11:02, Kamil Skalski pisze:
> > > mamy klasy Produkt, ProduktDuzy, .... i chcemy napisać makro, które doda
> > > do nich metodę DumpToFile zapisującą wartości pól klasy gdzieś tam.
> >
> > U mnie to jest funkcja, a nie makro. To nic, że trochę mniej efektywna.
>
> Hm, zatem masz funkcję, która w runtime potrafi zlistować pola klasy z ich
> typami?
Trudno powiedzieć, bo nie mam klas w Waszym sensie, ale dla typów
będących zwykłymi rekordami mam funkcję zwracającą listę nazw pól
w formie symboli (symboli w sensie Scheme, czyli czegoś izomorficznego
z internowanymi stringami).
Zresztą dla dowolnego typu można taką funkcję dodać ręcznie i jeśli
dodamy jeszcze inne drobiazgi (funkcję robiącą za konstruktor), to typ
o dowolnej reprezentacji może udawać rekord. Definicja rekordu dodaje tę
funkcję automatycznie (a raczej gałąź funkcji generycznej).
Z obiektu i dynamicznie danej nazwy pola można uzyskać (albo próbować
zmienić) wartość tego pola. To jest akurat wyrażone zwykłą aplikacją
obiektu do symbolu. Co znowu powoduje, że obiekt może to zrealizować jak
chce - w szczególności properties albo delegowanie wszystkich pól do
innego obiektu mam łatwe.
> Hmm, ty chciałbyś stworzyć kopię c z nową metodą (tak wnioskuję tez z tego co
> napisałeś o skutkach ubocznych).
Bynajmniej. Chciałem dodać nową metodę do istniejącej klasy, choć czuję,
że to jest u Was niemożliwe. Jeśli to jest niemożliwe, to co się robi,
jeśli chcę dodać nowy interfejs do isniejących już klas (np. do klas
standardowych)?
To, że tego nie można, jest moim największym zarzutem pod adresem
statycznie typowanych języków obiektowych. Dynamicznie typowane języki
obiektowe z reguły pozwalają dodawać metody do obcych klas po fakcie,
a system klas Haskella - choć "nieobiektowy" - też to umie.
BTW, jak zrobić coś takiego: definiuję typ np. listy, którego obiekty są
haszowalne o ile typ elementów jest haszowalny - a jeśli nie są, to
nasza lista też nie będzie haszowalna, ale w ogóle będzie używalna dla
tych elementów? Tego też nie można np. w Eifflu, a można w statycznie
typowanym Haskellu.
> Generalnie nasza idea jest taka, że marka zmieniają deklaracje przy których są
> napisane, podczas kompilacji:
> [MakeFileDumper] class Produkt { .... }
A, czyli już w momencie pisania klasy trzeba się zdecydować...
> > To znaczy złapie które wystąpienia "g" ze źródła / innych makr? Jeśli
> > wszystkie, to higiena jest iluzją i nigdy nie można być pewnym, że
> > identyfikator użyty lokalnie w rozwinięciu makra będzie prywatny dla
> > tego rozwinięcia. Trzeba kolorować na podstawie jakiegoś identyfikatora.
>
> Chyba mnie źle zrozumiałeś - pokazałem metodę na bezpośrednie
> wpisanie jakiegoś symbolu z generowany kod.
Generowany kod nie może używać po prostu symboli do identyfikacji
zmiennych, bo higiena makr powoduje, że symbole bywają przekolorowywane
i różne symbole o tym samym brzmieniu nie zawsze łapią się nawzajem!
Jeśli użytkownik napisze
def g = 5;
m ()
gdzie m jest makrem, które higienicznie wprowadza definicję "g" (czyli
generuje po prostu kod "def g = 6", co jest oczywiście innym "g")
i w jego zakresie używa makra m', natomiast m' jest makrem, które
niehigienicznie odwołuje się do "g" (tzn. wstawia w kod $("g" : var)) -
to które "g" zostanie przez m' złapane?
> def g = Macros.NewSymbol ();
> <[ def $(g : var) (x) { x + "aa" }; $(g : var) ("bb") ]>
To jest, jak rozumiem, ręcznie zrealizowana higiena. Taki sam efekt
powinno dać napisanie
<[ def g (x) {x + "aa"}; g ("bb") ]>
Czy też trzeba tak samemu pisać, żeby mieć higienę?
> Generalnie nie rozumiem....
Wrócimy do tego, jak przedyskutujemy higienę w ogólności. Obawiam się,
że musiałbym ciągnąć ten cytat przez około dwa posty, zanim osiągniemy
wspólny język.
> ale wydaje mi się, że obecność NewSymbol rozwiązuje wszystkie problemy,
> o których piszesz.
Noo, konieczność używania NewSymbol nie liczy się jako higiena, bo
pomijając to, że trzeba o tym pamiętać (czyli domyślny jest brak
higieny), rozwiązuje tylko część problemu. To jest równoważne makrom
Lispa, a nie makrom Scheme.
Czego nie rozwiązuje? Odwoływania się z poziomu kodu wklejanego przez
makro do rzeczy, których definicja jest widoczna w miejscu definicji
makra, a może być niedostępna w miejscu użycia makra (np. przykryta
czymś innym o tej samej nazwie).
Aha, jak zgaduję to o to chodziło w kwalifikowaniu niekwalifikowanych
nazw użytych w makrach? Może jeszcze zadbacie, żeby rozwinięcie makra
mogło się do tego odwoływać nawet jeśli jest prywatne? Abstrahując od
tego, czy to wystarczy (np. to wyklucza lokalne makra, ale słyszałem, że
i tak ich nie planujecje), nie jestem przekonany, czy to się w ogóle da
zrealizować łatwiej niż higienę. No bo w momencie wklejania kodu z makra
nie wiadomo jeszcze, które identyfikatory w nim użyte są tam związane,
a które odwołują się do definicji na zewnątrz, więc nie wiadomo, które
kwalifikować. To się okaże dopiero po przeanalizowaniu tego fragmentu
kodu, w międzyczasie on mógł być przenoszony przez makra w różne
miejsca.
> Generalnie niehigieniczność (a raczej potrzeba mycia rąk samemu)
> przydaje się w sytucajach takich jak:
>
> macro PrintTuple (tup, size : int)
> {
> def symbols = array (size);
> mutable pvars <- [];
> for (mutable i <- size - 1; i >= 0; i <- i - 1) {
> symbols[i] <- Macros.NewSymbol ();
> pvars <- <[ pattern: $(symbols[i] : var) ]> :: pvars;
> };
> mutable exps <- [];
> for (mutable i <- size - 1; i >= 0; i <- i - 1)
> exps <- <[ System.Console.WriteLine ($(symbols[i] : var)) ]> :: exps;
>
> exps <- <[ def (.. $pvars) = $tup ]> :: exps;
> <[ {.. $exps } ]>
> }
To makro jest higieniczne, tzn. nie komunikuje się z kodem używającym
makra za pomocą identyfikatorów, które nie zostały wprost napisane w
kodzie używającym makra. Ale rozumiem, co przykład miał pokazać -
rzeczywiście dla uzyskania tego samego identyfikatora w osobnych
cytatach trzeba je tam jawnie wsadzać i u mnie jest tak samo
(tyle że zamiast NewSymbol() wystarczy wpisać << cokolwiek >>,
tzn. <[ cokolwiek ]>, i zostanie to przemianowane na unikalny symbol).
Łamanie higieny w Scheme'owym systemie syntax-case jest zrealizowane
przez funkcję datum->syntax-object. Ja planuję coś niby mniej ogólnego,
za to wygodniejszego w użyciu - zamiast podawania makru identyfikatora,
na podstawie którego ma kolorować zbudowany przez makro symbol, makro
niejawnie dostaje do uzytku funkcję kolorującą identyfikator kolorem z
miejsca użycia makra. Czyli jeśli makro wstawi
let loop {...};
loop()
to nazwa "loop" będzie prywatną sprawą tego makra, a jeśli wstawi
let $(implicit :again) {...};
$(implicit :again)()
to nazwa "again" będzie dostępna w treści makra. Można to zresztą
zapisać prościej $implicit.again (korzystając z równoważności dostępu
do pola i aplikacji do symbolu).
--
__("< Marcin Kowalczyk
\__/ qrczak at knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/
More information about the devel-pl
mailing list