Saya berpikir mengapa ada (dalam semua bahasa pemrograman yang telah saya pelajari, seperti C ++, Java, Python) pustaka standar seperti stdlib, alih-alih memiliki "fungsi" yang sama menjadi primitif dari bahasa itu sendiri.
Saya berpikir mengapa ada (dalam semua bahasa pemrograman yang telah saya pelajari, seperti C ++, Java, Python) pustaka standar seperti stdlib, alih-alih memiliki "fungsi" yang sama menjadi primitif dari bahasa itu sendiri.
Jawaban:
Izinkan saya untuk sedikit memperluas jawaban yang baik (@) dari @ Vincent :
Mengapa kompiler tidak dapat menerjemahkan panggilan fungsi ke dalam sekumpulan instruksi?
Itu bisa, dan melakukannya melalui setidaknya dua mekanisme:
inlining panggilan fungsi - selama penerjemahan, kompiler dapat mengganti panggilan kode sumber dengan implementasinya langsung sebaris alih-alih membuat panggilan aktual ke fungsi. Masih fungsi perlu memiliki implementasi yang ditentukan di suatu tempat dan itu bisa di perpustakaan standar.
fungsi intrinsik - intrinsik adalah fungsi yang telah diinformasikan oleh kompiler tanpa harus menemukan fungsi di perpustakaan. Ini biasanya disediakan untuk fitur perangkat keras yang secara praktis tidak dapat diakses dengan cara lain, menjadi sangat sederhana sehingga bahkan overhead panggilan ke fungsi perpustakaan bahasa assembly dianggap tinggi. (Kompilator umumnya hanya secara otomatis memasukkan kode sumber dalam bahasanya, tetapi tidak fungsi-fungsi perakitan, yang merupakan mekanisme intrinsik.)
Masih ini dikatakan, opsi terbaik kadang-kadang adalah untuk kompiler untuk menerjemahkan panggilan fungsi dalam bahasa sumber menjadi panggilan fungsi dalam kode mesin. Rekursi, metode virtual, dan ukuran tipis adalah beberapa alasan yang membuat inlining tidak selalu memungkinkan / praktis. (Alasan lain adalah maksud dari pembangunan, seperti kompilasi terpisah (modul objek), unit beban terpisah (misalnya DLL)).
Tidak ada keuntungan nyata untuk membuat sebagian besar fungsi perpustakaan standar intrisik (yang akan menyulitkan kode lebih banyak pengetahuan ke dalam kompiler tanpa keuntungan nyata), jadi panggilan kode mesin lagi sering paling tepat.
C adalah bahasa yang terkenal yang bisa menghilangkan pernyataan bahasa eksplisit lainnya yang mendukung fungsi perpustakaan standar. Meskipun perpustakaan sudah ada sebelumnya, bahasa ini membuat pergeseran untuk melakukan lebih banyak pekerjaan dari fungsi perpustakaan standar dan kurang sebagai pernyataan eksplisit dalam tata bahasa. IO dalam bahasa lain, misalnya, sering diberikan sintaksisnya sendiri dalam bentuk berbagai pernyataan, sedangkan tata bahasa C tidak mendefinisikan pernyataan IO apa pun, alih-alih menunda ke perpustakaan standarnya untuk menyediakan, semua dapat diakses melalui pemanggilan fungsi, yang kompiler sudah tahu caranya.
Ini hanya untuk menjaga bahasa itu sendiri sesederhana mungkin. Anda perlu membedakan antara fitur bahasa, seperti jenis loop atau cara untuk menyampaikan parameter ke fungsi dan sebagainya, dan fungsi umum yang dibutuhkan sebagian besar aplikasi.
Perpustakaan adalah fungsi yang mungkin berguna bagi banyak programmer sehingga mereka dibuat sebagai kode yang dapat digunakan kembali yang dapat dibagikan. Perpustakaan standar dirancang untuk menjadi fungsi yang sangat umum yang biasanya dibutuhkan oleh pemrogram. Dengan cara ini, bahasa pemrograman segera bermanfaat bagi beragam programmer. Perpustakaan dapat diperbarui dan diperluas tanpa mengubah fitur inti dari bahasa itu sendiri.
PHP
sebagai contoh sulit membuat perbedaan antara fungsi bahasanya yang luas dan bahasanya sendiri.
include
, require
dan require_once
, jika / untuk / sementara (pemrograman terstruktur), pengecualian, suatu sistem yang terpisah dari 'kesalahan nilai-nilai', rumit aturan mengetik lemah, rumit peraturan operator didahulukan, dan seterusnya . Bandingkan ini dengan kesederhanaan, katakanlah, Smalltalk, Skema, Prolog, Keempat, dll;)
Selain apa yang sudah dikatakan oleh jawaban lain, menempatkan fungsi standar ke perpustakaan adalah pemisahan dari keprihatinan :
Adalah tugas kompiler untuk mengurai bahasa dan menghasilkan kode untuk itu. Bukan tugas kompiler untuk memuat apa pun yang sudah bisa ditulis dalam bahasa itu dan disediakan sebagai perpustakaan.
Ini pekerjaan perpustakaan standar (yang selalu tersedia secara implisit) untuk menyediakan fungsionalitas inti yang dibutuhkan oleh hampir semua program. Bukan tugas perpustakaan standar untuk memuat semua fungsi yang mungkin berguna.
Merupakan tugas pustaka standar opsional untuk menyediakan fungsionalitas bantu yang dapat dilakukan tanpa banyak program, tetapi yang masih cukup mendasar dan juga penting bagi banyak aplikasi untuk menjamin pengiriman dengan lingkungan standar. Bukan tugas pustaka opsional untuk memuat semua kode yang dapat digunakan kembali yang pernah ditulis.
Merupakan tugas pustaka pengguna untuk menyediakan koleksi fungsi yang dapat digunakan kembali yang berguna. Bukan tugas pustaka pengguna untuk memuat semua kode yang pernah ditulis.
Merupakan tugas kode sumber aplikasi untuk menyediakan bit kode yang tersisa yang benar-benar hanya relevan dengan satu aplikasi itu.
Jika Anda menginginkan perangkat lunak satu ukuran untuk semua, Anda mendapatkan sesuatu yang sangat rumit. Anda perlu memodulasi untuk menurunkan kompleksitas ke tingkat yang dapat dikelola. Dan Anda perlu memodulasi untuk memungkinkan implementasi parsial :
Pustaka threading tidak berguna pada pengontrol tertanam satu inti. Mengizinkan implementasi bahasa untuk pengontrol tertanam ini tidak menyertakan pthread
perpustakaan adalah hal yang tepat untuk dilakukan.
Perpustakaan matematika tidak berharga pada pengontrol mikro yang bahkan tidak memiliki FPU. Sekali lagi, tidak dipaksa untuk menyediakan fungsi seperti sin()
membuat hidup jauh lebih mudah bagi implementator bahasa Anda untuk mikrokontroler itu.
Bahkan pustaka standar inti tidak bernilai saat Anda memprogram kernel. Anda tidak dapat menerapkan write()
tanpa syscall ke dalam kernel, dan Anda tidak dapat menerapkan printf()
tanpa write()
. Sebagai pemrogram kernel, tugas Anda untuk menyediakan write()
syscall, Anda tidak bisa hanya mengharapkannya ada di sana.
Bahasa yang tidak memungkinkan untuk penghapusan seperti itu dari perpustakaan standar sama sekali tidak cocok untuk banyak tugas . Jika Anda ingin bahasa Anda dapat digunakan secara fleksibel di lingkungan yang tidak umum, itu harus fleksibel dalam apa yang termasuk pustaka standar. Semakin banyak bahasa Anda bergantung pada perpustakaan standar, semakin banyak asumsi yang dibuatnya pada lingkungan eksekusinya, dan karenanya membatasi penggunaannya pada lingkungan yang menyediakan prasyarat ini.
Tentu saja, bahasa tingkat tinggi seperti python dan java dapat membuat banyak asumsi tentang lingkungan mereka. Dan mereka cenderung memasukkan banyak, banyak hal ke dalam perpustakaan standar mereka. Bahasa tingkat rendah seperti C menyediakan lebih sedikit di perpustakaan standar mereka, dan menjaga perpustakaan standar inti jauh lebih kecil. Itu sebabnya Anda menemukan kompiler C yang berfungsi untuk hampir semua arsitektur, tetapi mungkin tidak dapat menjalankan skrip python di atasnya.
Salah satu alasan utama kompiler dan perpustakaan standar terpisah adalah karena mereka melayani dua tujuan yang berbeda (bahkan jika keduanya didefinisikan oleh spesifikasi bahasa yang sama): kompiler menerjemahkan kode tingkat yang lebih tinggi ke dalam instruksi mesin, dan perpustakaan standar menyediakan pra-uji implementasi fungsionalitas yang biasanya dibutuhkan. Nilai kompiler penulis modularitas sama seperti pengembang perangkat lunak lainnya. Bahkan, beberapa kompiler C awal lebih lanjut membagi kompiler menjadi program terpisah untuk pra-pemrosesan, kompilasi, dan penautan.
Modularitas ini memberi Anda banyak keuntungan:
Secara historis (setidaknya dari perspektif C), versi asli, pra-standardisasi bahasa tidak memiliki perpustakaan standar sama sekali. Vendor OS dan pihak ketiga sering menyediakan perpustakaan yang penuh dengan fungsi yang umum digunakan, tetapi implementasi yang berbeda termasuk hal yang berbeda dan mereka sebagian besar tidak kompatibel satu sama lain. Ketika C distandarisasi, mereka mendefinisikan "perpustakaan standar" dalam upaya untuk menyelaraskan implementasi yang berbeda ini dan meningkatkan portabilitas. Pustaka standar C dikembangkan terpisah dari bahasa, seperti yang dimiliki pustaka Boost untuk C ++, tetapi kemudian diintegrasikan ke dalam spek bahasa.
Jawaban tambahan untuk kasus sudut: Manajemen kekayaan intelektual
Contoh penting adalah implementasi Math.Pow (dobel, dobel) dalam .NET Framework yang dibeli oleh Microsoft dari Intel dan tetap dirahasiakan bahkan jika kerangka kerjanya open-source. (Tepatnya, dalam kasus di atas itu adalah panggilan internal daripada perpustakaan tetapi ide itu berlaku.) Perpustakaan yang terpisah dari bahasa itu sendiri (secara teoritis juga merupakan subset dari perpustakaan standar) dapat memberikan fleksibilitas yang lebih besar kepada para pendukung bahasa dalam menggambar batas antara apa yang harus tetap transparan dan apa yang harus tetap tidak diungkapkan (karena kontrak mereka dengan pihak ke-3 atau alasan terkait IP lainnya).
Math.Pow
tidak menyebutkan pembelian apa pun, atau apa pun tentang Intel, dan berbicara tentang orang-orang yang membaca kode sumber penerapan fungsi.
Bug dan debugging.
Bug: Semua perangkat lunak memiliki bug, perpustakaan standar Anda memiliki bug dan kompiler Anda memiliki bug. Sebagai pengguna bahasa, jauh lebih mudah untuk menemukan dan mengatasinya ketika bug tersebut ada di perpustakaan standar dibandingkan dengan di kompiler.
Debugging: Jauh lebih mudah bagi saya untuk melihat setumpuk perpustakaan standar dan memberi saya beberapa pengertian tentang apa yang mungkin salah. Karena jejak stack memiliki kode yang saya mengerti. Tentu Anda dapat menggali lebih dalam dan Anda juga dapat melacak fungsi intrinsik Anda, tetapi itu jauh lebih mudah jika dalam bahasa yang Anda gunakan sepanjang waktu dari hari ke hari.
Ini adalah pertanyaan yang sangat bagus!
Standar C ++, misalnya, tidak pernah menentukan apa yang harus diimplementasikan dalam kompiler atau di perpustakaan standar: itu hanya merujuk pada implementasi . Sebagai contoh, simbol-simbol yang dilindungi didefinisikan baik oleh kompiler (sebagai intrinsik) dan oleh perpustakaan standar, secara bergantian.
Namun, semua implementasi C ++ yang saya tahu akan memiliki jumlah intrinsik seminimal mungkin yang disediakan oleh kompiler, dan sebanyak mungkin disediakan oleh pustaka standar.
Jadi, sementara secara teknis layak untuk mendefinisikan perpustakaan standar sebagai fungsi intrinsik dalam kompiler, tampaknya jarang digunakan dalam praktik.
Mari kita pertimbangkan gagasan untuk memindahkan sebagian fungsionalitas dari pustaka standar ke kompiler.
Keuntungan:
Kekurangan:
std
) untuk bereksperimen.Ini berarti bahwa memindahkan sesuatu ke kompiler itu mahal , sekarang dan di masa depan, dan oleh karena itu memerlukan case yang solid. Untuk beberapa bagian fungsionalitas, perlu (mereka tidak dapat ditulis sebagai kode biasa), namun meskipun demikian membayar untuk mengekstraksi potongan-potongan minimal dan generik untuk pindah ke kompiler dan membangun di atasnya di perpustakaan standar.
Sebagai perancang bahasa sendiri, saya ingin menggaungkan beberapa jawaban lain di sini, tetapi berikan melalui mata seseorang yang sedang membangun suatu bahasa.
API belum selesai saat Anda selesai menambahkan semua yang Anda bisa ke dalamnya. API selesai saat Anda selesai mengambil semua yang Anda bisa darinya.
Bahasa pemrograman harus ditentukan menggunakan beberapa bahasa. Anda harus dapat menyampaikan makna di balik program apa pun yang ditulis dalam bahasa Anda. Bahasa ini sangat sulit untuk ditulis, dan bahkan lebih sulit untuk menulis dengan baik. Secara umum, ini cenderung menjadi bentuk bahasa Inggris yang sangat tepat dan terstruktur yang digunakan untuk menyampaikan makna bukan kepada komputer, tetapi untuk pengembang lain, terutama pengembang yang menulis kompiler atau penerjemah untuk bahasa Anda. Berikut ini contoh dari spesifikasi C ++ 11, [intro.multithread / 14]:
Urutan efek samping yang terlihat pada objek atom M, sehubungan dengan perhitungan nilai B dari M, adalah sub-urutan efek samping berdekatan maksimum dalam urutan modifikasi M, di mana efek samping pertama terlihat sehubungan dengan B , dan untuk setiap efek samping, bukan itu yang terjadi B sebelum itu. Nilai objek atom M, sebagaimana ditentukan oleh evaluasi B, harus merupakan nilai yang disimpan oleh beberapa operasi dalam urutan M yang terlihat sehubungan dengan B. [Catatan: Dapat ditunjukkan bahwa urutan efek samping dari nilai yang terlihat. perhitungan unik karena persyaratan koherensi di bawah ini. —Kirim catatan]
Blek! Siapa pun yang telah mengambil risiko untuk memahami bagaimana C ++ 11 menangani multithreading dapat menghargai mengapa kata-kata itu harus sangat buram, tetapi itu tidak memaafkan fakta bahwa itu ... yah ... begitu buram!
Bandingkan dengan definisi std::shared_ptr<T>::reset
, di bagian perpustakaan standar:
template <class Y> void reset(Y* p);
Efek: Setara dengan
shared_ptr(p).swap(*this)
Jadi apa bedanya? Pada bagian definisi bahasa, penulis tidak dapat berasumsi bahwa pembaca memahami primitif bahasa. Semuanya harus ditentukan dengan cermat dalam prosa bahasa Inggris. Setelah kita sampai ke bagian definisi perpustakaan, kita dapat menggunakan bahasa untuk menentukan perilaku. Ini seringkali jauh lebih mudah!
Pada prinsipnya, seseorang dapat memiliki membangun yang mulus dari primitif pada awal dokumen spesifikasi, sepanjang mendefinisikan apa yang akan kita pikirkan sebagai "fitur perpustakaan standar", tanpa harus menarik garis antara "primitif bahasa" dan fitur "perpustakaan standar". Dalam praktiknya, garis itu terbukti sangat berharga untuk digambar karena memungkinkan Anda menulis beberapa bagian bahasa yang paling rumit (seperti yang harus menerapkan algoritma) menggunakan bahasa yang dirancang untuk mengekspresikannya.
Dan kami memang melihat beberapa garis buram:
java.lang.ref.Reference<T>
mungkin hanya disubklasifikasikan oleh kelas perpustakaan standar java.lang.ref.WeakReference<T>
java.lang.ref.SoftReference<T>
dan java.lang.ref.PhantomReference<T>
karena perilaku Reference
sangat terkait dengan spesifikasi bahasa Jawa sehingga mereka perlu membatasi beberapa bagian dari proses yang diimplementasikan sebagai kelas "perpustakaan standar".Ini dimaksudkan sebagai tambahan untuk jawaban yang ada (dan terlalu panjang untuk dikomentari).
Setidaknya ada dua alasan lain untuk perpustakaan standar:
Jika fitur bahasa tertentu dalam fungsi perpustakaan dan saya ingin tahu cara kerjanya, saya bisa membaca sumber untuk fungsi itu. Jika saya ingin mengirimkan laporan bug / tambalan / permintaan tarik, biasanya tidak terlalu sulit untuk membuat kode kasus perbaikan dan pengujian. Jika ada di kompiler, saya harus dapat menggali internal. Bahkan jika itu dalam bahasa yang sama (dan memang harus demikian, kompiler yang menghargai diri sendiri harus di-host sendiri) kode kompiler tidak seperti kode aplikasi. Mungkin diperlukan selamanya bahkan untuk menemukan file yang benar.
Anda memutuskan diri dari banyak kontributor potensial jika Anda menempuh rute itu.
Banyak bahasa menawarkan fitur ini sampai tingkat tertentu, tetapi akan sangat rumit untuk memuat ulang kode yang melakukan pemuatan ulang yang panas. Jika SL terpisah dari runtime, ia dapat dimuat ulang.
Ini adalah pertanyaan menarik tetapi ada banyak jawaban bagus yang sudah diberikan, jadi saya tidak akan mencoba yang lengkap.
Namun, dua hal yang menurut saya kurang mendapat perhatian:
Pertama adalah bahwa semuanya bukan potongan super jernih. Ini sedikit spektrum persis karena ada alasan untuk melakukan sesuatu secara berbeda. Sebagai contoh, kompiler sering tahu tentang perpustakaan standar dan fungsinya. Contoh dari contoh: Fungsi "Hello World" C - printf - adalah yang terbaik yang dapat saya pikirkan. Ini adalah fungsi perpustakaan, itu semacam harus, karena sangat tergantung platform. Tapi itu perilaku (implementasi didefinisikan) perlu diketahui oleh kompiler untuk memperingatkan programmer tentang doa yang buruk. Ini tidak terlalu rapi, tetapi dipandang sebagai kompromi yang baik. Kebetulan, ini adalah jawaban nyata untuk sebagian besar pertanyaan "mengapa desain ini": banyak kompromi dan "sepertinya ide yang bagus pada saat itu". Tidak selalu "ini adalah cara yang jelas untuk melakukannya" atau "
Kedua adalah memungkinkan perpustakaan standar untuk tidak menjadi semua standar itu. Ada banyak situasi di mana bahasa diinginkan tetapi perpustakaan standar yang biasanya menyertainya tidak praktis dan diinginkan. Ini paling sering terjadi dengan bahasa pemrograman sistem seperti C, pada platform non-standar. Misalnya, jika Anda memiliki sistem tanpa OS atau penjadwal: Anda tidak akan memiliki threading.
Dengan model perpustakaan standar (dan threading didukung di dalamnya) ini dapat ditangani dengan bersih: kompilernya hampir sama, Anda dapat menggunakan kembali bit perpustakaan yang berlaku, dan apa pun yang tidak dapat Anda hapus. Jika ini dimasukkan ke dalam kompiler, hal-hal mulai menjadi berantakan.
Sebagai contoh:
Anda tidak bisa menjadi kompiler yang patuh.
Bagaimana Anda menunjukkan penyimpangan Anda dari standar. Catatan biasanya ada beberapa bentuk impor / sertakan sintaks Anda dapat gagal yaitu impor ular sanca atau C's yang dengan mudah menunjukkan masalah jika ada sesuatu yang hilang dalam model perpustakaan standar.
Masalah serupa juga berlaku jika Anda ingin mengubah atau memperluas fungsionalitas 'perpustakaan'. Ini jauh lebih umum daripada yang Anda kira. Hanya untuk bertahan dengan threading: windows, linux dan beberapa unit pemrosesan jaringan eksotis semua melakukan threading sangat berbeda. Sementara bit linux / windows mungkin cukup statis dan dapat menggunakan API yang identik, hal-hal NPU akan berubah dengan hari dalam seminggu dan API dengannya. Compiler akan dengan cepat menyimpang ketika orang memutuskan bit mana yang mereka butuhkan untuk mendukung / bisa-lakukan dengan cepat jika tidak ada cara untuk membagi hal semacam ini.