Saya sedang berpikir tentang membuat currying dan fungsi variadic keduanya tersedia dalam bahasa pemrograman fungsional yang diketik secara dinamis, tapi saya ingin tahu apakah itu mungkin atau tidak.
Berikut ini beberapa kodesemu:
sum = if @args.empty then 0 else @args.head + sum @args.tail
yang seharusnya meringkas semua argumennya. Kemudian, jika sum
itu sendiri diberi nomor, maka hasilnya adalah 0
. sebagai contoh,
sum + 1
sama dengan 1, dengan asumsi bahwa +
hanya dapat bekerja pada angka. Namun, meskipun sum == 0
benar, sum
masih akan mempertahankan nilai dan properti fungsionalnya tidak peduli berapa banyak argumen yang diberikan (maka "diterapkan sebagian" dan "variadik" pada saat yang sama), misalnya, jika saya menyatakan
g = sum 1 2 3
maka g
sama dengan 6
, bagaimanapun, kita masih bisa menerapkan lebih lanjut g
. Sebagai contoh, g 4 5 == 15
itu benar. Dalam kasus ini, kami tidak dapat mengganti objek g
dengan literal 6
, karena meskipun mereka menghasilkan nilai yang sama ketika diperlakukan sebagai integer, mereka mengandung kode yang berbeda di dalamnya.
Jika desain ini digunakan dalam bahasa pemrograman nyata, apakah akan menimbulkan kebingungan atau ambiguitas?
sumber
sum
adalah0
tanpa argumen dan secara rekursif menyebut dirinya dengan argumen.reduce
?args
:empty
,head
, dantail
. Itu semua adalah fungsi daftar, menunjukkan bahwa mungkin hal yang lebih mudah dan lebih mudah untuk dilakukan adalah dengan menggunakan daftar di mana hal-hal variadik akan terjadi. (Jadi,sum [1, 2, 3]
alih-alihsum 1 2 3
)Jawaban:
Bagaimana vararg dapat diimplementasikan? Kami membutuhkan beberapa mekanisme untuk menandai akhir dari daftar argumen. Ini bisa jadi
Kedua mekanisme ini dapat digunakan dalam konteks currying untuk mengimplementasikan varargs, tetapi pengetikan yang tepat menjadi masalah utama. Mari kita asumsikan bahwa kita berurusan dengan suatu fungsi
sum: ...int -> int
, kecuali bahwa fungsi ini menggunakan currying (jadi kita sebenarnya memiliki tipe yang lebih sukasum: int -> ... -> int -> int
, kecuali bahwa kita tidak tahu jumlah argumen).Kasus: nilai terminator: Membiarkan
end
menjadi terminator khusus, danT
menjadi tipesum
. Kita sekarang tahu bahwa diterapkanend
kembali fungsi:sum: end -> int
, dan yang diterapkan ke int kita mendapatkan sum-seperti fungsi lain:sum: int -> T
. Oleh karena ituT
adalah gabungan dari jenis:T = (end -> int) | (int -> T)
. Oleh penggantinyaT
, kita mendapatkan berbagai jenis mungkin sepertiend -> int
,int -> end -> int
,int -> int -> end -> int
, dll Namun, kebanyakan sistem tipe tidak mengakomodasi jenis tersebut.Kasus: panjang eksplisit: Argumen pertama ke fungsi vararg adalah jumlah vararg. Jadi
sum 0 : int
,sum 1 : int -> int
,sum 3 : int -> int -> int -> int
dll Hal ini didukung dalam beberapa sistem jenis dan merupakan contoh mengetik tergantung . Sebenarnya, jumlah argumen akan menjadi parameter jenis dan bukan merupakan parameter biasa - itu tidak masuk akal untuk arity fungsi bergantung pada nilai runtime,s = ((sum (floor (rand 3))) 1) 2
jelas sakit-mengetik: mengevaluasi ini baiks = ((sum 0) 1) 2 = (0 1) 2
,s = ((sum 1) 1) 2 = 1 2
ataus = ((sum 2) 1) 2 = 3
.Dalam praktiknya, tidak satu pun dari teknik ini harus digunakan karena mereka rawan kesalahan, dan tidak memiliki tipe (bermakna) dalam sistem tipe umum. Sebaliknya, hanya lulus daftar nilai sebagai salah satu Penyempitan:
sum: [int] -> int
.Ya, dimungkinkan untuk suatu objek muncul sebagai fungsi dan nilai, misalnya dalam sistem tipe dengan paksaan. Membiarkan
sum
menjadiSumObj
, yang memiliki dua paksaan:coerce: SumObj -> int -> SumObj
memungkinkansum
untuk digunakan sebagai fungsi, dancoerce: SumObj -> int
memungkinkan kita untuk mengekstrak hasilnya.Secara teknis, ini adalah variasi dari kasus nilai terminator di atas, dengan
T = SumObj
, dancoerce
menjadi un-wrapper untuk tipe tersebut. Dalam banyak bahasa berorientasi objek, ini sepele diimplementasikan dengan overloading operator, misalnya C ++:sumber
..., force=False)
untuk memaksa penerapan fungsi awal.curryList : ([a] -> b) -> [a] -> [a] -> b, curryList f xs ys = f (xs ++ ys)
.Anda mungkin ingin melihat implementasi printf ini di Haskell , bersama dengan deskripsi cara kerjanya . Ada tautan di halaman terakhir ke makalah Oleg Kiselyov tentang melakukan hal semacam ini, yang juga layak dibaca. Bahkan, jika Anda merancang bahasa fungsional, situs web Oleg mungkin harus membaca wajib.
Menurut pendapat saya, pendekatan ini sedikit meretas, tetapi mereka menunjukkan bahwa itu mungkin. Namun, jika bahasa Anda menggunakan pengetikan dengan ketergantungan penuh, itu jauh lebih sederhana. Fungsi variadic untuk menjumlahkan argumen integer-nya kemudian dapat terlihat seperti ini:
Sebuah abstraksi untuk mendefinisikan tipe rekursif tanpa perlu memberikannya nama eksplisit mungkin membuat penulisan fungsi-fungsi tersebut lebih mudah.
Sunting: tentu saja, saya baru saja membaca pertanyaan itu lagi dan Anda mengatakan bahasa yang diketik secara dinamis , pada saat itu jelas jenis mekanika tidak benar-benar relevan, dan karena itu jawaban @ amon mungkin berisi semua yang Anda butuhkan. Oh well, aku akan meninggalkan ini di sini kalau-kalau ada yang menemukan ini sambil bertanya-tanya bagaimana melakukannya dalam bahasa statis ...
sumber
Berikut ini adalah versi untuk penjelajahan fungsi variadic di Python3 yang menggunakan pendekatan "terminator" dari @amon, dengan mengambil keuntungan dari argumen opsional Python:
Fungsi yang dikembalikan
f
mengumpulkan argumen yang diteruskan ke dalam panggilan berturut-turut dalam array yang terikat dalam lingkup luar. Hanya ketikaforce
argumen itu benar, fungsi asli dipanggil dengan semua argumen yang dikumpulkan sejauh ini.Peringatan implementasi ini adalah bahwa Anda selalu harus melewati argumen pertama
f
sehingga Anda tidak dapat membuat "thunk", sebuah fungsi di mana semua argumen terikat dan hanya dapat dipanggil dengan daftar argumen kosong (tapi saya pikir ini sejalan dengan implementasi khas kari).Peringatan lain adalah bahwa sekali Anda melewati argumen yang salah (misalnya dari jenis yang salah) Anda harus kembali kari fungsi aslinya. Tidak ada cara lain untuk mengatur ulang susunan internal, ini hanya dilakukan setelah eksekusi yang sukses dari fungsi curried.
Saya tidak tahu apakah pertanyaan Anda yang disederhanakan, "bisakah objek menjadi fungsi dan nilai non-fungsi pada saat yang sama?", Dapat diimplementasikan dengan Python, sebagai referensi ke fungsi tanpa tanda kurung dievaluasi ke objek fungsi internal . Saya tidak tahu apakah ini bisa dibengkokkan untuk mengembalikan nilai arbitrer.
Mungkin mudah dalam Lisp, karena simbol Lisp dapat memiliki nilai dan nilai fungsi pada saat yang sama; nilai fungsi hanya dipilih ketika simbol muncul di posisi fungsi (sebagai elemen pertama dalam daftar).
sumber