Jak dotąd masz kilka dobrych odpowiedzi; dam ci niepraktyczny, ale bardzo edukacyjny przykład, w jaki sposób możesz zaprojektować język bez pojęcia stosów lub „kontroli przepływu”. Oto program, który określa silnie:
function f(i) => if i == 0 then 1 else i * f(i - 1)
let x = f(3)
Umieszczamy ten program w ciągu i oceniamy go przez podstawienie tekstowe. Więc kiedy oceniamyf(3)
, przeprowadzamy wyszukiwanie i zastępujemy 3 dla i, w ten sposób:
function f(i) => if i == 0 then 1 else i * f(i - 1)
let x = if 3 == 0 then 1 else 3 * f(3 - 1)
Wspaniały. Teraz wykonujemy kolejne podstawienie tekstu: widzimy, że warunek „if” jest fałszywy i zastępujemy kolejny ciąg znaków, tworząc program:
function f(i) => if i == 0 then 1 else i * f(i - 1)
let x = 3 * f(3 - 1)
Teraz wykonujemy kolejny ciąg zastępujący wszystkie podwyrażenia obejmujące stałe:
function f(i) => if i == 0 then 1 else i * f(i - 1)
let x = 3 * f(2)
I widzicie, jak to idzie; Nie będę dalej pracował nad tym. Moglibyśmy nadal wykonywać serię zamian łańcuchów, aż do tego dojdziemy let x = 6
i skończymy.
Stosu tradycyjnie używamy do zmiennych lokalnych i informacji o kontynuacji; pamiętaj, że stos nie mówi, skąd przyszedłeś, ale wskazuje, dokąd zmierzasz z tą wartością zwrotną w ręku.
W modelu programowania łańcuchowego podstawiania łańcuchów nie ma „zmiennych lokalnych” na stosie; parametry formalne są zastępowane ich wartościami, gdy funkcja jest zastosowana do jej argumentu, a nie umieszczana w tabeli odnośników na stosie. I nie ma „gdzieś dalej”, ponieważ ewaluacja programu polega po prostu na zastosowaniu prostych reguł do podstawiania łańcuchów w celu stworzenia innego, ale równoważnego programu.
Teraz, oczywiście, faktycznie zastępowanie ciągów prawdopodobnie nie jest dobrym rozwiązaniem. Jednak języki programowania, które obsługują „rozumowanie równe” (takie jak Haskell) logicznie korzystają z tej techniki.