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.