Ana içeriğe atla

Tip Sistemleri ve Programlama Dilleri

Tip Sistemi kavramı, günlük hayatlarının bir parçası olmasına rağmen bir çok yazılım profesyoneli için bile kulağa yabancı gelebilir. Tip Sistemleri; matematik ve mantık dallarındaki zengin bir konu olan "Tip Teorisinin" yazılım geliştirme ve programlama dillerine bir yansımasıdır. Tip sistemlerinin programlama dilleri için oldukça önemli ve belirleyici olduğunu söyleyebiliriz.  Bu yazıda herhangi bir programlama diline odaklanmadan teorik düzeyde tip sistemlerinden bahsetmekle yetineceğiz.


Tip sistemleri; bir programlama dilindeki değişken, fonksiyon vb yapı taşlarınının kullanım ve birbirleri ile etkileşimlerini, belli kurallara bağlayarak, oluşabilecek belirsizlikleri ve hataları engellemeye yönelik mantıksal sistemlerdir. Havalı cümlemizi kurduğumuza göre daha anlaşılır olarak açıklamak gerekirse örneğin

  • bir değişkenin hangi türde değerleri tutabileceği
  • bir fonksiyonun hangi türde parametrelerle çağrılması gerektiği
  • yine bir fonksiyonun bir değer üretip üretmediği ve üretiyorsa bunun hangi türde olacağı

tanımlı bir "tip" ile ilişkilendirilir ve ilgili değişkenin / fonksiyonun bu tipin izin verdiği şekilde kullanılıp kullanılmadığı kontrol edilir. 

Tip sistemleri (ve dolayısıyla programlama dilleri) bu tip ile ilişkilendirmenin hangi aşamada yapıldığı, atanan tipin sonradan değiştirilip değiştirilemeyeceği gibi kriterlere göre farklı kategorilere ayrılır. Tip ataması ve kontrolünün zamanı ve katılığına göre 2 temel ayrımdan bahsedebiliriz:

  • Statik ve Dinamik Tip Kontrollü
  • Güçlü Tipli ve Zayıf Tipli

Bu noktada şunu da belirtmek gerekir ki, yukarıda bahsettiğimiz ayrımların tanımları çok net olmamakla birlikte pratik programlama dillerinde kullanım açısından anlaşılır tanımlarını yapmak mümkündür.


Statik Tip Kontrolü

Statik tip kontrolü, tüm program yapıtaşlarının (değişken, fonksiyon vb) tiplerinin tanımlanma aşamasında belirtilmesi ve bu yapıtaşlarının tiplerine uygun kullanıldığının derleme sırasında kontrol edilmesidir.  Ayrıca, tip belirtildikten sonra değiştirelemez. Yazılım geliştirici tipleri belirtmekle ve derleyici de bu tipe uygun kullanımı kontrol etmekle görevlidir. Örneğin bir değişken; tanımlanırken tam sayı tipinde olduğu belirtilir ve derleyici program boyunca bu değişkene uygun değer atandığını kontrol eder. Tam sayı tipindeki değişkene örneğin bir karakter katarı (string) atanması durumunda derleme başarısız olur ve böylece programın çalışmasından önce tip hatalarının önüne geçilir. Örneğin; Java dilinde geçerli olabilecek bir kod örneği:

 
// var1 isimli değişkenin tam sayı (int = integer) tipinde olacağı
// değişkenin tanımlanması sırasında belirtiliyor ve değer ataması geçerli
int var1 = 5; //OK

// var2 isimli değişkenin tipi karakter katarı ve değer ataması geçerli
String var2 = "Test"; //OK

// bu satır derleyici tarafından hatalı olarak raporlanacak
// string bir değer tam sayı tipinde bir değişkene atanamaz
var1 = var2 // HATA!

// hesapla isimli fonksiyonun (Java'da metot daha yaygın kullanılan bir terimdir)
// tam sayı türünde a ve b isimli parametrelerle çağrılabileceğini ve yine
// sayı türünde bir sonuç üreteceği tanımlama sırasında geliştirici tarafından belirtilmiş
int hesapla(int a, int b) {    
    return a + b;
}

// hesapla fonksiyonu tam sayı türünde değer üretiyor ve var1 değişkeni de
// tam sayı türünde, fonksiyonun çağrılması ve sonucunun var1 değişkenine
// atanması geçerli
var1 = hesapla(1, 2); //OK

Statik tip sistemlerinin program çalıştırılmadan önce tip hatalarının yakalanması açısından önemli bir avantaj sunduğu açıktır. C benzeri dillerin (C, C++, C#, Java, Objective-C) statik tip sistemine sahip olmaları büyük (satır sayısı yüksek) projelerde tercih edilmelerinin en önemli sebeplerinden biridir diyebiliriz.

Birçok yazılım geliştirme aracı (IDE ve akıllı editörler) kodlama sırasında geliştiriciye yardımcı olacak öneriler sunmaktadır. Statik tipli diller için geliştirme araçlarının çok daha tutarlı öneriler sunması münkündür, bunu da bir kod örneği ile açıklayacak olursak:


// değer üretmeyen ve tam sayı tipinde 1 adet parametre ile çağrılması gereken bir
// fonksiyon tanımlanıyor
void hesapla2(int parametre1) {
  ...
}

// tam sayı tipinde var3 ve var4 değişkenleri tanımlanıyor
int var3 = 1;
int var4 = 2;

// string tipinde var5 ve var6 değişkenleri tanımlanıyor
String var5 = "1";
String var6 = "2";

Yukarıdaki satırların yer aldığı bir kod bloğu içinde geliştiricinin "hesapla2" isimli fonksiyonu çağırmak üzere yeni satırı yazmakta olduğunu düşünelim:

// hesapla2 fonksiyonu çağrılmak için tam bu satır geliştirici tarafından yazılırken

int sonuc = hesapla2(

// parantez açıldığı anda geliştirme aracı hesapla2 fonksiyonuna parametre olarak
// var5 ve var6 değişkenlerinin tipi uyuşmadığı için // var3 ve var4 değişkenlerinin verilmesini önerilebilir

Yazılım geliştirme aracına bu "akıllı" öneriyi sunma imkanı veren statik tip sistemidir. Günümüzde birçok yazılım geliştirme aracı bundan çok daha akıllı önerilerde bulunabilmektedir. Yine C benzeri dillerin güçlü ve büyük projelere elverişli geliştirme araçları ekosistemine sahip olmasında bunun büyük bir etkisi olduğu söylenebilir. Statik tip sistemlerinin, geliştirme araçlarına ek olarak, derleyicilere birçok optimizasyon yapma imkanı verdiğini de ekleyelim.


Dinamik Tip Kontrolü

Dinamik tip kontrollü sistemlerde programlama yapıtaşlarının tanımlanma aşamasında tipleri belirtilmez ve kullanıma göre dinamik olarak tip değişimine izin verilir. Yazılım geliştirici tipleri belirtmez ve derleme aşamasında (ki dinamik tipli dillerin bir kısmında derleme aşaması bulunmaz) tip kontrolleri yapıl(a)maz. Tip ile ilgili kontroller çalışma zamanında (runtime) yapılır. Bu tip dillerin önemli bir kısmında otomatik tip dönüşümleri de çalışma zamanı yorumlayıcısı tarafından sağlanır. Örneğin; JavaScript (Java ile JavaScript isimlerindeki benzerliğe rağmen birbirinden tamamen farklı 2 programlama dilidir) dilinde geçerli olabilecek bir kod kesiti:


// v1 isimli değişken tanımlanıyor, tip belirtimi yok
// tam sayı bir değişken atanıyor
var v1 = 5;

// v2 isimli değişken tanımlanıyor, tip belirtimi yok
// string bir değişken atanıyor
var v2 = "Test";

// v1 içinde daha önce tam sayı değer olmasına rağmen
// string tipinde değer ataması yapılıyor
// bir manada v1 değişkeni dinamik olarka tip değiştirmiş olarak düşünülebilir
v1 = v2;

Bu aşamda önemli bir noktayı vurgulamak gerekli: dinamik tip sistemlerinde tanımlamalar sırasında tip belirtilmemesi programın çalışması sırasında bir tip bilgisi olmadığı anlamına gelmemektedir. Program yapıtaşlarının (değişken, fonksiyon vb) değilse de değişkenlerin bir tipi olduğu gözden kaçırılmamalı ve yapı taşlarının kullanıma (veya atanan değere) göre tipinin dinamik olarak değişebildiği düşünülmelidir.

Dinamip tipli diller ve ilgili geliştirme araçlarında statik tipli dillere göre önemli bir fark tip bilgisinin kod seviyesinde belirtilmemesi sebebi ile bunun programcı tarafından sürekli akılda tutulması gerektiğidir. Bu durum en azından tip hataları bakımından bu sistemlerin daha hataya açık olması sonucunu doğurur. Bunun haricinde dinamik tip sistemi kullanan dillerin çoğunun çalışma zamanları (interpreter - yorumlayıcıları) otomatik tip dönüşümleri gerçekleştirebilmektedir. Örneğin;


// v3 isimli değişken tanımlanıyor, tam sayı bir değer atanıyor
var v3 = 5;

// v4 isimli değişken tanımlanıyor, string tipinde bir değişken atanıyor
var v4 = "Test";

// string ve tam sayı bir biri ile çarpılıyor ve sonuç v5 değişkenine atanıyor
var v5 = v3 * v4;

Programın bu noktasında v5 değişkenin değeri nedir ? Bir string ile bir tam sayı bir biri ile çarpılabilir mi ? Çarpılabilirse bu çarpma işlemi nasıl yapılmalıdır ? '5 çarpı "Test"' işleminin sonucu "Test" stringini 5 defa yan yana yazmak mıdır, yani "TestTestTestTestTest" midir ? JavaScript için bu sorunun cevabı:

// v5 değişkeninin içeriği ekrana bastırılıyor
console.log(v5);

> NaN

Yani NaN (Not A Number) şeklinde özel bir değer. Yani v5 değişkeninin tipi NaN isimli özel bir tipe otomatik olarak dönüştürülmü. Bir başka dinamik tipli programlama dili ise aynı işlem için çalışma zamanında bir hata üretmeyi veya yukarıda bahsettiğimiz gibi "TestTestTestTestTest" şeklinde bir değer üretmeyi seçebilirdi. Dinamik tipli sistemlerde program yapıtaşlarının atanan değere göre tip değiştirebilmesi ve otomatik tip dönüşümlerine izin verilmesi birçok bakımından belirsizliğe yol açıp hatalara sebep olabilir çıkarımını yapmamız hatalı olmayacaktır.

Önceki paragraftaki çıkarımımıza rağmen statik tipli sistemler dinamik tipli sistemlere göre daha iyidir demek mümkün değil. Dinamik tipli dillerin çoğunlukla derleme aşamasına ihtiyaç duymadan hızla çalıştırılabilmeleri, dinamik tip sisteminin izin verdiği esnekliğin bazı programlama görevlerini basitleştirmesi olumlu yönleri olarak görülebilir. Yine de bu tarz dillerin geliştirme araçlarının statik tipli dillere göre "akıllı" önerilerde bulunması ya mümkün değil yada çok daha zordur.

 

Özet

Tip sistemleri; programlama dillerini; söz dizimi (sentaks), performans, güvenlik ve geliştirme araçları bakımından derinden etkileyen ana tasarım unsurlarından biridir. Konu, matematiğin "Tip Teorisi" adlı çok daha geniş bir alanı içine girmekle birlikte pratik seviyede bahsettiğimiz kadarlık kısmı bile programlama dillerini daha iyi anlamak ve kullanmak adına faydalı olacaktır. Pratikte uygulanan hali ile:

  • Statik ve güçlü tip sistemine sahip dillerinin derleme aşamasında yapılan kontroller sayesinde belirsizlik ve hatalara daha kapalı programlar üretebildiği, geliştirme araçlarının yazılımcıya daha faydalı önerilerde bulunabileceği söylenebilir ancak derleme işleminin zaman alması ve statik tip sisteminin bazı programlama görevlerini daha zor hale getirmesi negatif yönler olarak sayılabilir
  • Öte yandan dinamik ve zayıf tip sistemine sahip dillerin çalışma zamanında belirsizlik ve hatalara daha açık programlar üretebilir ancak bu dillerin çoğunda derleme aşamasının olmaması dolayısıyla geliştirme ve deneyimleme hızının artması ve esnek tip sisteminin bazı programlama görevlerini kolaylaştırması da pozitif yönleridir 

 

Yazımızı, daha önce de belirttiğimiz gibi, bu tanımların net olmadığını ve bazı dillerin farklı seviyelerde bu kategorilerde bulunabildiğini gösteren bir görsel ile sonlandırılım.

 


Görüşmek üzere.

Yorumlar

Bu blogdaki popüler yayınlar

Uzaktan / Bulut İmza (Remote / Cloud Signature)

İmza, kişinin; o belgede yazanı onayladığını ve ona uyacağını gösteren işarettir. Bilgisayarlar hayatımıza girdiğinden beri belgeler bilgisayarlarda üretilir oldular ama sonuçta yazıcılardan çıkıp fiziksel olarak önümüze geldiler ve kalem mürekkep ile ıslak olarak imzalandılar. Günümüzde kurumlar ve devlet birimleri dijital dönüşüm ile gittikçe daha kağıtsız bir yapıya kavuşuyor. Belgeler bilgisayarlarda üretiliyor, hiç kağıda yazılmıyor ve tamamen elektronik ortamda kalıyorlar.  Bulut imza, Signature as a Service (Bulut Bilişim ile ilgili yazımıza buradan ulaşabilirsiniz) şeklinde kullanılmak üzere ve dijital dönüşüm yapbozundaki yasal/hukuki eksiği tamamlamak için geliyor. Bu büyük yeniliği anlamak için gelin şu anda elektronik ortamda imza nasıl atılıyor ve hangi zorlukları içeriyor bakalım. Sonra bulut imzayı tanıyalım ve ülkemizde teknik ve yasal mevzuat bakımından ne durumda olduğu ile yazımızı sonlandıralım. Elektronik ve Dijital İmza Elektronik imza; elektronik ortamda

V for Java - Project Valhalla - 1. Kısım

Project Valhalla OpenJDK tarafından yürütülmekte olan bir proje. Java'nın tip sistemi ile ilgili Java programlama dili ve JVM üzerinde yapılabilecek geliştirmelerin araştırıldığı ve adım adım sona yaklaşan bu proje bir takım ilginç yenilikler sunuyor. Bunları: Value objects / value classes (Değer nesneleri, değer sınıfları) Primitive objects / primitive classes (Primitif nesneler, primitif sınıflar) Primitif ve nesne tiplerinin birleştirilmesi olarak sıralayabiliriz. Tip sistemleri ile ilgili daha teorik bilgi için ilgili yazımıza göz atabilirsiniz. Güzel de, "value" nedir, "object" nedir biliyoruz ama "value object" neyin nesidir ? Gelin beraber bir göz atalım.     Primitifler, Sınıflar ve Kimlik (Identity) Java Tip Sistemi; 8 pritimif (byte, short, int, long, float, double, char ve boolean), nesne (object) ve dizi (array) olmak üzere 10 tanımlı tipten oluşur. Primitif tipler ile ifade edilemeyen daha karmaşık veri yapıları  (örneğin bir string yani