Memulai Pengembangan Haskell Modern dengan Stack

Bobby Priam
Paradigma Fungsional
12 min readAug 18, 2017
Siap meluncur dengan Haskell! [sumber]

Ketika kita bicara paradigma pemrograman fungsional, tentu kita akan mendengar nama Haskell sering disebut.

Menyadur dari materi kuliah CIS194, Haskell adalah bahasa pemrograman yang diciptakan pada akhir tahun 80-an dengan karakteristik fungsional, pure, lazy, dan statically-typed. Untuk lebih jauh mengenai kenapa Haskell, saya sangat menyarankan untuk membaca bagian awal materi kuliah CIS194 ini (dalam Bahasa Indonesia oleh Haskell-ID).

Jika Anda berasal dari bahasa pemrograman berorientasi objek seperti Java, C++, C#, Python, dan sebagainya, menulis program dalam bahasa Haskell dapat memberikan Anda insight yang belum pernah Anda miliki sebelumnya. Saya tidak mengklaim bahwa Haskell secara objektif lebih bagus dari bahasa-bahasa tersebut (walaupun saya sangat prefer pemrograman fungsional dibanding OOP :p). Haskell itu… berbeda.

Karena itu, saya selalu bilang bahwa sebagai developer, Anda sebaiknya mencoba untuk belajar Haskell setidaknya sekali saja selama karir Anda, dan saya yakin banyak hal dan konsep yang mampu Anda serap untuk menjadi developer yang lebih baik.

Cara belajar paling baik adalah dengan praktik langsung. Namun, saya belum melihat adanya tutorial instalasi Haskell berbahasa Indonesia yang cukup komprehensif.

Tutorial ini bertujuan untuk membawa Anda up and running dengan Haskell di komputer Anda untuk belajar lebih jauh. Ini artinya, sintaks dan konsep-konsep di bahasa Haskell sendiri berada di luar scope tutorial ini.

Dalam tutorial ini, kita akan berkenalan dengan Stack, yaitu tool manajemen project Haskell dan merupakan cara termudah dan ramah-pemula untuk menginstall dan mengembangkan program di Haskell secara modern.

Prasyarat

Tidak ada prasyarat khusus untuk mengikuti tutorial ini selain niat Anda :)

Meskipun demikian, saya menyarankan untuk memastikan Anda memiliki koneksi internet yang cukup stabil. Hal ini dikarenakan kita akan mengunduh beberapa file yang berukuran cukup besar selama tutorial.

Sudah siap? Ayo mulai!

Eksplorasi Stack

Stack memiliki situs dokumentasi yang cukup baik untuk instalasinya dalam Bahasa Inggris. Saya akan membantu Anda untuk meng-install-nya dan mengeksplor beberapa fiturnya. Pada tutorial ini, saya menggunakan sistem operasi OSX, namun untuk pengguna Linux dan Windows harusnya tidak jauh berbeda.

Instalasi

Cara termudah meng-install Stack adalah melalui script yang disediakan.

Jika Anda menggunakan Windows, Stack menyediakan installer untuk Windows 64-bit yang dapat diunduh pada tautan berikut: https://www.stackage.org/stack/windows-x86_64-installer (catatan: Anda akan otomatis mulai mengunduh filenya setelah meng-klik tautan tersebut!)

Untuk pengguna OSX dan Linux, Anda dapat menjalankan perintah berikut pada terminal:

curl -sSL https://get.haskellstack.org/ | sh

Script tersebut akan mengunduh dan memasang Stack di komputer Anda. Ukuran file yang diunduh adalah 12.8 MB. Berikut contoh keluaran ketika dijalankan di komputer saya (tanda $ adalah prompt terminal):

$ curl -sSL https://get.haskellstack.org/ | sh
Using generic bindist...
...Installing Stack to: /usr/local/bin/stack...-------------------------------------------------------------------Stack has been installed to: /usr/local/bin/stackNOTE: You may need to run 'xcode-select --install' to set
up the Xcode command-line tools, which Stack uses.

Dapat dilihat bahwa Stack telah berhasil di-install pada lokasi /usr/local/bin/stack. Di akhir juga terdapat beberapa catatan khusus yang harus dilakukan bagi pengguna OSX, yaitu menjalankan xcode-select --install.

Untuk memastikan instalasi telah berhasil, kita dapat menggunakan perintah stack --version:

$ stack --version
Version 1.5.1, Git revision 600c1f01435a10d127938709556c1682ecfd694e (4861 commits) x86_64 hpack-0.17.1

Versi yang muncul di komputer Anda mungkin akan berbeda. Pada saat menulis artikel ini, versi 1.5.1 adalah yang paling baru.

Membuat project baru

Seperti yang telah saya bilang tadi, Stack merupakan sebuah tool untuk manajemen project Haskell. Fitur pertama yang akan kita eksplor adalah membuat project baru dengan perintah stack new. Seperti pemrograman pada umumnya, mari kita buat project dengan nama helloworld:

stack new helloworld simple

Berikut contoh output-nya:

$ stack new helloworld simple
Downloading template "simple" to create project "helloworld" in helloworld/ ...
...Looking for .cabal or package.yaml files to use to init the project.
Using cabal packages:
- helloworld/helloworld.cabal
Selecting the best among 11 snapshots...Downloaded lts-9.1 build plan.
Selected mirror https://s3.amazonaws.com/hackage.fpcomplete.com/
Downloading root
Selected mirror https://s3.amazonaws.com/hackage.fpcomplete.com/
Downloading timestamp
Downloading snapshot
Downloading mirrors
Cannot update index (no local copy)
Downloading index
Updated package list downloaded
Populated index cache.
* Matches lts-9.1
Selected resolver: lts-9.1
Initialising configuration using resolver: lts-9.1
Total number of user packages considered: 1
Writing configuration to file: helloworld/stack.yaml
All done.

Terdapat beberapa hal yang dilakukan Stack di sini, yaitu:

  1. Mengunduh template bernama simple untuk menginisialisasi project kita,
  2. Mengunduh dan mem-populate indeks package kita di lokal (mungkin memakan waktu agak lama untuk pertama kali), dan
  3. Memilih build plan yang cocok untuk project kita (dalam hal ini yaitu lts-9.1).

Untuk menjelaskan langkah 2 dan 3, kita harus mengetahui sedikit sejarah package management Haskell. Sebelum adanya Stack, developer Haskell menggunakan sebuah tool yang bernama cabal. Namun, cabal agak sulit digunakan, dan seringkali menyebabkan sesuatu yang disebut cabal hell, yaitu situasi di mana package-package yang ter-install di komputer kita berantakan dan sangat sulit untuk dirapikan kembali.

Stack “membungkus” cabal dan menyediakan yang disebut dengan build-plan. Suatu build-plan mencakup versi compiler Haskell tertentu dan daftar package-package yang dapat dipergunakan dengan compiler tersebut. Dengan kata lain, tim di belakang Stack menjamin bahwa package-package yang terdaftar pada suatu build-plan telah terkurasi dan akan bekerja sebagaimana mestinya dengan kompatibilitas tinggi satu sama lain. Tidak ada lagi cabal hell!

Mengintip isi project baru kita

Project kita telah dibuat! Mari kita intip isinya dengan menggunakan perintah tree (atau melalui Explorer seperti biasa):

$ cd helloworld
$ tree
.
├── LICENSE
├── README.md
├── Setup.hs
├── helloworld.cabal
├── src
│ └── Main.hs
└── stack.yaml

(Seharusnya) cukup simpel! :) Kalau Anda sadari, saya mendefinisikan sebuah template bernama “simple” pada saat pemanggilan stack new di atas tadi. Saya menganggap template ini memiliki struktur paling sederhana sesuai namanya agar kita dapat memulai dengan cepat. Lebih jauh tentang template seperti template apa saja yang tersedia akan dibahas nanti di artikel ini.

File-file dengan ekstensi .hs adalah kode sumber Haskell. Jika Anda adalah pemula, untuk sementara Anda dapat mengabaikan semua file kecuali Main.hs. Ayo kita lihat isinya! Gunakan text editor kesayangan Anda untuk membuka Main.hs.

Catatan: ketika Anda membuka file ini, besar kemungkinan text editor Anda akan menampilkannya tanpa syntax highlighting. Saya sedang menyiapkan sebuah artikel untuk melakukan setup text editor untuk pengembangan Haskell. Tunggu, ya!

File yang singkat ini berisi sebuah program Haskell yang sederhana, yang akan mencetak teks “hello world” ke layar (dengan fungsi putStrLn). Saya tahu Anda sudah tidak sabar untuk menulis kode, tapi tunggu dulu! Masih ada yang perlu kita lakukan :) Kita akan mencoba menjalankan program ini terlebih dahulu.

Menyiapkan compiler Haskell

Sejauh ini, kita baru memiliki Stack. Sayangnya Stack bukan compiler Haskell, dan untuk menjalankan program Haskell kita butuh compiler-nya. Untungnya, Stack juga menyediakan fitur untuk meng-install compiler melalui perintah stack setup.

Saya butuh mengingatkan Anda untuk memastikan kembali koneksi internet Anda stabil, karena ukuran compiler ini cukup besar. Pada penulisan artikel ini, compiler yang digunakan adalah Glasgow Haskell Compiler (GHC) versi 8.0.2 yang berukuran 178.17 MiB. Namun Anda seharusnya akan cukup melakukannya sekali saja, dan project-project selanjutnya akan menggunakan compiler yang sama (selama build-plan-nya masih sama).

Berikut adalah contoh penjalanan perintah stack setup pada komputer saya:

$ stack setup
Preparing to install GHC to an isolated location.
This will not interfere with any system-level installation.
ghc-8.0.2: 5.34 MiB / 178.17 MiB ( 3.00%) downloaded...
Downloaded ghc-8.0.2.
Unpacking GHC into /Users/bobbypriambodo/.stack/programs/x86_64-osx/ghc-8.0.2.temp/ ...
Configuring GHC ...
Installing GHC ...
Installed GHC.
stack will use a sandboxed GHC it installed
For more information on paths, see 'stack path' and 'stack exec env'
To use this GHC and packages outside of a project, consider using:
stack ghc, stack ghci, stack runghc, or stack exec

Catatan: pada penjalanan aslinya, beberapa baris teks di atas ditimpa dengan teks di bawahnya, namun saya memasukkan semuanya agar terlihat progress-nya.

Kita telah berhasil meng-install GHC! Selanjutnya: meng-compile dan menjalankan program kita.

Compile dan jalankan!

Stack menyediakan sebuah perintah untuk meng-compile project kita, yaitu stack build. Ayo kita coba.

$ stack build
helloworld-0.1.0.0: configure (exe)
Configuring helloworld-0.1.0.0...
helloworld-0.1.0.0: build (exe)
Preprocessing executable 'helloworld' for helloworld-0.1.0.0...
[1 of 1] Compiling Main ( src/Main.hs, .stack-work/dist/x86_64-osx/Cabal-1.24.2.0/build/helloworld/helloworld-tmp/Main.o )
Linking .stack-work/dist/x86_64-osx/Cabal-1.24.2.0/build/helloworld/helloworld ...
helloworld-0.1.0.0: copy/register
Installing executable(s) in
/Users/bobbypriambodo/Projects/helloworld/.stack-work/install/x86_64-osx/lts-9.1/8.0.2/bin

Output-nya agak “berisik”, namun Anda dapat lihat bahwa file Main.hs kita berhasil di-compile dan executable hasilnya telah berada pada direktori yang tertulis di paling akhir. Untuk menjalankan programnya, kita dapat menggunakan stack exec:

$ stack exec helloworld
hello world

Wow! Kita telah berhasil meng-compile dan menjalankan program Haskell pertama kita dengan mudah.

Mengubah program dan menjalankan ulang

Mari kita coba ubah program kita! Pada bagian ini kita akan mulai menulis kode Haskell. Buka Main.hs pada text editor Anda dan ubah menjadi sebagai berikut:

Saya tidak akan membahas syntax Haskell terlalu jauh pada saat ini, namun secara singkat yang barusan kita lakukan adalah mendefinisikan sebuah fungsi baru bernama add yang menerima dua parameter x dan y lalu mengembalikan hasil penjumlahan kedua parameter tersebut.

Pada fungsi main, kita menghapus "hello world" dan menggantinya dengan (show (add 3 5)). Untuk saat ini, anggaplah show sebagai fungsi yang mengubah integer menjadi string. Ini diperlukan karena fungsi putStrLn hanya menerima tipe data string sebagai parameternya. Jadi, fungsi main akan menjumlahkan 3 dan 5, mengubahnya menjadi string, dan mencetaknya ke layar.

Mari kita compile dan jalankan lagi:

$ stack build
...output dari build...
$ stack exec helloworld
8

Yes! Kita mendapatkan hasil dari 3 + 5, yaitu 8.

Meng-install program secara global

Bagaimana kalau kita ingin meng-install program kita secara global? Artinya, kita ingin bisa menjalankan program dari mana saja kita berada di terminal. Stack kembali menyelamatkan kita! Stack menyediakan perintah stack install untuk melakukan persis hal tersebut. Mari kita coba:

$ stack install helloworld
Copying from /Users/bobbypriambodo/Projects/helloworld/.stack-work/install/x86_64-osx/lts-9.1/8.0.2/bin/helloworld to /Users/bobbypriambodo/.local/bin/helloworld
Copied executables to /Users/bobbypriambodo/.local/bin:
- helloworld

Secara default, Stack akan meng-install program dari perintah stack install ke direktori ~/.local/bin pada OSX, seperti yang ditunjukkan di atas. Anda perlu memastikan direktori tujuan ini ada pada variabel PATH di komputer Anda. Jika Anda menggunakan bash, cukup tambahkan potongan kode berikut ke .bashrc:

export PATH=$PATH:~/.local/bin

Ingat, tujuan instalasi mungkin berbeda pada komputer Anda! Silakan sesuaikan lokasinya.

Setelah melakukan hal tersebut, kita akan dapat menjalankan program kita sebagai helloworld saja. Ayo kita coba:

$ helloworld
8

Nice! Dengan metode ini, kita dapat membuat command-line programs atau utilities yang mampu dijalankan dari mana saja.

Read-eval-print-loop (REPL) Haskell: GHCI

Bahasa pemrograman fungsional (dan beberapa bahasa scripting) pada umumnya menyediakan konsol interaktif untuk bermain-main dengannya. Pada Haskell, konsol tersebut memiliki nama GHCI, dengan I berasal dari Interactive.

Stack menyediakan perintah bernama stack ghci untuk memunculkan GHCI. Masuk kembali ke direktori helloworld kita, lalu masukkan perintah tersebut:

$ stack ghci
...output kompilasi...
*Main>

Sukses, sekarang kita telah berada di prompt dari konsolnya (pada *Main>). Output-nya cukup banyak, karena secara default stack ghci akan otomatis melakukan build project ketika kita memanggilnya. Mari kita bermain-main dengan GHCI:

*Main> main
8

Di sini kita memanggil fungsi main dari module Main yang kita miliki. Kita juga dapat memanggil fungsi lainnya yang didefinisikan di module tersebut, yaitu add:

*Main> add 1 2
3

Keren! Kita bahkan bisa mengunakan GHCI sebagai kalkulator sederhana:

*Main> 2 * 3 + 10 / 4
8.5

Kemudian, di GHCI juga kita juga bisa melihat tipe data dari sebuah ekspresi, dengan perintah :t:

*Main> :t add
add :: Num a => a -> a -> a

Hmm, apa artinya itu? Secara singkat, notasi tersebut menjelaskan bahwa add adalah fungsi yang menerima dua buah parameter dan menghasilkan parameter dengan tipe yang sama (dari a -> a -> a di mana a adalah type variable yang bisa disubtitusikan dengan tipe lain). Namun, syaratnya adalah a harus merupakan instance dari typeclass Num. Jika penjelasan ini membingungkan Anda, tidak apa-apa. Fokus kita di sini belum soal belajar Haskell :)

Meskipun demikian, apakah kita bisa coba menyederhanakannya? Ya! Sekarang kita coba ubah add sebagai fungsi yang hanya bekerja pada Int — bilangan bulat. Buka kembali file Main.hs kita dan ubah menjadi seperti ini:

Yang baru saja kita lakukan adalah menambahkan anotasi type. Anotasi seperti ini membantu menjadi hint bagi compiler untuk menentukan tipe dari berbagai hal di program kita. Tanpa anotasi, compiler melakukan inferensi secara otomatis mengenai tipenya, namun dengan anotasi kita bisa secara eksplisit membatasi domain (masukan) dan range (keluaran) dari fungsi tersebut. Anotasi type juga berperan sebagai dokumentasi.

Sekarang, mari kita tes kembali di GHCI! Kalau Anda masih membuka GHCI dari sesi sebelumnya, Anda cukup mengetikkan :l Main yang artinya “load kembali module Main”.

*Main> :l Main
[1 of 1] Compiling Main ( /Users/bobbypriambodo/Projects/helloworld/src/Main.hs, interpreted )
Ok, modules loaded: Main.
*Main> :t add
add :: Int -> Int -> Int

Sukses lagi! Sekarang add telah berubah menjadi fungsi yang hanya bekerja pada tipe data Int. Saya tidak akan membahas lebih jauh mengenai tipe data dan hal-hal lain pada Haskell, karena ada lebih banyak resource yang lebih baik dan akan saya berikan pada akhir artikel ini. Yang perlu Anda ketahui, perintah :l dan :t pada GHCI dapat membantu Anda secara iteratif dan interaktif mencoba berbagai bagian dari program Anda.

Anda dapat menggunakan perintah :? untuk mengetahui kapabilitas lain yang dimiliki GHCI. Untuk keluar dari GHCI, gunakan perintah :quit (atau cukup dengan Ctrl+D):

*Main> :quit
Leaving GHCI.
$

Menambahkan dependensi ke project kita

Dalam membuat program, tentu kita suatu saat akan membutuhkan library (seringkali open source) buatan orang lain. Mari lihat bagaimana cara Stack membantu kita melakukan hal tersebut.

Sekarang, kita akan coba membuka file lain, yaitu helloworld.cabal. File tersebut berisi deksripsi meta tentang project Anda, seperti misalnya nama, versi, author, dan sebagainya. Selain itu, terdapat pula bagian executable di bagian bawah yang menjelaskan lebih spesifik mengenai struktur program kita, misalnya direktori tempat kita menaruh kode sumber (hs-source-dirs), module utama kita (main-is), dan, yang menjadi fokus kita saat ini, dependensi dari project kita pada build-depends.

build-depends menerima sebuah comma-separated list dari dependensi kita. Kita juga bisa menambahkan version constraint yang dibutuhkan oleh program kita. Kali ini, kita akan mencoba menambahkan sebuah dependensi sederhana yaitu text, sebuah library untuk pengolahan teks yang umum digunakan.

Ubah build-depends Anda menjadi sebagai berikut:

build-depends:       base >= 4.7 && < 5, text

Atau, dengan format yang lebih umum digunakan oleh developer Haskell:

build-depends:       base >= 4.7 && < 5
, text

(Koma ditaruh di awal baris agar dapat menghasilkan diff yang bersih ketika menggunakan version control.)

Setelah selesai, simpan file tersebut, lalu jalankan kembali stack build.

$ stack build
Progress 0/2
text-1.2.2.2: download
text-1.2.2.2: configure
text-1.2.2.2: build
text-1.2.2.2: copy/register
...output kompilasi...
Completed 2 action(s).

Dengan tambahan dependensi, kali ini stack build juga akan mengunduh dan meng-compile dependensi kita. Kita telah berhasil “membangun” program kita dengan dependensi yang telah kita definisikan! Penggunaan library pada program kita juga diluar scope tutorial ini :)

Sedikit tentang templates

Sebagai akhir dari eksplorasi Stack, kita akan membahas sedikit tentang templates yang kita gunakan di awal. Mari kita coba jalankan perintah stack templates:

$ stack templates
Template Description
...
hakyll-template - a static website compiler library
...
scotty-hello-world
scotty-hspec-wai
servant - a set of packages for declaring web APIs
servant-docker
simple
simple-hpack
simple-library
spock - a lightweight web framework
...
yesod-minimal
yesod-mongo
yesod-mysql
yesod-postgres
yesod-postgres-fay
yesod-simple
yesod-sqlite

Terdapat daftar template yang dapat kita gunakan untuk menggunakan suatu library, yang berasal dari repository stack-templates. Sayangnya, tim di belakang Stack masih ragu untuk meneruskan mengelola template-template ini, karena pada umumnya developer lebih sering menggunakan simple atau new-template. Namun, saya menganggap template ini dapat bermanfaat untuk belajar, khususnya untuk mencoba-coba project dengan library yang tersedia.

Pada saat penulisan ini, terdapat misalnya beberapa template untuk web framework seperti yesod, servant, scotty, dan spock. Untuk menggunakannya, cukup gunakan stack new <nama-project> <nama-template>, stack build, dan stack exec, seperti yang kita lakukan pada awal tutorial ini! Perlu diperhatikan bahwa untuk project yang dependensinya banyak, stack build mungkin akan memakan waktu agak lama dalam mengunduh dan meng-compile dependensinya.

Lalu, selanjutnya apa?

Masih bersama saya? Terima kasih telah mencapai sejauh ini!

Penjelasan tentang templates mengakhiri eksplorasi kita dengan Stack pada tutorial ini. Ada beberapa fitur Stack lain yang belum saya bahas, misalnya stack test yang akan membantu kita untuk testing, namun saya kira sampai sini sudah cukup untuk dasarnya.

Saya juga katakan tadi bahwa saya sedang menyiapkan artikel lain untuk melakukan setup text editor untuk pengembangan Haskell. Editor/IDE yang bagus dapat sangat membantu kita dalam pengembangan sehari-hari. Tunggu tanggal mainnya!

Seperti yang saya janjikan di awal, artikel ini bertujuan untuk menyiapkan sebuah development environment yang akan memudahkan kita untuk belajar Haskell. Saya harap tutorial ini berhasil mencapai tujuan tersebut. Untuk selanjutnya, ya, mari kita belajar Haskell :)

Untuk belajar, saya sangat merekomendasikan situs haskell.web.id. Situs tersebut memuat terjemahan materi kuliah CIS194: Introduction to Haskell dari University of Pennsylvania. Materi tersebut digadang-gadangkan sebagai yang terbaik untuk mempelajari Haskell dari awal. Bersiaplah untuk menemukan dunia yang baru.

Kalau Anda tertarik mengetahui soal paradigma pemrograman fungsional praktis dalam software development, Saya telah menuliskan sebuah seri artikel mengenai hal tersebut. Silakan kunjungi jika tertarik.

Baiklah, sampai di sini, sampai bertemu kembali! Mari buat programming kembali fun(ctional)!

Apakah Anda menyukai yang Anda baca? Apa saya melewatkan sesuatu? Beritahu saya melalui kolom komentar!

Paradigma Fungsional adalah sebuah blog tentang berbagai hal yang berkaitan dengan paradigma pemrograman fungsional (functional programming) di dunia pengembangan software. Tulisan-tulisan di blog ini akan dimuat dalam Bahasa Indonesia, karena menurut saya resource untuk belajar paradigma fungsional dalam bahasa kita masih sangat kurang.

Apabila Anda tertarik untuk mengetahui lebih lanjut tentang dunia paradigma fungsional, silakan ikuti blog ini!

--

--

Bobby Priam
Paradigma Fungsional

Software Engineer. Functional programming and distributed systems enthusiast. Java, JavaScript, Elixir, OCaml, Haskell.