Tworzenie większych programów

LJ
  • Rejestracja: dni
  • Ostatnio: dni
0

@pom

Nie da się. Da się tylko rozszerzyć istniejące specjalizery na equal/equalp/inny, korzystając z ogromnych możliwości
MOP. Nie można natomiast zaimplementować guardów:

fun x | x < 1 = ..
fun x | x > 25 = ..
fun x | x > 5 = ..
fun x = ..

A to niby dlaczego? Ja właśnie piszę pattern matcher. Mogę pokazać przykłady. Guardy jak najbardziej są.

  1. Nie wszystkie języki funkcyjne mają poszczególne patterny jako osobne definicje funkcji, można też zawrzeć wszystkie wzory w jednym miejscu.
  2. Nawet jak miałyby być osobno, co za problem?
Kopiuj
(defclass my-fn ()
  ()
  (:metaclass closer-mop:funcallable-standard-class))
==> #<SB-MOP:FUNCALLABLE-STANDARD-CLASS MY-FN>
(defmethod initialize-instance :after ((obj my-fn) &key fn)
  (check-type fn function)
  (closer-mop:set-funcallable-instance-function obj fn))
==> #<STANDARD-METHOD INITIALIZE-INSTANCE :AFTER (MY-FN) {BBDA271}>
(setf (symbol-function 'foo)
      (make-instance 'my-fn :fn (constantly "oh hi")))
==> #<MY-FN {B62F765}>
(foo)
==> "oh hi"

Skoro da się uczynić obiekt funkcją to da się w nim trzymać patterny :)

Nie za bardzo, trzebaby też zmienić każde odwołanie do zmiennej na wywołanie funkcji. Można leniwość zemulować w
znacznym zakresie i tyle.

Nie trzeba, pobranie wartości zmiennej nie ma efektów ubocznych. Niby są makra symboli, ale pierwsze co code walker robi to rozszerzenie makr.

Kiedyś napisałem code walkerem leniwość, testowałem go na jakichś przykładach z wikipedii i działało bez problemu.

W CLu każdy typ danych +JEST+ obiektem... (i to nawet dosłownie ;) Wszystko jest obiektem, nawet zwykła funkcja/
metoda ;)

Nie, fixnum to po prostu (na sbclu pod IA-32) 3 bity taga i 29 bitów 'signed int'. Obiekt to wskaźnik do klasy i wektor wartości slotów. Niby CLASS-OF działa na wszystkim ale to ściema, po prostu ma special-case na wbudowane typy. Nawet prototypy klas są, typu #:mu dla symboli czy 42 dla intów :)

A wymuszenie włożenia obiektu ;) do rejestru wymaga specjalnej deklaracji, którą OBIECUJESZ kompilatorowi że typ
będzie taki a taki i że ma to założyć z góry.

Chyba w jakichś obiektowych dynamicznie typowanych językach :) FIXNUM to zawsze word i tyle, co najwyżej floaty mogą być boksowane...

EDIT:

Dlaczego wszyscy mylą pattern matching z specjalizatorami w CLOS-ie? Pattern matching wcale nie musi być napisany na CLOS-ie, zresztą wtedy trzeba by było przepisać spory kawał MOP-a, łącznie z memoizacją. Zresztą, można zrobić fajne słowa kluczowe do wzorów nie mające nic wspólnego z typami, np. domknięcie Kleene'a albo destrukturyzację list.

PO
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 16
0
lisp-jihad napisał(a)
  1. Nie wszystkie języki funkcyjne mają poszczególne patterny jako osobne definicje funkcji, można też zawrzeć wszystkie wzory w jednym miejscu.

Nie. Ale taki Haskell jest statyczny - może wymagać wszystkiego w jednym miejscu. Lisp nie.

  1. Nawet jak miałyby być osobno, co za problem?

Napisałem to wyżej..

Nie trzeba, pobranie wartości zmiennej nie ma efektów ubocznych. Niby są makra symboli, ale pierwsze co code walker robi to rozszerzenie makr.

No dobra, jak będzie wyglądała implementacja leniwości czegoś takiego:
1.)

Kopiuj
(defmethod m (a ((b list)))
  (1+ a))

(let ((x (mapcar #'cos costam)))
  (m 23 x))

m powinno zostać wywołane - typ to lista, ale funkcja cos nie powinna być wywołana ani razu.
2.)

Kopiuj
(defmethod m (a ((b list)))
  (+ a (first b)))

(let ((x (mapcar #'cos costam)))
  (m 23 x))

Tutaj cos musi zostać wywołane raz (może wtedy rzucić wyjątkiem, itd).

Co ważne w obu przykładach - costam nie musi być listą! W takim wypadku druga wersja powinna rzucić wyjątek, a pierwsza nie.

Nie, fixnum to po prostu (na sbclu pod IA-32) 3 bity taga i 29 bitów 'signed int'. Obiekt to wskaźnik do klasy i wektor wartości slotów. Niby CLASS-OF działa na wszystkim ale to ściema, po prostu ma special-case na wbudowane typy. Nawet prototypy klas są, typu #:mu dla symboli czy 42 dla intów :)

Obiekt to pojęcie w systemie abstrakcji (że tak to nazwę). Możnaby napisać program z użyciem obiektów, dla którego żadne dynamiczne informacje o obiektach nie byłyby naprawdę potrzebne - wystarczająco inteligentny kompilator mógłby to zauważyć i wszystkie usunąć. Mimo tego wszystko ciagle byłoby obiektem.

Chyba w jakichś obiektowych dynamicznie typowanych językach :) FIXNUM to zawsze word i tyle, co najwyżej floaty mogą być boksowane...

'Chyba w jakichś językach', tylko co?
Teoretycznie wg. standardu CL poniższy kod ma prawo wywalić cały program:

Kopiuj
(defun x (a)
  (declare (fixnum a) (optimize (safety 0)))
  (the fixnum (1+ a)))
(x "ala ma kota")

Wg. standardu declare jest tylko i wyłącznie OBIETNICĄ.
(co prawda to co standard mówi nie ma zbyt większego znaczenia, i tak pisze się pod jedną konkretną implementację, no ale...)

Ta obietnica oznacza że kod nie ma obowiązku sprawdzać typu obiektu, tylko zakłada jeden; implementacja jest pochodną systemu (aksjomatów?), a nie odwrotnie, czyli implementacja nie może zmieniać parametrów systemu.

Kopiuj
CL-USER> (defun x (a)
	   (declare (fixnum a) (optimize (speed 3) (safety 0)))
	   (the fixnum (1+ a)))
STYLE-WARNING: redefining X in DEFUN
X
CL-USER> (disassemble #'x)
; 0B1F3F5A:       83C204           ADD EDX, 4                 ; no-arg-parsing entry point
;       5D:       8D65F8           LEA ESP, [EBP-8]
;       60:       F8               CLC
;       61:       8B6DFC           MOV EBP, [EBP-4]
;       64:       C20400           RET 4
;       67:       90               NOP
; 
NIL
CL-USER> (x "ala ma kota")
EXCEPTION_ACCESS_VIOLATION
   [Condition of type SIMPLE-ERROR]
...

(safety 0) jest oczywiście niezalecane :D

Dlaczego wszyscy mylą pattern matching z specjalizatorami w CLOS-ie? Pattern matching wcale nie musi być napisany na CLOS-ie, zresztą wtedy trzeba by było przepisać spory kawał MOP-a, łącznie z memoizacją. Zresztą, można zrobić fajne słowa kluczowe do wzorów nie mające nic wspólnego z typami, np. domknięcie Kleene'a albo destrukturyzację list.

Nie mylą - to jest właśnie pm. Bez tego możesz oczywiście zrobić na makrach lepsze cond/destructuring-bind... jakiś sens to ma. Ale prawdziwym pm trudno to nazwać.


BTW też tak macie że jak robicie coś w Emacsie to 'wiecie' jak to zrobić, ale jak usiłujecie pomyśleć jak to nie macie pojęcia i trzeba zaobserwować co się tak naprawdę naciska? :D

LJ
  • Rejestracja: dni
  • Ostatnio: dni
0

[No zaraz mnie szlag turystyczny trafi. Znowu mi Opera zjadla forma.
Całkiem fajny ten emacs-w3m, tylko to 'forum' się marnie renderuje bez
ceesesów, łobrazków i kolorków. Nie idzie odróżnić z wyglądu co
cytowane a co nie.]

@pąm

Nie. Ale taki Haskell jest statyczny - może wymagać wszystkiego w
jednym miejscu. Lisp nie.

Programy w Lispie to organiczne, mutujące struktury, nie zaś betonowe
wieżowce, do tego rytualnie czyste. Ale głupia analogia czy tam
metafora.

No dobra, jak będzie wyglądała implementacja leniwości czegoś takiego:

Zmienna X będzie miała w rezultacie coś w stylu:

(thunk (mapcar #'cos costam))

A potem:

Kopiuj
  (thunk (cons (thunk (funcall fn #| 1+ |#
                               (car list) #| 46 |#))
               (thunk (mapcar #'cos (cdr list) #| (cdr costam) |# ))))

W #| komentarzach |# wartości leksykalnych zmiennych.

Gdzie THUNK to makro co tworzy lambdę której zawartość wywołuje za
pierwszym razem a potem ma już zmemoizowane.

[Ale fajnie, paren matching w formie i nawet paredit i filladapt są.
Gdyby tylko wcięcia umiało lispowo robić to już by był raj na ziemi.
Prawie się da używać gdyby nie te niewidoczne cytaty. Może jakiś
multi-mode by się nadał.]

Wg. standardu declare jest tylko i wyłącznie OBIETNICĄ.

A co ma do tego deklaracja? Implementacja ma psi obowiązek zmieścić
FIXNUM do rejestru z safety 3 i debug 3 chyba, że jest interpreterem
(przydatne tylko jak nie ma innych lispów pod architekturę) albo jakimś
fikuśnym wynalazkiem typu target na JVM.

Trudno żyć o samym ANSI CL, trzeba jednocześnie pisać przenośny kod (gdzie jest to konieczne) ale i wiedzieć jak rozsądny kompilator się zachowa.

Teoretycznie wg. standardu CL poniższy kod ma prawo wywalić cały
program:

Teoretycznie cc ma prawo wywalić się przy:

Kopiuj
  char foo[5];
  &foo[5];

nawet na architekturach stronicowanych.

(safety 0) jest oczywiście niezalecane :D

safety jest dla cieniasów:

(declaim (optimize (speed 3) (safety 0) (debug 0)))

na samym początku systemu załatwia sprawę ;)

Nie mylą - to jest właśnie pm.

No dobra, ale dziwnie słyszeć, że w lispie się nie da bo w CLOS-ie się
nie da...

BTW też tak macie że jak robicie coś w Emacsie to 'wiecie' jak to
zrobić, ale jak usiłujecie pomyśleć jak to nie macie pojęcia i trzeba
zaobserwować co się tak naprawdę naciska? :D

Ja nie, ale to pamięć mięśni jest. W ogóle czasem mam, że zacznę pisać
słowo i mi końcówka wyjdzie z zupełnie innego słowa. Pewnie się zerwało
parę dendrytów.

[w3m nie umie wysłać posta. "Błąd" i tyle. Wiem, że plików nie umie poprawnie wysłać ale formularzy? W dodatku musiałem wpisać od tyłu jakiś ciąg znaków mimo, że się zalogowałem. Do chrzanu takie forum.]

EDIT:

A da się taki wzór zrobić w haskellu?

(list a (and b (not a)) a)

Nie mówię tu o guardzie 'a != b' :) Bo jak by napisać potem:

(list a (* a (and b (not a))) b)

to marnie to potem guardem parsować. W ogóle to zastanawiam się czy warto optymalizować makro pm do TAGBODY. Jak myślicie?

[chyba sobie po prostu odpalę emacsa obok Opery i potem wkleję, tak będzie najprościej.]

Możnaby napisać program z użyciem obiektów, dla którego żadne dynamiczne informacje o obiektach nie byłyby
naprawdę potrzebne - wystarczająco inteligentny kompilator mógłby to zauważyć i wszystkie usunąć.

To się nazywa "class sealing" i jest w ACL albo LW, nie pamiętam którym. W ogóle to SSC zamieniłby O(n!) na O(log n) albo O(1). Dla praktycznych zastosowań nie da się orzec czy klasę można zapieczętować bo... wiadomo.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.