[nem-pl] Uwagi różne

Kamil Skalski nazgul at omega.pl
Fri Feb 20 16:23:21 CET 2004


On Friday 20 of February 2004 14:20, Marcin 'Qrczak' Kowalczyk wrote:

> 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.

Ok, w dynamicznie typowanych językach z definicji to jest możliwe. Nie będę 
tutaj zaczynał dyskusji nad tym czy statycznie typowane języki są lepsze czy 
gorsze niż dynamicznie typowane. Powiem tylko, że nasza decyzja jest taka, że 
programy nie zwrócą nigdy wyjątku OjectHasNoSuchField. :)

> 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)?

W runtime oczywiście niemożliwe (patrz poprzedni punkt). W czasie kompilacji
spokojnie, właśnie to będą robić makra na typach:
c.AddInterface ("IHashable")
c.AddMethod...

Zmiana klas standardowych jest chyba nierozsądnym pomysłem, tym bardziej 
że trzebaby wtedy wygenerować bibliotekę, która zastąpi niektóre klasy z 
systemowej biblioteki - pomijając że się pogryzą, jest to jakieś takie 
nieprzenaszalne i nieelganckie... ale to już moja własna opinia.

> 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.

Nie wiem co dokładnie rozmiesz pod pojęciem "haszowalny", ale zastępując 
to dowolnym abstrakcyjnym słowem... jest możliwe. Takie coś robiłaby 
wspominana wcześniej DumpToFile - makro przeglądałoby pola i sprawdzało
czy one też mają DumpToFile, jeśli nie, to mamy kilka opcji. Możemy spróbować
rekurencyjnie zmienić te typy (o ile nie są biblioteczne) lub odmówić
współpracy i nie dodać metody DumpToFile. O ile się nie mylę, to jest 
to co pisałeś?

> A, czyli już w momencie pisania klasy trzeba się zdecydować...

To jest konsekwencja statycznego typowania. Zresztą jedyny powód, dla
którego możnaby to robić później, to że chcemy sparametryzować 
obecność owego przekształcenia danymi znanymi dopiero w runtime,
co IMHO jest i tak złym designem programu.

> 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!

Twierdzę, że może - czytaj dalej.

>
> 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?

oczywiście do "def g = 5;" czy jest w tym coś dziwnego?

>
> > 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") ]>

Dokładnie :)

> Czy też trzeba tak samemu pisać, żeby mieć higienę?
Nie

> 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.

No dobrze - z NewSymbol taki jest nasz obecny design (który jest jeszcze
dość płynny, bo tak naprawdę jeszcze higieny nie zaiplementowaliśmy),
ale nie widzę żadnych jego słabości. NewSymbol używać trzeba <=>
w generowanym kodzie definiuje się zmienne przez zapisanie ich w innym
quotowaniu (być może w innym makrze) niż się ich używa. To prawdę 
mówiąc nie jest takie złe, bo programista powinien zdawać sobie sprawę
o potrzebie higieny.
A pamiętać o tym nie trzeba, kompilator się upomni, bo napisanie
def y = <[ x ]> 
spowoduje u nas komunikat "unbound object-program variable `x'"
Chyba dzięki temu nie będzie się dało napisać niehigienicznego kodu
w nieświadomy sposób.

>
> 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).

System namespaceów .NETu załatwia to za nas. Jest tylko jedno A.B.C.x
i nie da się go w żaden sposób przykryć
namespace A {
  module B {
      public x : int

     macro f (..) {
        <[ x + 5 ]>
     }
 }
}

zostanie przetłumaczone na ... <[ A.B.x + 5 ]> co jest już jednoznaczne.

>
> 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? 

Impossible in .NET. System bezpieczeństwa .NETu nie pozwala na to,
po prostu kod wynikowy nie może mieć dostępu do czyichś prywatnych rzeczy.
Makra są pod tym względem przezroczyste i to jest IMHO dobra rzecz.
BTW. to można ominąć
module { 
  private x : int

  public GetX() : int  { x }

  macro (..) { <[ GetX () + 5 ]> }
}

> 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

Makr lokalnych rzeczywiście nie planujemy, ale to dodatkowo z innego powodu.
Makra obecnie (i pewnie w przyszłości) muszą być skompilowane 
(i najlepiej umieszczone w bibliotece) przed użyciem - ma to bardzo wiele
zalet, ale i wad (ja cały czas myślę, że warto to kiedyś zmienić, ale o to 
niech się Michał kłóci). Marka lokalne w takim modelu po prostu nie mają
wielkiego sensu.

No a biorąc pod uwagę namespace znikają wszyskie inne problemy
z "nie wiadomo" - wtedy już wszystko wiadomo. :D

> 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. 

Reference{cytat który powinien przewijać się od kilku postów} ;-)
Jeśli makra chcą się komunikować, to muszą to robić przez unikalne 
nazwy, najlepiej dzielone w jakiejś globalnej zmiennej inicjalizowanej przez
któreś z nich. Jeśli to nie o to co ci chodziło, to możesz podać przykład?

> (tyle że zamiast NewSymbol() wystarczy wpisać << cokolwiek >>,
> tzn. <[ cokolwiek ]>, i zostanie to przemianowane na unikalny symbol).

Uhum, choć IMHO to niespójne. ;-)  u nas def x = <[ y ]> spowoduje błąd 
kompilacji.

>
> Ł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).

Rozumiem, że masz na myśli "lokalizowanie" generowanych unikalnych 
symboli, ale nie widzę przewagi nad jednym, globalnym dla całego programu,
systemem generacji symboli - żadnych kolorowań, po prostu symbole.
A o co dokładnie chodzi z tym "implicit" to nie łapię... :|

Kamil




More information about the devel-pl mailing list