Scala’da Trait

Merhabalar!

Bu yazımda sizlere birkaç örnekle Scala’daki trait yapısından bahsedeceğim.

İşe IDEA ile TraitVeCaseClass isimli bir Scala projesi oluşturarak başladım. com.mehmetakiftutuncu.traitvecaseclass paket yapısını oluşturdum. Son olarak içinde boş bir main metodu olan Main.scala dosyasını oluşturdum.

Haydi başlayalım!

Scala’da Trait

Scala’daki trait’leri Java’daki interface‘ler ve abstract class‘ların birleştirilmiş haline benzetebiliriz. Bir trait sadece bazı değer ve metodları tanımlamak için kullanılabileceği gibi, bu değer ve metodların işlenişlerini de içerebilir. Kelime olarak özellik anlamına gelen trait çoğunlukla bir sınıfa veya objeye bir özellik kazandırmak için kullanılır. Bunlar örneklerle daha iyi anlaşılır sanırım. 🙂

Şimdi birkaç trait tanımlayalım ve bunları kullanalım.

Screenshot 2016-01-24 11.01.02.png

Gördüğünüz gibi trait’leri de tıpkı class veya object tanımlar gibi tanımlıyoruz ve trait özel kelimesini kullanıyoruz. Bu örnekteki trait’leri Traitler.scala dosyasına, bunları kullanan class’ları da Classlar.scala dosyasına yazdım.

Insan trait’i insana ait bir özelliği tanımlıyor ve gördüğünüz gibi sadece tanımlıyor, bir değer belirlemiyor. Bu yüzden bu değer daha sonra Insan trait’ini kullananlar tarafından tanımlanmak zorunda.

Muzisyen trait’i de enstrüman çalan bir insana ait özellikler tanımlıyor. Insan trait’inden türediğini (extends Insan) görüyorsunuz. Insan‘da olduğu gibi tanımlanan ama değer verilmeyen enstrumanAdi ve tecrube şeklinde 2 özelliği var. Bir de cal() adında metodu var fakat bu metodun gövdesi de tanımlanmış. Yani Muzisyen trait’ini kullanan hangi müzisyen olursa olsun, nasıl çalması gerektiğini bilecek.

Burada cal() metodu içindeki String kullanımını kısaca anlatmak istiyorum. Buna String interpolation deniyor. Başında s karakteri olan String’ler içine doğrudan değerler gömebiliyorsunuz. Bir değeri String içine gömmek için başına $ (dolar işareti) koymanız yeterli. Eğer ayrıca hesaplanması gereken bir değer varsa bunu ${} (dolar işareti ve ona bitişik açılan ve kapanan süslü parantezler) içine yazmanız gerekiyor. Küçük bir örnek:

val isim = "Akif"
val metin = s"Adım: $isim, büyük harflerle: ${isim.toUpperCase}"
// Sonuç => Adım: Akif, büyük harflerle: AKIF

İşleri çok kolaylaştırdığı kesin. Şimdi devam edelim. 🙂

Gitarist trait’i gitar çalan bir müzisyeni temsil edeceği için Muzisyen trait’inden türüyor ve enstrumanAdi özelliğine bir değer atıyor. Bir üst türde tanımlanan bir değeri veya metodu ezip değerini değiştirirken override kelimesini kullanıyoruz.

Virtuoz trait’i çaldığı enstümanda uzun yıllar tecrübe sahibi olup virtüöz sıfatına sahip bir müzisyeni temsil edeceği için Muzisyen trait’inden türüyor ve tecrube değerini 20 yıl olarak belirliyor.

Son olarak, Muhendis trait’i bir mühendislik alanı ve sorun çözecek bir metod tanımlıyor.

Burada gördüğünüz gibi trait’ler de tür kalıtımı (inheritance) yapabiliyor ve birbirlerinden türeyebiliyorlar.

Şimdi class’lara bakalım.

Ahmet sırasıyla GitaristMuzisyen ve Insan trait’lerinden türediği için 1 yıllık tecrübesi olan bir gitaristi temsil ediyor. Bu aynı zamanda Ahmet class’ından oluşturulmuş bir nesnenin tür olarak bir Gitarist, bir Muzisyen ve bir Insan olduğu anlamına geliyor.

Mehmet için durum biraz daha farklı. Mehmet aynı anda birden fazla türden türüyor. Buna çoklu kalıtım (multiple inheritance) da deniyor. Java bunu desteklemiyor. Java’da her sınıf yalnızca bir türden türeyebilirken Scala trait’ler ile bu sorunu çözmüş durumda. Trait’lerin en sık kullanılma nedenlerinden biri de bu. 🙂 Birden fazla türden türetirken önce extends kelimesini, sonraki her tür için with kelimesini kullandığımıza dikkat edin. Şimdi devam edelim. Mehmet bir gitarist ama aynı zamanda bir Virtuoz. Bu da Mehmet‘e önce Gitarist‘ten gelen tüm özelliklerin eklendiği anlamına geliyor. Sonra bunun üzerine Virtuoz ekleniyor ve Mehmet‘in tecrube özelliğine değer atanıyor. Bu değer önceden tanımlı olsaydı bile ezilecekti ve Virtuoz trait’inin belirlediği değer ile değişecekti.

Ali ise hem 5 yıldır piyano çalan bir müzisyen, hem de bilgisayar mühendisi. Muhendis trait’inde tanımlanmış olan sorunuCoz() metodunu implement ettiğini görüyorsunuz.

Trait’lerimizi ve sınıflarımızı tanıdığımıza göre Main.scala içine bunları kullandığımız örnekler yazalım.

Screenshot 2016-01-24 11.37.09

Gördüğünüz gibi karakterlerimiz kendilerine kazandırdığımız özellikleri sergiliyorlar. 🙂

Sonuç

Bu yazımda sizlere Scala’daki trait yapısından, string interpolation’dan ve multiple inheritance’tan bahsetmeye çalıştım. Asıl amacım bu yazıda case class’lardan da bahsetmekti ama yazı uzadıkça uzadı. Case class için de anlatılacak çok şey olduğundan onu bir sonraki yazımda anlatmaya karar verdim. Bu kararı sonradan aldığım için de yazı için oluşturduğum projenin adını değiştirmedim. Sonraki yazımda aynı proje üzerinden devam edeceğim. Anlayışınıza sığınıyorum. 🙂

Projeye Github’daki depomdan ulaşabilirsiniz. https://github.com/makiftutuncu/ScalaBlogOrnekleri/tree/master/TraitVeCaseClass

Bir sonraki yazıda görüşmek üzere.

Hoşça kalın!

Scala’da Class ve Object

Herkese merhabalar!

Bu yazımda sizlere Scala’daki class ve object yapılarını, aralarındaki farkları ve bunların Java’dan farklarını anlatmaya çalışacağım. Bir önceki yazımda IDEA ile geliştirme yapmaktan bahsettiğim için artık yazılarımı projeler halinde IDEA üzerinde geliştireceğim. Ayrıca bu projeleri Github’daki https://github.com/makiftutuncu/ScalaBlogOrnekleri depoma ekleyeceğim.

Başlamadan önce IDEA ile ClassVeObject isimli bir Scala projesi yarattım. com.mehmetakiftutuncu.classveobject paket yapısını oluşturdum. Son olarak içinde boş bir main metodu olan Main.scala dosyasını oluşturdum.

Haydi başlayalım!

Scala’da Class

Scala’da class tıpkı Java’daki gibi bir sınıfı ve o sınıfın türünü temsil eder. class özel kelimesi ile oluşturulur. Java’dan farklı olarak içine yazılacağı dosya ile aynı isme sahip olması şart değildir. Ayrıca bir Scala dosyası içine en dışarıda sadece tek bir class yazmak da şart değildir. Bir Scala dosyası içinde birden çok class (hatta object ve trait) yazılabilir.

Şimdi Java ile karşılaştırmalı bir örnek yapalım. Burada bahsettiğim dosyaları isimlerine tıklayarak Github deposunda görüntüleyebilirsiniz.

JavaSinifi1.java dosyasına bir Java sınıfı, ScalaSinifi1.scala dosyasına da onun Scala’daki birebir karşılığını yazdım. Bu örnek aynı zamanda projenizde Scala ile birlikte Java da kullanabileceğinizi gösteriyor. 🙂 Şimdi ScalaSinifi1 sınıfını inceleyelim.

Screenshot 2016-01-05 21.29.09

Buradaki ilk fark, public olan JavaSinifi1 sınıfının aynısı olan ScalaSinifi1 sınıfının tanımında public kelimesinin olmaması. Bunun basit bir nedeni var. Scala’da aksi söylenmedikçe her şey public olarak tanımlanır. Scala’nın gereksiz kelimeleri atmasına bir örnek daha. 🙂

Daha sonra iki class da birer private field (örnek değişkeni) tanımlıyor.  JavaSinifi1 bu field’ın ilk değerini varsayılan (boş) constructor (yapılandırıcı) metodunda atıyor. ScalaSinifi1 sınıfında ise durum biraz daha farklı. Scala’da bir class’ın gövdesinde metod haricindeki her şey aslında o class’ın varsayılan constructor’ını oluşturuyor. Yani ScalaSinifi1.scala dosyasındaki 7. satır aslında bu sınıfın varsayılan constructer’ında. Bu yüzden isim field’ının ilk değerini doğrudan atıyoruz. Ayrıca burada isim field’ının private olmasını özellikle belirttiğimizi görüyorsunuz.

Gelelim parametre alan ek constructor’a. Java’da sınıfın adına sahip bir metod gibi tanımladığımız ek constructor’ı, Scala’da sınıfın adı yerine this adıyla tanımlıyoruz. Scala’daki ek constructor’ların şöyle bir farkı var. Her ek constructor ilk önce this() diyerek varsayılan constructor’ı çağırmak zorunda. Yoksa hata alırsınız. Bu örnekte de isim field’ına önce boş bir String, sonra da constructor’a gönderilen değer atanıyor.

Buradaki ScalaSinifi1 sınıfını Java’daki örnekle birebir eşleşmesi için kasıtlı olarak böyle yazdım. Şimdi başka bir örnekle Scala’ya daha uygun ve daha iyi nasıl yazabileceğimizi görelim.

Screenshot 2016-01-05 22.18.01

Burada JavaSinifi2.java ile birebir aynı olan ScalaSinifi2.scala sınıfını görebilirsiniz. Gördüğünüz gibi Scala hali çok daha kısa ve basit.

Scala’da class’a ait public field’ları birer metod parametresi yazıyormuş gibi parantezler içine yazabiliyoruz. Bununla birlikte varsayılan değer atamalarını da kullanınca basit bir class tanımı tek satıra bile indirgenebiliyor. Aynı şeyi Java’da yapmak için fazladan 2 constructor yazmak zorunda kaldığımıza dikkat edin. 🙂

Son bir not olarak; bu sınıfların içinde birer de metod olsun istedim ve toString metodlarını tanımladım. Java’da @Override işaretlemesini kullanmamız gerekiyorken Scala’da override özel kelimesini kullanıyoruz.

Scala’da Object

Yazılım geliştirirken sık sık ihtiyaç duyduğumuz singleton pattern denilen bir kavram var. Bir nesneden bellekte sadece bir örnek olmasını sağlıyor. Java’da bunu sağlamak için singleton olmasını istediğimiz class’ın dışarıdan erişilebilen hiçbir constructor’ı olmaması, static şekilde tanımlanmış kendi türünden bir field’ı olması ve bu field’ı gerektiğinde geri döndüren dışarıya açık bir metodunun olması gerekiyor. Scala’da singleton yazmak istediğimizde tek bir kelime kullanmamız yeterli; object. 🙂 Scala’daki object kelimesi ile tıpkı bir class tanımlar gibi singleton’lar tanımlayabiliyoruz. Bir örnekle görelim.

Screenshot 2016-01-05 22.32.56

Burada JavaSingleton.java ile ScalaSingleton.scala arasındaki farkı görebiliyorsunuz. ScalaSingleton denilen tür, tanımı gereği bir singleton ve bellekte sadece bir örneği olacak. Object içinde tanımlanan metodlar, singleton class’a ait normal metodlar veya herhangi bir class’a ait static metodlar gibi çalışıyor. Yani, Main.scala içindeki örnekte de görebileceğiniz gibi, sınıfın bir örneği yerine doğrudan sınıfın adı üzerinden çağrılabiliyor.

Scala’da Companion (Yardımcı) Object

Object ile yapabileceğiniz tek şey kolayca singleton oluşturmak değil. Eğer class’ınızla aynı isimde bir de object’iniz varsa bu object o class’ın companion (yardımcı) object‘i oluyor. Eğer bir class’ın companion object’i varsa o tür için hem class’ın hem de object’in özelliklerini kullanabilmeye başlıyorsunuz. Bir örnek üzerinden görelim.

Screenshot 2016-01-05 23.07.39

JavaDunyasi.java dosyasında static tanımlanmış sabit dunyaAdi, static olmayan sabit surum, sürümü 1 olarak belirleyen varsayılan constructor, sürümü dışarıdan alan ek constructor, static tanımlanmış selam() metodu ve toString tanımlamasını görüyoruz. Şimdi Scala tarafına gelelim.

ScalaDunyasi.scala dosyasında bir önceki örnekteki gibi bir class tanımlaması görüyoruz. 6, 7 ve 8. satırlar bize surum sabitini, ek constructor’a olan ihtiyacı ve toString tanımlamasını sağlıyor. Şimdi sonrasında yazdığımız companion object’i inceleyelim.

11. satırda val olarak tanımladığımız için sabit olan, bir object içinde tanımladığımız için de static olan dunyaAdi değerini görüyoruz. Bu değere 7. satırda static olarak erişildiğine dikkat edin.

13 ve 17. satırlarda tanımlanan metodlar özel isimli metodlar. Scala’da bir tür adından sonra açılan ve kapanan parantezler () yazmak aslında o türün companion object’indeki apply metodunu çağırmak demek. Burada işte bu özellikten faydalanarak kendimize ek constructor’lar yazdık. Dikkat ederseniz tanımladığımız apply() metodları bizim tanımladığımız ScalaDunyasi türünden değerler döndürüyorlar. Ayrıca Main.scala içindeki 33 ve 34. satırlara dikkat ederseniz apply() metodları sayesinde yeni bir nesne oluştururken new kelimesini de kullanmak zorunda kalmadık. Aslında yaptığımız şey ScalaDunyasi object’indeki apply() metodunu çağırmak. Bu metodların gövdelerinde de yeni birer ScalaDunyasi nesnesi oluşturuyoruz. Böylece bu metodlar constructor gibi çalışıyorlar.

22. satırda da selam() metodunu görüyoruz. Bu metod bir önceki singleton örneğimizdekiyle aynı mantıkta çalışıyor.

Gördüğünüz gibi class’ı ve object’i bir arada kullanarak daha az kodla daha gelişmiş işler yapabiliyoruz. Unutmayın, bir object’in companion object olabilmesi için class’ı ile aynı isimde olması gerekiyor. Ayrıca ScalaDunyasi.scala dosyasında bir dosya içinde birden fazla class ve/veya object yazılabileceğinin de örneğini görmüş olduk. 🙂

Sonuç

Bu yazımda sizlere Scala’nın class ve object yapılarını, companion object’i ve bunların Java’daki hallerinden farklarını örneklerle anlatmaya çalıştım. Tüm terimleri Türkçeleştir(e)medim çünkü o zaman yazdıklarım çok daha anlaşılmaz bir hal alıyor. 🙂

Bu yazıdaki projeyi Github’daki https://github.com/makiftutuncu/ScalaBlogOrnekleri depomda bulabilirsiniz.

İnşallah anlaşılır olmuştur. Sonraki yazılarda görüşmek üzere.

Hoşça kalın!

Scala’da Koşul İfadeleri

Herkese merhaba!

Bu yazımda programlamanın en temel konularından olan koşul ifadelerinden bahsedeceğim. Anlatacaklarım daha çok bu ifadelerin Scala’daki işleyişi ve diğer dillerden, özellikle Java’dan farkları hakkında olacak.

Haydi başlayalım.

if Özel Kelimesi

if özel kelimesi neredeyse bütün programlama dillerinde var olan, İngilizcede eğer anlamına gelen bir kelime. Yaptığı şey de kodunuzun belli koşul(lar)a göre farklı davranmasını sağlamak. Scala’daki söz dizimi de Java’daki ile birebir aynı. Basitçe birkaç örnek ile nasıl göründüklerine bakalım.


if (1 == 1 && (true || false) {
// Bu her zaman çalışacak
val mesaj = "1 == 1"
println(mesaj)
} else if (2 != 2) {
// Bu hiç çalışmayacak
println("2 != 2")
} else {
// Bu hiç da çalışmayacak
println("1 != 1")
}
// Gövdede tek bir ifade olduğunda süslü parantez kullanmayabilirsiniz.
if (1 == 1 && true) println("1 == 1") else println("1 != 1")

Gördüğünüz gibi çok sıradışı bir şey yok. Süslü parantez kullanımı konusunda önerim; süslü parantezleri sadece eğer 14. satırdaki ifade kadar basit bir ifade yazacaksanız kullanmayın ve ifadenizi bu şekilde tek satırda yazın. Bunun dışındaki durumlarda if, else if ve else gövdelerinde süslü parantezler kullanın.

Şimdi Scala’daki if’in farklı bir yönüne bakalım. Scala’da koşul ifadeleri, döngü ifadeleri, süslü parantezler içine yazılmış birden fazla ifadeden oluşan bloklar gibi birçok yapı aslında yorumlanır ve bir değer üretir. Yani if için konuşacak olursak, bir if-else if-else topluluğunun bütünün ürettiği bir değer vardır. Bunun Java ile karşılaştırmalı bir örnek ile daha kolay anlaşılacağını düşünüyorum.


// Java
String metin = "merhaba";
int uzunlukSeviyesi; // 1, 2 veya 3
if (metin.length < 2) {
uzunlukSeviyesi = 1;
} else if (metin.length < 4) {
uzunlukSeviyesi = 2;
} else {
uzunlukSeviyesi = 3;
}
// Scala
val metin = "merhaba"
val uzunlukSeviyesi = if (metin.length < 2) {
1
} else if (metin.length < 4) {
2
} else {
3
}

Örnekte görebileceğiniz gibi, if ile başlayıp else bloğunun kapanan süslü parantezi ile biten kod parçasının bütünü aslında bir değer üretiyor. Bu durumda tür çıkarımı ile Int ürettiğini görüyoruz ve bunu uzunlukSeviyesi isimli val’a atıyoruz. Eğer if, else if ve else bloklarının gövdelerinin hepsi aynı türden bir değer üretmeseydi (örneğin else içinde 3 yerine “3” olsaydı), bu sefer tüm bloğun üreteceği değerin türü Int ve String’in ortak en yakın üst türü olan Any olacaktı. Kontrol ifadesi bu şekilde bir değer üretebildiği için, üretilen değer ile normalde herhangi bir değerle yapabildiğiniz tüm işlemleri yapabilirsiniz. Örnekte sonucu bir val’a atadık ama bunu bir metodun dönüş değeri olarak da belirleyebilir, başka bir metoda parametre olarak gönderebilir, kısacası bir değer ile yapabileceğiniz her şeyi yapabilirdik. 🙂

match … case İfadeleri

Scala’daki match … case ifadeleri, Java’daki ve başka bazı dillerdeki switch … case ifadelerine karşılık gelir ama onlardan çok daha fazla yeteneğe sahiptir. Hatta bir makalede “switch-case’in steroidli (daha güçlü) hali” şeklinde bir tanıma rastlamıştım. 🙂 match … case ifadeleri bir değeri farklı birçok durum ile karşılaştırır ve eşleşen case’in gövdesini çalıştırır. Tıpkı if gibi, match … case de değer üretir. Şimdi bir önceki örnekten devam ederek neler yapabildiğini görelim.
Java


String metin = "merhaba";
int uzunlukSeviyesi = metin.length < 2 ? 1 : (metin.length < 4 ? 2 : 3);
switch (uzunlukSeviyesi) {
case 1:
System.out.println("Çok kısa");
break;
case 2:
System.out.println("Kısa");
break;
default:
System.out.println("Çok kısa");
}

Scala


val metin = "merhaba"
val uzunlukSeviyesi = if (metin.length < 2) 1 else if (metin.length < 4) 2 else 3
uzunlukSeviyesi match {
case 1 => println("Çok kısa")
case 2 => println("Kısa")
// Yukarıdaki hiçbir case uygun değildi, varsayılan case
case _ => println("Uzun")
}
// Veya koşulları case ifadelerine yerleştirmek istersek if'i kullanabiliriz.
metin match {
// Sıra önemli, önce bu case çalışacak.
// Ayrıca if'ten sonra parantez kullanmak zorunda değilsiniz.
// Burada m için tür çıkarımı yapılacak, "metin" üzerinde match yapıldığı için m bir String olacak.
case m if m.length < 4 => {
println("Kısa")
}
case m: String if m.length < 2 =>
// match … case ifadelerinde süslü parantez kullanmak zorunda değilsiniz.
println("Çok kısa")
case _ => println("Uzun")
}
// Veya doğrudan metni karşılaştıralım ve cevap için değer üretelim.
val metin2 = "slm"
val cevap = metin2 match {
case "selam" => "Ve aleykümselam."
case "merhaba" => "Sana da merhaba."
// Bu bir MatchError
}
val cevap2 = metin2 match {
// Artık birden fazla durumu eşleştirebiliyoruz.
case "selam" | "slm" => "Ve aleykümselam."
case "merhaba" => "Sana da merhaba."
// Anlamadığımız için söyleneni aynen iade ediyoruz. 🙂
case _ => metin2
}

Java’daki switch … case kullanımını görüyorsunuz.  Şimdi Scala’daki haline bakalım.

2. satırdaki if ifadesinin tek satıra sadeleştirildiğini görüyorsunuz. Java’daki halini de tek satır haline getirdim ama aralarında ciddi bir anlaşılırlık farkı var. 🙂

4. satırda başlayan blok Java’daki switch … case ifadesinin birebir karşılığı ve uzunlukSeviyesi değeri üzerinde bir eşleştirme yapılıyor. Söz dizimindeki farklılıkları görüyorsunuz. match kelimesi eşleştirilecek değerden sonra geliyor, her case’ten sonra => (eşittir ve büyüktür) işareti geliyor, break kelimesi yok ve default kelimesi yerine değeri _ ile boş bırakılmış başka bir case var. Burada tıpkı Java’daki gibi case’lerin gövdelerini yazarken süslü paranteze ihtiyaç duymuyoruz. Okunabilirliği artırmak için kullanabilirsiniz de. Size kalmış. 🙂

12. satırda başlayan blokta eşleştirmeyi doğrudan metin değeri üzerinde yapıyoruz. Bu sefer yazdığımız case’lerimizde tanımlanan eşleştirmelere ek olarak koşullar da var. Eşleştirilen değeri farklı bir isimle kullanabiliyorsunuz. Bu vereceğiniz isim sadece o case’in gövdesinde tanımlı oluyor. Şu anda mantıklı gelmese de match … case ile ilgili daha gelişmiş konuları anlattığımda bunun aslında önemli bir özellik olduğunu göreceğiz. Diğer detayları koddaki yorum satırlarına yazdım o yüzden atlıyorum. Burada case’in gövdesini süslü parantezler içine yazabileceğimizi de görüyorsunuz.

28. satırda başlayan blokta türü String olan bir değeri doğrudan eşleştirmeye çalıştığımızı görüyorsunuz. Burada yazdığımız case’ler yetersiz kaldığı için eşleştirme yapılamayacak ve bir MatchError hatası alacağız.

35. satırda başlayan blokta ise yukarıdaki sorunu çözüyoruz ve match … case ifadesinden bir değer üretiyoruz. İlk case’te gördüğünüz gibi birden fazla değer ile eşleştirme yapılıyor. Bu değerler birbirinden | (dik çizgi) işareti ile ayrılıyor. Ayrıca olası bir MatchError’ı önlemek için varsayılan bir case de ekledik. Burada tüm case’lerden String türünde değerler üretildiği için match … case ifadesinin tamamının değeri, ve dolayısıyla cevap2, String türünde olacak.

Scala’daki match … case ifadeleri ile birçok şey yapılabildiğini ve Java’daki switch … case ifadelerinden daha güçlü olduğunu görüyorsunuz. Hatta yapabildikleri bunlarla da sınırlı değil. İleride ayrıca anlatacağım pattern matching denilen işlemi de match … case ifadeleri ile yazıyoruz ve bu oldukça güçlü bir araç.

Sonuç

Bu yazımda koşul ifadelerini ve bunların Scala’daki davranışlarını, diğer dillerdeki hallerine göre üstünlüklerini ve kullanışlılıklarını anlatmaya çalıştım. İnşallah faydalı olmuştur.

Görüşmek üzere. Hoşça kalın!

Scala’da Değişkenler (var), Değerler (val) ve Metodlar (def)

Merhabalar! Bundan sonraki yazılarımda daha çok ve somut örnekler ve karşılaştırmalar üzerinden giderek temel birçok kavrama değineceğim. Böylece Scala ile daha çok ve anlamlı uygulamalar geliştirmeye başlayabileceğiz. Bu yazımda en programlamadaki en temel kavramlardan biri olan değişkenlerden, Scala’da daha sık kullanılan değişmez değerlerden ve metodlardan bahsedeceğim.

Haydi başlayalım!

1. Değerler (val) ve Değişkenler (var)

Uygulama geliştirirken bir değeri veya nesneyi saklamak ve isimlendirmek için değişkenler kullanırız. Hatta bazen bu isimlendirdiğimiz şeyin bir daha değiştirilmemesini de isteyebiliriz. Scala’da bu işler için 2 özel kelime var.

1.1. Değişken (var)

Değerini sonradan değiştirebileceğiniz bir değer veya nesne oluşturmak için var özel kelimesini (İngilizce değişken anlamına gelen variable‘ın kısa hali) kullanabilirsiniz. Aşağıdaki birkaç değişik örnek yazdım.


var sayi: Int = 6
var metin = "Sayı çift mi?"
var ciftMi: Boolean = _
println("Sayı: " + sayi)
println(metin + " " + ciftMi)
ciftMi = sayi % 2 == 0
println("Tekrar bak.")
println(metin + " " + ciftMi)

İlk satırda sayi adında Int türünde, değeri 6 olan bir değişken oluşturuyoruz. Burası açık.

İkinci satırda adı metin olan, türünü belirtmediğimiz bir değişken yaratıyoruz. Böylece Scala String olduğunu çıkarım yaparak bulacak.

Üçüncü satırda ise yarattığımızda değerini belirtmediğimiz ciftMi adında Boolean türünde bir değişken oluşturuyoruz. Bir değişkeni değerini vermeden yaratmak için Java’da ve diğer dillerde sadece adını ve türünü yazmak yeterliyken, Scala’da değerinin sonradan verileceğini belirtmemiz gerekiyor. Bunu da _ (alt tire) yer tutucu (placeholder) karakteri ile sağlıyoruz. Değerini vermeden değişken yaratacağımız zaman – Scala değeri bilmeden tür çıkarımı yapamayacağı için – türü belirtmemiz gerekiyor. Değerini belirtmeden değişken yarattığımızda aslında o değişkene belirttiğimiz türe göre değişecek varsayılan bir değer atanıyor. Bu varsayılan değer sayı türleri için 0, Boolean için false, bunlar dışındaki referans türleri için de null. O yüzden 6. satırda ciftMi değişkeninin değeri olarak ekrana false yazılacak. ciftMi değişkeninin değerinin sonradan değiştirilebildiğini görüyorsunuz.

var ile ilgili bir not; eğer Scala’yı önerildiği gibi, fonksiyonel programlamaya uygun kullanmak istiyorsanız hiç var kullanmamaya çalışın. İleride değişmezlik (immutability) hakkında yazacağım yazı(lar)da bu konuya tekrar değineceğim. 🙂

1.2. Değer (val)

Değerini sonradan değiştiremeyeceğimiz bir değer veya nesne oluşturmak için val özel kelimesini (İngilizce değer anlamına gelen value‘nun kısa hali) kullanabiliriz. val ile değerler yaratmak Java’da ve başka bazı dillerde değişken tanımının önüne final özel kelimesini getirmeye eşdeğerdir. Bir kere oluşturulur, değeri sabittir, yeniden atama yapılamaz. var için olana benzer bir örnek yapalım.


val sayi: Int = 6
val metin = "Sayı çift mi?"
// Bu hata verecek.
val ciftMi: Boolean = _
// Bu da hata verecek.
sayi = 5

Burada da aynı şekilde bir Int ve String oluşturuyoruz yalnız bu sefer bu değerlere yeniden atama yaparak sakladıkları değerleri değiştiremeyeceğiz.

4. satırda gördüğünüz gibi val ile oluşturulmuş bir değerin saklayacağı değeri belirtmeden geçemiyorsunuz çünkü o val bir kere oluşturulduktan sonra değerinin değiştirilmesi engellenecek, sonradan değer ataması yapılamayacak.

7. satırdaki işlem de hata verecek çünkü daha önceden tanımlanmış bir val’ın sakladığı değerin yerine başka bir değer atamaya çalışıyoruz.

var ile ilgili yazdığım notu tamamlayayım; değişmezliği sağlayabilmek için var değil val kullanmaya çalışın. 🙂

2. Metodlar (def)

Aslında daha önceki yazılarımdaki örneklerde metodları kullanmıştık. Scala’da metodlar def özel kelimesi ile tanımlanırlar. Hemen birkaç örnek görelim.


def buBirMetod {}
def buDa = {}
def hattaBuDa: Unit = {}
def veHattaBuDa(): Unit = {}
buBirMetod
veHattaBuDa()
def sayi = 5
def sayi2() = 6
def topla(s1: Int, s2: Int) = s1 + s2
def carp(s1: Int)(s2: Int) = s1 * s2
def karistir() = {
val sonuc1 = topla(sayi, sayi2())
val sonuc2 = carp(sayi)(sayi2())
topla(sonuc1, sonuc2)
}
val toplam = topla(2, 3)
val carpim = carp(3)(5)
karistir()

Yukarıdaki örnekte birçok değişik metod tanımlaması görüyorsunuz. İlk 4 metod tanımlaması da aslında bir işlem yapmayan, Unit dönüş türüne sahip, gövdesi boş metodlar. Ama tanımlanışlarında farklılıklar var.

İlk metod hiç parametre tanımlamıyor. Şimdiye kadar gördüklerimiz aksine dönüş türü de belirtmiyor. Gövdesinin boş olduğunu görüyorsunuz, yani bir iş yapmıyor, yani aslında dönüş türünün Unit olması gerekirdi. Scala’da Unit dönüş türüne sahip metodları tanımlarken = (eşittir) işaretini ve dönüş türünü birlikte atlayabiliyoruz.

İkinci metod da aslında ilk metod ile birebir aynı. Parametre almıyor ve dönüş türü Unit. Yalnız bu sefer = kullandığımız için Scala tür çıkarımı yapıyor. Gövde boş olduğu için de (aslında Unit döndüren bir şeylerle dolu da olabilirdi) metodun dönüş türünün Unit olduğuna karar veriyor.

Üçüncü metod da ikinci metod ile aynı. Bu sefer dönüş türünü kendimiz belirttik.

Dördüncü metodda diğer üç metoddan farklı bir nokta var. Bu metod aslında diğerleri gibi parametresiz bir metod değil. Parametre alacak şekilde ama parametresi olmadan tanımlanmış. Scala’da parametresiz tanımlanmış bir metodu parantezlerle, parametre alacak bir metodu da parantezleri olmadan çağırmaya kalkarsak hata alıyoruz. 6. ve 7. satırlarda bu metodları çağırma sırasındaki farkı görüyorsunuz.

9. satırdan itibaren tanımladığımız metodlar (tür çıkarımı ile) geriye Int döndüren metodlar.

sayi ve sayi2() metodları arasında buBirMetod ve veHattaBuDa() metodlarının arasındaki ilişkinin aynısı var. Sonraki satırlarda farklı şekillerde çağırıldıklarını görüyorsunuz.

topla metodu 2 tane Int parametresi olan bir metod. Belki de buradaki en anlaşılır metod. 🙂

carp metodunda ilginç bir farklılık var. Bu metodun 2 tane parametre grubu var. Scala’da metodların birden fazla parametre grupları olabiliyor. Ayrı parantezler halinde yazılıyorlar ve metodun imzasına dahil ediliyorlar. Buradaki metod carp(Int)(Int): Int şeklinde bir imzaya sahip. Metodun nasıl çağrıldığını 21. satırda görüyorsunuz.

karistir() metodu şimdiye kadar tanımladıklarımızı kullanıp bazı hesaplamalar yapan bir metod. Bu metod da veHattaBuDa() metodu gibi parametre alacak şekilde ama parametresi olmadan tanımlanmış. Gövdesinde birden fazla ifade olacağı için süslü parantezler {} kullanılıyor. Son olarak, gövdesindeki son ifadenin değerini geri döndürüyor (tür çıkarımı da buna göre yapılıyor).

Scala’da metodlar, Java’daki gibi sadece bir class içinde değil, başka yerlerde de tanımlanabiliyor, örneğin başka bir metodun içinde. 🙂 Bunun bir örneğini görelim.


def beni {
def ben {println("Bir ben vardır bende benden içeri.")}
println("Beni bende deme bende değilim,")
ben
}
beni
// Bu hata verecek
ben

Burada ben metodunun beni metodunun içinde tanımlandığını görüyorsunuz. Scala’da bu mümkün. Bir metodun içinde tanımlanan başka bir metod, sadece tanımlandığı metodun gövdesi içinde ulaşılabilir durumdadır. Yukarıdaki örnekte 10. satırda ben metodu bulunamadığı için hata alınacak.

Metodlarla ilgili değinmek istediğim son bir konu var. Scala’da parametreleri olan metodları çağırırken, bu parametreleri isimlerini de kullanarak gönderebilirsiniz. Yapmanız gereken parametrenin değerinin önüne adını yazmak ve = (eşittir) atama işaretini koymak. Bu özelliği kullanarak parametre listesini birebir takip etmeden bile metodunuzu çağırabilirsiniz. Bunun da örneklerini görelim.


def tanit(adi: String, soyadi: String, yasi: Int, erkekMi: Boolean): Unit = {
def cinsiyet(e: Boolean): String = {
if (e) {
"Erkek"
} else {
"Kadın"
}
}
println("Adı : " + adi)
println("Soyadı : " + soyadi)
println("Yaşı : " + yasi)
println("Cinsiyeti: " + cinsiyet(erkekMi))
}
tanit("Mehmet Akif", "Tütüncü", 24, true)
tanit(adi = "Mehmet Akif", soyadi = "Tütüncü", yasi = 24, erkekMi = true)
tanit("Mehmet Akif", soyadi = "Tütüncü", 24, erkekMi = true)
tanit(soyadi = "Tütüncü", erkekMi = true, adi = "Mehmet Akif", yasi = 24)

Bu örnekte tanit adında, 4 tane parametresi olan, Unit döndüren bir metod tanımlıyoruz. Burada cinsiyet metoduyla, yukarıda bahsettiğim metod içinde başka bir metod tanımlamanın bir örneğini daha görüyorsunuz. Gönderilen erkekMi parametresinin değerine göre kişinin cinsiyetine karar verecek küçük yardımcı bir metod. Bu metodun gövdesinde karar vermeye yarayan if özel kelimesi var. Bundan sonraki yazılarımda daha detaylı bahsedeceğim. Şimdi 16. satırdan itibaren metodun farklı şekillerde çağrılışlarını inceleyelim.

16. satırda şimdiye kadar bildiğimiz yöntemle, sırayla parametreleri vererek çağırıyoruz.

17. satırda birer birer bütün parametrelerin isimlerini de vererek çağırıyoruz. Gördüğünüz gibi bu yöntem kodun okunduğunda daha kolay anlaşılmasına yardımcı oluyor. Tabi bunun için metodu tanımlarken parametrelere anlamlı isimler vermek gerekiyor.

18. satırda parametrelerin hepsine değil, sadece bazılarına isimlerini de vererek değer gönderiyoruz. Bu yine bazı durumlarda kodun anlaşılabilirliğini artırmak için güzel bir yöntem. Özellikle türü Boolean olan parametrelerin değerlerini verirken isimleriyle çağırmanızı tavsiye ederim. Burada hala parametreleri gerçek sıralarıyla çağırdığımıza dikkat edin.

19. satırda işler biraz daha değişiyor. Artık parametreleri kendi istediğimiz sırada gönderiyoruz. Tüm parametreleri tanımladığımız için ve hepsini ismiyle gönderdiğimiz için Scala metod çağrılarını bu şekilde yapmamıza bile izin veriyor.

Ekleme: Metod Parametrelerinde Varsayılan Değerler

Yazıyı yazıp yayımladıktan sonra atladığımı fark ettiğim için eklemek istediğim, metodlar yazarken sıkça kullanılan bir konu daha var. Scala’da metodların parametrelerine varsayılan değerler belirleyebilirsiniz. Böylece metodu çağırırken varsayılan değeri olan bir parametreyi yazmadan da geçebilirsiniz. Böyle durumlarda metod tanımlanırken belirtilen varsayılan değer kullanılır. Bir örnek ile görelim.


def tanit(adi: String, soyadi: String, yasi: Int, erkekMi: Boolean, sadeceAdiKullan: Boolean = false): Unit = {
def cinsiyet(e: Boolean): String = {
if (e) {
"Erkek"
} else {
"Kadın"
}
}
println("Adı : " + adi)
if (!sadeceAdiKullan) {
println("Soyadı : " + soyadi)
println("Yaşı : " + yasi)
println("Cinsiyeti: " + cinsiyet(erkekMi))
}
}
// Bunlar önceki örnekteki gibi çalışacak.
tanit("Mehmet Akif", "Tütüncü", 24, true)
tanit("Mehmet Akif", "Tütüncü", 24, erkekMi = true)
// Bunlar sadece adı yazdıracak.
tanit("Mehmet Akif", "Tütüncü", 24, true, true)
tanit("Mehmet Akif", "Tütüncü", 24, true, sadeceAdiKullan = true)
tanit("Mehmet Akif", "Tütüncü", 24, erkekMi = true, sadeceAdiKullan = true)

Bir önceki örnekteki metoda sadeceAdiKullan adında bir Boolean parametre daha ekledim. Bu parametrenin varsayılan bir değeri var, false. Ve metodun işleyişi bu parametrenin değerine göre değişiyor. 20 ve 21. satırlarda gördüğünüz gibi sadeceAdiKullan parametresini hiç dahil etmeden de metodu çağırabiliyoruz. Bu durumda bu parametrenin değeri varsayılan değer olan belirtilen değer oluyor. Ama 24, 25 ve 26. satırlardaki kullanımda sadeceAdiKullan parametresini de işin içine katıyoruz ve istediğimiz değeri belirtiyoruz.

Bu eklemeyle beraber metod parametrelerini isimleriyle kullanma konusunda bir tavsiyede bulunayım. Metodun parametrelerini gönderirken okunurluğu artırmak adına parametreleri, özellike de türü Boolean olan parametreleri, isimlerini kullanarak gönderin. Yukarıdaki örnekte 21. ve 26. satırdaki kullanımlar buna örnekler. 🙂

Sonuç

Bu yazımda programlamadaki temel kavramlardan değişken, sabit değer ve metodları ve bunların Scala’daki kullanılışlarını anlatmaya çalıştım. İnşallah anlaşılır olmuştur.

Kaynak

Yunus Emre’nin Severim Ben Seni Candan İçeri şiiri: http://www.yunusemre.gov.tr/index.php/eserleri/siirleri/siirlerin-devami/severim-ben-seni-candan-iceri

Görüşmek üzere. Hoşça kalın!

 

Scala’da Türler ve Tür Çıkarımı (Type Inference)

Herkese merhaba! Bu yazımda Scala’nın veri türlerinden ve türlerle ilgili güzel bir özelliğinden bahsedeceğim.

Haydi başlayalım.

Scala’daki Veri Türleri ve İlişkileri

Diğer birçok programlama dilini öğrenirken olduğu gibi Scala’yı öğrenirken de dilde bulunan temel veri türlerini, bunların birbirleriyle, daha karmaşık türlerle ve sizin sonradan tanımlayabileceğiniz türlerle ilişkilerini mümkün olduğunda erken öğrenmek gerekiyor. Aşağıda Scala’daki türlerle ilgili genel bir şema görüyorsunuz.

602885_762560083757382_1615763773_n

Şimdi detaylara inelim.

1. Temel Türler

Başka dillerde, özellikle Java’da, geliştirme yaptıysanız aşina olduğunuz basit veri türleridir.

Bellekte kapladığı alana göre küçükten büyüğe Byte, Short, Int, Long, Float, Double türleri sayısal değerler için; Char türü tek bir karakter saklamak için; Boolean değeri true veya false olabilen mantıksal değerler için; Unit de Java’daki void türüne karşılık gelen hiçlik için tanımlanmış türlerdir. Bu türlerin hepsi AnyVal türünden türer. Ayrıca bu türlerin hepsi derlenmiş bytecode seviyesinde doğrudan Java’daki karşılıkları olan türlerdir. Yani Scala bu türleri kullandığınızda arka tarafta doğrudan Java türlerini kullanır.

Scala bu temel türler arasında gizli dönüşümler yapabilir. Böylece dönüşüme uygun bir türü başka bir türü gerektiren yerde kullanabilirsiniz. Bu dönüşümler yukarıdaki şemada da görebileceğiniz gibi; sayı türleri için küçük olanından büyük olana dönüşümler ve Char türünden Int türüne dönüşümdür. Örneğin Double gerektiren bir yerde Float türünden bir değeri kullanabilirsiniz, dönüşüm kendiliğinden yapılacaktır.

2. Any ve Nothing

Any türü tüm türlerin üstündedir. Tüm Scala değerleri, nesneleri ve türleri Any’den türerler. (Java’daki Object’ten farklıdır, birazdan bahsedeceğim.)

Nothing türü de tüm türlerin altındadır. Nothing, tüm Scala değerlerinden, nesnelerinden ve türlerinden türer. Bu tür, özellikle birçok tür için genel (Array gibi herhangi bir türde olabilen, türü de parametre olarak alan yapılarda) kodlar yazarken, türler arasındaki ilişkileri tanımlamada işe yarayabiliyor.

3. String, Null ve ScalaObject

Scala’daki String türü özel bir tür değildir. Aynen temel türler gibi aslında doğrudan Java’daki String türünü kullanır.

Null türü Nothing türü gibidir ama her türün değil, temel türler hariç tüm türlerin altındadır. Bellekteki geçersiz bir referansı temsil eder. Null kavramı başta Java olmak üzere diğer birçok dilde sevilmeyen bir kavramdır ve Scala’da da uzak durulmasını tavsiye ederim. 🙂

ScalaObject türü Scala’da tanımlanmış, temel türler dışındaki tüm türlerin üstündedir. Bahsi geçen tüm türler ScalaObject’ten türerler. Java’daki Object’e benzer ama birebir karşılığı değildir.

4. AnyVal ve AnyRef

AnyVal türü Scala’daki temel türlerin üst türüdür. Bu tür sayesinde sadece temel türden olan değerleri karşılayacak kodlar yazmak daha kolay hale geliyor.

AnyRef türü de Java’da tüm türlerin üstünde olan Object‘e karşılık gelen türdür. ScalaObject olan her tür (temel türlerden olmayan, referans türü olan tüm Scala nesneleri) ve Scala kodu içinde erişilen Java nesneleri AnyRef’ten türerler.

Tür Çıkarımı/Tahmini (Type Inference)

Scala statik türlü bir dildir. Yani tanımladığınız her değerin daha derleme anında bir türü olması gerekir. Java ve onun gibi statik türlü başka birçok dilde bir değer, değişken, parametre vb. tanımlarken türünü de belirtmeniz gerekir. Bu durumlarda Scala’da da tür belirtebilirsiniz. Ama dilerseniz türü yazmayabilirsiniz ve Scala sizin yerinize tür hakkında çıkarım yapabilir. Türü belirtmediğinizde dinamik türlü gibi görünen, daha kısa ve öz bir koda sahip olursunuz ama hala yazdığınız kod statik türlüdür ve türle ilgili bir sorun varsa bunu derleme anında yakalarsınız. Daha önce Scala’da aslında kesinlikle gerekli olmayan şeylerin atılabildiğinden bahsetmiştim. Tür çıkarımı da buna güzel bir örnek. 🙂 Küçük bir kod parçasıyla tür çıkarımının nasıl göründüğüne bakalım.


val a: Char = 'a'
val b: Int = 3
val c: Long = 5
val d: Boolean = true
// Scala Double'ı Float'a tercih eder. Özellikle Float istediğimiz için tür dönüşümü yapıyoruz.
val e: Float = (3.14).asInstanceOf[Float]
val f: String = "e"
val any1: Array[Any] = Array(a, b, c, d, e, f)
val k = 'k' // k bir Char olacak.
val l = 5 // l bir Int olacak.
val m = 8L // m bir Long olacak.
val n = false // n bir Boolean olacak.
val o = 3.14 // o bir Double olacak. (e'deki not yüzünden)
val p = "o" // p bir String olacak.
// any2 bir Array[Any] olacak.
val any2 = Array(k, l, m, n, o, p)

Yukarıdaki örnekte tür çıkarımından yararlanarak tanımladığımız değerlerin türlerini belirtmeyebileceğimizi görüyorsunuz. Ayrıca tür dönüşümü yapmanın da bir örneği var. Hatta türler arasındaki ilişki yüzünden any1 ve any2 değerlerinin, verilen türlerin hepsinin üst türü olan Any türünden bir Array olduğunu da görüyorsunuz. Yukarıdaki şemada bulunan ama açıklamadığım List, Seq, Iterable gibi türler ve burada bahsettiğim Array türleri ile – hatta Scala’nın koleksiyon kütüphanesi ile – ilgili daha sonra detaylı bir yazı yazacağım. Scala’nın koleksiyon kütüphanesi öyle kısaca anlatılacak gibi değil çünkü. 🙂

Peki tür çıkarımı herhangi bir yerde türleri yazmaktan vazgeçebileceğiniz anlamına mı geliyor? Hayır. Aşağıdaki örneğe bakalım.


def dogru(a: String, b: Int, c: Boolean): Long = {
// Burada Long türünde bir değer olacak.
}
// Metodun dönüş değerinin türü de çıkarımla bulunabilir çünkü gövdedeki son ifadenin değeri geri döndürülecek.
// Haliyle metodun dönüş değerinin türü de bu son ifadenin değerinin türü olacak.
// Dolayısıyla metod imzasından dönüş türünü atabiliriz.
def buDaDogru(a: String, b: Int, c: Boolean) = dogru(a, b, c)
// Bu hata verecek çünkü Scala statik türlü.
// Metod a, b, ve c ile bir şeyler yapacak. Türlerinin bilinmesi gerek.
def buYanlis(a, b, c) = {
// Neredeyim ben? Neler oluyor?
}

Burada metodların dönüş türünü tür çıkarımı için yazmayabileceğinizi ve tür çıkarımını kullanmanın yaratabileceği bir sorunu görüyorsunuz. Henüz bahsetmedim ama, kendi class’larınızı ve türlerinizi tanımlarken ve buradaki gibi metod parametreleri tanımlarken – türün kesin olarak bilinmesi gereken yerlerde – tür çıkarımı kullanmaya çalışmak hata almanıza neden olacaktır.

Tür çıkarımı ilginç, güzel, kullanışlı ama aynı zamanda biraz da tehlikeli bir özellik. Muhtemelen ilk başlarda her yerde kullanmak isteyeceksiniz ama 2 yıldır Scala ile geliştirme yapan birisi olarak tür çıkarımını kullanmak konusunda şu tavsiyeleri verebilirim:

  1. Tür çıkarımı kullanacaksanız isimlendirmelerinizde mutlaka okuduğunuzda size tür hakkında da fikir verecek bir isim seçin. Sonra kodunuz büyüdükçe
    val temp = Foo.bar

    gibi bir koda bakıp

    Bu ‘temp’ değeri ne şimdi? Türü ne bunun?

    diye saç baş yolabiliyorsunuz çünkü. 🙂

  2. Metodlarınızın dönüş türlerini yazmayıp tür çıkarımı yapmak istiyorsanız bir kere daha düşünün. Yukarıda bahsettiğim gibi kodunuz büyüdükçe metodun ne döndürdüğünü bir bakışta anlayamamak sizin için sorun olmaya başlayacaktır. Eğer metod küçükse ve ne döndürdüğünü bir bakışta görebiliyorsanız tür çıkarımı kullanmakta bir sakınca yok.

Sonuç

Bu yazımda Scala’daki türlerden, birbirleriyle ve Java’daki türlerle olan ilişkilerinden ve tür çıkarımından bahsetmeye çalıştım. İnşallah anlaşılır olmuştur.

Bir sonraki yazıda görüşmek üzere. Hoşça kalın!

Kaynaklar

Türlerle ilgili şema için: http://docs.scala-lang.org/tutorials/tour/unified-types.html