Railway Oriented Programming
(fsharpforfunandprofit.com)- Railway Oriented Programming, doğrulama, logging ve ağ hataları gibi gerçek dünya uygulamalarında tekrar eden başarısızlık yollarını fonksiyonel pipeline içinde ele alan bir yaklaşımdır
- Yalnızca başarı yolunu birbirine bağlayan örneklerin sınırlılığını demiryolu benzetmesi ile açıklayarak, F# kullanıcılarının da hata akışını görsel olarak kolayca anlamasını sağlaması temel noktadır
- Haskell açısından
Eithertipi ve Kleisli bileşimine yakındır; ancak soyut teoriden çok hata işleme reçetelerine ve uygulama kalıplarına odaklanır - Pratik uygulama için NuGet uyumlu F# kütüphanesi Chessie, C#·F# karşılaştırmalı GitHub örneği ve FizzBuzz uygulama örneği birlikte sunulur
- Yararlı bir yöntemdir, ancak her probleme aşırı uygulanmamalıdır; F#’ta type class olmadığı için örnek kütüphane gerekli fonksiyonları doğrudan tanımlar
Sunum materyalleri ve örnek kod
- Railway Oriented Programming sayfası, sunum slaytlarını ve kodunu tek yerde toplayan bir kaynaktır
- Fonksiyonel programlama örneklerinin sıkça varsaydığı “happy path” tek başına gerçek dünya uygulamaları geliştirmek için yeterli değildir; çıkış noktası bu problemdir
- Sağlam uygulamalar doğrulama, logging, ağ hataları, servis hataları ve diğer istisnai durumları da birlikte ele almalıdır
- Amaç, bu tür hata işlemeyi temiz bir fonksiyonel yaklaşımla nasıl ele alabileceğinizi demiryolu benzetmesi üzerinden kısa ve anlaşılır biçimde tanıtmaktır
- Benzer konu recipe for a functional app serisinde de devam eder
- Gerçek kodu C# ve F#’ta ROP yaklaşımını karşılaştıran GitHub projesinde görebilirsiniz
- Hata işleme için yararlı olsa da uç noktada uygulanmaması gerektiği uyarısıyla birlikte “Against Railway-Oriented Programming” da başvurulacak kaynaklar arasındadır
Videolar ve slaytlar
- Bu konu NDC London 2014’te sunulmuştur
- Aynı sunumun başka videoları da yayımlanmıştır
- Functional Programming eXchange’in 14 Mart 2014 tarihli sunum slaytları vardır
- PowerPoint slaytları GitHub üzerinden de indirilebilir; serbestçe ödünç alınıp kullanılabileceği belirtilir
Either monadı ve Kleisli bileşimi
- Haskell kullanıcıları için bu yaklaşım
Eithertipi gibi görünebilir - Burada
Leftdurumunda özel hata tipi listesi kullanacak şekilde özelleştirilmiştir- Örnek tip:
type TwoTrack a b = Either [a] (b,[a])
- Örnek tip:
- Yeni olan şey hata işleme kavramının kendisi değil, daha çok bunu açıklayan demiryolu benzetmesidir
- Standart Haskell terimlerini öne çıkarmamasının nedeni açıktır
- Bu materyal bir monad öğreticisi değil, hata işleme problemini çözmeye odaklanır
- F# ile tanışan birçok kişi monadlara aşina olmadığı için, görsel ve daha az göz korkutucu bir açıklama daha sezgisel olabilir
bindiçeren two-track tipini doğrudan monad olarak adlandırmak isabetli değildir; monad yasaları da ele alınmazEitherçok genel bir araçtır; burada aracın kendisinden çok bir reçete sunulmak istenir
Hata işleme reçetesinin bileşenleri
- Bu yaklaşım, “sadece
Eithervebindkullanmak yeterli” demekten daha kapsamlı bir teknikler kümesidir - İçerdiği teknikler şunlardır
Either String agibi basit bir biçim yerine hem sol hem sağ tarafta özel hata tipi listeleri kullanma- Monadik fonksiyonları pipeline’a entegre etmek için
bind(>>=) kullanma - Monadik fonksiyon bileşimi için Kleisli bileşimi (
>=>) kullanma - Monadik olmayan fonksiyonları pipeline’a entegre etmek için
map(fmap) kullanma - F# IO monadı kullanmadığından, unit fonksiyonlarını pipeline’a entegre etmek için
teekullanma - İstisnaları hata durumlarına eşleme
- Doğrulama gibi durumlarda monadik fonksiyonları paralel birleştirmek için
&&&kullanma - Domain-driven design’da özel hata tiplerinin sağladığı avantajlardan yararlanma
- Logging, domain event’ler ve telafi işlemleri gibi genişletmeler
- Amaç, neredeyse her durumda kullanılabilecek kadar çok yönlü, ama tutarlı bir stili zorunlu kılacak kadar sınırlayıcı bir şablon oluşturmaktır
- Kod yazma biçimi fiilen tek bir yapıya yakınsadığında, daha sonra bakım yapacak kişinin kodun organizasyonunu hızla anlaması kolaylaşır
- Bu yöntem tek çözüm değildir, ancak iyi bir başlangıç noktası olabilir
- Haskell topluluğu içinde bile hata işleme yöntemleri tutarlı değildir; bu durum yeni başlayanlar için kafa karıştırıcı olabilir
Kendi koduma nasıl uygularım
- Hazır bir F# kütüphanesine ihtiyacınız varsa NuGet ile kullanılabilen Chessie project’e bakabilirsiniz
- Bu tekniğin uygulandığı örnek web servisi GitHub projesi olarak sunulur
- ROP yaklaşımının FizzBuzz’a uygulandığı örneği Railway Oriented Programming: carbonated sayfasında görebilirsiniz
- F#’ta type class olmadığı için monadları yeniden kullanmanın genel bir yolu yoktur
- FSharpX library yararlı bir yaklaşım sunar; ancak
Rop.fskütüphanesi tüm fonksiyonları sıfırdan doğrudan tanımlar - Bu şekilde izole etmek, hiçbir dış bağımlılığın olmaması avantajını sağlar
Daha fazla okuma
- Bir monadı doğru tanımlamak için yalnızca
bindvereturnimplement etmek yeterli değildir - Monad, monad yasalarını sağlaması gereken cebirsel bir yapıdır; bu yasalar belirli durumlarda monoid yasalarıyla ilişkilidir
Eitherve Kleisli bileşimiyle ilgileniyorsanız şu kaynaklara bakabilirsiniz- Genel olarak monadlar
Eithermonadı- Kleisli kategorileri ve bileşim
- Kapsamlı hata işleme yaklaşımları
2 yorum
Hatasız programlar oluşturmak için Railway-Oriented Programming
Hacker News yorumları
Elixir,
withanahtar sözcüğü/makrosuyla bu sorunu epey temiz biçimde ele alıyorwith {:ok, file_handle} <- File.open(filename), ... do {:ok, parsed} endörneğinde olduğu gibi fonksiyonları yukarıdan aşağıya çalıştırıyor; her dönüş değeri soldaki kalıpla eşleşmezse o değerle erken dönüş yapıyorBöylece her fonksiyonu hem
{:ok, value}hem de{:error, reason}alacak şekilde yazmak yerine, fonksiyonun yalnızca ilgilendiği değeri almasını sağlayıpwithbloğundaki pattern matching ile hata yayılımını ele almasına izin verebilirsinizÖrneğin
File.open{:error, reason}döndürürseIO.readçalıştırılmaz ve tümwithsonucu{:error, reason}olur. Sonuçta yalnızca başarı yolunu yazıp, başarısızlık yolunu çağıranın isterse eşleştirmesine bırakabilirsinizBu, istisna işlemeyi yarı yarıya yeniden icat etmek gibi hissettiriyor;
catchyerineelsegibi bir ad verilirse sadece istisna işleme gibi daha az görünürwhereve Lisp’tekilet’ten etkilenmiş bir yapıFonksiyonun önkoşullarını kurmak sayılır; Elixir’de ayrıca
elsebloğu da kullanılabiliyorIO.readbaşarısız olursa dosya tanıtıcısını kimin kapatacağı da bir sorunYazar birkaç yıl sonra Against Railway Oriented Programming adlı bir devam yazısı da yazmış: https://fsharpforfunandprofit.com/posts/against-railway-orie...
Demiryolu odaklı programlama ilginç ve kullanılabileceği yerler var, ama büyük bir uyarı etiketi gerektirdiğini düşünüyorum. Pratikte bunun çoğu zaman exception handling’i kötü biçimde yeniden icat etmek için kullanıldığını gördüm; exception’ları doğru anlayıp kullanırsanız çoğu hata durumunu daha temiz ve etkili biçimde ele alabilirsiniz.
Exception’ların avantajı, çoğu durumda güvenli seçeneğin varsayılan olmasıdır. Bir hata durumu, kodun spesifikasyona göre yapması gerekeni yapamadığının sinyalidir; o durumda devam etmek, yanlış varsayımlarla verinin bozulmasına yol açabilir. Exception’lar varsayılan olarak temizlikten sonra çağırana yayılır; demiryolu odaklı programlama gibi yaklaşımlar ise unutması kolay, hata yapması kolay çok sayıda boilerplate gerektirir.
Yine de iki durumda faydalıdır. Biri, doğrulama gibi belirli ve iyi tanımlanmış beklenen hataların oluştuğu noktada ele alınması gereken durumlar; diğeri ise exception handling kullanılamayan ortamlardır. Eski jQuery’nin Promise tabanlı asenkron kodu neredeyse demiryolu odaklı programlamanın bir implementasyonu gibiydi; ancak modern JavaScript’te
async/awaitile yeniden exception handling kullanılabiliyor.Demiryolu odaklı programlamanın büyük ödülü, yalnızca fonksiyon imzasına bakarak hangi hataların çıkabileceğini hemen anlayabilmektir.
Boilerplate, dile bağlı olarak azaltılabilir. Haskell’de
donotasyonu var; F#’ta ise result computation expression’larıyla çok temiz biçimde yazılabilir. ÖrneğinLoginError = InvalidUser | InvalidPwd | Unauthorized of AuthErrorşeklinde tanımlayıp, akışıResult.requireSome,Result.requireTrue,Result.mapErrorile kurabilirsiniz.Tersine, exception’ların dezavantajlarının da azaltılıp azaltılamayacağını merak ediyorum. Java IDE’leriyle entegre olup belirli bir kod satırında fırlatılabilecek yakalanmamış exception’ları otomatik gösteren bir linter ya da statik analiz aracı var mı, bilmek isterim.
https://demystifyfp.gitbook.io/fstoolkit-errorhandling/fstoo...
Öncelikle birçok dil, exception yakalamak için tuhaf ve gereksiz scope’lar dayatıyor. Exception fırlatabilecek bir fonksiyonun sonucuyla bir değişken bildirilip başlatılacaksa, başarısızlık durumunda başka bir değer koymak isteseniz bile bildirimi ve başlatmayı ayırmanız gerekiyor.
Daha büyük sorun, hata durumlarının doğru biçimde ele alınması için yeterli bağlamın bulunduğu en düşük seviyede ele alınması gerekirken, exception’ların varsayılanının “en tepeye kadar fırlatmak” olmasıdır. Tek bir dosya önbelleği eklediğinizde üst seviyedeki
HandleRequestfonksiyonu birden I/O exception’ı fırlatabilir hale gelebilir; soyutlamalar kolayca sızar.Bir fonksiyonun döndürebileceği her şeyin açıkça imzanın parçası olması gerektiğini, çağıranın da bunu açıkça ele alması ya da yukarı aktardığını en azından belirtmesi gerektiğini düşünüyorum. Dilin bunun için çok fazla boilerplate istemesi gerekmez.
Resulttipi/trait’leri hata işlemeyi doğru yaptı.Bir dosya ya da kaydın bulunmaması da beklenip beklenmediğine göre değişir. Kullanıcının verdiği ID ile yapılan bir sorguysa, o sorgunun kendisi kullanıcı girdisi doğrulamasıdır. Ama bir veritabanı kaydında saklanan blob adıyla depolamaya sorgu atıldığında blob yoksa, bu exception olarak ele almaya uygun bir durumdur.
Hata ile exception arasındaki sınır bulanık ve ekibe, problem alanına göre değişiyor; ama bunun makul bir pratik kural olduğunu düşünüyorum.
C++ kodunda veriyi başta denetleyip, denetim başarısız olursa uygulamayı çökertip, bulunan hata durumuna göre ilk denetim kodunu debug edip düzelterek ilerlemek mümkün.
Ele aldığım veri, bilinen bir CAD veri biçimine ya da geometrik topolojiye uyan görece açık bir veri olduğundan, doğru verinin ne olduğunu önce anlayabilmek bakımından hata koşullarını ele almak “epey kolay” sayılıyor.
Açıkçası bu sitenin tamamı bir hazine. Fonksiyonel dil kullanmayı düşünmeseniz bile başka bir bakış açısı kazandırıyor; bu da kişisel olarak bana çok yardımcı oldu. Diğer yazıları da öneririm.
Yine de monad’ın ne olduğunu galiba asla anlayamayacağım.
https://fsharpforfunandprofit.com/series/understanding-parse...
“Hata işlemeyi çağrı noktasından uzağa itmek kötüdür” iddiasının geçerli olup olmadığını merak ediyorum. Hatayı en iyi çağıran taraf işleyebileceğine göre, aşağıya aktarmak yerine doğrudan işlemesi gerekmez mi diye düşünüyorum
Örneğin
validate and-then update-db and-then send-emailakışındavalidatehatasını çağırana döndürmek sorun olmayabilir. Amaupdate-dbbaşarısız olursa yeniden denenecek mi, başka bir servis mi kullanılacak, kuyruğa geri mi konacak, kullanıcıya mı bildirilecek buna karar vermek gerekir.send-emailhatası için de aynı şey geçerliSonuçta
ValidationErrorkullanıcıya bildirilir,DBError5 dakika sonra yeniden denenir ya da uzaktan yeniden denendikten sonra bildirilir,EmailFailedise e-postayı tekrar kuyruğa koyup başarılı sayar gibi daha karmaşık bir dallanma yapısına dönüşmez mi diye düşünüyorum. Belki de bu kabul edilebilirdirGo’dan hoşlanmayanlar hata koşullarını çok sık kontrol etmek zorunda kalmaktan yakınır; Java’nın checked exception’ları da benzerdir.
Eitherkullanırsanız kontrol etmeye zorlanmazsınız; ister doğrudan kontrol eder, ister çağırana aktarabilirsinizJava’nın unchecked exception’larından hoşlanmayanlar ise neyin ne zaman fırlatılacağını bilememekten yakınır.
Eitherkullanırsanız bu açık hâle gelirAsıl nokta, hata olduğunda ne yapılacağını bilmememizdir;
Eitherde bu bilinmezliğe iyi uyar. Mevcut bir Java kod tabanındaLimit getUserLimit(User)bir değer bulamadığındaNotFoundexception’ı fırlatabilir,nulldöndürebilir ya da varsayılan değer döndürebilir; kodun içine dalmadan bunu bilemezsinizEitherolsaydı çağıran taraf iç kodu okumadan güvenebilirdi veorElse(null)ya daorElseThrow(...)gibi yerleşik araçlarla bunu kolayca başka hata koşullarına dönüştürebilirdi. AyrıcaEitherbir ifade olduğu için soyutlamaya uygundur; bu sayedeDbUpdateriçine özel yeniden deneme kodu yerine genel bir yeniden deneme mantığı yazılabilirtry-catchtarzıyla uygulamayı denedim; iç içetryblokları,catch(DBError),sleep(5), uzaktan yeniden deneme,EmailFailedErroriçin yeniden kuyruğa alma veValidationErrorbildirimi birbirine girince çok daha kötü görünüyorhttps://gist.github.com/Andrewp2/9d97bd213b061166d6df565ce26...
Birisi daha iyi bir
try-catchsürümü yazabilir ama bunun sunulan demiryolu tarzı sürümden çok daha kötü olduğunu düşünüyorumDDD’yi seviyorum ama benim gibi sıradan bir programcının yönetebileceği makul bir bakım döngüsüne ihtiyaç var. Değiştirilebilir kodda TDD’nin iyi tarafı, bakımın bilişsel yükünü azaltmasıydı
Demiryolu tarzı programlama, birçok dönüşümden geçerken çok fazla bağlam taşıyabilir; hattın sonunda hazır olmayan iş ve sistem bağlamlarıyla yüklü bir tren almak istemem
DDD ve onion architecture’ın iki tarafın avantajlarını sunduğunu düşünüyorum. Sonuçta yine bir demiryolu oluşur ama mikro uygulamanın içine gömülü değil, dışındadır. Demiryolu tarzı konuların açık ve biraz zahmetli biçimde kodlanması daha iyidir; iyi yapıldıysa demiryolu tarzı kararların çoğu, akıllı kodlama yapılarıyla kaçınılmaması gereken iş kararlarıdır
Monad tarzı programlama harikadır ama gerekli iş kararlarından kaçmak için kullanma yönünde bir cazibesi vardır. Belirsizliği tip sistemine koymak, ileride zor ya da imkânsız durumlara yol açabilir
try/catchiçinde özelleşmiştry/catchblokları olmasından farklı değildirÖrneğin
send-emailfonksiyonunun içinde çok hızlı geçici hataları işlemeyi seçebilirsiniz. İzin verilen süre içinde bağlantı kurulursa başarılı yola geri dönülür; kurulamazsa çağırana döndürülürKesin bir kural varsa, insan müdahalesi gerektiren işlemlerin mutlaka sarılıp yukarı aktarılması gerektiğidir. Başarılı yola geri dönülse bile uyarı log’u bırakmak iyi olur
notify-user,ValidationErrorveDBErroriçin kullanıcı bildirimlerinden sorumlu olur;try-update-db,update-db, 5 dakika sonra yeniden deneme ve uzaktan yeniden denemeyi bir araya getirir;try-send-emailise hata durumunda e-postayı tekrar kuyruğa koyup bunu başarıya çevirebilirBöylece en üst seviye
validate and-then try-update-db and-then try-send-email or-then notify-usergibi korunur.and/or/thenyapısınımap/bind’a iyi uyarlamış gibi görünüyorBu site, gerçekçi ve pratik kavramlar öğretmesi bakımından karşılaştığım programlama eğitim siteleri arasında en iyisiydi. Özellikle geçersiz durumları ifade edilemez hâle getirme kavramını seviyorum ve dilden bağımsız olarak uygulamaya çalışıyorum. Elbette bazı diller bunu daha kolaylaştırıyor
https://fsharpforfunandprofit.com/posts/designing-with-types...
Çağrı/dönüş yerine veri akışı kullanınca bu sorun daha iyi çözülür ve büyük ölçüde kendiliğinden ortadan kalkar.
Çağrı/dönüş modelinde bir şey döndürmek gerekir; dolayısıyla hata yüzünden döndürülecek değer yoksa hatayı döndürmek ya da Go’da olduğu gibi hata ile normal dönüş değerini birlikte döndürmek gerekir. Bu da başarı yolunu kirletir. Polimorfik bir kapsayıcıyla sonraki işlemleri birbirine geçirmek biraz iyileştirir, ama sorun kalır.
Veri akışında bir sonraki filtre aşamasına hiçbir şey göndermemek yeterlidir; bu yüzden başarı yolu hiç etkilenmez. Hatalar standart hata çıktısı gibi bir yere gönderilebilir ve uygulama düzeyinde merkezileştirilebilir.
Kulağa fazla iyi gelebilir, ama Wunderlist’te bunu gerçekten kullandık ve iyi çalıştı. Değer olmadığında kullanılan aynı teknik, henüz değer olmadığında, yani asenkron işleme için de aynen geçerlidir.
Basit bir veri akışında hata oluşunca bir sonraki aşamaya veri gitmediği için durması sorun değil. Ama
sqrt(-1)noktasında hemen “çöküp” hata çıktısına ya da günlüğe atlanırsa, belirli kullanım senaryolarında negatif sayılar için başka bir değer verecek şekilde alanı genişletmek istediğinizde zorlaşır.Bu noktada
Maybegibi açık hata tipleri işe yarar. Başarı yolu, hata yokmuş gibi yazılır; ancak fonksiyon bileşimi için farklı bir operatör kullanılır. Hatadan kurtulmak istiyorsanız o noktada iki yolu da ele alırsınız ve sonrasında artık hataya izin vermeyebilirsiniz. Soyut bir “olabilir değer”den gerçek değeri çıkarmak için iki yolu da zarif biçimde ele almanın gerekmesi özellikle güzel.Açık hata tipleri kullanınca başarı/hata ikiliğinin de dışına çıkabilirsiniz. “Değer yok ama…” ya da “değer var ama…” gibi durumlar da ifade edilebilir; paralel işlenebilen birden çok görevde hatalar biriktirilip sonuç olarak toplanabilir. Daha da ötesi, belirlenimsizlik kodlanarak grafik dolaşımı gibi alanlarda kullanılabilir.
Haskell programcılarının yaptığı gibi durumu saf bir biçimde taşıyabilirsiniz; kötü şöhretli Haskell
IOmonadı ise gerçek bir yolu temsil etmekten çok, dış dünyayla temas noktalarınımainile sınırlayan bir düzenek gibidir. Bu saflık içindir, ama saflığın her zaman mutlak biçimde peşinden gidilmesi gereken bir değer olduğunu düşünmüyorum.Bu teknik basit bir demiryolundan çok daha çeşitlidir ve saflığı korurken yalnızca başarı yolunu yazmanın temiz bir yolunu sunar. Denklem tabanlı statik analiz ve mock’sız birim testleri için de avantajlıdır. Daha fazla ana akım dilin bu monad araç kutusunu daha iyi sunmasını isterdim.
İlgili bağlantılar: Railway Oriented Programming - https://news.ycombinator.com/item?id=31404643 - Mayıs 2022, Railway-Oriented Programming (2015) - https://news.ycombinator.com/item?id=17337155 - Haziran 2018, Railway oriented programming - https://news.ycombinator.com/item?id=11955917 - Haziran 2016, Railway Oriented Programming - https://news.ycombinator.com/item?id=9166943 - Mart 2015, Railway-oriented programming - https://news.ycombinator.com/item?id=7887134 - Haziran 2014
Ek olarak What is railway oriented programming? (2020) - https://news.ycombinator.com/item?id=34245639 - Ocak 2023, Railway Oriented Programming in Elixir (2015) - https://news.ycombinator.com/item?id=11958578 - Haziran 2016
Bu stil, diğer monadlar gibi bulaşıcıdır; tüm iş mantığı bu şekle bürünür. Bu sizin için sorun değilse okumaya devam edebilirsiniz.
İstisnalar, adından da anlaşılacağı gibi beklenmeyen hatalardır; yakalandıkları yere kadar yığını geri sarar ve gerekli temizliği otomatik olarak yapar. I/O hataları ya da bellek yetersizliği gibi, iş fonksiyonunun kapsamı dışında oluşan hatalar için uygundur.
Veriyi doğrulamak istiyorsanız istisna işleyicisine ya da monada dayanmanız gerekmez. Fonksiyonel pipeline’ın sonucunu veri haline getirmeniz yeterlidir. Doğrulama hataları varsa bunları bir kümede toplayıp pipeline bittikten sonra kontrol edebilirsiniz. Genellikle ilk hatada başarısız olmak istememe ihtimaliniz yüksektir.
Pipeline içinde yan etki gerekiyorsa bunu yapmayın; o etkiyi veri olarak tarif edip daha sonra çalıştırın. Ortada erken çıkmanız gerekiyorsa pipeline’ı o noktada bölün, sonucu işleyin ve sonra başka bir pipeline’a koyun.
Ya da başarı yolunun devam edip etmeyeceğine karar vermesi için bir interceptor zinciri kullanabilirsiniz. Dil destekliyorsa pattern matching kullanabilirsiniz. Önemli olan, pipeline’ın bir kısmının atlanıp atlanmayacağına dair kararın iş fonksiyonunun dışında verilmesidir; böylece iş fonksiyonu yalnızca saf veri dönüşümüne odaklanır ve yeniden kullanılabilirliği artar.
Böyle bir konunun yeniden gündeme gelip tartışılması bile güzel. Bu siteyi bir yıl boyunca okudum ve örnekleri takip ettim; en iyi F# kaynaklarından biriydi.
Bu sitede ele alınan kavramlar tek başına bile tüm dillerde fonksiyonel düşünmeye yardımcı oldu.
Eski bir site; şimdi ilgi görüyorsa bu F#’ın ivme kazandığı anlamına mı geliyor merak ediyorum. Ayrıca başka dillerdeki programcıların da Rust’taki gibi demiryolu tarzı hata işlemeyi kullanıp kullanmadığını merak ediyorum.
Yine de F#’a geçemememin nedeni, zamanımın çoğunu masaüstü frontend’ini ayarlamakla geçirmem. Bu alanda F# pek avantaj sağlamıyor gibi görünüyor. Üretime hazır masaüstü frontend’leri yerel F# değil, çoğunlukla C# tabanlı nesne yönelimli kod oluyor ve F# ile ince bir glue code katmanıyla bağlanıyor.
Biraz olsun karmaşık UI özelleştirmesi gerektiğinde yeniden nesne yönelimli dünyaya dönmek gerekiyor; öyle olacaksa bunu doğrudan C#’ta yapmak daha iyi bence. Bana göre F#’ın ivme kazanması için yerel bir UI framework’üne ihtiyacı var.
Tve sonuç koşulunu tanımlayan belirli bir enum ile parametrize edilebilen bir template dönüş tipi tanımlıyoruz.Acele durumlarda
std::pairile de yapılabilir; hata metnini bir string içinde döndürüp string boş değilse erken çıkmak da mümkün. Tüm fonksiyonlar böyle bir tip döndürürse hata metnini en üste kadar bubble up ettirebilirsiniz.Bu yazının birkaç yılda bir yeniden gündeme gelmesini seviyorum. Gerçekten harika bir yazı ve sunum.
Her yeniden okuduğumda teknik yolculuğumda beceri ya da felsefe açısından farklı bir yerde oluyorum; bu yüzden her seferinde farklı görünmesi de ilginç.