Category Archives: F#

F# Programını Çalıştırmak

F# programlarını Visual Studio içerisinde çalıştırmanın iki yolu var; F# projesi oluşturmak – tıpkı C# projesi oluşturmak gibi – veya F# interactive penceresini kullanmak. F# dili, Visual Studio 2013 içerisinde aşağıdaki proje tiplerini destekliyor;

– Console Application, klasik konsol uygulama projesi template’i.

– Library, sınıf kütüphanesi template’i.

– Tutorial, F# örnekleri içeren bir konsol uygulaması. Buradaki örnek uygulamalara kesinlikle göz atmanızı tavsiye ederim.

– Portable Library, Silverlight, Windows Phone ve Windows 8.x ortamlarında çalıştırılabilen sınıf kütüphaneleri.

Adım adım bir konsol uygulaması oluşturalım hemen.

fsharp1.pngfsharp2.png

F# Interactive Kullanımı

Basit programlar için F# dili F# Interactive (FSI) adında bir seçenek sunar. Bu özelliği küçük kod parçacıklarını test etmek için kullanabilirsiniz. Hangi geliştirme profilini kullandığınıza bağlı olarak Visual Studio içerisinde bu özelliği View veya View –> Other Windows seçekelerini kullanarak bulabilirsiniz.

fsharp3.png

fsharp4.png

FSI penceresi input olarak kod yazmanıza imkan veriyor. Bu yüzden kodunuzu direkt olarak çalıştırabilirsiniz. Farklı olarak statement’lar için tek noktalı virgül (;) yerine iki noktalı virgül (;;) kullanmalısınız. Intellisense özelliğini desteklememesi kötü bir yanı tabi ki.

F# source file ve F# script’i arasındaki en temel fark build action kısmı. F# source file, compile olabilen ve .fs uzantılı bir dosya ve build action kısmı Compile atanmış. F# script’i ise .fsx uzantılı ve build action kısmı None atanmıştır. Fakat hangi dosyayı seçerseniz seçin, seçtiğiniz kodu F# Interactive üzerinde sağ tıklayıp ya da ALT+ENTER klavye kombinasyonu ile çalıştırabilirsiniz.

Gelin 1’den 50’ye kadar olan tek sayıların toplamını bulalım;

let mutable toplam = 0
for i = 0 to 50 do     if i%2 <> 0 then toplam <- toplam + i
printf "1'den 50'ye olan tek sayıların toplamı: %A" toplam

F# Interactive’de sonuç şu şekilde görünür;

1’den 50’ye olan tek sayıların toplamı: 625
val mutable toplam : int = 625

FSIAnyCPU

Bu özellik Visual Studio 2012 ile eklendi. FSI var olan 64-bit işletim sisteminde 64-bit işlemci ile çalışacaktır. Bunun dışında F# Interactive 32-bit işlemci ile çalışır. Bu FSIAnyCPU özelliği Visual Studio’nun Tool-> Options-> F# Tools –> F# Interactive kısmından aktif edebilirsiniz.

FSIAnyCPU işlemcisinin hangisi ile çalıştığına sizeof ve System.IntPtr ile de bakabilirsiniz. 32-bit işlemcilerde 4, 64-bit işlemcilerde 8 değerini geri döndürecektir.

> sizeof<System.IntPtr>;;
val it : int = 4

F# Interactive Directive’leri

F# Interactive içerisinde bulunan directive’leri şu sayfadan görebilirsiniz. Ayrıca F# Interactive içerisinde #help;; komutu ile mevcut direvtive’leri listeleyebilirsiniz.

F# Interactive directives:

#r “file.dll”;;              Reference (dynamically load) the given DLL
#I “path”;;                Add the given search path for referenced DLLs
#load “file.fs” …;;    Load the given file(s) as if compiled and referenced
#time [“on”|”off”];;   Toggle timing on/off
#help;;                      Display help
#quit;;                       Exit

 

F# Derleyici Directive’leri

F# Interactive küçük programlarınızı çalıştırmak için güzel bir özellik. Fakat çalıştırılabilir binary dosyalarınız için iyi bir seçim olmuyor. Bunun için Visaul Studio’ya ihtiyaç duyuyoruz. Şimdi de F# Derleyici Directive’lerine bir göz atalım;

#if koşullu derleme için kullanılır. Syntax’ı şu şekilde if <sembol>. Eğer sembol derleyici’de tanımlıysa, if directive’inden sonraki kod bloğu derlenir.

#else koşullu derleme için kullanılır. Eğer sembol tanımlanmadıysa, else bloğundan sonraki kod bloğu derlenir.

#endif koşullu derleme için kullanılır ve koşullu derlemenin bitimini gösterir.

#line orijinal kod satır numarasını ve dosya ismini gösterir.

#nowarning bir veya daha fazla uyarıyı etkisezliştmeyere yarar. F# genel olarak C#’tan daha fazla uyarı üretir.

F# ile Temel Veri Tipleri

F# bir .NET ailesi dili. Bu yüzden, temel veri tipleri ve referans’ları C# ile çok benziyor.

F# bir strongly typed dildir. Bu nedenle tür dönüşümlerinde oluşan bir hata compile time’da meydana gelir. Bu sayede bu tür hatalar, yazılım geliştirmenin erken evrelerinde kontrol edilip müdahele edilebilir.

C# ile F# arasındaki farklardan biri de; F# örnekleri açıkça tip belirtilmesine ihtiyaç duymaz, bu yüzden genellikle atanan değerden tipi anlayabilir. C# geliştiricileri için bu durum var keyword’ünü anımsatır. Fakat let ile var anahtar sözcükleri arasında bazı temel farklılıklar da mevcuttur.

Aşağıdaki tablo Literals (F#) sayfasının bir kopyası gibi olacak ama yine de C# ve F# gösterimini göstermek istedim;

Veri Tipi C# Gösterimi F# Gösterimi
string string s = “abc” let s = “abc”
char char c = ‘c’ let c = ‘c’
int int i = 5 let i = 5 veya
let i = 5l
Uint uint i = 5U let i = 5u veya
let i = 5ul
decimal decimal d = 5m let d = 5m veya
let d = 5M
short short s = 5 let s = 5s
long long l = 5L let l = 5L
ushort ushort u = 5 let u = 5us
ulong ulong u = 5UL let u = 5UL
byte byte b = 64 let b = 64y
let b = ‘b’B
sbyte sbyte sb = 64 let sb = 64uy
boolean bool b = true let b = true
double double d = 0.3
double d = 0.3d
double d = 3e-1
let d = 3.0 veya
let d = 3e-1 veya
let d = 3.
float float f = 0.3f
float f = 0.3F
let f = 0.3F

 

İlginç bir F# özelliği de, “B” suffix’i ile bir ASCII string’i gösteren byte dizisi oluşturabiliyorsunuz. Direkt olarak Encoding.ASCII.GetBytes fonksiyonunu çağırmak yerine bu suffix kullanılabilir. .NET Framework içerisinde string yapısı Unicode tabanlıdır. Bu yüzden bu yapı biraz garip gelebilir size. Aşağıdaki örnekte, AsciiString bir byte[] tipindedir.

let asciiString = "xyz"B // F# gösterimi
 
byte[] asciiBytes = Encoding.ASCII.GetBytes("xyz"); // C# gösterimi

C#’ın aksine, float tipi F# içerisinde 64 bit’tir (double precision floating-point) ki C#’ta double tipine eşdeğerdir. C# içerisinde float 32 bit (single precision floating-point) memory kaplar ki bu da F#’ta float32 tipine eşdeğerdir.

F# derleyicisi “implicit type conversion”’a izin vermez. Bir C# geliştiricisi için int tipi bir değer float tipinde bir değere herhangi bir işlem yapmadan çevirebilir. Burada bu değer 13’ten 13.0’a dönüşür. Bu F# içerisinde yasak olduğundan integer 13’ü bir float 13’e dönüştürmek için “explicit conversion” yapmanız gerekir.

String tipine bakacak olursa F# 2.0 versiyonunda aynı şu andaki C# string literal’de olduğu gibi iki farklı string var görünüyor; normal string ve verbatim string. Bir de F# 3.0 versiyonu ile triple-quoted string eklenmiş.

let a = "Son karakter bir tab \t"
let b = @"Son karakter bir tab \t"

Bunların F# interactive penceresinde çıktıları şu şekilde olur;

val a : string = "Son karakter bir tab    "
val b : string = “Son karakter bir tab\t"

Bunun dışında C#’tan bildiğiniz gibi escape sequence karakterleri (örneğin; ve \) göstermek isterseniz onları escape etmeniz gerekir. Örneğin;

let a = "Bu gecerli bir \"string\""
let b = @"Bu gecerli bir ""string"""

F# 3.0 ile gelen triple-quoted string bu acıyı hafifletmiş gibi. 3 tane çift tırnak’ın (”””) arasındaki herşey verbatim olarak algılanıyor. Bu yüzden çift tırnak ya da backslash gibi karakterleri escape etmenize gerek yok. Burada da şöyle bir kural var; string tek bir çift tırnak ile başlayabilir ama tek bir çift tırnak ile bitemez. Ayrıca XML tarafında da faydalı olduğu söyleniyor bu üçlü çift tırnak yapısının ama o konuda pek bilgim yok.

let a = """Bu gecerli bir "string" """
let b = """" Bu gecerli bir string """
let c = """ Bu gecersiz bir string """" // Geçersiz string

F# değişken isimlendirmede de bazı kolaylıklar sağlıyor. Bildiğiniz gibi okunabilir bir değişken ismi her zaman yazılımcılar için iyi bir pratiktir. F#’ta alfabetik olmayan değişken isimleri yazmak için çift ters tırnak kullanabilirsiniz.

//Boşluk içeren değişken ismi
let ``benim değişkenim`` = 4
 
//Keyword ile değişken ismi
let ``if`` = 5
 
//Tek tırnaklı değişken ismi
let a' = 6
 
// # içeren değişken ismi
let ``F#`` = "F#"

Akış Kontrolleri

F#’ta imperative stili programlar yazmanız için akış kontrollerini bilmeniz gerekir. For loop, while loop ve if expression bunlardan bazıları. C#’ta { ve } scope ayrımları için kullanırken F#’ta boşluk kullanılır. Visual Studio otomatik olarak tab’ı boşluk’a çevirir.

F#’ta iki çeşit for döngüsü bulunuyor: for…to/downto ve for…in.

for…to/downto döngüsü bir başlangıç değerinden bir bitiş değerine tekrarlanır. C#’taki for döngüsüne çok benzerdir.

for…in döngüsü ise bir pattern ile eşleşen enumerable collection içinde tekrarlanır. Örneğin; Range expression, List, diziler.. C#’taki foreach döngüsüne çok benzer. C#’ta 1’den 100’e kadar olan sayılar üzerinde şu şekilde dönebilirsiniz;

for (int i = 0; i < 100; i++)
{
}

F#’ta şu şekillerde gösterebiliriz;

for i = 0 to 100 do ...
 
for i in [0...100] do ...

Veya 100’den geriye olarak;

for i = 100 downto 0 do ...

Peki for…to/downto döngüsü ile ikişer ikişer nasıl ilerleyebiliriz? Bildiğim kadarıyla bu döngü bunu desteklemiyor. Mecburen for…in ile bir sequence veya list kullanmamız gerekiyor.

F#’ta while loop C#’taki ile aynı şekilde işlev görür. C#’ta şu şekilde;

int i = 0;
while (i <= 10)
{
    i++;
}

F#’ta ise şu şekilde gösterilir;

let mutable i = 0
while i <= 10 do    
i <- i + 1

F#’ta statement’lar sonunda noktalı virgül (;) kullanımı zorunlu değildir. Fakat bir satırda birden fazla statement yazmanız gerektiği bir durumda noktalı virgül kullanmak zorunda kalırsınız.

Gördüğünüz üzere yukarıdaki F# örneğinde mutable anahtar sözcüğünü kullandık. Bunun anlami i bir mutable değişkendir ve <- operatörü ile değeri değiştirebilir. Bu bizi F#’ta farklı bir kavrama götürür. Bunun anlamı; F#’ta mutable keyword’ü ile tanımlanmayan bir değişken immutable object’tir. Onun değerini değiştiremezsiniz. Bu yüzden C#’ta int i = 0 statement’ı F#’ta let mutable i = 0 statement’ına eşdeğerdir. Bu küçük bir değişiklik olarak görünse de, aslında çok temel bir değişikliktir. C#’ta değişkenler varsayılan olarak mutable iken F# değişkenler default olarak immutable’dır. Bu kavramı F# ile Fonksiyonel Programlama yazısında anlatmıştım.

Yukarıda söylediğim gibi F#’ta if bir expression’dır. Bunun anlamı geriye bir değer döndürür. Her if/else kolu aynı tipte değer döndürmelidir. Eğer if kolu herhangi bir değer döndürmüyorsa else kısmı opsiyoneldir. Eğer if bir değer döndürüyorsa else kullanılmak zorundadır. Bu yapısıyla C#’taki ?: operatorüne benzer. If yapısı içerisinde başka bir koşul yerleştirmek için C#’taki if…else if yapısına istinaden F#’’ elif kullanır.

if a<b then "küçük"
elif a>b then "büyük"
else "eşit"

C# eşleşme statement kullanımında switch yapısını kullanırken F# match expression’ını kullanır.

C# gösterimi;

int i = 1;
switch (i)
{
	case 1 :
		Console.WriteLine (1);
		break;
	case 2 :
		Console.WriteLine (2);
		break;
	default:
		Console.WriteLine ("Sayı değil");
		break;
}

F# gösterimi;

let mutable i = 1;
match i with    
	| 1 -> printfn "1"    
	| 2 -> printfn "2"    
	| _ -> printfn "Sayı değil"

C# içerisinde kullandığımız Console.WriteLine() ile konsola yazdırdığımız çıktıları F# ile de kullanabiliriz. Kendisi bir .NET dili olduğu için sorun olmayacaktır. Ama F# bize daha güçlü bir yapı olan printfn yapısını sunuyor. Örneğin;

C#

Console.WriteLine("Toplam: {0}", toplam);

F#

printfn "Toplam: %A" toplam

F# dilinde printfn, Console.WriteLine() methoduna göre daha katıdır. C#’ta {numara} her türlü değişkeni alır ve bu değişkenin tipi hakkında endişelenmeniz gerekmez. F# spesifik bir tipteki değişken için spesifik formatlara ihtiyaç duyar. Bu şekilde F# bununla ilgili hata oluşma olasılığını azaltır. Tüm formatları şu sayfada görebilirsiniz.

F# ile Fonksiyonel Programlama

fsharpF# öğrenmeye karar verdim.

Neden mi? Aslında bunun temel olarak 3 nedeni var;

  • Hayatımda hiçbir zaman fonksiyonel programlama temelinde bir dil öğrenmedim. Bunun nedeni de galiba OOP’nin günümüzde en popüler progrmalama paradigması olmasını gösterebilirim. Üniversite hayatım boyunca ve kısa süreli çalışma hayatım boyunca fonksiyonel programlamaya hiç ihtiyaç duymamam da buna neden oldu diyebilirim.
  • “Guru” olarak adlandırabileceğim birçok kişiden bu dili öğrenmenin ne kadar eğlenceli olduğunu, uygularken de bulmaca çözmek gibi bir his verdiğini duyunca açıkçası daha fazla dayanamadım. Adeta bir lezzetli çipetpet havası var F#’ta.
  • Yeni bir programlama dili öğrenmek ne zaman eğlenceli olmadı ki?

 

Kısaca Fonksiyonel Programlama

Fonksiyonel programlama, programları, argümanları ve dönüş değeri (return value) olan fonksiyonlar toplamı olarak görür. Nesneye yönelik programlamanın aksine, iteration işlemi için loop’lar kullanmak yerine çoğunlukla recursion kullanır. Program içerisindeki fonksiyonlar daha çok matematiksel fonksiyonlara benzerler zira programın durumunu değiştirmezler. Kısaca, bir değer bir identifier’a atandığında, asla değişmez. Fonksiyonlar parametre değerlerini değiştirmezler ve bu fonksiyonların sonuçları tamamen yeni bir değer döndürürler. Daha da kısaca, bir değer bir bellek bölgesine atandığında, asla değişmez. Fonksiyon sonuçları oluşturmak ise, önce fonksiyonlar değerleri kopyalar, bu kopyalanan değerleri değiştirerek işlem yaparlar. Orijinal değerleri başka fonksiyonlar kullanabilsin diye serbest bırakırlar ve artık onlara ihtiyaçları kalmadığında boşa çıkartırlar. Garbage Collection kavramının temelini oluşturur bu.

Bu sayede Fonksiyonel programlamanın birçok karmaşık hesaplama problemlerine çözümler sunduğu söylenir.

Fonksiyonel programlama ile ilgili birkaç kavramı da bilmek gerekiyor;

  • Higher order functions: Matematik ve genel bilgisayar bilim’inde bu fonksiyonlar en azından şu iki işlevden birini yapar;
    • Bir veya birden fazla fonksiyonu input alma veya
    • Output olarak bir fonksiyon verme. Diğer tüm fonksiyonlar first-order functions olarak isimlendirilir. Bu fonksiyonlara örnek olarak türev ve integral gösterilebilir. Zira ikisi de input olarak bir fonksiyon alıp geriye bir fonksiyon döndürürler. F# içerisinde bulunan List.map buna örnek olarak gösterilebilir.
  • First class functions: Bir elementin first class function olabilmesi için şunlardan biri olması gerekir;
    • Bir fonksiyona parametre olarak gönderilebilme
    • Fonksiyonlardan bir değer olarak dönebilme veya
    • Değişkenlere atanabilme. Programın herhangi bir yerinde bulunabilirler.
  • Pure functions: Bu fonksiyonlar şu iki maddeye birlikte sahiptirler;
    • Bu fonksiyonlar aynı input veya inputlarda aynı output’ları üretirler.
    • Bu fonksiyonların sonucu programda herhangi bir yan etkiye (side effect) neden olmazlar. (Mutable nesnelerin değişimi gibi veya I/O değişimi) Bu gibi fonksiyonlara örnek olarak Cos(x)  verilebilir.
  • Impure functions: Pure fonksiyonların tam tersi mantıkta çalışırlar. Örneğin;
    • Zamanı parametre alıp geriye haftanın günlerini döndüren bir fonksiyon. Zira farklı zamanlarda farklı değerler döndürebilir.
    • Random() fonksiyonu. Zira her çağırışımızda farklı bir değer döndürür. Ama Random(seed) fonksiyonu bir pure fonksiyondur. Zira bu fonksiyonu bu parametre ile her çağırışımızda aynı değer döndürür.
    • C’deki printf() fonksiyonu. Zira side effect olarak I/O’ya bir output verir.
  • Strict evaluation: Fonksiyonel programlama dilleri strict ve non-strict evaluation olarak kategorilendirilebilir. Temel konsept olarak fonksiyon argümanları nasıl işlenir ve expression’lar ne zaman hesaplanır kavramlarına dayanır bu kategorilendirme. Örneğin;
    • Strict evaluation altında
       print length([1+1, 2*2, 3/0, 4+4]) 

      kodu çalışmaz zira 3 / 0 division by zero döndürür. Fakat non-strict (lazy) evaluation altında bu geriye 4 sonucunu döndürür zira length içerisindeki expression’lar çalıştırılmaz. Sadece bunların uzunluğu bilgisi döndürülür. Strict evaluation altında bir fonksiyon çağırılmadan önce fonksiyonun tüm argümanları çalıştırılır.

Fonksiyonel programlamanın avantajlarını görmek için FP’nin yasakladıklarından çok, izin verdiği özelliklere bakılması gerektiği söylenilir. Örnek olarak fonksiyonel programlama ile fonksiyonlara bir değer gibi bakmak ve onları başka fonksiyonlara geçirmek. Bu başta pek önemli gözükmese de, etkileri olağanüstü olduğu söyleniyor. Data ve fonksiyonu birbirinden ayırmanın birçok problemin çözümüne olanak sağladığı söylenir. OOP programlamara göre FP programları daha kısa ve daha modülerdir.

Kısaca F#

F#’ın birçok sıkıntılı hesaplama problemlerini çözmek için en iyi yaklaşımlardan biri olduğu söyleniyor. F#, fonksiyonel programlama dışında imperative programming ve object-orianted yaklaşımlarına uyumluluk gösterir. Strongly typed özelliği mevcuttur, bu sayede programcıların belirsiz durumlar olmadığı sürece değişken tiplerini açıkça belirtmelerine gerek yoktur. C#’ta buna örnek olarak var keyword’ü gösterilebilir (tam karşılığı olmasa da). Bunun dışında inferred typing özelliğini de destekler.

F# bir “önce fonksiyon” (functional-first) dilidir. Buna rağmen az önce bahsettiğim gibi imperative programming ve object-orianted yaklaşımlarına da uyumluluk gösterdiğinden bu özellikler diğer Microsoft dillerine göre F#’ı daha esnek kılıyor.

F# temel olarak OCalm (object-orianted fonksiyonel programlama) dilinden modellendi. Ve .NET ile güçlendirildi. Generics kısmını hiçbir kod değiştirmeden destekler. Hatta IL (Intermediate Language) kodunu destekler. F# derleyicisi, sadece herhangi bir CLI (Common Language Infrastructure) için çalıştırılabilir dosyalar üretmez, ayrıca CLI olan her platformda çalışabilir. Bunun anlamı F# sadece Windows işletim sistemi ile sınırlı değildir. Ayrıca Linux ve Mac OS X üzerinde de çalışabilir.

Bu yazıdan yaklaşık bir ay önce OSS-approved license altında F# tamamen open source oldu. Microsoft tarafında, Visual Studio 2010, 2012 ve 2013 için tool’lar geliştiriliyor. Ayrıca F#’ın internet ortamında çok hareketli bir topluluğu mevcut. Birçok geniş ölçekli projelerde F# kullanarak katkıda bulunuyorlar. Birçok konferansta F# hakkında konuşmalar yapıyorlar. Twitter adreslerine şuradan ulaşabilirsiniz. Stack Overflow’da sorulara cevap veriyorlar. İnsanlara F# öğrenebilecekleri birçok eğitici site oluşturuyorlar.

F# Kurulumu

Yukarıda bahsettiğim gibi, F# birden fazla platformu destekler. Bu listeye buradan ulaşabilirsiniz.

Eğer windows üzerinde kullanıyorsanız, Visual Studio en iyi seçenek görünüyor. Visual Studio 2010 ve üzerinde F# default olarak kurulu olarak geliyor. Visual Studio 2008 kullanıyorsanız, MSDN üzerinden download edebilirsiniz. Eğer Vlsual Studio kullanmıyorsanız, Visual Studio Integrated Shell’i download edip, sonra F#’ı kurabilirsiniz. Temel olarak command-line compiler olan fsc.exe ve F# Interactive fsi.exe ihtiyaçlarınızı karşılayacaktır. Visual F# Resources sayfasından daha fazla bilgiye ulaşabilirsiniz.

Faydalı Linkler