C Dilinde Coroutine’ler (2000)
(chiark.greenend.org.uk)- Üretici ve tüketici kodu birbirleriyle veri alışverişi yaparken bir tarafı çağrılan işlev biçimine tersyüz ederek yazarsanız, başlangıçta görünen algoritma yapısı durum geçişlerinin içinde kolayca kaybolabilir
- Knuth tarzı coroutine’ler, iki rutinin yürütme konumunu saklayıp denetimi birbirine devrettiği bir modeldir; ancak C’nin yığın tabanlı çağrı yapısı içinde bunu taşınabilir biçimde doğrudan uygulamak zordur
- Bu yazının temel hilesi, bir
switchalt bloğunacaseyerleştirmeye izin veren C sözdizimini ve__LINE__makrosunu kullanarak,returnsonrasındaki konuma yeniden giren örtük bir durum makinesi oluşturmaktır crBegin,crReturn,crFinishmakrolarıyla açma çözücü ve ayrıştırıcının özgün döngü yapısı korunabilir; ancak korunması gereken yerel değişkenlerstaticolmalı vecrReturnaçık birswitchiçinde ya da aynı satırda kullanılmamalıdır- Gerçek kodda, yeniden girilebilirlik ve çok iş parçacıklılık kısıtları nedeniyle bağlam yapısı geçiren geliştirilmiş bir biçime ihtiyaç vardır;
coroutine.hhem basitscrmakrolarını hem de yeniden girilebilirccrmakrolarını birlikte sunar
Üretici ve tüketiciyi bağlarken ortaya çıkan yapı sorunu
- Büyük programlarda çoğu zaman bir kod veri üretir, başka bir kod da onu tüketir; bu durumda hangi tarafın çağıran, hangi tarafın çağrılan olacağı tasarımı zorlaştırır
- Örnek iki küçük rutinden oluşur
- Run-length sıkıştırma açma kodu, girdiyi
getchar()ile okur ve karakterleri tek tekemit()ile çıkarır - Ayrıştırıcı kodu, karakterleri
getchar()ile okuyup alfabetik ardışık bölümleriWORD, diğer karakterleri isePUNCTolarak işler
- Run-length sıkıştırma açma kodu, girdiyi
- İki rutin ayrı ayrı bakıldığında doğaldır; ancak açma çözücünün
emit()çıktısının ayrıştırıcınıngetchar()girdisine doğrudan bağlanması için arada bir bağlantı yapısına ihtiyaç vardır - Bu, iki süreç ya da iki iş parçacığı arasındaki bir pipe ile de çözülebilir
- Açma çözücünün
emit()işlevi pipe’a yazar, ayrıştırıcınıngetchar()işlevi karşı taraftan okur - Bu yöntem basit ve sağlamdır; ancak ağırdır ve taşınabilirliği düşüktür, bu yüzden çoğu durumda basit bir iş için iş parçacıklarına bölmek istenmez
- Açma çözücünün
İşlevi yeniden yazmanın okunabilirlik kaybı
- Geleneksel çözüm, iletişim kanalının bir ucunu çağrılabilir bir işlev biçiminde yeniden yazmaktır
- Açma çözücüyü her çağrıda bir karakter döndüren bir işleve çevirirseniz, mevcut ayrıştırıcı
getchar()yerinedecompressor()çağırabilir - Tersine, ayrıştırıcıyı her karakter aldığında çağrılan bir işleve çevirirseniz, mevcut sıkıştırma açma kodu
emit()yerineparser()çağırabilir - İkisini birden değiştirmek gerekmez; yalnızca bir tarafı değiştirmek de bağlantı için yeterlidir, fakat yeniden yazılan kod özgün hâlinden çok daha zor okunur
- Özgün açma çözücü ve ayrıştırıcıda algoritma akışı döngülerin içinde doğal biçimde görünür
- Yeniden yazılmış biçim ise
staticdurum değişkenlerine veswitchdurum geçişlerine dayanır; bu yüzden sıkıştırma biçimini veya ayrıştırıcı dilbilgisini koddan okumak zorlaşır
- Amaç, iki taraftan hiçbirini açık bir durum makinesi gibi tersyüz edip yazmadan bağlamaktır
Knuth tarzı coroutine’ler ve C’nin sınırları
- Donald Knuth’un coroutine çözümü, çağıran ve çağrılan ayrımını ortadan kaldırır ve iki süreci işbirliği yapan eşit varlıklar olarak ele alır
- Bu modelin çağrı ilkesi normal işlev çağrısından farklıdır
- Mevcut yürütme konumu yığında değil, ayrı bir yerde saklanır
- Diğer rutinin son kaydettiği yürütme konumuna atlanır
- Açma çözücü bir karakter yaydığında kendi program sayacını saklar ve ayrıştırıcının kaydedilmiş konumuna gider
- Ayrıştırıcı bir sonraki karaktere ihtiyaç duyduğunda kendi program sayacını saklar ve açma çözücünün kaydedilmiş konumuna gider
- Denetim, iki rutin arasında gerektiği kadar gidip gelir
- Bu yöntem teorik olarak iyidir, ancak pratikte yalnızca assembly dilinde mümkündür
- C gibi yüksek seviyeli diller yığın tabanlı yapıya dayanır; bu nedenle işlevler arası denetim aktarımında bir taraf çağıran, diğer taraf çağrılan olmak zorundadır
- Taşınabilir C kodunda saf coroutine yaklaşımı, Unix pipe çözümü kadar pratik değildir
C’de “return and continue” taklidi
- C’de gereken davranış, çağrılan işlevin
returnettikten sonra bir sonraki çağrıda oreturnifadesinin hemen sonrasındaki konumdan yürütmeye devam etmesidir; yani return and continue - Örneğin
for (i = 0; i < 10; i++) return i;biçimindeki bir işlev 10 kez çağrıldığında 0’dan 9’a kadar sırayla döndürse ideal olur - İlk uygulama bir durum değişkeni ve
gotokullanır- İşlevin başına ve her
returnsonrasına etiket konur - Çağrılar arasında korunan
statedeğişkeni, bir sonraki devam etme etiketini gösterir - İşlev başında
switch(state)ile uygun etikete gidilir returnhemen öncesinde, sonraki çağrıda dönülecek etiketstateiçine kaydedilir
- İşlevin başına ve her
- Bu yöntem çalışır; ancak etiket yönetimi elle yapıldığı için bakım yükü büyüktür
- Her
returneklendiğinde yeni bir etiket oluşturmak ve başlangıçtakiswitche de eklemek gerekir - Bir
returnkaldırıldığında karşılık gelen etiketi de kaldırmak gerekir - İşlev gövdesi ile
switchlistesinin tutarlılığını sürekli korumak gerekir
- Her
Duff’s device ile gizlenen durum makinesi
- C’nin ünlü Duff’s device tekniği,
switche karşılık gelencaseifadelerinin onun alt bloklarının içine de yerleştirilebilmesini sağlayan sözdiziminden yararlanır - Bu özellik coroutine hilesine uygulanınca,
switchhangigotonun çalışacağını seçmek yerine,switchin kendisi yeniden giriş atlaması gibi davranır - Temel biçim şöyledir
static int statebir sonraki devam noktasını saklar- İşlev başında
switch(state) { case 0: ... }ile girilir returnhemen öncesinde sonrakicasedeğeristateiçine kaydedilir- İlgili
caseetiketireturnifadesinin hemen arkasına konur
- Bunlar makrolarla sarıldığında coroutine gibi görünen bir arayüz ortaya çıkar
crBegin:static int state=0; switch(state) { case 0:kısmını gizlercrReturn:statedeğerini saklayıp bir değer döndürür, ardından aynı konuma bircaseetiketi yerleştirircrFinish: açık blokları kapatır
crReturn,do ... while(0)ile sarıldığı içinifileelsearasında süslü parantez olmadan kullanıldığında da sözdizimi sorunu çıkmaz- Başta
crReturn(1, i)gibi durum numarasını elle vermek gerekir; ancak ANSI C’nin__LINE__makrosu kullanılırsa mevcut kaynak satırı numarası durum değeri olarak kullanılabilir - Bu iyileştirmeden sonra yalnızca
crReturn(x)yazmak yeterlidir; fakat aynı satıra ikicrReturnkoymama kuralı eklenir
Makro kullanım kuralları ve örnekler
- Makro tabanlı coroutine’ler birkaç kuralı varsayar
- İşlev gövdesi
crBeginvecrFinishile sarılır crReturnaşılırken korunması gereken yerel değişkenlerstaticolarak bildirilir- Açık bir
switchdeyiminin içine kesinliklecrReturnkonmaz __LINE__tabanlı uygulamada aynı satıra ikicrReturnkonmaz
- İşlev gövdesi
- Açma çözücü örneği, özgün döngü yapısını koruyarak karakter yayarken
emit(c)yerinecrReturn(c)kullanır - Ayrıştırıcı örneği, yeni bir karakter gerektiğinde
crReturn()ile çağırana döner ve sonraki çağrıda parametreciçinde yeni karakteri almış olarak yürütmeye devam eder - Ayrıştırıcıda küçük bir yapı değişikliği vardır
- İlk karakter işlev girişinde zaten
ciçinde bulunduğu için, özgün döngünün başındakigetchar()a karşılık gelencrReturndöngünün sonuna taşınır - İstenirse ayrıştırıcı için bir başlatma çağrısı gerektiği de kararlaştırılabilir
- İlk karakter işlev girişinde zaten
- İki rutinin de coroutine makrolarıyla değiştirilmesi gerekmez; yalnızca bir tarafı değiştirip diğer tarafı çağıran olarak bırakmak da mümkündür
- Sonuçta ANSI C, önişlemci ve
switchin daha az kullanılan sözdizimi birleştirilerek üretici ile tüketici arasındaki veri aktarımı, açık bir durum makinesi olarak yeniden yazılmadan işlenir
Kodlama standartları ile algoritma açıklığı arasındaki çatışma
- Bu teknik genel kodlama standartlarını ciddi biçimde ihlal eder
- Makroların içine eşleşmeyen süslü parantezler girer
- Alt blokların içinde
casekullanılır crReturn,switch,returnvecaseifadelerini tek bir makronun içine gizler
- Sözdizimsel yapıyı gizleyen makrolar, kodlama standartları açısından açıklığı zedeleyen unsurlar olarak görülebilir
- Ancak açık bir durum makinesi olarak yeniden yazılmış işlevler de küçük
case STATEblokları ve durum geçişlerinden oluşur; görsel yapı olarakgotoetiket bloklarının sıralandığı bir işlevden çok farklı değildir - İşlev uzadıkça durum makinesi olarak yeniden yazmak, özgün algoritma yapısını daha fazla bozar
- Bu teknik, sözdizimsel yapının bir bölümünü gizleme pahasına algoritma yapısını daha iyi görünür kılmaya çalışan bir ödünleşimdir
Yeniden girilebilir geliştirilmiş biçim ve sağlanan kod
- Basit oyuncak uygulama
staticdeğişkenlere dayandığı için yeniden girilebilir değildir ve çok iş parçacıklı kullanım için de uygun değildir - Gerçek uygulamalarda aynı işlevin birden çok bağlamda çağrılabilmesi ve her bağlam için son
returnsonrasından yürütmeye devam edebilmesi gerekir - Geliştirme yöntemi, ek parametre olarak bir bağlam yapısı işaretçisi geçirmektir
- Yerel durum ve coroutine durum değişkenlerinin tamamı yapı üyeleri olarak tutulur
- Döngü sayaçları gibi değişkenlere de
iyerinectx->işeklinde erişmek gerekir - Kod biraz daha çirkinleşir; ancak yeniden girilebilirlik sorununu ortadan kaldırırken rutinin genel yapısını korur
- C++ kullanıcıları coroutine’i sınıf üyesi hâline getirip yerel değişkenlere karşılık gelen durumu sınıf içinde tutarak kapsamı daha doğal şekilde yönetebilir
- Sağlanan
coroutine.h, bu coroutine hilesini önceden tanımlı bir makro kümesi olarak uygularscrönekli makrolar,staticdeğişkenler kullanan basit biçimdirccrönekli makrolar, yeniden girilebilir gelişmiş biçimdir- Ayrıntılı belgeler başlık dosyasındaki yorumlarda bulunur
- Visual C++ 6, varsayılan hata ayıklama ayarı olan “Program Database for Edit and Continue” altında
__LINE__makrosunu tuhaf biçimde ele aldığı için bu hileyi sevmez- VC++ 6’da coroutine kullanan bir programı derlemek için Edit and Continue kapatılmalıdır
- Proje ayarlarında “C/C++” sekmesi, “General” kategorisi, “Debug info” ayarında “Program Database for Edit and Continue” dışında bir seçenek seçilmelidir
- Başlık dosyası MIT lisansı ile sunulur
İlgili kaynaklar ve gerçek kullanım
- Donald Knuth’un The Art of Computer Programming, Volume 1, Section 1.4.2 bölümü coroutine’leri saf biçimiyle ele alır
- Tom Duff’ın Duff’s device tartışmasında, benzer bir coroutine hilesini bağımsız olarak akla getirmiş olabileceğini düşündüren içerik vardır; 2005-03-07 güncellemesinde Tom Duff bunu blog yorumu olarak doğrulamıştır
- PuTTY içindeki SSH protokol kodu bu coroutine hilesini gerçekten kullanır
- PuTTY örneği, ciddi üretim kodunda nadir görülen güçlü bir C hack’i düzeyindedir
1 yorum
Hacker News yorumları
Bir C projesinde API karmaşıklığını azaltmaya çalışırken bu sayfaya birkaç kez geri döndüm ve kontrol akışı açıklamasının harika olduğunu düşünüyorum
Yığının içinde ve dışında durum saklama ile farklı yaklaşımların okunabilirlik farklarını daha net düşünmeme de yardımcı oldu
Şu an vardığım sonuç, C coroutine kullanılıp kullanılmayacağına kütüphane kullanıcısının karar vermesinin daha iyi olduğu. Örneğin Mongoose(https://github.com/cesanta/mongoose) asenkronluğu olay callback’leriyle ele alıyor; böyle kütüphaneleri efsanevi çapraz platform C coroutine’lerine ya da daha kötüsü
std::thread’e port etmeye çalışmaktansa, her sistemin thread/task temel öğeleriyle sarmalamak çok daha rahat[1] https://www.chiark.greenend.org.uk/~sgtatham/putty/
[2] https://www.chiark.greenend.org.uk/~sgtatham/puzzles/
Coroutine gerçekten harika bir kavram; özellikle çoğunlukla Microsoft tarafındaki kişilerin sunduğu CppCon C++ coroutine videolarını izlemek de keyifli. “Negatif maliyetli soyutlama” kancası da epey iyi
Birkaç yıl önce Meta’daki arkadaşlarım C++ coroutine kullanmaya başlamıştı; sonunda bunun büyük bir hata olduğunu söylediler. Derleyici implementasyon hatalarıyla uğraşmak zorunda kalmışlar ve bunları takip etmek oldukça pis bir iş olmuş olmalı. Google’da ise google3/ ile düzgün entegrasyonu yapan parlak kişilerin artık kullanılabilir olduğunu söylemesini bekliyoruz
Bu yazı, Duff’ın cihazı [1] üzerinden makro tabanlı yapısal
gotoyu bir C coroutine implementasyon stratejisi olarak açıklıyor. Kilit nokta, birswitchbloğunun neredeyse herhangi bir yerinecaseifadeleri koyabilmek; tüm fonksiyonuswitchile sarmalayıp son coroutine dönüş konumunu birstaticdeğişkende saklamak ve hercoReturn’ücaseolarak etiketlemek şeklindeSustrik’in C coroutine yazısı da ilginç olabilir [2]
[1] https://en.wikipedia.org/wiki/Duff%27s_device
[2] https://250bpm.com/blog:48/index.html
co_yield,co_return,co_awaitgibi asenkron kodların serpiştirilmiş olmasının hem artıları hem eksileri vargoogle3 içindeki yöntemle karşılaştırıldığında avantajı, kodu okurken her parçanın asenkronluğunun açıkça görülmesi. Google’daki bazı programcılar dallanma biriminin ötesinde threading modelini bilmeden yaşayıp sonradan ciddi bug’lar üretebiliyordu
Dezavantajı daha basit. Çok sayıda kod “asenkron olabilir” hale geldiği için, zamanla sırf programcı o modda yazıyor diye her şey asenkronlaşıyor. Spinlock mu yoksa yield eden mutex mi kullanılacağı, kritik bölgenin boyutuna ve o andaki threading durumuna göre belirlenmeli; fakat okunabilirlik ve tutarlılığı korumaya çalışınca tüm projenin bir tarafa kayması kolay
Varsayılanı iki taraftan birine koymadan, önceki çalıştırmanın profilini temel alarak bir sonraki çalıştırmayı daha iyi optimize eden, bunu da kod değişikliği ya da bug olmadan yapan threading dili implementasyonları hakkında daha çok şey öğrenmek isterim
lc-addrlabels.hiçinde yer alan koda katkıda bulunmuştumGCC’nin yerel label özelliğini de kullanarak
__LINE__kullanımından tamamen kaçındım; aynı satıra birden fazlacoReturnkoymak da mümkündüswitchbloğunun neredeyse herhangi bir yerinecaseifadesi konabileceğini fark ettiği söylenebilir ve bu muhtemelen doğru; ama özelliğin kendisi neredeyse kesin olarak bilinçli tasarlanmış bir özellikYazının sonunda da belirtildiği gibi Duff, bunun üzerine coroutine kurulabileceğini de fark etmişti; ama bu fikri “iğrenç” bulmuştu
C’nin
switchyapısını ifade gücü düşük pattern matching gibi düşünürseniz “fallthrough” kolayca bug gibi görünür, ama öyle değil. Fortran’dakilerle aynı aileden hesaplanmışgotodur; değerlerin ardışık olması gerekmez ve label’ların hepsini en üste dizmek zorunda olmadığınız için daha kullanışlıdır. Böyle yazınca hesaplanmışCOMEFROM’a daha yakın gibi de görünüyor“Yaygın kullanılan yüksek seviyeli diller coroutine desteklemiyor” sözü 2000’de doğru olabilir, ama bugün C++20, Lua, Python, Ruby gibi birçok dil destekliyor
yieldanahtar sözcüğü de o zaman ya da çok geçmeden ortaya çıkmış gibi geliyorYazının sonundaki iyileştirme önerisi olan “ek bir fonksiyon argümanı olarak bir context struct pointer’ı koymak ve tüm yerel durum ile coroutine durum değişkenlerini bu struct’ın elemanları olarak bildirmek” yaklaşımı, closure implementasyonu gibi görünüyor. Çağrılan tarafı lambda yapıp dış değişkenleri/context’i/durumu kullanarak ne yapılacağına ya da hangi değerle yapılacağına karar verdirmek gibi; bunu böyle anlamam doğru mu merak ediyorum
switchyöntemi çok nadir değildir, ama genelde ilklendirme fonksiyonuna ve coroutine fonksiyonuna aktarılan bir durum işaretçisi bulunurGömülü projelerde bu yöntemi çok kullandım; bir coroutine motorun hızlanma/yavaşlamasını yönetirken, başka bir coroutine yalnızca hangi yöne gidileceğini söylüyordu. Bir ağ kütüphanesinde[1] de kullandığım oldu. Standart kütüphanede de
strtok()[2] gibi coroutine fonksiyonları varYönetilebilir kılmak için makro cehennemine kadar gitmeye gerek yok, ama
switch/caseakışını okumaktan hiç keyif aldığımı hatırlamıyorum[1]: https://github.com/REONTeam/libmobile/blob/master/relay.c#L3...
[2]: https://manpages.debian.org/bookworm/manpages-dev/strtok.3.e...
Aynı yazarın Simon Tatham's Portable Puzzle Collection’ı da var
https://www.chiark.greenend.org.uk/~sgtatham/puzzles/
Bu size C kara büyüsü gibi görünüyorsa, aynı yazarın makrolarla keyfi kontrol yapıları oluşturma yazısı da okunmaya değer: https://www.chiark.greenend.org.uk/~sgtatham/mp/
Coroutine’ler eğlenceli, ama gerçek kodda gerçek thread kullanmayı da düşünmek gerekir. Modern işlemcilerde çok sayıda çekirdek var, ancak coroutine’ler çoğu zaman yalnızca tek çekirdeği kullanır
Bu pratikte de bir sorun. Yakın zamana kadar coroutine’leri yoğun kullanan qemu, blok aygıt I/O’sunun önemli bir bölümünü tek bir thread üzerinden gönderiyordu ve bu performans sorunlarına yol açıyordu. Kevin Wolf ve diğerleri bunu yıllar içinde düzeltti; modern qemu I/O için birden fazla thread kullanır hâle geldi ve bu çalışma RHEL 9.4’e girecek
Coroutine, tek thread’li yürütmeyi yapılandırmanın bir yoludur ve kendi başına yararlıdır. Metindeki üretici-tüketici kalıbı buna iyi bir örnektir; bir stream’i parser’a bağlamak paralel bir algoritma değildir, dolayısıyla thread bunu yazmak için işe yaramaz
Paralel yürütülebilecek işler için tek thread’li bir paradigma kullanırsanız bunun verimsiz olması doğaldır; ama coroutine yoksul insanın paralelliği değil, bağımsız olarak anlamlı bir kontrol yapısıdır. Bir web sunucusunda event loop’un, dispatcher ile birlikte, birden çok bloklayan event’in arasından coroutine’leri geçirmesi gibi thread’lerle verimli biçimde birleştirilebilir; runtime çekirdek başına thread açarak paralelleştirdiğinde de thread’ler arası koordinasyon, her iş kuyruğunun derinliğini kontrol edip isteği daha az yoğun olana aktarmak düzeyine iner
Coroutine fikri, yerel bir iş ve senkron veri olduğunda; bunu, çağıranın iç döngüyü “çektiği” fonksiyonel paradigmadan ziyade, fonksiyonun bir şeyler üzerinde döngü kurup sonuçları başka bir yerdeki soyutlanmış tüketiciye “ittiği” ters çevrilmiş biçimde ifade etmenin daha kolay olduğu durumlarda kullanmaktır
Ardından coroutine’leri zamanlayıcılar arasında neredeyse hiç taşımamak ve farklı zamanlayıcılardaki coroutine’ler arasında veri paylaşımını da neredeyse hiç yapmamak
Coroutine’ler, işbirlikçi zamanlama sayesinde hiç lock gerektirmeyen rahat bir eşzamanlılık programlama tarzı sağlar. Genelde zamanlama gecikmesi artar, ancak atomik işlem/lock overhead’i ortadan kalktığı ve preemptive zamanlama için timer sürekli yürütmeyi kesmediği için throughput oldukça yüksek olabilir
Bu yaklaşımın C++ sürümü: https://www.codeproject.com/Tips/29524/Generators-in-C
Ben de Sciter’ımda ne olur ne olmaz diye kullanıyorum; oldukça iyi çalışıyor ve kullanışlı
Bunu modüler ve güvenli biçimde başarmanın yolu muhtemelen effect handler’lardır. Python’daki
yield’a benzer, ama değer döndürebilir; fonksiyon çağrısıyla sınırlı değildir ve istisnalar gibi scope’a sahiptir. Aşina değilseniz bu yazı iyi bir motivasyon sağlarDirect style’da yazılmış her fonksiyon, kontrolün başka bir yere gitmesi gerektiğinde bir “effect” gerçekleştirebilir. Burada
c=getchar()veemit(c)bu durumlardırBöylece kontrol effect handler’a gider; bu durumda muhtemelen iki fonksiyonun çağırıcısı sırada ne yapılacağına karar verir. Açıcı bir karakter yaydığında, o karakteri parser koduna verip devam ettirmek; parser daha fazlasına ihtiyaç duyduğunu söyleyene kadar ilerledikten sonra açıcıyı yeniden devam ettirmek gibi
Effect’ler verimli uygulanabilir; özellikle continuation’ın yalnızca bir kez çağrılmasına izin verecek şekilde sınırlandırılırsa. OCaml’de durum böyledir. Direct style kod ile tip/bellek güvenliğini birlikte sağlar ve eşzamanlılık ortamlarında da çok kullanışlıdır
Örnek burada: https://effekt-lang.org/docs/casestudies/lexer
“Bu numara elbette tüm kodlama standartlarını ihlal ediyor… Kodlama standartlarının yanlış olduğunu savunurum” kısmına hiç katılmıyorum
Kodlama standartlarının bu kodu reddetmesi yanlış değil; bu kod yalnızca sevimli bir numaradan ibaret. Büyük ölçekli yazılım mühendisliği, sürprizleri ortadan kaldırma ve sabah 3’te çağrılıp hata ayıklamak zorunda kalan uykusuz kişinin bile okuyabileceği kod üretme işidir. Programcının her zaman dört temel kuralı hatırlamasını bekleyemeyiz
switch,return,casegibi önemli öğeleri “obfuscation” makrolarının içine saklayıp sözdizimsel yapıyı bulanıklaştırırken algoritmik yapıyı ortaya çıkardığı iddiasını da ikna edici bulmak zor. İyi bir programda hem sözdizimsel yapı hem de algoritmik yapı net olmalı; bu yöntem bunun gerisinde kalıyor. Bence Rust’ınasyncfonksiyonlarda örtük durum makineleri oluşturma biçimi burada model alınmalıC dünyasında üçlü koşul operatörü bile fazla kışkırtıcıdır, C99 da yeni moda bir şey muamelesi görür. C++ dünyasında template metaprogramming’den caydırmanın tek nedeni, kullandığınız standartta aynı işi
constexprile yapabiliyor olmanızdır