1 puan yazan GN⁺ 2024-09-27 | 1 yorum | WhatsApp'ta paylaş
  • 1989’da FM TOWNS için çıkan High C Compiler, DOS ortamını desteklemenin ötesinde, dönemin C derleyicileri için nadir sayılacak birçok kullanıcı odaklı dil özelliği içeriyordu
  • Phar Lap’in DOS extender’ı ile birleşerek, 16 bit MS-DOS ortamında 32 bit 80386’dan yararlanan geliştirme akışı içinde FM TOWNS’un 1st-party C derleyicisi oldu
  • Sayısal literal alt çizgileri, etiketli argümanlar, case aralıkları, iç içe fonksiyonlar, generator’lar gibi özellikler C/C++ standartlarına çok daha sonra girdi ya da hâlâ standartta yok
  • İç içe fonksiyonlar, fonksiyon işaretçisi ile bağlam işaretçisini birlikte aktaran dışarı kaçmayan closure biçiminde “full function value” sağlayarak sıradan C fonksiyon işaretçilerinden daha ifade gücü yüksek bir yapı sunuyordu
  • Generator’lar, iç içe fonksiyonların üzerinde sözdizimsel şeker olarak uygulanmıştı; çağıranın for döngüsü gövdesini iç içe bir fonksiyona çevirip yield argümanı olarak geçirmek gibi basit bir yapıyla çalışıyordu

FM TOWNS ve High C’nin konumu

  • FM TOWNS ile ilgili kitap yığınından çıkan 1980’ler C derleyici kılavuzu, beklenenden daha zengin dil uzantıları içeriyordu
  • C ve onun soyundan gelen dilleri gerçek ortamlarda kullanmak için uzun süre üretici uzantılarına ihtiyaç vardı
    • Günümüzde GCC, Clang, MSVC merkezli ortamlarda uzantılar daha çok platforma özgü işlemler veya düşük seviyeli ayrıntı kontrolü etrafında yoğunlaşma eğiliminde
    • 1980’lerde ise daha küçük ve daha çok şirket benimsenme yarışı verdiğinden uzantılar da daha çeşitliydi
  • Phar Lap, 16 bit MS-DOS ortamında 32 bit 80386 işlemcisinden yararlanmayı sağlayan erken dönem DOS extender’lardan birini yaptı
  • MetaWare, Phar Lap’in isteğiyle High C Compiler’ı Phar Lap’in DOS extender SDK’sına port etti
  • Fujitsu, 803386 tabanlı FM TOWNS platform işletim sistemine Phar Lap’in DOS extender’ını entegre etti ve High C bu platformun 1st-party C derleyicisi oldu
  • FM TOWNS, ilk ANSI C standardı olan C89’un onaylanmasından hemen önce, 1989’da piyasaya çıktı

Standardın önüne geçen küçük kolaylıklar

  • Sayısal literal’larda alt çizgi ayırıcıları

    • Uzun sayısal literal’ları okunabilir kılmak için sayıların içine alt çizgi ayırıcıları konabiliyordu
    • C++, C++14’te 1'000'000 gibi kesme işareti ayırıcılarını getirdi
    • C ise benzer bir özelliği ancak C23’te getirdi
  • Etiketli argümanlar

    • Çok parametreli ya da bool gibi çağrı noktasında anlamı kolay anlaşılmayan türleri sık kullanan fonksiyonlarda argüman adları belirtilebiliyordu
    • High C’nin etiketli argümanları Python’ın popüler özelliğine benzer çalışıyordu
      • Argüman etiketi isteğe bağlıydı
      • Etiket varsa argumentName => value sözdizimiyle argümanlar istenen sırada belirtilebiliyordu
      • Etiketsiz ve etiketli argümanlar karıştırılabiliyordu, ancak fonksiyonun tüm parametreleri için karşılık gelen bir argüman bulunmalıydı
    • Standart C ve C++’ta bu özellik hâlâ yok
  • case aralıkları

    • Pascal’daki case low..high gibi, bir değer aralığını tek seferde eşleme özelliği sunuyordu
    • Standart C ve C++ bu özelliği benimsemedi

İç içe fonksiyonlar ve full function value

  • High C, Pascal gibi bir fonksiyonun içinde iç içe fonksiyonlar bildirmenize izin veriyordu
  • Uygulama yöntemi, standart Pascal’dan ya da GCC’nin iç içe fonksiyon uzantısından daha eksiksiz bir biçime yakındı
  • High C, iç içe fonksiyon bildiriminin yanı sıra full function value türleri de bildirebiliyordu
    • Geleneksel C fonksiyon işaretçilerinden farklı olarak, fonksiyon işaretçisiyle birlikte bir bağlam işaretçisi de taşıyordu
    • İç içe fonksiyonun yakaladığı bağlamın yeniden bulunmasını sağlıyordu
    • Dış fonksiyon döndükten sonra ömrü devam etmeyen, dışarı kaçmayan bir closure’dı
  • GCC’nin iç içe fonksiyon uzantısı, iç içe fonksiyonu normal bir fonksiyon işaretçisiyle referans edebilmek için çağrı yığınına çalıştırılabilir kod yazarak bağlam işaretçisini thunk ediyordu
    • Bu yöntem ciddi güvenlik risklerine yol açtı ve birçok platformun bu özelliği tamamen devre dışı bırakmasına neden oldu
  • High C’nin yerel fonksiyon referansları birinci sınıf değerler gibi kullanılabiliyordu, ancak ömürleri dış fonksiyonun dönüşünden sonrasına uzamıyordu
  • İç içe fonksiyonlar ebeveyn fonksiyona goto da yapabiliyordu
    • Smalltalk blokları gibi, iç içe fonksiyonun dışına çıkmayı sağlayan yerel olmayan çıkış mümkündü
    • Bununla kontrol akışı gibi davranan fonksiyonlar oluşturulabiliyordu
  • Objective-C, 2009’da escaping closure olarak kullanılabilen blocks özelliğini aldı; C++ ise 2011’de lambdas’ı getirdi
  • Bu iki özellikte de yerel olmayan çıkış yeteneği yoktu
  • Standart C’de hâlâ resmi bir iç içe fonksiyon özelliği yok

Generator coroutine’leri

  • MetaWare, bir bölümün tamamını ayıracak kadar generator özelliğini öne çıkarıyordu
  • High C, 1989’da plain C içinde Python tarzı generator coroutine’lerini destekliyordu
  • Generator fonksiyonu void foo(Arg arguments) -> (Yield yields) sözdizimiyle bildiriliyordu
    • Fonksiyon içinde sihirli yield(values...) fonksiyonu birden çok kez çağrılarak bir değer dizisi üretilebiliyordu
    • Çağıran taraf, üretilen değerleri for variable... <- foo(arguments...) do { ... } biçimindeki yeni for döngüsü sözdizimiyle sırayla dolaşıyordu
  • Bu uygulama iç içe fonksiyonlarla karmaşık biçimde birleştirilebiliyordu
    • Generator içindeki iç içe fonksiyon, dış generator’ın yield davranışını yakalayabiliyordu
    • İç içe fonksiyon kendisini özyinelemeli çağırıp ağaçları veya özyinelemeli veri yapılarını dolaşırken her adımda yield edebiliyordu
  • Bu biçim, Python’da veya birçok ana akım generator coroutine dilinde uygulanması zor görünen bir yöntem gibi duruyor

Generator uygulama yöntemi ve standart dillerle farkı

  • High C’nin generator’ları gelişmiş bir runtime olmadan iç içe fonksiyonlar üzerinde sözdizimsel şeker olarak çalışıyordu
  • void foo(Arg arguments) -> (Yield yields) biçimindeki generator bildirimi, sıradan void foo(void yield(Yield yields)!, Arg arguments) fonksiyon bildirimiyle eşdeğerdi
    • yield, “full function value” türünde örtük bir parametreydi
    • Generator gövdesindeki yield(values) çağrısı, bu örtük fonksiyon parametresini çağıran sıradan bir fonksiyon çağrısıydı
  • Çağıran taraftaki for döngüsü gövdesi iç içe bir fonksiyona dönüştürülüyordu
    • Bu iç içe fonksiyon generator’ın yield argümanı olarak geçiriliyordu
    • Yapı basitti ama etkiliydi
  • İç içe fonksiyonlar yerel olmayan çıkışı desteklediğinden, for döngüsü gövdesinin dışına çıkan break, continue, goto da uygun döngü dışı konuma goto edecek şekilde çalışıyordu
  • Standart C’nin böyle bir özelliği entegre etmeye çalışması pek olası değil
  • C++20, derleme zamanı coroutine dönüşümüne dayanan çok esnek ve karmaşık bir coroutine özelliği sunuyor
    • Bununla generator uygulamak mümkün görünüyor
    • Ancak ortaya çıkan sonucun yerel fonksiyonlarla bu kadar sezgisel biçimde birleşmesi pek olası görünmüyor

1 yorum

 
GN⁺ 2024-09-27
Hacker News yorumları
  • 2011'de iteratör tabanlı for hakkında bir özet hazırlamıştım. Bu, zaten uzun zaman önce unutulmuş özelliklerden biriydi; o dönemde C++ standardına girse nasıl görünebileceğini de ele almıştım
    Neyse ki elimde İngilizce High C/C++ Language Reference'ın bir kopyası var
    http://jdebp.uk./FGA/metaware-iterator-driven-for.html
    http://jdebp.uk./Proposals/metaware-iterator-driven-for.html
    • break veya return ifadesinin nasıl derlendiğini merak ediyorum. yield fonksiyonunun durum kodu döndürecek şekilde dönüştürülüp çağrı noktasında kontrol edildiği bir yöntem miydi?
    • Bu ters çevrilmiş gülen yüzü bilerek mi yaptılar?
  • D'de, hatta Das BetterC'de bile, şu özellikler var: sayısal literal içinde alt çizgi, case aralığı, adlandırılmış argümanlar, iç içe fonksiyonlar, statik iç içe fonksiyonlar, jeneratöre benzer özellikler
    Örneğin int a = 1_234_567;, case 5 .. case 6:, test(b:3, a:4); gibi biçimler mümkün
    Statik iç içe fonksiyonlar dış fonksiyonun frame değişkenlerine erişemediği için Error: static function test.foo.plus cannot access variable i in frame of function test.foo gibi bir hata veriyor
    Jeneratöre benzer özellikler https://dlang.org/spec/statement.html#foreach_over_struct_an... adresinde var
    • Bu yazıyı okurken sürekli D aklıma geldi. Yorumlarda Walter Bright'ın ortaya çıkacağını düşündüm
    • D'nin çöp toplayıcısının da gerçekten iyi bir özellik olduğunu düşünüyorum. Düşük seviyeli kodda bazen elle bellek yönetimi gerekebiliyor, ama pratikte çoğu zaman önemli olmuyor ve çöp toplayıcısı işleri çok daha kolaylaştırıyor
      Örneğin bellek içi bir önbellek servisi yapıyorsanız, önbellek öğelerinin kendisinin çöp toplayıcısı tarafından izlenmemesi daha iyi olabilir. Çöp toplayıcısı gerçek erişim desenlerini çoğu zaman bilmediği için engel olabilir. Ama o servisin diğer bileşenlerinin büyük kısmı için çöp toplayıcısı olması daha uygundur
    • Bir sorum var. İnsanların C'de iç içe fonksiyonlar fikrinden neden hoşlanmadığını bilen var mı?
      Adlandırılmış argümanlar neden test(a:4, b:3) biçiminde de test(.a=4, b.=3); biçiminde değil?
      C'de birinci sınıf türleri nasıl ele alabileceklerini de merak ediyorum
  • Bununla bağlantılı olarak lcc-win C derleyicisi operatör aşırı yükleme, varsayılan fonksiyon argümanları ve fonksiyon aşırı yükleme ekledi. Belgelerde “generic functions” bölümüne bakılabilir [1]
    Plan 9 C derleyicisi de çeşitli dil uzantıları getirdi; bunlardan anonim struct/union gibi bazıları daha sonra C standardına girdi. Günümüzde GCC, -fplan9-extensions bayrağını kabul ediyor [2] ve fonksiyon çağrıları ile atamalarda struct pointer'ları anonim alanlara otomatik dönüştürme gibi oldukça yararlı özellikleri açabiliyor
    [1] https://lcc-win32.services.net/C-Tutorial.pdf
    [2] https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html
  • Bu özellikleri tasarlayan dâhi kimdi acaba? Şirket içinde gerçekten öngörülü biri varmış gibi görünüyor
    Bunların daha geniş yayılıp dil standartlarını etkileyememiş olması üzücü. Bu kadar uzun zaman önce böyle özelliklerin var olması şaşırtıcı
    Daha önce Hacker News'te de gündeme gelmişti: https://news.ycombinator.com/item?id=38938402
    Bir yerde PDF kopyası var mıdır?
    • CLU, daha 1970'lerin ortaları ile sonlarında iteratörler, yani jeneratörler ve yield içeren for döngülerine sahipti [0]. Benzer dönemin Icon dili de benzer jeneratör özelliklerine sahipti [1] ve yield yerine suspend kullanıyordu. Ada'da da (1983) buna benzer bir özellik olduğunu biliyorum
      Yani bu tür dil özellikleri tamamen bilinmiyor değildi
      [0] https://publications.csail.mit.edu/lcs/pubs/pdf/MIT-LCS-TR-2...
      [1] https://dl.acm.org/doi/pdf/10.1145/800055.802034
    • Bitsavers'da HC 1.2 referans kılavuzunun (1985) bir kopyası var
      Sayıların içinde alt çizgi, case aralıkları, adlandırılmış parametreler, iç içe fonksiyonlar, hatta tam teşekküllü fonksiyon değişkenlerini açıklıyor
      https://bitsavers.org/pdf/metaware/…
      Dosyanın sonundan yaklaşık 50 sayfa gerideki Appendix A'ya bakın
    • MetaWare, 80'ler ve 90'larda Santa Cruz'da bulunan, çok ürün veren bir derleyici şirketiydi. Yaptıklarını severdim, kültürleri de oldukça ilginçti
      Eskiden kod öğrenip kullandığım zamanlarda onları biraz şüpheli siteler üzerinden tanımıştım
    • O kadar da şaşırtıcı değil. FORTRAN, Lisp, ALGOL ve COBOL sonrası yüksek seviyeli programlama dilleri arşivlerine dalarsanız bu tür dil fikirlerinden bolca görürsünüz
      Sistem programlama dillerinin de zengin bir tarihini keşfedersiniz. C ile Go'nun tasarım açısından, başka ekosistemlerde olup bitenleri ve geçmiş deneyimleri göz ardı etmeleri bakımından ne kadar benzer olduğunu da görürsünüz
    • Bu özelliklerin çoğu programlama dillerinin sunduğu standart özellikler listesinin bir parçası olmak yerine hâlâ yeni özellikler gibi görünüyor olması üzücü

Derleyici kılavuzu bağlantısı burada: https://winworldpc.com/product/metaware-high-c-cpp/33x
C kılavuzu PDF'inde 2007 telif hakkı ibaresi var

  • Önceki gönderi ve yorumlar burada: https://news.ycombinator.com/item?id=38938402
    • Görünüşe göre Joe Groff bugün bunu FediVerse'te yeniden gündeme getirmiş, bu yüzden burada da tekrar yükselmiş
      https://f.duriansoftware.com/@joe/113195961485703110
    • Görünüşe göre yazar dün aynı şeyi farklı bir URL'de yeniden yayımlamış. Tuhaf
  • Resimdeki örnekte dize sabitinin neden \n yerine ¥n ile bittiğini merak ediyorsanız, bu kod örnekleri muhtemelen Shift-JIS ile yazılmış. Shift-JIS'te ASCII'deki \ konumunda ¥ bulunur
    • Aslında başlangıçta 1969 tarihli Japonca ASCII türevi JIS Roman [0] vardı. Shift-JIS çok daha sonra çift baytlı karakter kümesi desteği ekledi
      [0] https://en.wikipedia.org/wiki/JIS_X_0201
    • Sorun şu ki Shift-JIS'te ters eğik çizginin ASCII kodu, 2 baytlı bir karakterin ikinci baytı olarak da kullanılır. Bu yüzden C'de Japonca dize sabitleri bazen düzgün çalışmaz
      Bu kullanım için EUC-JP daha iyidir. Çünkü bu sorun yoktur. Pascal'da { } yorumları yerine (* *) yorumları kullanılırsa Shift-JIS'te bu sorun yaşanmaz
    • Yazar bu kitabın ne zaman çıktığına dair bilgi vermemiş ve arayınca da bir bilgi bulunmuyor. Ama kitap yayımlandığında Shift-JIS standardı muhtemelen henüz yoktu
      Bunun yerine, Shift-JIS'in temelini oluşturan JIS X 0201'in (https://en.m.wikipedia.org.org/wiki/JIS_X_0201) kullanılmış olması daha olası
    • Benzer şekilde, Japonca DOS istemi C:\ değil C:¥ idi
  • Bu uzantılar Ada özellikleri. Ada'da Call (Param_A => 1, Param_B => "Foo"); biçiminde etiketler, rastgele tabanlı sayılarda alt çizgi kullanımı (X : Integer := 1_000;), iç içe alt programlar ve aralık tabanlı denetimler var
    • Yazıda da denildiği gibi Pascal, Ada'dan bile önce bu özelliklerin bir kısmına sahipti ve giriş noktaları olan görev tipleri esasen generator olarak görülebilir
      O dönemde C'nin birçok başka dille karşılaştırıldığında inanılmaz derecede ilkel olduğunu sık sık unuttuğumuzu düşünüyorum
  • İçerikten bağımsız olarak bu kitabın tipografisi ilginç. Aynı anda hem güzel hem korkunç bir havası var
    Japon yazım sistemi ya da kerning kuralları hakkında yeterince bilgim yok ama hem kanji hem Latin harfleri içeren orantılı bir yazı tipini alıp sabit genişlikli hücrelere zorla sığdırmışlar gibi görünüyor
    Her hâlükârda kod örneklerinin, bende bulunan birçok kitaptaki gibi 8 punto olmaması güzel
  • Generator'ları görünce Rust'taki iç/dış yineleme meselesi ve try_fold() aklıma geldi (https://scribe.rip/@veedrac/rust-is-slow-and-i-am-the-cure-3...)
  • Özellikle generator'lara bakınca, zamanının çok ilerisindeymiş gibi görünüyor. Belki de Fujitsu uzun standardizasyon sürecini umursamadığı için bunları doğrudan uygulayabildi
    Ama tam da bu yüzden bu uzantılar nispeten az bilindi ve onlarca yıl sonra modern C/C++ içinde yeniden keşfedilip yeniden icat edilmeleri gerekti
    • Fujitsu değil, MetaWare'di. MetaWare derleyici konusunda epey deneyimli bir şirketti ve aynı dönemde oldukça tanınan bir Pascal derleyicisi de vardı. Pascal'da zaten iç içe fonksiyonlar vardı
    • C, ikinin tümleyenini bile standarda almamak gerektiğinde ısrar eden kişiler tarafından ele geçirilmeseydi çok daha iyi bir dil olabilirdi
    • Coroutine'ler ve generator'lar o zamanlar zaten iyi anlaşılmış kavramlardı. Bunun için Icon'a bakmak yeterli. Bu yüzden asıl nedenin gerçekten de standardizasyon yükünü dert etmek zorunda olmamaları olduğu anlaşılıyor