GHCi sebagai arena bermain

Evaluasi ekspresi, penelusuran tipe dengan :t, dan perintah-perintah dasar GHCi.

Daftar isi

GHCi adalah lingkungan interaktif yang disertakan bersama GHC untuk mengevaluasi ekspresi Haskell tanpa harus menulis program utuh. Topik ini membahas cara kerja lingkungan tersebut, evaluasi ekspresi, penelusuran tipe dengan perintah :t, pemuatan berkas definisi, dan perintah-perintah dasar yang sering digunakan. Setelah mengikuti topik ini, GHCi dapat digunakan sebagai arena untuk mencoba gagasan sebelum gagasan itu dituliskan menjadi program.

Lingkungan interaktif GHCi#

GHCi menerapkan pola REPL (read-eval-print loop), yaitu siklus yang membaca satu ekspresi, mengevaluasinya, mencetak hasilnya, lalu menunggu ekspresi berikutnya. Lingkungan ini dimulai dengan perintah ghci pada terminal, dan prompt yang tampil menunjukkan nama modul yang sedang dimuat, misalnya Prelude>. Keluar dari lingkungan dilakukan melalui perintah :quit atau pintasan :q. Walaupun berperan seperti penerjemah, GHCi tidak menafsirkan ekspresi baris demi baris secara naif, melainkan mengompilasi setiap ekspresi menjadi bytecode sebelum mengevaluasinya, sehingga jawaban muncul dengan cepat.

Hasil evaluasi pada sesi interaktif tersimpan dalam nama it, sehingga hasil sebelumnya dapat dirujuk pada ekspresi berikutnya. Karena seluruh fungsi Haskell murni, evaluasi ekspresi di GHCi tidak mengubah keadaan apa pun dan mencoba ulang ekspresi yang sama selalu menghasilkan keluaran yang sama. Sifat ini menjadikan GHCi tempat yang aman untuk eksperimen karena tidak ada berkas maupun keadaan lain yang berubah akibat evaluasi.

Evaluasi ekspresi#

Ekspresi apa pun yang menghasilkan nilai dapat dievaluasi, mulai dari aritmetika, panggilan fungsi pustaka standar, sampai kombinasi keduanya. Opsi -e menerima satu ekspresi dalam tanda kutip, mengevaluasinya, lalu mencetak hasilnya ke terminal. Contoh berikut memuat tiga evaluasi yang berjalan langsung dari terminal.

bash
# evaluasi ekspresi aritmetika
ghci -e '2 ^ 10'
# terapkan fungsi pengali dua ke setiap elemen list
ghci -e 'map (* 2) [1 .. 5]'
# potong teks menjadi list kata
ghci -e 'words "haskell adalah bahasa fungsional"'
text Keluaran
1024
[2,4,6,8,10]
["haskell","adalah","bahasa","fungsional"]

Baris pertama menggunakan operator pangkat ^ untuk menghitung dua pangkat sepuluh, yaitu 1024. Baris kedua menerapkan ekspresi (* 2), yaitu fungsi yang mengalikan masukannya dengan dua, pada setiap elemen list [1 .. 5]. Baris ketiga memanggil words, yaitu fungsi pustaka standar yang memotong teks bertipe String menjadi list kata. Hasil yang bertipe list dicetak dalam tanda kurung siku dengan pemisah koma, sedangkan String dicetak dengan tanda kutip. Apabila hasil sebuah ekspresi tidak dapat dicetak, misalnya fungsi yang belum menerima seluruh argumennya, GHCi menampilkan pesan error yang menyebut ketiadaan instans Show untuk hasil tersebut.

Penelusuran tipe dengan :t#

Perintah :t, singkatan dari :type, menampilkan tipe sebuah ekspresi tanpa mengevaluasinya. Perintah ini menjadi alat utama untuk membaca pustaka standar karena tipe yang tampil menjelaskan apa yang diterima dan dikembalikan sebuah fungsi sebelum kodenya dicoba. Kebiasaan menelusuri tipe lebih dahulu juga membantu menemukan argumen yang belum diberikan.

bash
# telusuri tipe bilangan literal
ghci -e ':t 42'
# telusuri tipe fungsi map dari pustaka standar
ghci -e ':t map'
# telusuri tipe operator penggabung list
ghci -e ':t (++)'
text Keluaran
42 :: Num a => a
map :: (a -> b) -> [a] -> [b]
(++) :: [a] -> [a] -> [a]

Tanda :: memisahkan ekspresi dari tipenya, sedangkan huruf kecil seperti a dan b adalah variabel tipe (type variable), yaitu tempat yang dapat diisi tipe apa pun. Baris map :: (a -> b) -> [a] -> [b] dibaca dari kiri ke kanan: map menerima fungsi dari a ke b, kemudian list berisi a, dan menghasilkan list berisi b. Baris 42 :: Num a => a memuat tanda => yang menyatakan batasan (constraint), yakni 42 cocok untuk sebarang tipe yang mendukung operasi aritmetika yang didefinisikan oleh type class Num, bukan berarti 42 tanpa tipe. Terakhir, operator (++) ditulis dalam tanda kurung agar dapat dirujuk sebagai nilai, dan tipenya menunjukkan bahwa kedua list yang digabung harus bertipe sama.

Memuat definisi dari berkas#

Definisi yang ditulis sendiri dapat dicoba dengan pola yang sama. Berkas berikut memuat dua fungsi kecil yang menjadi bahan eksperimen sepanjang topik ini. Bagian main di akhir berkas menjadikannya program utuh, sehingga keluarannya dapat direproduksi melalui runghc.

haskell kali-dua.hs
-- Definisi kecil untuk dicoba di GHCi
kaliDua :: Int -> Int
kaliDua x = x * 2

bertambah :: Int -> Int
bertambah x = x + 1

main :: IO ()
main = do
  print (kaliDua 21)
  print (bertambah 20)
  print (kaliDua (bertambah 20))
text Keluaran
42
21
42

Pada sesi interaktif, berkas dimuat melalui ghci kali-dua.hs atau perintah :load, sehingga fungsi di dalamnya dapat langsung dipanggil pada prompt. Opsi berkas pada ghci -e meniru pemuatan tersebut dalam satu perintah, seperti pada contoh berikut. Ketiga perintah menggunakan berkas yang sama, sehingga hasilnya mudah dibandingkan berdampingan.

bash
# evaluasi fungsi dari berkas yang dimuat
ghci -e 'kaliDua 21' kali-dua.hs
# gabungkan dua fungsi dalam satu ekspresi
ghci -e 'kaliDua (bertambah 20)' kali-dua.hs
# telusuri tipe fungsi yang dimuat
ghci -e ':t kaliDua' kali-dua.hs
text Keluaran
42
42
kaliDua :: Int -> Int

Ekspresi ketiga menunjukkan bahwa :t juga berlaku untuk definisi buatan sendiri, sehingga tipe sebuah fungsi dapat diverifikasi tanpa membuka berkasnya. Apabila berkas disunting saat sesi interaktif masih berjalan, versi lama tetap termuat sampai perintah :r dijalankan. Dengan demikian, perubahan hanya berlaku setelah pemuatan ulang dan tidak berlaku secara diam-diam.

Perintah-perintah dasar#

Tabel berikut merangkum perintah GHCi yang paling sering digunakan sehari-hari. Semua perintah diawali tanda titik dua, dan beberapa di antaranya memiliki bentuk panjang yang setara. Bentuk pendek itulah yang lazim diketik, sedangkan bentuk panjangnya dicantumkan pada kolom kedua sebagai rujukan.

PerintahFungsi
:t ekspresimenampilkan tipe ekspresi, singkatan :type
:i namamenampilkan informasi lengkap sebuah nama, termasuk instans type class-nya, singkatan :info
:k Tipemenampilkan kind sebuah tipe, singkatan :kind
:l berkasmemuat berkas sumber ke sesi yang berjalan, singkatan :load
:rmemuat ulang berkas yang telah termuat, singkatan :reload
:browse Modulmenampilkan seluruh nama yang diekspor sebuah modul beserta tipenya
:qkeluar dari GHCi, singkatan :quit

Dua perintah pada tabel yang belum dipraktikkan adalah :k dan :browse. Perintah :k menampilkan kind, yaitu tipe bagi tipe, sedangkan :browse menampilkan isi sebuah modul. Contoh berikut mempraktikkan keduanya.

bash
# telusuri kind dari tipe Maybe
ghci -e ':k Maybe'
# telusuri isi modul Data.Bool
ghci -e ':browse Data.Bool'
text Keluaran
Maybe :: * -> *
Data.Bool.bool :: a -> a -> Bool -> a
(&&) :: Bool -> Bool -> Bool
type Bool :: *
data Bool = False | True
not :: Bool -> Bool
otherwise :: Bool
(||) :: Bool -> Bool -> Bool

Keluaran pertama berarti Maybe membutuhkan satu tipe untuk menjadi tipe lengkap, sehingga Maybe Int sah sedangkan Maybe sendiri belum menjadi tipe lengkap. Keluaran kedua memuat tipe data Bool, konstruktornya False dan True, serta fungsi-fungsi modul tersebut beserta tipenya. Nama Data.Bool.bool tampil berawalan modul karena Prelude juga mengekspor fungsi bernama bool, sehingga keduanya perlu dibedakan.

Kesalahan umum#

Kesalahan paling lazim saat bereksperimen di GHCi berupa pemanggilan fungsi sebelum berkasnya dimuat. Kesalahan serupa muncul pula pada sesi interaktif apabila :load belum dijalankan. Contoh berikut memanggil kaliDua tanpa menyertakan berkas yang mendefinisikannya.

bash
# memanggil fungsi sebelum berkasnya dimuat
ghci -e 'kaliDua 21'
text Keluaran
<interactive>:0:1: error: [GHC-88464]
    Variable not in scope: kaliDua :: t0 -> t

Pesan Variable not in scope berarti nama tersebut tidak dikenal pada lingkup saat perintah dijalankan. Keluaran seperti ini bukan tanda kerusakan pemasangan, melainkan petunjuk arah yang menyebut nama yang belum dikenal beserta tipenya. Perbaikannya dengan menyertakan nama berkas pada perintah atau memuat berkasnya melalui :load terlebih dahulu, sebagaimana contoh pada bagian Memuat definisi dari berkas.

Kesalahan kedua yang lazim berupa perilaku lama yang masih muncul setelah berkas disunting. Sesi interaktif tidak membaca ulang berkas secara otomatis, sehingga perintah :r perlu dijalankan setelah setiap penyuntingan sebelum hasil baru dapat dicoba. Kesalahan ini tidak menimbulkan pesan apa pun, sehingga penyuntingan yang belum dimuat ulang mudah terlewat.

Ringkasan#

  • GHCi adalah REPL yang membaca ekspresi, mengevaluasinya, dan mencetak hasilnya, dengan ghci -e 'ekspresi' sebagai mode satu evaluasi.
  • Perintah :t menampilkan tipe ekspresi dan menjadi cara utama membaca tipe fungsi pustaka standar sebelum digunakan.
  • Definisi buatan sendiri dimuat melalui nama berkas atau :load, lalu fungsi di dalamnya dapat dipanggil dan ditelusuri tipenya.
  • Perintah :k, :browse, :r, dan :i melengkapi pekerjaan harian, dari kind hingga pemuatan ulang berkas.

Referensi#