Quantcast
Channel: Mert Kavi » Mert Kavi
Viewing all articles
Browse latest Browse all 23

Programming in Scala Kitabından Derlemeler

$
0
0

Yazıyı okurken çok fazla konu atlamaları, bir anda konunun değişmesi gibi edebi sorunlarla karşılaşabilirsiniz. Bu durumun sebeblerden biri, bir çok blog yazısından oluşması. Yani daha önce her biri farklı blog yazılarındaydı fakat şimdi bunlar birleşti. İkinci sebebi ise Programming in Scala kitabı ile birebir gitmeye çalışmam.

ScalaSelamlar, bundan önceki yazımda fonksiyonel programlama ile ilgili geniş bir yazı yazmıştım. Ama o yazı daha soyut bir yazıydı, sabit tek bir fonksiyonel dil içermiyordu. Scala’ya Başlangıç serisi ile fonksiyonel programlamayı Scala kullanarak anlatmaya çalışacağım. Ama bu yazılar sadece fonksiyonel programlama ile ilgili konular içermeyecek Scala hibrit bir dil olduğu için imperative programlama ile ilgili konulara da değineceğim.

Scala ismi scalable kelimesinden geliyor. Statically typed bir dil. Scala ile geliştirdiğimiz kod yapısı itibariyle daha özlü ve okunaklı.

Eğer hiç kurulum vesaire yapmadan direk Scala konu çalıştırmak istiyorsanız simplyscala.com işinizi görecektir.

Bir örnek göstereyim.

Bu örnekteki notasyona dikkat ettiyseniz noktalı virgül de içermez, değişken tipleri de. Merak etmeyin bunların hepsine daha sonra ayrıntılı değineceğiz. Scala yeni tipler eklemenize izin verir. Bu prensip kontrol yapılarında da geçerlidir. Kendi kontrol yapılarımızı da ekleyebiliriz. Java’nın threading (iş parçacığı) modeli hafıza paylaşımı ve hafıza kilitleme modeliyle çalışır. Bu model çoğu zaman sorunlar çıkarır. Özellikle sistem karmaşıklaştığında ve makine sayısı arttığında race condition ve deadlock sorunlarına yol açar. Scala Erlang’ın actor model implementasyonunu sunar. Actor’ler thread’lerin üstünde implemente edilen eşzamanlı soyutlamalardır. İki temel operasyon sunar; mesaj gönder ve mesaj al.

Bu actor sum adında local bir değişken tanımlar ve başlangıç değeri olarak sıfırı atar. Devamlı döngü içinde receive ifadesini kullanarak mesajlar için bekler. Data mesajı gelince ne yapacağı ortada zaten. GetSum mesaj gelince biraz farklılık var. request GetSum mesajına gömülü bir field. sum‘ın varolan değerini geri gönderiyor. ! gibi operatörler Scala’nın actor kütüphanesinde tanımlıdır. Actor kütüphanesinde ! için bir metod tanımlıdır. actor, loop, receive, ! yapıları Scala dilinden tamamen bağımsızdır.

Scala hibrit bir dil demiştim. Scala pür nesne yönelimli dillerle çok fazla benzerlikler barındırır.

Scala diğer dillerden daha gelişmiş nesne ilişki modeline sahiptir. Örneğin Scala’da ki trait‘ler. Java’daki interface’lerle benzerdir fakat metod implementasyonlarına ve field’lara izin verir. Bir trait superclass’ında olmayan bir fonksiyonu kendine ekleyebilir bu yönüyle sınıfardan ayrılır. Bu trait’leri sınıflardan daha pluggable yapar.

Dediğim gibi Scala aynı zamanda fonksiyonel bir dildir. Fonksiyonel programlama iki ana fikirden oluşur. Birincisi fonksiyonlar first-class değerlerdir. Fonksiyonlar integer, string gibi değer olabilir. Fonksiyonları diğer fonksiyonlara argüman aracılığı ile geçirebilirsiniz, onları fonksiyonlardan döndürebilirsiniz, değişkenlerde saklayabilirisiniz. Fonksiyon içinde fonksiyon tanımlayabilirsiniz. Her hangi bir isme sahip olmadan bir fonksiyon tanımlayabilirsiniz.

İkinci ana fikir verinin immutable olmasıdır. Java’nın String implementasyonu buna örnektir. Java’da string immutable bir veridir. Siz bir string’i değiştirmek isterseniz o varolan nesnede olmaz yeni bir nesne oluşturulur. Siz s.replace(‘;’,’.’) dediğinizde size s’ten farklı olarak yeni bir string nesnesi döner. Immutable veri yapıları fonksiyonel programlamanın en önemli özelliklerinden biridir. Scala’da immutable list, tuple, map ve set implementasyonları bulunur.

İkinci ana fikrin bize sağladığı olay metodların side effect barındırmamasıdır. Fonksiyonel programlama immutable veri yapıları ve transparan metodlar kullanmaya teşvik eder.

Kısaca neden Scala sorusuna bir kaç cevap:

  • Scala implicit conversion’lara izin verir
  • Type inference sistemi daha concise koda izin verir
  • High-level’dır
  • Statically typed’dır

High-level olmasına bir örnek vereyim.

Java’da bir string içinde büyük var mı yok mu aşağıdaki metodla öğrenebiliriz.

Bir de bunu Scala’da yapalım.

_.isUpper fonksiyonel literallerine örnektir. _ bu karakterle fonksiyon tanımlayacak ve bu fonksiyon bir karakter alacak ve büyük mü küçük mü bize söyleyecek.

Birinci kısmı bitirirken eklemek isterim ki Martin Odersky Scala bir “silver bullet” değil diyor bunu da unutmadan hayatımıza devam edelim :)

ScalaTekrar atanabilirlik üzerinde duralım. Örnek kod parçasından yola çıkalım.

Bu üç satır kod bize Scala’da ki val tip değişken konseptini anlamamıza yardımcı olur. Daha önceden de söylediğim gibi val ile tanımlanan bir değişken yalnızca bir kez atanabilir. Fakat nesne hala potansiyel bir değişime açıktır. greetString farklı bir diziye tekrar atanamaz fakat aynı dizi instance’ının içinde dizi elemanları değiştirilebilir. Dizinin kendisi mutable‘dır. for (i <- 0 to 2) Eğer Scala’da bir metod yalnızca bir parametre alıyorsa nokta veya parantez kullanmadan metod çağırılabilir. Yukarıdaki ifade aslında (1).to(2) dir. Dolayısıyla 1 + 2 işlemi dahi aslında bir metod çağırısıdır. 1 Int tipinde bir nesnedir, bu nesneden “+” metodu çağırılarak 2 metoda pass edilir. greetString(0) = “Mert” aslında, greetString.update(0, “Mert”) tir.

Array

Bir dizi val numNames = Array(“zero”,”one”,”two”) şeklinde initialize edilir. Array’ler companion object lerdir. Daha sonraki yazılarda bu konuya değineceğim.

List

Daha öncede söylediğim gibi fonksiyonel programlamada metodların yan etkileri yoktur. Metodların görevi hesap (computation) yapıp, değişken döndürmektir. Bu bize daha reliable ve reusable kod geliştirmemizi sağlar. Aynı tip nesneler barındıran, immutable nesne serileri için Scala’da List yapıları kullanılır. Java’da ki listelerden farkı her zaman immutable olmalarıdır.

Listelerde en çok kullanılan operatörler bu şekildedir.

Neden append (+) etmedikte :: operatörü kullandık ? Çünkü append işlemi listenin boyutuna göre lineer olarak artan bir zaman alır fakat :: operatörü sabit zamanda işlem yapar. Listlerin tail metodu da çok kullanılır, o başlığı şimdilik atlıyorum.

Tuple

Scala’da Tuple yapıları bu şekilde kullanılır. Tuple’nin en karakteristik özelliği birden fazla tipte nesne barındırabilmesidir. Şimdi düşünüyorsunuzdur neden bir elemente pair(0) diye erişmedik ? Bunun sebebi her (ek bir bilgi; pair(0) demek pair.apply(0) demektir) apply metodunun döndürdüğü tip aynıdır fakat _1 başka _2 dediğimizde başka tip nesneler döner.

Set ve Map

Scala’da diziler her zaman mutable, listeler ise her zaman immutable‘dır. Fakat Set ve Map yapılarında Scala her iki halini de bize implemente etmiştir.

Immutable Set’lerde de, mutable Set’lerde de + operatörü (metodu) bulunur fakat davranışları farklıdır. Birinde (mutable olanda) eleman direk kümeye eklenir diğerinde ise yeni bir set oluşturulur ve ona eklenir.

Map’lerde de durum benzer şekildedir ve kullanımı aşağıdaki gibidir.

Fonksiyonel stilde kod yazmak

Nesne yönelimli programlamaya alışkın olmak fonksiyonel programlamayı kavramayı ve etkili kod yazmayı zorlaştırıyor.

Yukarıda 2. metod 1. metoddan refactor edilmiştir. Fakat hala saf fonksiyonel bir ifade değildir. Çünkü yan etkilere sahiptir. println fonksiyonu yan etkiye sebep olur. Unit tipi zaten yan etkinin habercisidir. Çünkü bu metodun kodun diğer bölümlerinde değişiklik yapacağı aşikardır. Burada fonksiyonel yaklaşım ekrana basılacak argümanların formatlanması ve bir kerede ekrana basılmasıdır.

formatArgs metodu ile bu sağlanır.

Scala

Scala’nın sınıf ve nesne yapılarına biraz bakalım.

Scala’da default erişim seviyesi public‘tir. Önemli bir nokta, metod parametlerinin karakteristiği val tipindedir. Bu ne demek, bu metod içinde bir değişken tekrar atanamaz demek. Böyle atama yapmaya çalışırsak bu kod derlenemeyecektir.

Yukarıda bir sınıf tanımlaması görülüyor. Buradaki add metodu yan etkiye sahiptir. sum değişkeni yeniden atanmaktadır ve dış scope’ta bir değişikliğe sebep olmaktadır. Yine enteresan bir durum aşağıda görülmekte.

Scala static üyelere sahip olamaz. Bunun yerin singleton nesneler vardır. Aynen sınıf tanımlar gibi tanımları fakat class keyword’u yerine object keyword’u kullanılır. Bu örnekte singleton obje sınıf ile aynı ismi taşır. Sınıfın companion object’i olarak adlandırılır. Aynı dosyada tanımı olmak zorundadır. Bu sınıfta companion class olarak adlandırılır.

Olayı özetlersek, bir sınıf ve bu sınıfın companion nesnesi birbirlerinin private üyelerine erişebilirler. Singleton bir nesne tanımalamak bir tip tanımlamaz, o tipte bir değişken oluşturamazsınız. Ancak singleton nesneler bir superclass’tan türetilebilir ve trait’lerle soyutlanabilir. Sınıflar parametre alabilir fakat singleton nesneler alamaz. Çünkü (zaten) singleton nesneden bir instance oluşturamazsınız. Scala kolaylık sağlaması açısında Application isminde bir trait implement etmiştir. Bu trait’ten türeyen sınıflarda main metodu tanımlanmak zorunda değildir, süslü parantez içine yazılan kodlar main metodu içindeymiş gibi execute edilir. Fakat komut satırı argümanlarını kullanacaksanız Appication trait’i bunu sağlamaz. Ayrıca uygulama JVM’in kısıtlamalarından dolayı tek thread üzerinden çalışır, multi-thread çalışması için main metodu explicit olarak tanımlanmalıdır. Biraz daha komplike bir örnek verecek olursak;

Çoğu şeyi yorum satırları ile belirttim. Fakat burada güzel bir nokta var. Implicit keyword’u ile tanımlanan bir metod var. Bu ne işe yarıyor ?

Bu tanımlamanın yapılmadığını düşünelim. Scala interpreter’a

2 * r

Komutunu yazarsak (r: Rational), bu 2.*(r) ifadesidir. 2 integer bir değer fakat burada Rational tipinde nesnelerin çarpıldığı bir * operatörü yok. Burada bu işi çözmemiz gerekiyor. Bu çözüme implicit conversion deniyor. Integer otomatik olarak Rational bir tipe dönüştürülmüş oluyor ve hesaplama yapılıyor.


Viewing all articles
Browse latest Browse all 23

Latest Images