Mengapa tidak menjelaskan parameter fungsi?

28

Untuk membuat pertanyaan ini dapat dijawab, mari kita asumsikan bahwa biaya ambiguitas dalam pikiran seorang programmer jauh lebih mahal daripada beberapa penekanan tombol tambahan.

Mengingat hal itu, mengapa saya membiarkan teman satu tim pergi tanpa membuat anotasi parameter fungsi mereka? Ambil kode berikut sebagai contoh kode yang mungkin jauh lebih kompleks:

let foo x y = x + y

Sekarang, pemeriksaan cepat tooltip akan menunjukkan kepada Anda bahwa F # telah menentukan Anda dimaksudkan untuk x dan y sebagai int. Jika itu yang Anda maksudkan, maka semuanya baik-baik saja. Tapi saya tidak tahu apakah itu yang Anda maksudkan. Bagaimana jika Anda telah membuat kode ini untuk menggabungkan dua string bersama? Atau bagaimana jika saya pikir Anda mungkin bermaksud menambah ganda? Atau bagaimana jika saya tidak ingin harus mengarahkan mouse ke setiap parameter fungsi tunggal untuk menentukan tipenya?

Sekarang ambil ini sebagai contoh:

let foo x y = "result: " + x + y

F # sekarang mengasumsikan Anda mungkin bermaksud untuk menggabungkan string, jadi x dan y didefinisikan sebagai string. Namun, sebagai orang bodoh yang menjaga kode Anda, saya mungkin melihat ini dan bertanya-tanya apakah mungkin Anda bermaksud untuk menambahkan x dan y (ints) bersama-sama dan kemudian menambahkan hasilnya ke string untuk keperluan UI.

Tentu saja untuk contoh sederhana seperti itu orang bisa membiarkannya pergi, tetapi mengapa tidak menegakkan kebijakan anotasi jenis eksplisit?

let foo (x:string) (y:string) = "result: " + x + y

Apa salahnya menjadi tidak ambigu? Tentu, seorang programmer dapat memilih jenis yang salah untuk apa yang mereka coba lakukan, tetapi setidaknya saya tahu mereka bermaksud, bahwa itu bukan hanya kekhilafan.

Ini adalah pertanyaan serius ... Saya masih sangat baru di F # dan sedang mencari jalan untuk perusahaan saya. Standar yang saya adopsi kemungkinan akan menjadi dasar untuk semua F # coding masa depan, tertanam dalam copy-paste tanpa akhir yang saya yakin akan menembus budaya selama bertahun-tahun yang akan datang.

Jadi ... apakah ada sesuatu yang istimewa tentang inferensi tipe F # yang menjadikannya fitur yang berharga untuk dipertahankan, hanya memberi catatan jika diperlukan? Atau apakah pakar F # membiasakan membubuhi keterangan parameter mereka untuk aplikasi non-sepele?

JDB
sumber
Versi beranotasi kurang umum. Itu untuk alasan yang sama Anda lebih suka menggunakan IEnumerable <> dalam tanda tangan dan bukan SortedSet <>.
Patrick
2
@ Patrick - Tidak, tidak, karena F # menyimpulkan tipe string. Saya tidak mengubah tanda tangan fungsi, hanya membuatnya eksplisit.
JDB
1
@ Patrick - Dan bahkan jika itu benar, dapatkah Anda menjelaskan mengapa itu hal yang buruk? Mungkin tanda tangan harus kurang umum, jika itu yang dimaksudkan oleh programmer. Mungkin tanda tangan yang lebih umum sebenarnya menyebabkan masalah, tapi saya tidak yakin seberapa spesifik programmer yang dimaksudkan tanpa banyak penelitian.
JDB
1
Saya pikir itu adil untuk mengatakan bahwa, dalam bahasa-bahasa fungsional, Anda lebih suka generalisasi, dan membuat catatan dalam kasus yang lebih spesifik; misal ketika Anda menginginkan kinerja yang lebih baik, dan dapat menggunakannya dengan mengetikkan petunjuk. Menggunakan fungsi beranotasi di mana-mana akan mengharuskan Anda untuk menulis kelebihan untuk setiap jenis tertentu, yang mungkin tidak dijamin dalam kasus umum.
Robert Harvey

Jawaban:

30

Saya tidak menggunakan F #, tetapi dalam Haskell itu dianggap bentuk yang baik untuk membubuhi keterangan (setidaknya) definisi tingkat atas, dan kadang-kadang definisi lokal, meskipun bahasa memiliki inferensi tipe yang meresap. Ini karena beberapa alasan:

  • Membaca
    Ketika Anda ingin tahu cara menggunakan suatu fungsi, sangat berguna untuk memiliki tipe tanda tangan yang tersedia. Anda cukup membacanya, daripada mencoba menyimpulkannya sendiri atau mengandalkan alat untuk melakukannya untuk Anda.

  • Refactoring
    Ketika Anda ingin mengubah suatu fungsi, memiliki tanda tangan eksplisit memberi Anda beberapa jaminan bahwa transformasi Anda mempertahankan maksud dari kode asli. Dalam bahasa jenis disimpulkan, Anda mungkin menemukan bahwa kode yang sangat polimorfik akan mengetik centang tetapi tidak melakukan apa yang Anda inginkan. Tipe tanda tangan adalah "penghalang" yang mengkonkretkan informasi jenis pada suatu antarmuka.

  • Kinerja
    Di Haskell, jenis fungsi yang disimpulkan mungkin kelebihan beban (melalui typeclasses), yang mungkin menyiratkan pengiriman runtime. Untuk tipe numerik, tipe standarnya adalah integer presisi arbitrer. Jika Anda tidak memerlukan generalisasi penuh fitur-fitur ini, maka Anda dapat meningkatkan kinerja dengan mengkhususkan fungsi ke tipe spesifik yang Anda butuhkan.

Untuk definisi lokal, letvariabel-terikat, dan parameter formal ke lambdas, saya menemukan bahwa jenis tanda tangan biasanya lebih mahal dalam kode daripada nilai yang mereka tambahkan. Jadi dalam ulasan kode, saya sarankan Anda bersikeras pada tanda tangan untuk definisi tingkat atas dan hanya meminta penjelasan yang bijaksana di tempat lain.

Jon Purdy
sumber
3
Ini terdengar seperti respons yang sangat masuk akal dan seimbang.
JDB
1
Saya setuju bahwa memiliki definisi dengan tipe yang didefinisikan secara eksplisit dapat membantu ketika refactoring, tetapi saya menemukan bahwa pengujian unit juga dapat memecahkan masalah ini dan karena saya percaya bahwa unit test harus digunakan dengan pemrograman fungsional untuk memastikan fungsi berfungsi seperti yang dirancang sebelum menggunakan Berfungsi dengan fungsi lain, ini memungkinkan saya untuk mengabaikan tipe-tipe dari definisi dan memiliki keyakinan bahwa jika saya membuat perubahan, itu akan ditangkap. Contoh
Guy Coder
4
Di Haskell itu dianggap benar untuk membubuhi keterangan fungsi Anda, tapi saya percaya F # dan OCaml idiomatik cenderung menghilangkan anotasi kecuali diperlukan untuk menghapus ambiguitas. Itu tidak berarti itu berbahaya (meskipun sintaksis untuk anotasi dalam F # lebih buruk daripada di Haskell).
KChaloux
4
@KChaloux Di OCaml, sudah ada ketik anotasi di antarmuka ( .mli) file (jika Anda menulis satu, yang Anda sangat dianjurkan. Anotasi jenis cenderung dihilangkan dari definisi karena mereka akan mubazir dengan antarmuka.
Gilles ' BEGITU berhenti menjadi jahat
1
@Gilles @KChaloux memang, sama dalam .fsifile F # signature ( ), dengan satu peringatan: F # tidak menggunakan file tanda tangan untuk jenis inferensi, jadi jika sesuatu dalam implementasi ini ambigu, Anda masih harus membubuhi keterangan lagi. books.google.ca/…
Yawar Amin
1

Jon memberikan jawaban yang masuk akal yang tidak akan saya ulangi di sini. Namun saya akan menunjukkan kepada Anda opsi yang mungkin memuaskan kebutuhan Anda dan dalam proses ini Anda akan melihat jenis jawaban yang berbeda selain dari ya / tidak.

Akhir-akhir ini saya telah bekerja dengan parsing menggunakan parser combinator. Jika Anda tahu parsing maka Anda tahu Anda biasanya menggunakan lexer di fase pertama dan parser di fase kedua. Lexer mengubah teks menjadi token dan parser mengubah token menjadi AST. Sekarang dengan F # sebagai bahasa fungsional dan kombinator yang dirancang untuk digabungkan, kombinator parser dirancang untuk memanfaatkan fungsi yang sama di lexer dan parser namun jika Anda menetapkan tipe untuk fungsi kombinator parser Anda hanya dapat menggunakannya untuk lex atau mengurai dan tidak keduanya.

Sebagai contoh:

/// Parser that requires a specific item.

// a (tok : 'a) : ('a list -> 'a * 'a list)                     // generic
// a (tok : string) : (string list -> string * string list)     // string
// a (tok : token)  : (token list  -> token  * token list)      // token

atau

/// Parses iterated left-associated binary operator.


// leftbin (prs : 'a -> 'b * 'c) (sep : 'c -> 'd * 'a) (cons : 'd -> 'b -> 'b -> 'b) (err : string) : ('a -> 'b * 'c)                                                                                    // generic
// leftbin (prs : string list -> string * string list) (sep : string list -> string * string list) (cons : string -> string -> string -> string) (err : string) : (string list -> string * string list)  // string
// leftbin (prs : token list  -> token  * token list)  (sep : token list  -> token  * token list)  (cons : token  -> token  -> token  -> token)  (err : string) : (token list  -> token  * token list)   // token

Karena kode tersebut merupakan hak cipta, saya tidak akan memasukkannya di sini tetapi tersedia di Github . Jangan menyalinnya di sini.

Agar fungsi berfungsi, mereka harus dibiarkan dengan parameter umum, tetapi saya menyertakan komentar yang menunjukkan tipe yang disimpulkan tergantung pada fungsi yang digunakan. Ini membuatnya mudah untuk memahami fungsi pemeliharaan sementara meninggalkan fungsi generik untuk digunakan.

Guy Coder
sumber
1
Mengapa Anda tidak menulis versi generik ke dalam fungsi? Bukankah itu akan berhasil?
svick
@vick Saya tidak mengerti pertanyaan Anda. Apakah maksud Anda bahwa saya mengabaikan tipe dari definisi fungsi, tetapi dapat menambahkan tipe generik ke definisi fungsi karena tipe generik tidak akan mengubah arti fungsi? Jika demikian alasannya lebih merupakan preferensi pribadi. Ketika saya mulai dengan F #, saya menambahkannya. Ketika saya mulai bekerja dengan pengguna F # yang lebih maju, mereka lebih suka mereka dibiarkan sebagai komentar karena lebih mudah untuk memodifikasi kode tanpa tanda tangan di sana. ...
Guy Coder
Dalam jangka panjang cara saya menunjukkannya sebagai komentar bekerja untuk semua orang begitu kode itu berfungsi. Ketika saya mulai menulis fungsi saya memasukkan jenis. Setelah fungsi berfungsi, saya memindahkan tanda tangan jenis ke komentar dan menghapus sebanyak mungkin jenis. Beberapa kali dengan F # Anda harus meninggalkan dalam tipe untuk membantu menyimpulkan. Untuk sementara saya sedang bereksperimen dengan membuat komentar dengan let dan = sehingga Anda dapat menghapus komentar pada baris untuk pengujian, kemudian berkomentar lagi ketika selesai, tetapi mereka tampak konyol dan menambahkan tanda dan = tidak terlalu sulit.
Guy Coder
Jika komentar ini menjawab apa yang Anda minta, beri tahu saya agar saya dapat memindahkannya ke dalam jawaban.
Guy Coder
2
Jadi, ketika Anda memodifikasi kode, Anda tidak mengubah komentar, mengarah ke dokumentasi yang sudah ketinggalan zaman? Itu tidak terdengar seperti keuntungan bagi saya. Dan saya tidak benar-benar melihat bagaimana komentar berantakan lebih baik daripada kode berantakan. Bagi saya, sepertinya pendekatan ini menggabungkan kelemahan dari dua opsi.
svick
-8

Jumlah bug berbanding lurus dengan jumlah karakter dalam suatu program!

Ini biasanya dinyatakan sebagai jumlah bug yang sebanding dengan baris kode. Tetapi memiliki lebih sedikit baris dengan jumlah kode yang sama memberikan jumlah bug yang sama.

Jadi, meskipun bagus untuk eksplisit, parameter tambahan apa pun yang Anda ketikkan dapat menyebabkan bug.

James Anderson
sumber
5
“Jumlah bug berbanding lurus dengan jumlah karakter dalam suatu program!” Jadi kita semua harus beralih ke APL ? Menjadi terlalu singkat adalah masalah, sama seperti terlalu bertele-tele.
svick
5
" Untuk membuat pertanyaan ini dapat dijawab, mari kita asumsikan bahwa biaya ambiguitas dalam pikiran seorang programmer jauh lebih mahal daripada beberapa penekanan tombol tambahan. "
JDB