Transparansi referensial, disebut fungsi, menunjukkan bahwa Anda dapat menentukan hasil penerapan fungsi itu hanya dengan melihat nilai argumennya. Anda dapat menulis fungsi yang secara transparan transparan dalam bahasa pemrograman apa pun, misalnya Python, Skema, Pascal, C.
Di sisi lain, dalam sebagian besar bahasa Anda juga dapat menulis fungsi yang tidak transparan secara referensial. Misalnya, fungsi Python ini:
counter = 0
def foo(x):
global counter
counter += 1
return x + counter
sebenarnya tidak transparan, pada kenyataannya panggilan
foo(x) + foo(x)
dan
2 * foo(x)
akan menghasilkan nilai yang berbeda, untuk argumen apa pun x
. Alasan untuk ini adalah bahwa fungsi menggunakan dan memodifikasi variabel global, oleh karena itu hasil setiap doa tergantung pada keadaan yang berubah ini, dan tidak hanya pada argumen fungsi.
Haskell, sebuah bahasa yang sepenuhnya fungsional, secara ketat memisahkan evaluasi ekspresi di mana fungsi - fungsi murni diterapkan dan yang selalu transparan secara referensi, dari pelaksanaan tindakan (pemrosesan nilai-nilai khusus), yang tidak transparan secara referensial, yaitu melaksanakan tindakan yang sama dapat dilakukan setiap kali hasil berbeda.
Jadi, untuk fungsi Haskell
f :: Int -> Int
dan bilangan bulat apa pun x
, selalu benar itu
2 * (f x) == (f x) + (f x)
Contoh tindakan adalah hasil dari fungsi perpustakaan getLine
:
getLine :: IO String
Sebagai hasil dari evaluasi ekspresi, fungsi ini (sebenarnya konstan) pertama-tama menghasilkan nilai tipe murni IO String
. Nilai dari tipe ini adalah nilai seperti yang lain: Anda bisa menyebarkannya, meletakkannya di struktur data, menyusunnya menggunakan fungsi khusus, dan sebagainya. Misalnya, Anda dapat membuat daftar tindakan seperti:
[getLine, getLine] :: [IO String]
Tindakan khusus karena Anda dapat memberitahu runtime Haskell untuk menjalankannya dengan menulis:
main = <some action>
Dalam hal ini, ketika program Haskell Anda dimulai, runtime berjalan melalui tindakan terikat main
dan mengeksekusinya , mungkin menghasilkan efek samping. Oleh karena itu, eksekusi tindakan tidak transparan secara referensial karena mengeksekusi aksi yang sama dua kali dapat menghasilkan hasil yang berbeda tergantung pada apa yang didapat runtime sebagai input.
Berkat sistem tipe Haskell, sebuah aksi tidak pernah dapat digunakan dalam konteks di mana tipe lain diharapkan, dan sebaliknya. Jadi, jika Anda ingin menemukan panjang string, Anda dapat menggunakan length
fungsi:
length "Hello"
akan kembali 5. Tetapi jika Anda ingin menemukan panjang string dibaca dari terminal, Anda tidak dapat menulis
length (getLine)
karena Anda mendapatkan kesalahan tipe: length
mengharapkan input dari daftar jenis (dan sebuah String, memang, daftar) tetapi getLine
merupakan nilai tipe IO String
(suatu tindakan). Dengan cara ini, sistem tipe memastikan bahwa nilai aksi seperti getLine
(yang pelaksanaannya dilakukan di luar bahasa inti dan yang mungkin transparan non-referensial) tidak dapat disembunyikan di dalam nilai tipe non-tindakan jenis Int
.
EDIT
Untuk menjawab pertanyaan yang ada, berikut ini adalah program Haskell kecil yang membaca garis dari konsol dan mencetak panjangnya.
main :: IO () -- The main program is an action of type IO ()
main = do
line <- getLine
putStrLn (show (length line))
Tindakan utama terdiri dari dua subaksi yang dieksekusi berurutan:
getline
dari jenis IO String
,
- yang kedua dibangun dengan mengevaluasi fungsi
putStrLn
tipe String -> IO ()
pada argumennya.
Lebih tepatnya, aksi kedua dibangun oleh
- mengikat
line
nilai yang dibaca oleh tindakan pertama,
- mengevaluasi fungsi-fungsi murni
length
(menghitung panjang sebagai integer) dan kemudian show
(mengubah integer menjadi sebuah string),
- membangun tindakan dengan menerapkan fungsi
putStrLn
pada hasil show
.
Pada titik ini, tindakan kedua dapat dijalankan. Jika Anda mengetik "Halo", itu akan mencetak "5".
Perhatikan bahwa jika Anda mendapatkan nilai dari suatu tindakan menggunakan <-
notasi, Anda hanya dapat menggunakan nilai itu di dalam tindakan lain, misalnya Anda tidak bisa menulis:
main = do
line <- getLine
show (length line) -- Error:
-- Expected type: IO ()
-- Actual type: String
karena show (length line)
bertipe String
sedangkan notasi mensyaratkan bahwa suatu tindakan ( getLine
bertipe IO String
) diikuti oleh tindakan lain (misalnya putStrLn (show (length line))
bertipe IO ()
).
EDIT 2
Definisi Jörg W Mittag tentang transparansi referensial lebih umum daripada definisi saya (saya telah meningkatkan jawabannya). Saya telah menggunakan definisi terbatas karena contoh dalam pertanyaan ini berfokus pada nilai balik fungsi dan saya ingin menggambarkan aspek ini. Namun, RT secara umum merujuk pada makna keseluruhan program, termasuk perubahan keadaan global dan interaksi dengan lingkungan (IO) yang disebabkan oleh evaluasi ekspresi. Jadi, untuk definisi umum yang benar, Anda harus merujuk ke jawaban itu.