![Jenis Bahasa Pemrograman dalam Software Engineering](https://images.ctfassets.net/7wjlcr9d5ii8/7lWpnDtj5iGPDvYKnZGDuh/7ba177009fb5953cfdc99df2f914a647/_d7e40db1-f0ce-47ec-8f18-d4f81f087b0e.jpg?w=1024&h=1024&fl=progressive&q=100&fm=jpg&fit=thumb&f=faces)
“Bahasa” dalam software engineering adalah perantara antara manusia dengan mesin agar dapat berkomunikasi mengirimkan instruksi yang diinginkan terhadap program yang dibuat. Dalam software engineering, pemilihan penggunaan bahasa cukup penting saat memulai project. Pilihan bahasa yang ingin digunakan perlu disesuaikan dengan produk yang ingin dikembangkan. Right tools for the right jobs. Ibaratnya kalau dari Jakarta mau ke Bandung paling enak ya naik mobil. Kalau dari Jakarta mau ke Bali enaknya ya naik pesawat. Dari Jakarta ke Bandung naik pesawat juga bisa, tapi terlalu berlebihan. Dari Jakarta ke Bali naik mobil juga bisa, tapi ga worth it. Masing-masing bahasa punya keunikan dan keunggulan tersendiri. Bahasa tersebut dibagi ke dalam beberapa kategori seperti Interaction Level, Paradigm, Typing System, dan Execution System.
Kategori pertama adalah berdasarkan level interaksi pemrograman antara manusia dengan mesin. Setidaknya ada 3 level interaksi, yaitu Low-Level, High-Level, dan Intermediary.
Bahasa Low-Level adalah bahasa yang ditulis dalam bahasa mesin dan dieksekusi langsung oleh mesin. Ini adalah jenis bahasa paling cepat dibanding jenis lainnya karena tidak perlu translate, melainkan instruksinya langsung dimengerti oleh mesin. Tapi bahasa Low-Level adalah bahasa yang paling kompleks, sulit dibaca, dikendalikan serba manual, termasuk memory management yang juga manual, dan sulit untuk dikembangkan. Makanya bahasa Low-Level jarang kita temukan di industri komersil. Bahasa seperti ini biasanya digunakan untuk mengembangkan aplikasi yang super cepat di mana performa adalah hal yang sangat kritikal. Contohnya pada aplikasi driver yang mengkoneksikan antara hardware dengan software. Contoh bahasa Low-Level adalah Assembly dan Machine Code.
Bahasa High-Level adalah bahasa yang didesain agar mudah dimengerti manusia. Ini kebalikan dari bahasa Low-Level. Bahasanya lebih user-friendly, maintainable, dan gampang dipelajari. Memory management di-handle otomatis oleh fitur bahasa pemrogramannya, walau ada juga yang memiliki opsi handle memory secara manual. Ini adalah jenis bahasa yang paling sering kita jumpai di industri. Contoh bahasa High-Level adalah Java, Javascript, Go, PHP, C#, Python, dan hampir semua bahasa pemrograman yang kita kenal.
Contoh bahasa Intermediary adalah C dan C++. Menurut sebagian expert jaman dulu, bahasa C dan C++ itu termasuk High Level karena bukan bahasa mesin. Menurut sebagian lagi bahasa C dan C++ masuk Low Level karena memory managementnya harus di-handle manual. Perbedaan pendapat itu terjadi karena terdapat pergeseran makna High Level dan Low Level pada jaman dulu dengan sekarang. Jadinya ada sebagian lagi yang mengkategorikan C dan C++ sebagai Bahasa Intermediary karena bukan bahasa mesin tapi memory managementnya harus di-handle manual. Bahasa tersebut didesain mendekati bahasa mesin sehingga implementasinya cukup kompleks dibanding bahasa High-Level, namun instruksinya masih bisa dibaca manusia. Secara performa bahasa ini juga cepat, tapi ga secepat bahasa Low-Level. Bahasa seperti ini biasanya digunakan untuk mengembangkan aplikasi di mana performa sangat kritikal, tapi codenya juga harus dapat dibaca manusia. Aplikasi yang umumnya menggunakan bahasa ini adalah sistem database dan sistem operasi.
Paradigm adalah kategori berdasarkan pendekatan struktur dari sebuah code. Paradigma bahasa umumnya terdiri dari Procedural Programming, Object Oriented Programming, Functional Programming, dan Multi-Paradigm Programming.
Procedural Programming adalah bahasa yang dieksekusi secara step-by-step. Konsepnya seperti stack atau tumpukan instruksi. Isinya kumpulan instruksi yang dieksekusi secara berurutan. Contoh bahasa yang menggunakan paradigma ini adalah C, PHP versi 5 ke bawah, Pascal, Assembly, dan lainnya. Berikut contoh prosedur membuat hello world menggunakan Assembly yang gw copy-paste dari google🫣:
section .data
hello: db 'Hello, World!',10 ; 'Hello, World!' plus a linefeed character
helloLen: equ $-hello ; Length of the 'Hello world!' string
section .text
global _start
_start:
mov eax,4 ; The system call for write (sys_write)
mov ebx,1 ; File descriptor 1 - standard output
mov ecx,hello ; Put the offset of hello in ecx
mov edx,helloLen ; helloLen is a constant, so we don't need to say
; mov edx,[helloLen] to get it's actual value
int 80h ; Call the kernel
mov eax,1 ; The system call for exit (sys_exit)
mov ebx,0 ; Exit with return "code" of 0 (no error)
int 80h;
Instruksinya ditulis dari awal sampai akhir secara berurutan.
Object Oriented Programming (OOP) adalah bahasa pemrograman yang strukturnya menggunakan konsep object. Semua komponen dalam bahasa tersebut dibungkus dalam bentuk object yang nantinya bisa digunakan oleh komponen object lainnya. Contoh bahasa yang menggunakan paradigma OOP adalah C++, C#, Java, dan lain-lain. Contoh OOP di Java seperti ini:
public class Addition {
void sum(int x, int y){
Sytem.out.println(x + y);
}
public static void main(String[] args) {
Addition addition = new Addition();
addition.sum(1, 3);
}
}
Kita define dulu classnya, trus dibungkus logic penambahan di dalamnya. Lalu bikin objectnya dan execute sum()
.
Functional Programming (FP) adalah bahasa pemrograman yang strukturnya menggunakan konsep function. Berbeda dengan OOP, di sini semua komponen dalam bahasa tersebut dibungkus ke dalam bentuk function yang nantinya dapat digunakan oleh komponen function lainnya. Kita juga bisa menggunakan function sebagai parameter pada function lain atau sebagai return value sebuah function (Higher-Order Function), inilah yang paling membedakan antara function pada FP dengan function di paradigma lain. Contoh bahasa yang menggunakan paradigma FP yang terkenal adalah Clojure, Haskell dan Lisp. Contoh FP seperti ini di Clojure:
(defn sum [x y myfunction]
(myfunction (+ x y))
)
(sum 10 15 println)
Di sini kita define function logic penambahan dan membungkus function println sebagai parameter argument.
Ini adalah bahasa pemrograman yang memiliki paradigma lebih dari satu. Jadi kita bisa bebas menggunakan paradigma Procedural, OOP, maupun FP, atau bahkan gabungannya. Beberapa bahasa pemrograman modern jaman sekarang menggunakan Multi-Paradigm karena lebih variatif. Ada beberapa hal yang enak di-handle menggunakan OOP, dan ada sebagian lagi yang enaknya di-handle menggunakan FP. Contoh bahasa yang menggunakan Multi-Paradigm adalah Javascript, Go, Java sejak versi 8, dan beberapa bahasa modern lainnya. Pada Javascript contohnya seperti ini:
let sum = (x, y, myFunction) => myFunction(x + y);
class Multiplier{
multiply(x, y){
console.log(x * y);
}
}
let multiplier = new Multiplier();
multiplier.multiply(7, 8);
sum(3, 7, console.log);
Kita define logic penambahan dan membungkus function console.log()
sebagai parameter argument seperti FP dan logic perkalian didefine seperti OOP.
Kategori selanjutnya adalah berdasarkan cara code tersebut dieksekusi. Cara code dieksekusi dikategorikan menjadi Compiled, Interpreted, dan Just in Time Compiled.
Ahead of Time (AOT) Compiled Language atau yang sering disebut singkat sebagai Compiled Language adalah bahasa yang instruksinya di-translate ke dalam bahasa mesin secara keseluruhan. Jadi bahasa tersebut tidak langsung dimengerti mesin, melainkan ada proses compile terlebih dahulu. Setelah di-compile barulah hasil compile tersebut bisa dimengerti oleh mesin. Analoginya seperti saat kita menulis instruksi dalam Bahasa Indonesia yang dibaca oleh orang yang cuma ngerti Bahasa Arab. Jadinya kita tulis dulu seluruh instruksinya panjang lebar ke dalam Bahasa Indonesia, lalu dicek lagi apakah kalimat-kalimatnya udah benar, abis itu baru kita translate pakai Google Translate ke Bahasa Arab dan diberikan ke orang tersebut. Si penerima langsung paham apa aja instruksi yang kita tulis karena udah full Bahasa Arab. Walaupun hasil akhirnya adalah bahasa mesin sepenuhnya, tapi tetap saja bahasa tersebut tidak secepat bahasa Low-Level karena terdapat beberapa fitur dari bahasa tersebut yang bisa membatasi kinerja aplikasi seperti handle memori otomatis dan beberapa level abstraksi. Analoginya sama seperti hasil terjemahan Google Translate yang kadang terlalu kaku, jadi yang ngebaca perlu memproses makna instruksi tersebut. Tapi Compiled Language masih cenderung lebih cepat dibanding Interpreted Language secara performa karena hasil akhirnya dalam bentuk bahasa mesin. Sehingga banyak perusahaan komersil yang membutuhkan produk dengan performa tinggi menggunakan Compiled Language seperti e-commerce, aplikasi trading, dan sebagainya. Contoh Compiled Language adalah C, C++, C#, Java, Go, Erlang, Rust, dan masih banyak lagi.
Interpreted Language adalah bahasa yang instruksinya di-translate line-by-line. Interpreted Language itu seperti menerjemahkan kalimat satu-satu secara bertahap, bukan secara keseluruhan seperti Compiled Language. Analoginya seperti kita memberi instruksi dalam Bahasa Indonesia ke orang yang cuma ngerti Bahasa Arab. Tapi kita ngomong dulu satu kalimat untuk instruksi pertama, trus di-translate ke Bahasa Arab. Lalu setelah orang tersebut melakukan instruksi pertama, kita ngomong lagi untuk instruksi kedua dan di-translate lagi ke Bahasa Arab, begitu seterusnya. Jadi tiap kita ngomong di-translate dulu satu-satu. Makanya secara performa Interpreted Language itu tidak secepat Compiled Language. Beda dengan Compiled Language yang setelah compiled jadi aplikasi dengan bahasa mesin seutuhnya. Tapi bukan berarti Interpreted Language ini merupakan bahasa yang jelek. Kelebihan Interpreted Language adalah bisa dijalankan langsung tanpa compile terlebih dahulu. Ibaratnya kita ga perlu cek ulang instruksi tersebut sebelum disampaikan, tapi langsung tinggal ngomong -> translate -> eksekusi. Gitu aja terus. Sehingga proses develop aplikasinya lebih simple dibanding Compiled Language. Saat ada perubahan ketika develop aplikasi yang sedang dijalankan, perubahan tersebut biasanya bisa langsung dieksekusi. Berbeda dengan Compiled Language yang ketika terjadi perubahan code maka harus di-compile ulang. Interpreted Language biasanya digunakan oleh aplikasi yang kebutuhan performa eksekusinya tidak terlalu kritikal namun butuh proses develop yang cepat dan mudah dipelajari seperti Sistem Informasi Sekolah, Aplikasi Perkantoran, Blog, Data Modelling, dan sebagainya. Contoh Interpreted Language adalah PHP, Javascript, Python, Perl, Ruby, dan semacamnya.
Karena Interpreted Language itu cenderung lambat dan Compiled Language itu cederung ribet, maka muncul jalan tengah, yaitu Just In Time (JIT) Compiled Language. Ini sebenarnya engine khusus dari Interpreted Language atau Compiled Language sebagai penengah dari kedua masalah di atas. Di sini bahasa tersebut masih di-translate satu-satu seperti Interpreted Language, tapi saat eksekusi selanjutnya untuk hal yang sama tidak perlu translate lagi. Analoginya seperti kita ngasih instruksi pertama pakai Bahasa Indonesia ke orang yang cuma ngerti Bahasa Arab: “Cari orang dengan nama Obama!”, itu akan di-translate ke Bahasa Arab. Lalu setelah instruksi tersebut dilaksanakan, lanjut ke instruksi kedua: “Cari negara dengan nama Brunei!”, itu juga akan di-translate ke Bahasa Arab. Setelah instruksi kedua dilakukan, kita kembali menginstruksikan: “Cari orang dengan nama Obama!”. Nah, di sini tidak perlu di-tranlsate ulang, karena si orang Arab udah ngerti, bahwa itu artinya dia disuruh nyari orang bernama Obama sebab sebelumnya udah pernah dapat instruksi yang sama💡. Begitulah JIT Compiled, makanya secara performa lebih cepat dari Interpreted Language karena tidak perlu translate ulang untuk hal yang sama, tapi tetap saja tidak secepat Compiled Language karena masih di-translate satu-satu. Beberapa Interpreted Language yang lebih modern biasanya memiliki engine ini, seperti Javascript V8 engine yang didevelop oleh Chrome dan Python Cinder yang didevelop oleh Instagram. Compiled Language seperti Java juga ada engine JIT Compiler menggunakan GraalVM yang didevelop oleh Oracle untuk meningkatkan kemudahan development.
Kategori ini berdasarkan cara handle tipe data. Secara umum terbagi dari Static Type, Dynamic Type, Inferred Type, Duck Type, dan Structural Type.
Static Type adalah bahasa pemrograman yang tipe datanya harus ditulis dan tidak dapat diubah saat runtime. Keunggulannya adalah secara performa lebih baik dibanding Dynamic Type karena compiler memberitahu mesin tipe datanya sehingga dapat dioptimasi. Selain itu, Static Type dapat mempermudah development. Misalnya ada function dengan return value, kita bisa tahu tipe data value tersebut sehingga mengurangi human error saat development. Contoh bahasa yang menggunakan Static Type adalah Java, C, C++, C#, dan sebagian besar Compiled Language menggunakan Static Type. Contohnya pada C++ seperti berikut:
int myInteger = 5;
double myDouble = 9.98;
char myChar = 'D';
int sum(int x, int y){
return x + y;
}
Dynamic Type atau Loosely Type adalah bahasa pemrograman yang tipe datanya tidak ditulis dan bisa diubah saat runtime. Keunggulannya adalah codenya jadi lebih simple dibanding Static Type. Tapi di sisi lain juga bisa mempersulit development karena kita ga tau suatu function itu valuenya apa, sehingga bisa mengakibatkan human error. Mayoritas Interpreted Language menggunakan Dynamic Type. Bahasa yang menggunakan Dynamic Type yang paling simple adalah PHP. Contohnya seperti ini:
<?php
$word = "hello world";
$myNumber = 6;
function sum($x, $y){
return $x + $y;
}
?>
Untungnya sejak PHP 7 ada fitur deklarasi tipe data untuk mempermudah development biar ga nyari tau value secara manual lagi.
Inferred Type adalah Static Type yang mirip Dynamic Type. Ini sebenarnya bahasa dengan Static Type, tapi bisa ditulis tanpa menuliskan tipe data. Compiler akan menebak tipe datanya saat compile berdasarkan value yang ditulis. Jadi hasil akhirnya nanti sama seperti Static Type. Contoh bahasa yang menggunakan Inferred Type adalah Kotlin, Groovy, Java sejak versi 11, dan beberapa bahasa lain. Contohnya pada Java 11 ke atas kita bisa menulis seperti ini saat develop:
var contoh = "hello world!";
var angka = 2;
Nanti saat compile, compiler akan menerjemahkan itu masing-masing sebagai tipe data String dan int karena saat assign value menggunakan string literal dan integer number.
Duck Type didefinisikan sebagai: “Jika itu berjalan seperti bebek, bersuara seperti Bebek, maka itu adalah Bebek!”. Jadi tipe data yang punya behavior yang sama, walaupun secara struktur tidak semuanya sama, maka itu bisa compatible saat runtime. Contoh bahasa yang paling terkenal menggunakan Duck Type adalah Javascript dan Python. Contoh codenya seperti ini pada Python:
class Bird:
def fly(self):
print("I'm a Bird, because I can fly!")
def egg(self):
print("I have a eggs")
class Superman:
def fly(self):
print("I can fly too! Am I a Bird?")
def power(self):
print("I have superpower")
def execute(bird):
bird.fly()
execute(Bird())
execute(Superman())
Jadi meskipun secara tipe & struktur keduanya beda, Bird ada behavior egg()
dan Superman ada behavior power()
, tapi keduanya compatible pada execute(bird)
karena sama-sama memiliki behavior fly()
.
Structural Type merupakan Static Type yang mirip dengan Duck Type. Perbedaannya dengan Duck Type adalah Structural Type ini dicek saat compile apakah semua strukturnya sama persis atau tidak. Hanya tipe data yang memiliki struktur sama persis yang compatible. Contoh bahasa yang menggunakan Structural Type adalah Typescript dan Go. Contohnya pada Typescript seperti ini:
interface Bird {
fly(): void;
egg(): void;
}
interface Chicken {
fly(): void;
egg(): void;
}
interface Superman {
fly(): void;
}
let chicken: Chicken = {
fly(): void {
console.log("I can fly too!?");
},
egg(): void {
console.log("I also have eggs!");
},
};
let superman: Superman = {
fly(): void {
console.log("I can fly! Am I a Bird?");
},
};
function execute(bird: Bird){
bird.fly();
};
execute(chicken);
Pada code di atas, Chicken compatible dengan Bird pada function execute(bird)
. Meskipun beda tipe, tapi keduanya memiliki struktur yang sama persis. Sedangkan Superman tidak compatible karena hanya ada behavior fly()
tanpa behavior egg()
.
Itulah kumpulan jenis bahasa pemrograman berdasarkan Interaction Level, Paradigm, Typing System, dan Execution System. Secara level interaksi bahasa dibedakan jadi High-Level, Low-Level, dan Intermediary. Secara paradigma bahasa dibedakan menjadi Procedural, OOP, FP, dan Multi-Paradigm. Berdasarkan cara handle tipe data bahasa dibedakan lagi jadi Static Type, Dynamic Type, Inferred Type, Duck Type, dan Structural Type. Berdasarkan cara eksekusi bahasa dibedakan jadi Compiled, JIT Compiled, dan Interpreted. Secara umum Compiled Language & Static Type lebih cepat daripada Interpreted Language & Dynamic Type karena menjadi bahasa mesin seutuhnya dan dioptimasi oleh mesin sebab tipe datanya diketahui mesin. Tapi Interpreted Language & Dynamic Language menawarkan proses development yang simple dibanding Compiled Language & Static Type. Jadi sebelum memilih bahasa pemrograman perlu dipertimbangkan beberapa hal di atas. Ga jarang beberapa produk yang sebelumnya menggunakan Interpreted Language migrasi ke Compiled Language karena alasan performa eksekusi yang menjadi kritikal. Seperti Facebook yang dulunya pakai PHP migrasi dengan cara menciptakan bahasa pemrograman sendiri, yaitu Hack dengan syntax mirip PHP tapi dengan metode eksekusi compile. Twitter dulu menggunakan Ruby, lalu migrasi ke Scala. Spotify saat awal dibangun menggunakan Python, tapi sekarang hampir semua servicenya migrasi ke Java. Shopee untuk legacy codenya menggunakan Python, tapi untuk fitur barunya sebagian besar pakai Go. Shopify dari Ruby saat masih monolith, migrasi ke Rust saat jadi microservices. Di Indonesia juga ada, dulu ada e-commerce yang menggunakan Perl, sekarang migrasi ke Go. Ada juga unicorn yang dulu menggunakan Ruby, kemudian juga migrasi ke Go. Ada lagi aplikasi pemesanan tiket yang dulunya PHP lalu migrasi ke Java. Untuk instansi pemerintahan atau swasta non-IT mereka cenderung menggunakan Interpreted Language karena kebutuhan systemnya emang ga sekompleks perusahaan IT. Jadi ga ada yang salah antara Interpreted Language dan Compiled Language, balik lagi tergantung kebutuhan, bukan soal keren atau adu canggih. Beberapa produk yang menggunakan microservices kadang memiliki lebih dari satu bahasa yang digunakan tergantung spesifik kebutuhannya. Contohnya Instagram yang menggunakan Python sebagai backend karena fitur utamanya kurang lebih seperti blog yang isinya foto, video, dan komen, tapi untuk fitur yang lebih kritikal seperti DM Instagram yang didesain untuk mengirim pesan secara real time menggunakan Erlang. Youtube juga sama, untuk hal sederhana terkait data user backendnya menggunakan Python, tapi untuk hal yang kritikal seperti streaming menggunakan C++ dan untuk transcoding & encoding video menggunakan Go.