Julia: memungkinkan overloading sintaks akses bidang ab

Dibuat pada 10 Jan 2013  ·  249Komentar  ·  Sumber: JuliaLang/julia

decision parser

Komentar yang paling membantu

Berikut implementasi 3 baris yang menyenangkan dari ini:

diff --git a/base/boot.jl b/base/boot.jl
index cd3ae8b..a58bb7e 100644
--- a/base/boot.jl
+++ b/base/boot.jl
@@ -266,6 +266,9 @@ Void() = nothing

 (::Type{Tuple{}})() = ()

+struct Field{name} end
+(::Field{f})(x) where {f} = getfield(x, f)
+
 struct VecElement{T}
     value::T
     VecElement{T}(value::T) where {T} = new(value) # disable converting constructor in Core
diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm
index b4cb4b5..59c9762 100644
--- a/src/julia-syntax.scm
+++ b/src/julia-syntax.scm
@@ -1685,7 +1685,7 @@
     (if (and (pair? e) (eq? (car e) '|.|))
         (let ((f (cadr e)) (x (caddr e)))
           (if (or (eq? (car x) 'quote) (eq? (car x) 'inert) (eq? (car x) '$))
-              `(call (core getfield) ,f ,x)
+              `(call (new (call (core apply_type) (core Field) ,x)) ,f)
               (make-fuse f (cdr x))))
         (if (and (pair? e) (eq? (car e) 'call) (dotop? (cadr e)))
             (make-fuse (undotop (cadr e)) (cddr e))

Pemikiran saya adalah bahwa a.b seharusnya benar-benar memanggil fungsi proyeksi Field{:b}() daripada getfield , sehingga Anda mendapatkan fungsi seperti x->x.a secara gratis. Itu juga memungkinkan getfield selalu berarti akses bidang tingkat rendah.

Implementasi di atas benar-benar berfungsi tetapi cukup sulit pada kompiler (sysimg + 5%, yang sebenarnya merupakan kejutan yang menyenangkan). Jadi ini akan memerlukan beberapa heuristik spesialisasi dan beberapa pengoptimalan awal perlu diperbarui, tetapi semoga ini akan dapat dilakukan.

Semua 249 komentar

Kemampuan menggunakan titik sebagai gula sintaksis untuk metode mutator / pengakses akan bagus untuk banyak hal. Saya selalu menghargai ini dalam bahasa yang menyediakannya, sehingga Anda dapat mengubah bidang struktur menjadi abstraksi yang lebih rumit tanpa merusak API.

+1

Saya memiliki cara yang sangat bagus untuk menerapkan ini.

Tertarik membicarakannya? Saya tahu bahwa Tom Short sangat tertarik memiliki ini untuk DataFrames, meskipun saya menjadi semakin skeptis tentang kebijaksanaan menggunakan fitur ini.

Ini akan membuat memanggil kode Python (melalui PyCall) secara signifikan lebih baik, karena saat ini saya dipaksa untuk melakukan a[:b] daripada a.b .

@JeffBezanson , apakah ada peluang memiliki ini untuk 0,3? Akan sangat bagus untuk antar-bahasa, baik untuk PyCall dan untuk JavaCall (cc @aviks).

@JeffBezanson if _not_, adakah kemungkinan Anda dapat memberikan arahan tentang bagaimana Anda ingin ini diterapkan? ( I have an absolutely awesome way to implement this. )

Menurut pengalaman saya, tidak ada cara yang lebih cepat atau lebih pasti untuk membuat Jeff mengimplementasikan sesuatu selain mengimplementasikan versi yang tidak dia sukai ;-)

Ide dasarnya adalah Anda menerapkan

getfield(x::MyType, ::Field{:name}) = ...

sehingga Anda dapat membebani secara berlebihan per bidang. Itu memungkinkan akses ke bidang "nyata" untuk tetap bekerja secara transparan. Dengan fallback yang sesuai getfield(::MyType, ::Symbol) juga berfungsi.

Masalah terbesar adalah bahwa modul memiliki perilaku khusus sehubungan dengan . . Secara teori, ini hanya akan menjadi metode lain dari getfield , tetapi masalahnya adalah kita perlu menyelesaikan referensi modul _earlier_ karena pada dasarnya mereka berperilaku seperti variabel global. Saya pikir kita harus menjaga kasus khusus ini dalam perilaku . . Ada juga sedikit masalah efisiensi kompilator, karena menganalisis (# jenis) * (# bidang) definisi fungsi tambahan. Tapi untuk itu kita lihat saja apa yang terjadi.

@JeffBezanson Apakah Anda juga merujuk ke perilaku const dalam modul? Akan berguna untuk memiliki tipe pengguna yang meniru modul dan dapat memberi tahu compiler ketika hasil pencarian bidang dinamis adalah konstan. (pendekatan lain adalah memulai dengan modul aktual dan dapat "menjebak" jl_get_global gagal dan memasukkan binding baru sesuai permintaan)

Saya akan menemukan bahwa akan sangat berguna jika dikombinasikan dengan # 5395. Kemudian seseorang dapat mencegat panggilan ke fungsi atau metode yang tidak ditentukan MyMod.newfunction(new signature) dan menghasilkan binding ke API (mungkin besar) sesuai permintaan. Ini kemudian akan di-cache seperti biasa const binding, saya kira.

Izinkan saya, seorang pemula Julia yang sederhana, menyajikan sedikit perhatian: Saya pikir kemungkinan membebani operator titik secara berlebihan mungkin menyiratkan bahwa "kemurnian" akses bidang entah bagaimana hilang.

Pengguna umumnya akan kehilangan pengetahuan jika melakukan ab hanyalah akses ke referensi / nilai atau jika ada mesin fungsi besar yang dipanggil di belakang. Saya tidak yakin bagaimana itu bisa menjadi buruk, itu hanya perasaan ...

Di sisi lain, saya melihat bahwa memang ini adalah keinginan besar untuk gula sintaks untuk banyak kasus (PyCall, Dataframes ...), yang sangat bisa dimengerti.
Mungkin sudah waktunya untuk .. # 2614?

Saya mendukung melakukan ini.

Tetapi kemurnian memang memiliki sesuatu untuk dikatakan, bahkan jika seseorang dapat menggunakan names(Foo) untuk mencari tahu apa komponen sebenarnya dari Foo .

Argumen kemurnian terkait erat dengan perhatian praktis utama yang saya miliki, yaitu bagaimana seseorang menangani konflik nama ketika bidang tipe mengganggu nama yang mungkin ingin Anda gunakan. Di DataFrames, saya pikir kami akan menyelesaikan ini dengan melarang penggunaan columns dan colindex sebagai nama kolom, tetapi ingin tahu apa rencana orang-orang untuk ini.

Saya kira getfield(x::MyType, ::Field{:foo}) = ... harus dilarang ketika MyType memiliki bidang foo , jika tidak akses ke bidang nyata akan hilang (atau cara untuk memaksa akses ke bidang harus tersedia).
Tapi kemudian getfield hanya bisa didefinisikan untuk tipe konkret, karena yang abstrak tidak tahu apa-apa tentang field.

(Sementara itu, saya menemukan ini tentang C ++ .)

Itu bukan masalah besar. Kami dapat memberikan sesuatu seperti Core.getfield(x, :f) untuk memaksa akses ke bidang yang sebenarnya.

Oke, mungkin saya sudah terjual. Tapi kemudian mendefinisikan jalan pintas ke Core.getfield(x, :f) (mis., x..f ) akan bagus, jika tidak kode internal tipe yang membebani . untuk semua simbol (kerangka data, mungkin kamus) harus dipenuhi dengan Core.getfield s.

Saya tidak khawatir tentang aspek kemurnian - sampai kita memiliki ini, satu-satunya kode
yang seharusnya menggunakan akses bidang sama sekali adalah kode yang dimiliki oleh
implementasi dari tipe tertentu. Jika akses bidang adalah bagian dari api, Anda
harus mendokumentasikannya, seperti api lainnya. Saya setuju bahwa ini mungkin berguna dengan
beberapa sintaks pintas untuk core.getfield, saat menulisnya
implementasi.

Itu sudah ditunjukkan di # 4935, tetapi mari kita tarik ke sini: kelebihan titik dapat sedikit tumpang tindih dengan pengiriman ganda Julian klasik jika tidak digunakan dengan benar, karena kita dapat mulai melakukannya

getfield (x :: MyType, :: Field {: size}) = .........
untuk i = 1: y.ukuran .....

dari pada

ukuran (x :: MyType) = ..........
untuk i = 1: ukuran (y) ....

Sementara titik akan bagus untuk mengakses item dalam koleksi (Dataframe, Dicts, PyObjects), entah bagaimana itu dapat mengubah cara properti objek (bukan bidang) diakses.

Menurut saya satu hal yang perlu dipertimbangkan adalah jika Anda dapat membebani bidang pengaksesan secara berlebihan, Anda juga harus dapat membebani _setting_ bidang secara berlebihan. Jika tidak, ini akan menjadi tidak konsisten dan membuat frustrasi. Apakah Anda tidak masalah untuk melangkah sejauh itu?

@nalimilan , seseorang benar-benar membutuhkan setfield! selain getfield . (Mirip dengan setindex! vs. getindex untuk [] ). Saya tidak berpikir ini kontroversial.

Setuju dengan @stevengj : DataFrames pasti akan mengimplementasikan setfield! untuk kolom.

Saya mendukung ini.

Pengalaman dengan bahasa lain (misalnya C # dan Python) memang menunjukkan bahwa sintaks titik memang memiliki banyak nilai praktis. Cara penerapannya melalui metode khusus sebagian besar menangani masalah regresi kinerja.

Namun, penting untuk memastikan bahwa _inlineability_ metode tidak akan terpengaruh secara serius oleh perubahan ini. Misalnya, sesuatu seperti f(x) = g(x.a) + h(x.b) tidak akan tiba-tiba menjadi tidak dapat disejajarkan setelah ini.

Jika kita memutuskan untuk mewujudkannya, akan berguna juga untuk menyediakan makro untuk membuat definisi properti lebih mudah, yang mungkin terlihat seperti:

# let A be a type, and foo a property name
<strong i="10">@property</strong> (a::A).foo = begin
    # compute the return the property value
end

# for simpler cases, this can be simplified to
<strong i="11">@property</strong> (a::A).foo2 = (2 * a.foo)

# set property 
<strong i="12">@setproperty</strong> (a::A).foo v::V begin
    # codes for setting value v to a property a.foo
end

Di balik layar, semua ini dapat diterjemahkan ke definisi metode.

Saya tidak yakin bahwa <strong i="5">@property</strong> (a::A).foo = jauh lebih mudah daripada getproperty(a::A, ::Field{foo}) = ...

Bagaimanapun, gula sintaksis yang lebih baik adalah sesuatu yang dapat ditambahkan setelah fungsi dasar mendarat.

Mengenai sebaris, selama akses lapangan sebaris sebelum keputusan dibuat apakah akan sebaris fungsi sekitarnya, maka saya tidak melihat mengapa hal itu akan terpengaruh. Tapi mungkin ini bukan urutan inlining yang saat ini dilakukan?

getproperty(a::A, ::Field{:foo}) = menyerang saya karena terlalu banyak titik dua :-) Saya setuju bahwa ini adalah hal kecil, dan mungkin kita tidak perlu khawatir tentang itu sekarang.

Perhatian saya adalah apakah ini akan menyebabkan kemunduran kinerja. Saya tidak begitu jelas tentang mekanisme pembuatan kode internal. @JeffBezanson mungkin mengatakan sesuatu tentang ini?

Akses lapangan adalah level yang sangat rendah, jadi saya tidak akan melakukannya tanpa memastikan performa dipertahankan.

Bagaimanapun, saya tidak yakin membebani bidang adalah ide yang bagus. Dengan proposal ini, akan selalu ada dua cara untuk menyetel properti: x.property = value dan property!(x, value) . Jika kelebihan bidang diterapkan, kami memerlukan panduan gaya yang sangat kuat untuk menghindari berakhirnya kekacauan total di mana Anda tidak pernah tahu sebelumnya solusi mana yang telah dipilih penulis untuk jenis tertentu.

Dan kemudian akan muncul pertanyaan apakah bidang bersifat publik atau pribadi. Tidak mengizinkan kelebihan beban bidang akan membuat sistem jenis lebih jelas: bidang akan selalu bersifat pribadi. Metode akan menjadi publik, dan tipe akan dapat mendeklarasikan mereka mengimplementasikan antarmuka / protokol / sifat, yaitu bahwa mereka menyediakan satu set metode tertentu. Ini bertentangan @stevengj 's https://github.com/JuliaLang/julia/issues/1974#issuecomment -12.083.268 tentang overloading bidang dengan metode untuk menghindari melanggar API: hanya menawarkan metode sebagai bagian dari API, dan tidak pernah bidang .

Satu-satunya tempat di mana saya akan menyesali field overloading adalah untuk DataFrames , karena df[:a] tidak sebaik df.a . Tapi kedengarannya tidak membutuhkan perubahan besar seperti itu. Kasus penggunaan lainnya tampaknya adalah PyCall, yang mungkin menunjukkan bahwa kelebihan beban bidang harus diizinkan, tetapi hanya untuk kasus penggunaan non-Julian yang sangat spesifik. Tetapi bagaimana cara mencegah orang menyalahgunakan fitur setelah tersedia? Sembunyikan di modul khusus?

@nalimilan , saya akan mengatakan bahwa preferensi harus menggunakan sintaks x.property sebanyak mungkin. Masalahnya adalah orang _ benar-benar_ menyukai sintaks ini - sangat menyenangkan. Mengambil sintaks yang bagus dan secara khusus mengatakan bahwa itu hanya boleh digunakan untuk akses internal ke objek tampaknya benar-benar sesat - "hah, sintaks yang bagus ini ada; jangan gunakan!" Tampaknya jauh lebih masuk akal untuk membuat sintaks untuk mengakses hal-hal pribadi menjadi kurang nyaman dan cantik daripada memaksa API untuk menggunakan sintaks yang lebih jelek. Mungkin ini adalah kasus penggunaan yang baik untuk operator .. : operator akses bidang privat _real_.

Saya sebenarnya berpikir bahwa perubahan ini dapat membuat segalanya lebih jelas dan lebih konsisten daripada kurang begitu. Pertimbangkan rentang - saat ini ada semacam campuran mengerikan dari step(r) versus r.step gaya di luar sana sekarang. Terutama sejak saya memperkenalkan FloatRange ini berbahaya karena hanya kode yang menggunakan step(r) yang benar. Alasan pencampuran adalah bahwa beberapa properti rentang disimpan dan beberapa dihitung - tetapi properti tersebut telah berubah seiring waktu dan pada kenyataannya berbeda untuk berbagai jenis rentang. Akan lebih baik gaya jika setiap akses adalah gaya step(r) kecuali definisi step(r) itu sendiri. Tetapi ada beberapa hambatan psikologis yang curam untuk melawannya. Jika kita membuat r.step panggilan metode yang defaultnya menjadi r..step , maka orang-orang dapat melakukan apa yang biasanya mereka lakukan.

Untuk bermain sebagai pendukung iblis (dengan saya sendiri), haruskah kita menulis r.length atau length(r) ? Ketidakkonsistenan antara fungsi dan metode umum adalah masalah yang dialami Python, sementara Ruby berkomitmen penuh pada gaya r.length .

+1 untuk .. sebagai Core.getfield !

@StefanKarpinski Masuk akal, tetapi kemudian Anda perlu menambahkan sintaks untuk bidang pribadi, dan antarmuka harus menentukan metode dan bidang publik. Dan memang Anda membutuhkan panduan gaya untuk memastikan konsistensi; kasus length adalah kasus yang sulit, tetapi ada juga misalnya size , yang sangat mirip tetapi membutuhkan indeks dimensi. Keputusan ini membuka sekaleng cacing ...

Dalam hal ini, saya juga mendukung .. untuk mengakses bidang aktual, dan . untuk mengakses bidang, baik itu metode atau nilai nyata.

Untuk bermain sebagai pengacara iblis (dengan saya sendiri), haruskah kita menulis r.length atau length(r) ? Ketidakkonsistenan antara fungsi dan metode umum adalah masalah yang dialami Python, sementara Ruby berkomitmen penuh pada gaya r.length .

Faktor kunci yang mungkin mengganggu untuk masalah ini adalah apakah Anda ingin dapat menggunakan sesuatu sebagai fungsi urutan yang lebih tinggi atau tidak. Yaitu f di f(x) adalah sesuatu yang Anda dapat map lebih dari koleksi, sedangkan f di x.f tidak (singkatnya menulis x -> x.f ) - situasi yang sama untuk semua metode dalam bahasa pengiriman tunggal.

Mengapa berhenti di akses lapangan? Bagaimana dengan memiliki x.foo(args...) setara dengan getfield(x::MyType, ::Field{:foo}, args...) = ... ? Kemudian kita bisa memiliki x.size(1) untuk ukuran sepanjang dimensi pertama. (tidak yakin apakah saya menyukai saran saya, tapi mungkin sesuatu untuk dipertimbangkan. Atau mungkin tidak, karena orang hanya akan menulis kode yang mirip OO?)

Itu dimungkinkan dengan fungsi ini. Yang mana salah satu hal yang membuatku jeda. Saya tidak punya masalah dengan kode gaya oo seperti itu - seperti yang saya katakan, ini cukup menyenangkan dan orang-orang sangat menyukainya - tetapi itu memperkenalkan cukup banyak pilihan dalam cara menulis hal-hal yang kami _ benar-benar_ membutuhkan kebijakan yang kuat tentang apa yang harus Anda lakukan karena Anda akan sangat bebas dengan apa yang dapat Anda lakukan.

Ketika saya mulai belajar Julia, sintaks tanpa titik banyak membantu saya secara mental melepaskan gaya pemrograman-OO. Jadi karena alasan itu saja, saya pikir saran saya buruk.

Juga, untuk overloading sederhana (yaitu hanya a.b sans (args...) ), saya setuju dengan komentar @nalimilan di atas. Dalam edisi # 4935, konsensus tampaknya adalah bahwa bidang seharusnya tidak menjadi bagian dari API tetapi hanya metode; akibatnya sepertinya masalah itu akan ditutup. Memiliki sintaks . -overloading akan membuatnya kurang jelas bahwa bidang-normal seharusnya tidak menjadi bagian dari API dan mungkin akan mendorong untuk menjadikan bidang bagian dari API.

Tapi ya, sintaks . itu praktis ...

Bagaimana dengan: single . harus _only_ menjadi gula sintaksis untuk getfield(x::MyType, ::Field{:name}) = ... dan akses field adalah _only_ sampai .. (yaitu . sekarang).

Ini akan memungkinkan untuk membuat perbedaan yang jelas:

  • . adalah untuk API publik untuk mengakses hal-hal seperti nilai dari instance-tipe
  • .. adalah untuk akses bidang dan umumnya tidak boleh digunakan dalam API publik

Tentu saja, ini akan menjadi perubahan besar.

Pada dasarnya itulah yang saya usulkan, kecuali bahwa . default menjadi .. jadi tidak melanggar.

Maaf, saya harus membaca ulang!

Tapi saya pikir . tidak default ke .. mungkin sebenarnya bagus (selain itu melanggar), karena akan memaksa keputusan pengembang tentang apa itu API publik dan apa yang tidak. Selain itu, jika pengguna menggunakan .. daripada yang diharapkan kodenya bisa rusak, sedangkan . seharusnya tidak.

Itu poin yang bagus. Kita bisa pergi ke rute itu dengan memiliki a.b default ke a..b dengan peringatan penghentian.

Dari perspektif gaya, saya pikir saya lebih suka melihat

a = [1:10]
a.length()
a.size()

dari

a.length
a.size

Saya pikir ini membantu melestarikan gagasan bahwa fungsi sedang dipanggil alih-alih hanya properti yang diambil yang entah bagaimana disimpan dalam tipe (kembali ke masalah "kemurnian" di atas). Saya ingin tahu apakah ada cara untuk membantu memastikan gaya semacam ini sehingga segala sesuatunya tidak menjadi berantakan seperti di beberapa bahasa lain.

Saya tidak terlalu suka

a.length()

sejak saat itu saya tidak tahu apakah ada bidang fungsi di tipe aslinya. Jika . tidak pernah mengakses bidang, itu jelas bukan masalah. Kalau tidak, sepertinya membingungkan saya.

A priori, saya merasa bahwa kita seharusnya tidak melakukan a.length() atau a.length . Tapi pertanyaannya kenapa? Apa yang membuat r.step berbeda dari r.length ? Apakah ini berbeda? Jika tidak berbeda, haruskah kita menggunakan step(r) dan length(r) atau r.step dan r.length ?

Dengan semantik yang disarankan oleh Stefan dan penambahan oleh saya akan menjadi jelas bahwa . selalu merupakan pemanggilan fungsi (seperti + juga), sedangkan .. selalu merupakan bidang mengakses.

Tentang masalah apakah a.length , dll adalah ide yang bagus: bagaimana dengan . akses hanya boleh digunakan untuk mengakses data aktual dalam tipe, kurang lebih seperti yang akan digunakan entri dari sebuah dict . Sedangkan kita tetap menggunakan fungsi untuk properti non-data seperti, size , length , step dll. Karena beberapa dari mereka akan membutuhkan parameter tambahan dan, menurut saya, a.size(1) jenis sintaksnya buruk.

Inilah pendapat saya tentang topik ini:

  • Sintaks titik sebaiknya hanya digunakan untuk atribut dari suatu tipe / kelas. Harap diingat bahwa ini bukan hanya tentang pengambil tetapi juga penyetel dan sesuatu seperti a.property() = ... terasa sangat salah.
  • Sementara saya menyukai situasi saat ini di mana fungsi mendefinisikan API publik dan bidang bersifat pribadi, saya berbagi pendapat Stefans bahwa sintaks titik terlalu bagus untuk dilarang untuk API publik. Tapi tolong biarkan membatasi ini untuk atribut sederhana. a.length adalah contoh yang baik, a.size(1) bukan karena membutuhkan argumen tambahan.
  • Tolong biarkan . default ke .. . Julia tidak dikenal sebagai bahasa boilerplate. Tetap seperti itu

Tolong biarkan . default ke .. . Julia tidak dikenal sebagai bahasa boilerplate. Tetap seperti itu

Saya cenderung setuju dengan ini. Sintaks untuk menyetel bahkan properti sintetis hanya akan menjadi a.property = b , bukan a.property() = b .

Tentu, saya hanya ingin menjelaskan mengapa a.property() sebagai sintaks IMHO tidak bagus

Atau lebih jelasnya: Hal penting tentang sintaks titik bukanlah bahwa seseorang dapat mengaitkan fungsi dengan tipe / kelas tetapi kemampuannya untuk menulis getter / setter dengan cara yang bagus. Dan getter / setter penting untuk enkapsulasi data (menjaga antarmuka tetap stabil tetapi mengubah implementasinya)

Perubahan ini akan sangat bagus dari perspektif perancang API tetapi saya setuju bahwa itu harus disertai dengan semacam panduan gaya untuk membatasi ketidakkonsistenan di masa depan.

Ini akan mengaktifkan Ruby seperti dsl ...

amt = 1.dollar + 2.dollars + 3.dollars.20.cents 

Tapi bersiaplah untuk java seperti kegilaan:

object.propert1.property2.property3 ....

Hanya beberapa pemikiran:

  • Saya paling menginginkan sintaks . untuk Diktik dengan Simbol sebagai kuncinya. Hanya lebih baik menggunakan d.key lalu d[:key] . Tapi pada akhirnya itu tidak penting.
  • Saya pikir a->property dibaca lebih baik daripada a..property . Tapi sekali lagi itu bukan masalah besar dan saya tidak tahu apakah itu akan bekerja dengan sintaks julia.

@Bobortmann Saya tidak setuju. Kamus adalah objek kontainer, API untuk objek kontainer adalah obj [indeks] atau obj [kunci]. Saat ini karena kami tidak memiliki properti di Julia, API penampung kelebihan beban untuk menyediakan fungsionalitas ini di pustaka seperti PyCall dan di OpenCL. Perubahan ini membantu memperkuat perbedaan dari API penampung karena tidak akan dibebani untuk menyediakan fungsi tambahan.

Menggunakan a->property untuk bidang pribadi akan menjadi cara yang baik untuk menjauhkan peretas C dari Julia ;-)

Saya menyukai sintaks .. .

Sintaks a->property sudah digunakan - itu adalah fungsi anonim. Namun, operator a..b telah diperebutkan untuk sementara waktu. Ada beberapa kasus di mana Anda menginginkan sesuatu yang seperti dikt tetapi memiliki banyak bidang opsional. Menggunakan sintaks pengambil / penyetel untuk itu akan lebih baik daripada sintaks pengindeks dict.

"Sintaks properti a-> sudah digunakan - itu adalah fungsi anonim."

Ya tentu saja. Sepertinya tidak ada spasi di sekitar -> .

Sebagai pedoman gaya, bagaimana jika merekomendasikan properti (x) digunakan untuk properti hanya-baca dan properti x.property digunakan untuk properti baca / tulis?

Untuk properti yang dapat ditulis, x.foo = bar benar-benar jauh lebih bagus daripada set_foo! (X, bar).

Memiliki foo(x) untuk membaca dan x.foo untuk menulis cukup membingungkan. Sebenarnya, inilah yang membuat properti begitu menarik. Memiliki sintaks yang sama untuk akses baca dan tulis, yaitu sintaks paling sederhana yang bisa didapatkan (untuk pengambil dan penyetel)

Mengenai gaya, ada pertanyaan besar apakah kita ingin memiliki x.length dan length(x) jika fitur ini diterapkan atau apakah formulir yang akan datang harus dihentikan dan dihapus.

Pendapat saya adalah bahwa kita hanya memiliki satu cara untuk melakukannya dan hanya menggunakan x.length di masa depan. Dan mengenai gaya menurut saya cukup sederhana. Segala sesuatu yang merupakan properti sederhana dari suatu tipe harus diimplementasikan menggunakan sintaks bidang. Segala sesuatu yang lain dengan fungsi. Saya telah sering menggunakan properti di C # dan jarang menemukan kasus di mana saya tidak yakin apakah sesuatu harus menjadi properti atau tidak.

Saya menentang mengubah sekumpulan fungsi 1-argumen yang dipilih secara acak menjadi x.f sintaks. Saya pikir @ mauro3 membuat poin yang baik bahwa melakukan ini mengaburkan sifat bahasa.

a.b adalah, setidaknya secara visual, semacam konstruksi pelingkupan. b tidak perlu menjadi pengenal yang terlihat secara global. Ini adalah perbedaan penting. Misalnya, pemfaktoran matriks dengan bagian atas memiliki properti .U , tetapi ini sebenarnya bukan hal yang umum --- kami tidak menginginkan fungsi global U . Tentu saja ini agak subjektif, terutama karena Anda dapat dengan mudah menentukan U(x) = x.U . Tapi length adalah hal yang berbeda. Akan lebih berguna jika menjadi kelas pertama (mis. map(length, lst) ).

Berikut adalah pedoman yang saya sarankan. Notasi foo.bar sesuai jika:

  1. foo sebenarnya memiliki bidang bernama bar . Contoh: (1:10).start .
  2. foo adalah turunan dari grup tipe terkait, beberapa di antaranya sebenarnya memiliki bidang bernama .bar ; bahkan jika foo sebenarnya tidak memiliki bidang bar , nilai bidang tersebut tersirat oleh tipenya. Contoh: (1:10).step , (0.1:0.1:0.3).step .
  3. Meskipun foo tidak secara eksplisit menyimpan bar , ia menyimpan informasi yang setara dalam bentuk yang lebih ringkas atau efisien yang kurang nyaman digunakan. Contoh: lufact(rand(5,5)).U .
  4. Anda meniru API dari yang lain seperti Python atau Java.

Mungkin masuk akal jika properti bar dapat dialihkan dalam kasus 1 dan 3 tetapi tidak 2. Dalam kasus 2, karena Anda tidak dapat mengubah tipe nilai, Anda tidak dapat mengubah properti bar yang tersirat oleh tipe itu. Dalam kasus seperti itu, Anda mungkin ingin melarang mutasi properti bar tipe terkait lainnya, baik dengan membuatnya tidak dapat diubah atau dengan secara eksplisit membuat kesalahan foo.bar = baz .

@tknopp , saya tidak menyarankan menggunakan x.foo untuk menulis dan foo(x) untuk membaca. Saran saya adalah bahwa _jika sebuah properti dapat dibaca dan ditulis, maka mungkin Anda ingin _both_ membaca dan menulisnya dengan x.foo .

@StefanKarpinski : Tapi bukankah length kasus 3. dimana ukurannya adalah apa yang biasanya disimpan dan length adalah produk dari ukuran?

Saya melihat poin Jeff bahwa perubahan ini akan membuat fungsi-fungsi ini bukan lagi kelas satu.

@stevengj :

@tknopp - panjangnya diturunkan dari ukuran, tetapi tidak setara dengan ukurannya. Jika Anda mengetahui ukurannya, Anda dapat menghitung panjangnya tetapi tidak sebaliknya. Tentu saja, ini adalah garis yang agak kabur. Alasan utama mengapa hal ini dapat diterima untuk lufact adalah karena kami belum menemukan API yang lebih baik dari itu. Pendekatan lain adalah dengan mendefinisikan upper dan lower fungsi umum yang memberikan bagian segitiga atas dan segitiga bawah dari matriks umum. Namun, pendekatan ini tidak menggeneralisasi faktorisasi QR, misalnya.

Dikatakan bahwa hanya ada beberapa kasus yang _really_ tampaknya meminta sintaks ini: pycall, faktorisasi, dan mungkin kerangka data.
Saya cukup khawatir berakhir dengan tumpukan acak f(x) vs. x.f ; itu akan membuat sistem lebih sulit untuk dipelajari.

Bukankah poin 1 dari daftar @StefanKarpinski berarti bahwa setiap field dari suatu tipe secara otomatis menjadi milik API publik?

Saat ini saya dapat mengetahui apa itu API publik dari sebuah modul: semua fungsi dan jenis yang diekspor (tetapi bukan bidangnya). Setelah perubahan ini, tidak mungkin untuk mengetahui bidang mana yang seharusnya dimiliki oleh API publik dan mana yang tidak. Kita bisa mulai menamai bidang pribadi a._foo atau lebih, seperti di python, tapi itu sepertinya tidak terlalu bagus.

Secara pribadi saya pikir case DataFrames sedikit berlebihan. Jika kita melakukan ini, saya akan menambahkan fungsionalitas ke DataFrames, tetapi saya menemukan hilangnya konsistensi jauh lebih mengganggu daripada menyimpan beberapa karakter.

Saya juga tidak akan membuat keputusan bergantung pada DataFrames, PyCall (dan Gtk juga menginginkannya). Entah kita menginginkannya karena menurut kita bidang harus menjadi bagian dari antarmuka publik (karena "terlihat bagus") atau kita tidak menginginkannya.

... pycall ...

dan JavaCall

Karena kasus penggunaan utama untuk ini tampaknya adalah interaksi dengan sistem non-Julia, bagaimana dengan menggunakan operator .. diusulkan alih-alih membebani . berlebihan?

Saya ingin tahu apakah solusi yang lebih sederhana di sini adalah tip topi yang lebih umum untuk OO:

#we already do
A[b] => getindex(A,b)
#we could have
A.b(args...) => b(A, args...)
# while
A..b => getfield(A,::Field{:b})
# with default
getfield(A, ::Field{:b}) = getfield(A, :b)

Sepertinya ini akan memungkinkan JavaCall / PyCall untuk melakukan definisi metode "dalam" kelas, sementara juga memungkinkan gaya umum jika orang ingin memiliki beberapa jenis kode OO, meskipun sangat transparan A.b() hanyalah penulisan ulang. Saya pikir ini akan sangat wajar bagi orang-orang yang berasal dari OO.
Juga memiliki getfield dengan A..b untuk memungkinkan overloading di sana, meskipun overloading di sini sangat tidak disarankan dan hanya digunakan untuk field-like / properties (Saya menduga itu tidak akan digunakan secara luas karena sedikit kelebihan beban getfield(A, ::Field{:field}) .

@ mauro3 :

Bukankah poin 1 dari daftar @StefanKarpinski berarti bahwa setiap field dari suatu tipe secara otomatis menjadi milik API publik?

Itu tadi daftar kapan boleh menggunakan notasi foo.bar , bukan saat diperlukan. Anda dapat menonaktifkan notasi foo.bar untuk bidang "pribadi", yang kemudian hanya dapat diakses melalui foo..bar .

@ Karbarcca : Saya tidak terlalu jelas tentang apa yang Anda usulkan di sini.

fwiw, saya penggemar mengambil pendekatan setuju-dewasa-dengan-konvensi dan menghasilkan . sepenuhnya dapat dimuat. Saya pikir proposal titik ganda akan menyebabkan lebih banyak kebingungan daripada lebih sedikit.

@ihnorton - jika Anda menentang penggunaan a..b sebagai sintaks inti (unoverloadble) untuk akses bidang atau melawan penggunaan a..b untuk sintaks yang dapat dimuat?

Salah satu fitur terbaik Julia adalah kesederhanaannya. Overloading x.y terasa seperti langkah pertama menuju C ++.

@StefanKarpinski tapi ini berarti perubahan paradigma dari bidang privat default ke bidang publik default.

Sebuah kesadaran yang baru saja saya alami, mungkin ini sudah jelas bagi orang lain selama ini. Pemrograman penuh gaya OO dapat dilakukan dengan . -overloading dasar (meskipun itu jelek). Mendefinisikan

getfield(x::MyType, ::Field{:foo}) = args -> foofun(x, args...) # a method, i.e. returns a function
getfield(x::MyType, ::Field{:bar}) = x..bar+2                  # field access, i.e. returns a value

lalu x.foo(a,b) dan x.bar kerja. Jadi diskusi tentang apakah x.size(1) harus diimplementasikan atau hanya x.size masih diperdebatkan.

@StefanKarpinski terhadap umumnya kelebihan beban a..b dan suam-suam kuku sekitar a..b -> Core.getfield(a,b) .

Saya mulai melihat perlunya operator lain di sini, tetapi a..b tidak cukup meyakinkan. Membutuhkan dua karakter terasa sangat ... kelas dua. Mungkin a@b , a$b , atau a|b (operator bitwise tidak sering digunakan). Kemungkinan luar juga a b`, yang mungkin dapat dibedakan oleh parser dari perintah.

Saya akan baik-baik saja dengan menggunakan operator "jelek" untuk akses bidang primitif. Saya pikir pengalaman telah menunjukkan bahwa karena ini adalah operasi konkret, jarang digunakan, dan memang agak berbahaya untuk digunakan.

Saya menyarankan untuk mengizinkan simulasi pengiriman tunggal OO dengan konvensi / penulisan ulang:

type Type end
# I can define methods with my Type as 1st argument
method(T, args...) = # method body
t = Type()
# then I can call that method, exactly like Java/Python methods, via:
t.method(args...)
# so
t.method(args...) 
# is just a rewrite to
method(t, args...)

Pembenaran di sini adalah kita sudah melakukan penulisan ulang sintaks yang serupa untuk getindex / setindex !, jadi mari kita izinkan sintaks OO penuh dengan ini. Dengan begitu, PyCall dan JavaCall tidak perlu melakukannya

my_dna[:find]("ACT")
# they can do
my_dna.find("ACT")
# by defining the appropriate find( ::PyObject, args...) method when importing modules from Python/Java

Saya suka ini karena ini adalah transformasi yang cukup jelas, seperti getindex / setindex, tetapi memungkinkan simulasi sistem OO pengiriman tunggal jika diinginkan, terutama untuk paket bahasa OO.

Saya kemudian menyarankan penggunaan operator .. untuk akses lapangan, dengan opsi untuk membebani. Penggunaan di sini akan memungkinkan PyCall / JavaCall untuk mensimulasikan akses bidang dengan membebani panggilan ke .. , memungkinkan DataFrames untuk membebani .. untuk akses kolom, dll. Ini juga akan menjadi akses bidang default baru di umum untuk semua tipe.

Saya memiliki titik lemah untuk penulisan ulang sintaks murni. Ini bisa dibilang hal yang buruk bahwa Anda dapat menulis a.f(x) sekarang dan membuatnya berfungsi tetapi berarti sesuatu yang sangat berbeda dari kebanyakan bahasa OO.

Tentu saja sisi lain dari koin itu adalah fragmentasi gaya yang mengerikan, dan fakta bahwa a.f tidak memiliki kesamaan dengan a.f() , menyebabkan ilusi rusak dengan cepat.

Salah satu fitur terbaik Julia adalah kesederhanaannya. Overloading x.y terasa seperti langkah pertama menuju C ++.

Perasaan yang sama di sini. Saya sedang mempertimbangkan, jika kebutuhan sebenarnya untuk ini benar-benar untuk sejumlah tipe interop terbatas, bagaimana jika hanya membuatnya valid jika secara eksplisit diminta dalam deklarasi tipe? Misalnya kata kunci tambahan selain type dan immutable bisa menjadi ootype atau semacamnya.

dan fakta bahwa af tidak memiliki kesamaan dengan af (), menyebabkan ilusi rusak dengan cepat.

Bisakah Anda menjelaskan apa artinya @JeffBezanson?

Saya berharap bahwa a.f adalah semacam objek metode jika a.f() berfungsi.

Ah, mengerti. Ya, Anda pasti tidak akan bisa melakukan sesuatu seperti map(t.method,collection) .

Saya akan setuju dengan @ mauro3 bahwa dengan mengizinkan obj.method(...) , ada risiko bahwa pengguna baru mungkin hanya melihat julia sebagai bahasa berorientasi objek lain yang mencoba bersaing dengan python, ruby, dll., Dan tidak sepenuhnya menghargai kehebatan yang multi-pengiriman. Risiko lainnya adalah gaya oo standar kemudian menjadi dominan, karena ini adalah yang lebih dikenal oleh pengguna, dibandingkan dengan gaya yang lebih julian yang dikembangkan selama ini.

Karena kasus penggunaan, selain DataFrames, dibatasi untuk antar-operasi dengan bahasa oo, dapatkah ini semua ditangani oleh makro? yaitu <strong i="8">@oo</strong> obj.method(a) menjadi method(obj,a) ?

@ karbarcca ini berarti bahwa secara otomatis semuanya dapat ditulis dalam dua cara:

x = 3
x.sin()
sin(x)
x + 2
x.+(2) # ?!

@karbarcca https://github.com/JuliaLang/julia/issues/1974#issuecomment -38830330

t. metode (args ...)
# hanyalah penulisan ulang ke
metode (t, args ...)

Hal itu tidak diperlukan bagi PyCall karena titik yang dapat diisi berlebih hanya dapat digunakan untuk memanggil pyobj[:func] dengan pyobj.func . Maka pyobj.func() akan menjadi (pyobj.func)() .

Menulis ulang a.foo(x) sebagai foo(a, x) tidak akan menyelesaikan masalah untuk PyCall, karena foo bukan dan tidak bisa menjadi metode Julia, ini adalah sesuatu yang perlu saya cari secara dinamis saat runtime . Saya perlu menulis ulang a.foo(x) sebagai getfield(a, Field{:foo})(x) atau yang serupa [atau mungkin sebagai getfield(a, Field{:foo}, x) ] sehingga getfield{S}(::PyObject, ::Type{Field{S}}) dapat melakukan hal yang benar.

@JeffBezanson https://github.com/JuliaLang/julia/issues/1974#issuecomment -38837755

Saya mulai melihat perlunya operator lain di sini, tetapi a..b kurang meyakinkan. Membutuhkan dua karakter terasa sangat ... kelas dua

Saya akan mengatakan bahwa, di sisi lain, .. diketik jauh lebih cepat daripada $ , @ atau | karena tidak ada tombol shift yang perlu ditekan , dan meskipun menjadi dua karakter, jari tetap pada tombol yang sama: smile:

@stevengj Ah, begitu. Tapi maksud saya masih tetap, bahwa penulisan ulang bisa dilakukan dengan makro.

Untuk JavaCall, saya sebenarnya hanya membutuhkan penangan unknownProperty dasarnya. Saya sebenarnya tidak perlu menulis ulang atau menghalangi pembacaan atau penulisan properti yang ada. Jadi apakah aturan bahwa "ax ditulis ulang ke getfield (a,: x) hanya jika x bukan properti yang ada" membantu menjaga semuanya tetap waras?

@simonbyrne , memerlukan makro akan mengalahkan keinginan untuk panggilan antarbahasa yang bersih dan transparan. Selain itu, akan sulit membuatnya bekerja dengan andal. Misalnya, Anda memiliki type Foo; p::PyObject; end , dan untuk objek f::Foo Anda ingin melakukan foo.p.bar mana bar adalah pencarian properti Python. Sulit membayangkan makro yang dapat dengan andal membedakan arti dari dua titik di foo.p.bar .

Sejujurnya, saya tidak melihat masalah besar dengan gaya. Paket berkualitas tinggi akan meniru gaya Base dan paket lain jika memungkinkan, dan beberapa orang akan menulis kode aneh apa pun yang kita lakukan. Jika kita meletakkan dot overloading di bagian selanjutnya dari manual, dan merekomendasikan penggunaannya hanya dalam beberapa kasus yang dipilih dengan cermat (mis. Interoperabilitas antarbahasa, baca / tulis properti, mungkin untuk menghindari polusi namespace untuk hal-hal seperti factor.U , dan secara umum sebagai alternatif yang lebih bersih untuk foo[:bar] ), maka saya tidak berpikir kita akan dibanjiri dengan paket yang menggunakan titik untuk semuanya. Hal utama adalah memutuskan untuk apa _we_ akan menggunakan dan merekomendasikan ini, dan mungkin kita harus membuat daftar penggunaan yang direkomendasikan sangat singkat dan hanya memperpanjangnya saat kebutuhan dunia nyata muncul.

Kami tidak menambahkan sintaks seperti OO yang sangat mudah seperti type Foo; bar(...) = ....; end untuk foo.bar(...) , jadi itu akan membatasi godaan untuk pemula juga.

Saya pada dasarnya setuju penuh dengan @stevengj di sini. Saya suka a..b untuk akses lapangan nyata karena itu

  1. terlihat mirip dengan a.b
  2. kurang nyaman, sebagaimana mestinya
  3. hanya _slightly_ kurang nyaman
  4. tidak memiliki makna yang ada dan kami belum menemukan penggunaan yang menarik untuk itu selama lebih dari setahun
  5. tidak aneh seperti a b`

Dengan perubahan ini dan mungkin (https://github.com/JuliaLang/julia/issues/2403) akankah hampir semua sintaks Julia menjadi kelebihan beban? (Operator terner adalah satu-satunya pengecualian yang dapat saya pikirkan) Bahwa hampir semua sintaks diturunkan menjadi metode pengiriman yang dapat dibebani tampaknya menjadi fitur pemersatu yang kuat bagi saya.

Saya setuju bahwa ini sebenarnya semacam penyederhanaan. Operator terner dan && dan || sebenarnya adalah aliran kendali, jadi itu agak berbeda. Tentu saja jenis argumen yang menentang menjadikan a..b akses bidang nyata sejak itu _that_ akan menjadi satu-satunya sintaks yang tidak dapat dibebani. Tapi menurutku itu ide yang bagus. Konsistensi itu baik tetapi tidak terpenting demi kepentingannya sendiri.

Oh, ada juga pemanggilan fungsi yang tidak overloadable. Sangat mendasar saya lupa tentang itu.

Itulah yang dialamatkan pada masalah # 2403.

Ya. Tapi ini jauh lebih dekat untuk terjadi daripada itu.

Satu-satunya alasan bagi saya di sini adalah bahwa akan sangat menyenangkan menggunakan operator akses bidang nyata untuk modul, tetapi itu mungkin tidak akan terjadi karena tidak ada yang mau menulis Package..foo .

Penyelesaian tab setelah titik menjadi sedikit jelek; secara teknis Anda harus memeriksa metode apa yang mungkin dipanggil x. untuk melihat apakah cocok untuk membuat daftar nama bidang objek atau nama modul. Dan saya harap tidak ada yang mencoba mendefinisikan getfield(::Module, ...) .

Saya pikir penyelesaian tab dapat dilakukan seperti ini: foo.<tab> mencantumkan "bidang publik" dan foo..<tab> mencantumkan "bidang pribadi". Untuk modul, apakah boleh mengizinkan implementasi default Mod.foo menjadi Mod..foo dan beri tahu orang-orang untuk tidak menambahkan metode getfield ke Module ? Maksud saya, Anda sudah dapat mendefinisikan ulang penambahan bilangan bulat dalam bahasa - semua kacau dan Anda mendapatkan segfault tetapi kami tidak mencoba mencegahnya. Ini tidak bisa lebih buruk dari itu, bukan?

Sebenarnya sedikit lebih buruk dari itu, karena bahasa pemrograman hanya peduli pada penamaan. Menyelesaikan nama jauh lebih penting daripada menambahkan bilangan bulat.

Kami tidak punya banyak pilihan selain memiliki Mod.foo default ke Mod..foo , tapi kami mungkin harus menggunakan Mod..foo untuk bootstrap di beberapa tempat. Operator .. sangat membantu di sini, karena tanpanya Anda bahkan tidak dapat memanggil Core.getfield untuk menentukan fallback. Dengan itu, kami mungkin hanya akan menghapus Core.getfield dan hanya memiliki .. .

Itu poin yang adil - penamaan adalah masalah besar dalam pemrograman :-). Sepertinya cara yang baik untuk pergi - hanya .. dan tidak ada Core.getfield .

Dua gagasan ini,

[...] letakkan dot overloading di bagian selanjutnya dari manual, dan rekomendasikan penggunaannya hanya dalam beberapa kasus yang dipilih dengan cermat @stevengj https://github.com/JuliaLang/julia/issues/1974#issuecomment -38847340

dan

[...] preferensinya adalah menggunakan sintaks x.property sebanyak mungkin @StefanKarpinski https://github.com/JuliaLang/julia/issues/1974#issuecomment -38694885

jelas ditentang.

Saya pikir jika ide pertama akan dipilih maka hanya membuat operator .. untuk "kasus yang dipilih dengan cermat" lebih masuk akal.
Sebagai keuntungan, menggunakan ..name untuk kasus di mana saat ini [:name] digunakan (DataFrames, Dict {Symbol, ...}) akan lebih mudah mengetik / ramah sintaks sementara dengan jelas menyatakan bahwa sesuatu yang berbeda dari akses bidang sedang terjadi. Selain itu, titik ganda di ..name dapat dilihat sebagai titik dua yang diputar, petunjuk untuk sintaks simbol :name , dan juga tidak akan ada masalah dengan penyelesaian tab.
Sebagai kerugian, penggunaan di PyCall et al. tidak akan terlalu dekat dengan sintaks asli (dan bahkan bisa membingungkan untuk kasus ketika . benar-benar harus digunakan). Tapi jujur ​​saja, Julia tidak akan pernah sepenuhnya kompatibel dengan sintaks Python, dan akan selalu ada kasus di mana seseorang harus banyak mengetik di Julia dengan PyCall untuk melakukan instruksi sederhana dengan Python. .. untuk meniru . dapat memberikan keseimbangan yang baik di sini. (Tolong jangan salah paham, saya sangat suka PyCall dan menganggapnya sebagai fitur penting yang memerlukan perhatian khusus)

Ide kedua, yang saat ini saya sukai, memiliki keputusan besar tentang kapan property(x) atau x.property harus digunakan, yang membutuhkan definisi yang elegan, baik, dan jelas, jika hal seperti itu ada .. .
Tampaknya jika orang menginginkan . yang dapat kelebihan beban itu karena mereka lebih suka gaya API x.property di tempat pertama.
Bagaimanapun, saya lebih suka melihat . bukan sebagai operator akses bidang yang dapat dibebani tetapi sebagai operator akses "properti" yang dapat diisi ( getprop(a, Field{:foo}) mungkin?) Yang defaultnya adalah operator bidang yang tidak dapat dibebani .. .
Keputusan lain juga harus diambil, misalnya yang akan digunakan dalam kode implementasi konkret untuk akses lapangan, .. atau . ? Misalnya, untuk contoh langkah Ranges, mana yang idiomatis? step(r::Range1) = one(r..start) atau step(r::Range1) = one(r.start) ? (belum lagi pertanyaan apakah step harus berupa metode atau properti).

Itulah mengapa saya mundur dari sudut itu dan mengusulkan kriteria ini: https://github.com/JuliaLang/julia/issues/1974#issuecomment -38812139.

Hanya satu pikiran yang muncul di kepala saya saat membaca utas yang menarik ini. Ekspor dapat digunakan untuk mendeklarasikan bidang publik, sementara semua bidang terlihat di dalam modul yang menentukan, misalnya:

module Foo
   type Person
     name
     age
   end
   export Person, Person.name
   <strong i="6">@property</strong> Person :age(person) = person..age + 1
end

Dalam situasi ini, Orang yang diekspor masih terlihat seperti 'nama' dan 'usia' kecuali dalam hal ini usia hanya dapat dibaca melalui fungsi yang menambahkannya. Mengekspor semua Orang dapat dilakukan sebagai orang ekspor. * Atau serupa.

[pao: kutipan]

@emeseles Berhati-hatilah menggunakan backtick untuk mengutip hal-hal yang seperti kode Julia - ini memastikan pemformatan dipertahankan, dan mencegah makro Julia membuat pemberitahuan GitHub untuk pengguna dengan nama yang mirip.

. dan .. membingungkan: sintax yang jelas dan mudah diingat adalah sesuatu yang baik

Saya sangat menantikan untuk dapat melakukan ini. Apakah ini perubahan yang cukup besar untuk membuatnya (atau WIP di # 5848) ditandai sebagai proyek 0,4?

Ya, ini pasti sebuah proyek.

Saya pikir sebagian besar dari kita setuju bahwa penggunaan yang disarankan harus dibatasi, setidaknya untuk memulai. Perasaan saya adalah bahwa itu pada awalnya harus direkomendasikan hanya untuk dua penggunaan: interoperabilitas (dengan bahasa lain, seperti di PyCall, dan lebih umum untuk perpustakaan eksternal di mana notasi titik alami), dan mungkin untuk objek dengan keadaan yang bisa berubah (sejak get_foo(x) dan set_foo!(x, val) jelek).

Sekalipun kami merekomendasikannya hanya untuk interoperabilitas panggilan asing, tujuan itu saja sudah cukup untuk membenarkan fitur ini menurut saya. Untuk bahasa baru seperti Julia, berbicara lancar dengan dunia perangkat lunak lainnya sangatlah penting.

Steven, saya tidak 100% yakin tentang pengambil / penyetel karena saya takut hal itu akan segera menyebabkan ketidakkonsistenan tetapi saya setuju dengan kasus penggunaan lainnya. Di atas yang kita miliki di properti dinamis Gtk.jl yang juga akan mendapat manfaat dari sintaks. Favorit pribadi saya adalah implementasi enum yang diuraikan Stefan di # 5842.

Menabrak. Apa yang menghalangi kemajuan dalam masalah ini? Apakah keputusan diperlukan, atau apakah masalah ini bergantung pada perubahan internal lainnya, belum selesai, atau hanya coding?

Apa yang menghalangi kemajuan dalam masalah ini?

Seseorang yang melakukan pekerjaan dan beberapa ragu apakah itu hal yang benar untuk dilakukan.

Perhatikan bahwa @ihnorton sudah membuat implementasi draf awal di # 5848. Saya pikir pekerjaan terhenti terutama karena pernyataan yang jelas dari tim inti Julia diperlukan tentang apakah ini fitur yang diinginkan.

Saya setuju dengan ini. @JeffBezanson tampaknya berada di pagar.

Bagi saya, memiliki fitur ini, akan membuat transisi dari basis kode Python kami yang besar ke Julia lebih mudah. Untuk menjelaskan kepada siswa, jika mereka menggunakan kode Python, mereka memerlukan sintaks yang sangat berbeda dari biasanya, ini mungkin menjadi sulit.

Kami memiliki diskusi di atas di utas ini dan saya masih tidak dapat melihat persetujuan penuh. Saat ini beberapa orang mengira bahwa API publik terbuat dari fungsi / metode sedangkan API pribadi adalah bidang dari tipe komposit. Saya dapat melihat pengecualian yang sangat jarang dari skema ini. ( .U dalam dekomposisi LU?)

Ini tidak berarti saya menentang ini karena akses dan enum Python adalah kasus di mana ini berguna. Masih ada yang bisa mempertanyakan seberapa mendesak kebutuhan di sini dan apakah bijaksana untuk mendorong ini di akhir siklus pembangunan.

@ ufechner7 , saya setuju bahwa motivasi utamanya adalah interop language. @tknopp , kami tidak akan pernah mendapatkan persetujuan bulat tentang hal seperti ini. Pada akhirnya itu tergantung pada apa yang diputuskan oleh @StefanKarpinski .

Saya pikir banyak keragu-raguan berasal dari apa yang saya bayangkan sebagai mimpi terburuk Jeff:

module DotOrientedProgramming
  Base.getfield(x, ::Field{:size}) = size(x)
  Base.getfield(x, ::Field{:length}) = length(x)
  ⋮
end

Saya akan sangat tidak menyukai ini juga - paket apa pun yang memutuskan untuk menyalahgunakannya seperti ini akan menyebabkan penyalahgunaannya pada _all_ type di sistem, termasuk milik saya. Fitur ini sangat kuat dan akan mengubah cara penulisan Julia. Lebih baik dan (mungkin, tapi semoga tidak) lebih buruk.

Ya pasti Steven, itu mungkin tidak diungkapkan dengan benar dari saya. Hal yang ingin saya sampaikan adalah bahwa perubahan ini dapat memiliki pengaruh besar dalam perkembangan bahasa. Dan gagasan "antarmuka formal" yang kita miliki di terbitan lain juga dipengaruhi dengan membuat . kelebihan beban. Jadi ya, mari @JeffBezanson dan @StefanKarpinski memutuskan. Masih pertanyaannya adalah apakah keputusan itu harus ditegakkan sekarang ...

Untuk apa nilainya, saya menyukai membuat hampir semua sintaks menjadi overloadable dan kemudian mengandalkan budaya untuk menolak menjadi terlalu liar dengannya.

+1 . Saya pikir ada analogi filosofis yang kuat (dan mungkin praktis ...) ke call overloading di sini. Manual membutuhkan bagian berjudul Don't do stupid stuff: we won't optimize that . (tentu saja, kelebihan muatan call sebagian adalah _untuk_ alasan kinerja - tetapi hal itu penuh dengan potensi penyalahgunaan)

  • : 100: untuk itu. Saya pikir budaya harus menjadi motivasi yang cukup untuk tidak menyalahgunakan ini

Secara keseluruhan, saya mendukung. Potensi pelecehan bukanlah kekhawatiran terbesar saya. Bagi saya masalah besarnya adalah

  • Sintaks yang dapat diterima untuk akses bidang "nyata". Saya tidak terlalu suka a..b .
  • Modul. Nama yang berkualifikasi sangatlah penting. Mengekspresikannya dengan metode pengiriman dimungkinkan, tetapi memiliki kesulitan praktis. Yaitu, Anda perlu melalui banyak fase kompilator (bahkan mungkin melalui inlining) hanya untuk mengetahui bahwa Anda memiliki nama yang memenuhi syarat. Ini membuat hidup lebih sulit bagi siapa pun alat tulis yang mengonsumsi AST. Ini juga membuatnya sangat mudah untuk menangani kasus ini secara tidak benar di alat semacam itu.

Masalah-masalah ini dapat diselesaikan dalam satu langkah dengan menggunakan sintaks yang sama untuk keduanya, tetapi hampir tidak mungkin membayangkan menggunakan apa pun kecuali . untuk modul pada saat ini. _Internally_ pasti akan ada sintaks abstrak untuk referensi modul; akan membuat frustasi jika tidak ada cara yang bagus untuk mengungkapkannya.

Dua sen saya untuk pertanyaan ini: mengapa tidak menggunakan : untuk nama yang memenuhi syarat? Ini sudah digunakan untuk sesuatu yang serupa:

import Base: call, show, size

Ini akan memberikan sesuatu seperti

module Foo
    module Bar
        f(x) = 3*x
    end
    const a = 42
end

<strong i="10">@assert</strong> Foo:a == 42

Foo:Bar:f(789)

Atau apakah mereka sudah terlalu banyak menggunakan simbol : ? Simbol :: (gaya C ++) tampaknya terlalu bertele-tele bagi saya.

: sudah menjadi simbol yang paling banyak kelebihan beban di Julia, jadi itu tidak akan membantu, saya khawatir.

Bisakah kita menyederhanakan masalah penamaan yang memenuhi syarat dengan membuat module.name tidak dapat dimuat berlebihan? Karena binding modul bersifat konstan, itu akan memungkinkan kita untuk mempertahankan semantik yang sama tetapi melakukan hubungan singkat semua logika normal untuk pencarian nama yang memenuhi syarat segera setelah diketahui bahwa LHS a.b adalah modul. Saya pikir cukup masuk akal untuk tidak mengizinkan orang menimpa apa artinya mencari nama dalam modul.

Saya lebih suka sintaks a..b untuk akses bidang nyata. Apa keberatan Anda?

Selain: Saya agak berharap kami telah menggunakan ( ) untuk daftar impor seperti beberapa bahasa fungsional. Yaitu:

import Base (call, show, size)

Alasan saya adalah kita bisa membuat koma opsional dan mengizinkan koma tambahan. Benar-benar mengganggu saya bahwa semua nama yang diimpor membutuhkan tanda koma kecuali yang terakhir yang tidak dapat memilikinya.

Ya, saya baru saja akan menyebutkan kemungkinan menghasilkan a.b berarti "jika a adalah modul, lakukan pencarian modul terlebih dahulu". Itu mungkin membantu, dan kami tentu tidak ingin menimpa arti pencarian modul. Itu memang memiliki beberapa biaya kompleksitas, karena kami kemudian tidak dapat mewakili a.b sebagai panggilan getfield(a,:b) . Ini harus berupa node AST khusus dengan cabang implisit. Tentu saja kita bisa menggunakan cabang _explicit_, tapi saya khawatir tentang AST mengasapi itu.

Tampaknya tidak ada jalan keluar yang mudah dari konflik besar antara kebutuhan front end dan back end.

Jika semua orang menyukai a..b saya rasa saya bisa belajar menerimanya. Bagi saya itu hanya terlihat seperti itu berarti sesuatu yang sama sekali berbeda, mungkin sebuah interval.

Saya juga tidak suka a..b tetapi saya bertanya-tanya mengapa hal itu diperlukan sama sekali. Saat membaca utas ini, orang mendapat kesan bahwa kelebihan beban hanya akan digunakan dalam pembungkus bahasa dan kasus penggunaan dinamis di mana akses bidang nyata tidak diperlukan.

Karena pada titik tertentu Anda perlu mengakses representasi suatu objek untuk melakukan apa pun dengannya. Seseorang dapat berargumen bahwa ini akan relatif jarang, dan bisa juga jelek seperti get_actual_field(a,:x) , tetapi ini sepertinya operasi yang terlalu penting untuk tidak memiliki sintaks.

Saya melihatnya tetapi ini terdengar seperti kami mencari sintaks yang kami ingin tidak digunakan oleh siapa pun, bukan?

Tidak memberikan .. akan menjadi cara untuk mengatakan ya untuk kasus penggunaan dinamis tetapi tidak untuk pemrograman berorientasi titik

Saya tidak melihat bagaimana hal itu akan mencegah pemrograman berorientasi titik; Anda masih bisa melakukan contoh @mbauman di atas.

Sementara sintaks a..b terlihat seperti interval (saya telah menggunakannya seperti itu), saya hanya tidak berpikir bahwa aritmatika interval membutuhkan sintaks inputnya sendiri - menulis Interval(a,b) hanya baik-baik saja dan tidak banyak orang lain yang ingin menggunakan sintaks itu, karena sudah menjadi operator di Julia selama bertahun-tahun sekarang dan tidak ada yang menggunakannya untuk banyak hal. Ini juga terlihat seperti akses lapangan.

Satu lapisan perak untuk ini adalah kita dapat mengganti module_name mengerikan dengan m..name . Tidak dapat mengakses bidang objek Modul telah menjadi kutukan.

Ya, saya baru saja akan menyebutkan kemungkinan menghasilkan a.b berarti "jika a adalah modul, lakukan pencarian modul terlebih dahulu". Itu mungkin membantu, dan kami tentu tidak ingin menimpa arti pencarian modul. Itu memang memiliki beberapa biaya kompleksitas, karena kami kemudian tidak dapat mewakili a.b sebagai panggilan getfield(a,:b) . Ini harus berupa node AST khusus dengan cabang implisit. Tentu saja kita bisa menggunakan cabang _explicit_, tapi saya khawatir tentang AST mengasapi itu.

Bisakah kita menangani ini dengan membuat a.b berarti tanpa syarat getfield(a,:b) dan kemudian membuat kesalahan untuk menambahkan metode ke getfield yang memotong metode getfield(::Module, ::Field) ? Ini semacam cara yang aneh untuk menegakkan perilaku itu, tetapi pada akhirnya akan memiliki efek yang sama. Kemudian menurunkan hanya dapat menggunakan fakta bahwa kami tahu Anda tidak dapat melakukan itu untuk menipu dan menurunkan module.name ke pencarian nama yang memenuhi syarat.

Ok saya menyatakan sebaliknya: Apakah ada orang di utas ini yang menggunakan .. dan jika ya, apa yang akan menjadi kasus penggunaan teladan? (yaitu mungkin sepenuhnya membayangi akses bidang internal baik-baik saja)

@StefanKarpinski Ya, itu mungkin berhasil. Bisa jadi kasus lain di mana kita menginginkan semacam metode "tertutup".

@tknopp Mengakses module..name dan module..parent :) Juga, untuk memperjelas, apakah Anda menganjurkan sintaks pemanggilan fungsi seperti get(obj,:field) untuk akses bidang tingkat rendah?

Tidak, saya tidak menganjurkan sintaks tertentu. Saya hanya berpikir akan lebih baik untuk memastikan mengapa fitur ini diperlukan dan apa kasus penggunaannya. Untuk kasus penggunaan dinamis, itu tidak masalah

  • a.b adalah akses field jika Base.getfield(a, ::Field{:b}) belum ditentukan
  • a.b adalah versi dinamis jika Base.getfield(a, ::Field{:b}) ditentukan. Dalam hal ini, akses bidang nyata dapat dibayangi

Pertanyaan saya adalah jika ada kasus penggunaan di mana shadowing tidak ok.

Iya; Anda mungkin ingin mendefinisikan pyobject.x sehingga x selalu dicari di kamus pyobject, untuk semua x . Maka mekanisme terpisah diperlukan untuk mengakses bidang julia pyobject.

Ahhh, jadi semuanya atau tidak sama sekali? Saya entah bagaimana mendapat kesan bahwa seseorang bisa saja

type A
  c
end

Base.getfield(a::A, ::Field{:b}) = 3

a = A(1)

a.c # This still calls the field access
a.b # This calls the function

Ya, Anda _dapat_ melakukannya, tetapi tidak semua objek akan melakukannya. Beberapa akan ingin mendefinisikan getfield(a::A, ::Field) untuk mencegat semua bidang.

Ok terima kasih sekarang saya mengerti. Semua kasus penggunaan dinamis menginginkan getfield(a::A, ::Field) dan karenanya memerlukan cara apa pun untuk memanggil field internal.

Maka pendapat saya adalah bahwa Core.getfield sudah cukup kecuali seseorang menemukan kasus penggunaan praktis di mana hal ini mengganggu.

Ini mungkin diberikan, tetapi kami juga akan mengizinkan penggantian setfield! , bukan? Saya sangat ingin itu untuk mengekspos tampilan yang bisa berubah ke dalam database di mana baris menjadi tipe.

Ya, itulah kesan saya.

Ok, IMHO apakah akan menggunakan .. untuk akses lapangan nyata atau Core.getfield bukanlah masalah besar. Seseorang dapat memperkenalkan fitur umum sebagai eksperimental dan menjadikan subjek ini berubah.

Pertanyaannya adalah apakah ini akan sesuai dengan kerangka waktu 0,4 atau tidak. Jadi # 5848 dekat dengan implementasi akhir dan modul thingy solvable?

@johnmyleswhite : Saya juga akan memilih untuk membuat ini simetris dan juga mengizinkan setfield! . Di Gtk.jl kami akan menggunakan keduanya.

Tampaknya tidak terlalu jelas apa yang akan menjadi aturan kapan harus menggunakan fitur ini dan kapan tidak menggunakannya. Saya melihat poin untuk PyCall, di mana metode / bidang harus dicari secara dinamis, dan dengan demikian tidak dapat menjadi metode Julia / tipe komposit (dan sintaks yang dihasilkan lebih dekat dengan Python). Tapi kemudian, mengapa menggunakannya untuk Gtk.jl? Jika mulai melakukan foo.bar = x alih-alih setbar!(foo, x) , maka kode Julia standar akan mulai menggunakan pola ini juga: apakah ini yang kita inginkan? Mungkin ya, tapi mari kita perjelas tentang itu.

Apakah dapat diterima / disarankan untuk menggunakan fitur ini untuk mengimplementasikan pengambil dan penyetel properti yang ditentukan untuk jenis abstrak (dan konkret juga)?
Saya rasa itu akan memungkinkan penghindaran nama benturan metode yang digunakan untuk mendapatkan properti dari berbagai jenis modul yang berbeda.

Ref .: https://github.com/JuliaLang/julia/issues/4345 , https://groups.google.com/forum/#!msg/julia -users / p5-lVNlDC8U / 6PYcvvsg29UJ

@nalimilan : Gtk memiliki sistem properti dinamis, bukan tentang pengambil / penyetel.

@tknopp Ah, oke, memang. Tapi untuk properti paling umum, Anda memiliki fungsi pengambil / penyetel (cepat), ditambah properti dinamis. Jadi, apakah Anda akan merekomendasikan menggunakan fungsi pengambil / penyetel jika tersedia, dan sintaks bidang kelebihan beban hanya untuk properti yang tidak memilikinya? Kedengarannya bagus untuk saya - tetapi ada baiknya memiliki kebijakan yang jelas tentang IMHO ini.

Dalam pandangan saya saat ini (dan saya pikir kita perlu sedikit bereksperimen dengan ini untuk mengetahui aturan yang tepat), f(x) lebih baik ketika f masuk akal sebagai konsep mandiri umum seperti " length "sementara x.f harus digunakan jika f tidak benar-benar tidak tergantung pada x . Untuk mencoba menyesuaikan contoh saya sebelumnya, tidak terlalu berguna untuk memiliki fungsi step generik karena sebagian besar vektor dan koleksi tidak memiliki gagasan tentang langkah apa pun - _only_ masuk akal bila Anda memiliki rentang dari beberapa jenis. Jadi, tidak masalah memiliki x.step menjadi cara untuk mendapatkan langkah kisaran x . Ini sedikit panggilan penilaian, tapi saya rasa hidup penuh dengan itu.

Saya tidak suka .. karena tidak memberikan akses langsung kepada saya. Bagaimana dengan foo.bar. Titik ekstra di ujung pin itu untuk akses langsung.

Bisa juga memilih simbol unicode: kita masih memiliki banyak yang tersisa ...

@GlenHertz , itu tidak benar-benar berfungsi jika Anda harus mengakses bidang rantai.

@ Simonbyrne , saya biasanya tidak

Dalam pandangan saya pada titik ini (dan saya pikir kita perlu sedikit bereksperimen dengan ini untuk mengetahui aturan yang tepat), f (x) lebih baik ketika f masuk akal sebagai konsep mandiri umum seperti "panjang" sementara xf harus digunakan ketika f tidak benar-benar independen dari x.

Aturan pribadi saya untuk menggunakan fitur ini adalah: hanya gunakan ini untuk interop bahasa atau untuk hal-hal yang berupa "hampir bidang" atau "bidang tambahan". Misalnya, saya mungkin menggunakan ini untuk memperbarui cache yang bergantung pada nilai semua bidang dalam satu tipe.

Satu pertanyaan besar yang saya miliki tentang fitur ini: bagaimana ini berinteraksi dengan jenis inferensi? Sepertinya Anda akan mendefinisikan fungsi getfield(x::T, s::Symbol) yang menghasilkan jenis keluaran yang berbeda untuk nilai yang berbeda dari s . Apakah itu hanya berhasil karena getfield ajaib? Dapatkah Anda mendefinisikan kembali keluaran getfield(x, s) untuk tetap x dan s pada titik mana pun dalam program? Jika demikian, bagaimana kaitannya dengan ketidakmampuan untuk mendefinisikan ulang suatu tipe?

Sepertinya Anda akan mendefinisikan fungsi getfield(x::T, s::Symbol) yang menghasilkan jenis keluaran yang berbeda untuk nilai yang berbeda dari s .

Itulah mengapa rencananya adalah untuk menyatakan ini sebagai getfield{s}(x::T, f::Field{s}) dimana s adalah sebuah simbol.

Saya telah melewatkannya. Terima kasih telah meluruskan saya.

@nalimilan : Ya, bidang yang kelebihan beban hanya akan digunakan untuk properti dinamis. Ini adalah bagaimana Jameson ingin menangani ini dan saya pikir ini bagus. Semua getter dan setter yang sebenarnya dibuat secara otomatis tetapi masih berfungsi tanpa semua penamaan get / set. Live di modul GAccessor (pendek G_ )

Pada sintaks, mengapa tidak menggunakan <- untuk akses bidang nyata?
Ini mirip dengan -> dalam c ++ yang digunakan untuk lamda tetapi <- saat ini tidak digunakan.
Bisa dibaca dari jenisnya, dapatkan nilainya secara langsung.

Itu akan meninggalkan .. tidak digunakan bagi mereka yang ingin menggunakannya pada interval tetap dan akan menggunakan pasangan yang tidak terpakai yang tidak memiliki kegunaan lain yang dapat saya pikirkan sejauh ini.

Jangan gunakan notasi tugas R untuk akses lapangan.

Kita mungkin bisa menggunakan -> untuk mencerminkan C / C ++ secara langsung dan mendapatkan sintaks baru
fungsi anonim. Saya tidak pernah terlalu peduli dengan fungsi anonim
sintaks karena sedikit singkat / tidak dapat dibaca. Mungkin kita bisa melakukannya
sesuatu seperti

func (x) x ^ 2 akhir

atau lebih lama, lebih konsisten

fungsi (x) x ^ 2 akhir

Mungkin akan ada cara untuk menghasilkan sintaks yang baik yang tidak membutuhkan
menggunakan ujung.

Bukan untuk mengubah diskusi terlalu banyak, tapi pasti masuk akal
untuk menggunakan -> untuk akses lapangan nyata.
</s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> orang </s>

Pada Rabu, 28 Jan 2015 jam 8:49, John Myles White [email protected]
menulis:

Jangan gunakan notasi tugas R untuk akses lapangan.

-
Balas email ini secara langsung atau lihat di GitHub
https://github.com/JuliaLang/julia/issues/1974#issuecomment -71857083.

@quinnj : func (x) x^2 end sudah berfungsi. Tetapi bagus untuk memiliki sintaks yang sangat ringkas untuk fungsi anonim: map(x->x^2, 1:10)

Saya tidak berpikir akses bidang memerlukan sintaks khusus (karakter unicode seperti yang disarankan @simonbyrne tidak apa-apa seperti pada opsi). Saya tidak ingin kehilangan x -> x^2 .

Sepertinya masalah ini masih terbuka / menunggu diskusi? Menarik membaca semua berbagai komentar di sini tentang operator titik.

Apakah sudah ada saran untuk menambah token operator baru? Menggunakan sesuatu seperti :> mungkin bisa menjadi alternatif yang bagus. Ini memiliki paralel dengan |> dan mungkin memiliki Julia _feel_ yang lebih asli untuk itu:

c = foo.a + foo.b
pyobj:>obj:>process(c)

Meskipun masih jauh lebih mudah untuk menulis daripada:

pyobj[:obj][:procees](c)

Atau membandingkan:

someobj :> array |> length 
# vs
length(get_array(someobj)) 

Saya baru mengenal Julia, tetapi saya dengan cepat mendapatkan apresiasi yang kuat untuk pendekatan pengiriman ganda. Pemrograman berorientasi objek - terutama untuk pemrograman ilmiah - membuat banyak tugas menjadi lebih rumit. Saya khawatir paradigma / sintaksis OO akan berdampak negatif pada perkembangan gaya Julia.

Atau sebagai alternatif, karena saya lupa tentang interning string dan / atau kutipan:

someobj <: field_name |> length

@elcritch , <: saat ini digunakan sebagai operator "adalah subtipe" dari Julia, dan jika :> diperkenalkan, kemungkinan besar kita ingin menggunakannya untuk sesuatu yang berhubungan dengan tipe karena itu warisan.

Jika menggunakan instance..member adalah suatu masalah, berikut beberapa kemungkinannya. Lindungi matamu! Kemungkinan semua ini lebih buruk:

  • instance^.member (wortel dot)
  • instance~.member (titik tilde)
  • instance:.member (titik dua)
  • instance*.member (titik bintang)
  • instance-.member (tanda hubung titik)
  • [email protected] (di tanda titik)
  • instance&.member (amper dot)
  • instance$.member (dolar titik)
  • instance<.>member (titik pesawat ruang angkasa)

Sejujurnya saya berpikir bahwa (a) .. tampaknya cukup baik dan (b) tidak masalah apakah tampilannya bagus karena ini akan selalu menjadi sudut bahasa yang tidak jelas. Kebanyakan orang akan menggunakan instance.member karena mereka hanya memiliki salah satu bidang atau metode getfield , tetapi tidak keduanya.

(Dalam hal ini, kebanyakan orang yang ingin menggunakan baik anggota maupun metode mungkin tidak akan repot-repot mempelajari tentang .. . Mereka hanya akan menetapkan metode untuk foo.member dan menamai "real "field foo._member . Bisa dibilang, ini adalah gaya yang lebih baik - ini berarti bahwa ketika Anda membaca definisi type , akan segera terlihat jelas bahwa _member tidak seharusnya menjadi sesuatu Anda dapat atau harus mengakses secara langsung. Hal ini akan memperdebatkan untuk membuat .. sesuatu yang jelek dan tidak jelas seperti :. daripada menggunakan tanda baca yang berharga di properti.)

Saya akan kehilangan kemampuan untuk menggunakan .. sebagai operator interval infix, tetapi akses bidang yang dapat diisi berlebihan adalah trade-off yang berharga. Meskipun saya ragu untuk sedikit takut menambahkan arti lain ke titik dua, :. sepertinya tidak terlalu buruk.

Perhatikan bahwa :. sebenarnya adalah sintaks yang valid untuk symbol(".") sekarang, jadi mungkin tidak baik untuk menggunakannya. Poin bahwa .. berpotensi berguna telah diambil; kita tidak boleh menyia-nyiakannya pada sintaks yang hampir tidak ada orang yang akan menggunakannya. Saya akan sangat senang menggunakan sesuatu yang bahkan lebih jelek seperti @. (karena . bukan nama makro yang valid juga tidak dapat memulai pengenal, ini sepertinya tidak bertentangan dengan apa pun ). Sekali lagi, ini akan menjadi sudut yang tidak jelas dari Julia sehingga tidak ada gunanya mencoba membuatnya cantik.

+1 untuk menyelesaikan ini menggunakan .. dan mengabaikan potensi keburukan apa pun

Ya, mari kita pergi untuk .. bagaimanapun jika ada bisa digunakan untuk .. maka saya pikir itu akan menjadi _range_ konstruktor, tapi hei itu sudah ada dengan titik dua misalnya. start:stop .

Satu punt terakhir: bagaimana dengan .: ? Apakah terlalu halus untuk memiliki ab, a. (B), a. (: B) _and_ a.:b?

@hayd , sepertinya terlalu halus dan mudah digunakan secara tidak sengaja.

@ihnorton , adakah kemungkinan untuk menghidupkan kembali versi # 5848? Kita dapat melakukan punt pada pertanyaan sintaks dan hanya menggunakan Core.getfield(x, Field{y}) untuk mengakses bidang "nyata".

Bikeshed sekitar Core.getfield sintaks, apakah ada pertanyaan substantif yang tersisa?

Dalam # 5848, @tknopp menyarankan agar hanya membuat akses bidang "nyata" yang dapat kelebihan muatan, bertentangan dengan saran @JeffBezanson bahwa semuanya harus dapat kelebihan muatan. Secara pribadi, saya akan senang dengan membuatnya tidak mungkin untuk membebani bidang nyata, kecuali bahwa sifat dinamis dari bahasa mungkin akan membuat itu jauh lebih rumit untuk diterapkan. mis. dengan pendekatan "everything-overloadable", jika Anda memiliki x::Vector{Any} , maka melakukan x[i].y dapat diartikan getfield(x[i], Field{:y}) dan sistem pengiriman akan melakukan hal yang benar terlepas dari apakah y adalah bidang nyata, sedangkan jika Anda hanya ingin memanggil getfield untuk bidang "virtual" maka codegen harus mengimplementasikan subset miniatur dari sistem pengiriman untuk pemeriksaan runtime x[i] jenis.

Pertanyaan lain adalah apakah Module.foo seharusnya bisa dibebani. Di satu sisi, ada konsistensi tertentu untuk menggunakan getfield untuk semuanya, dan contoh Vector{Any} disebutkan di atas dapat memiliki anggota array Module jadi kita harus menangani kasus itu bagaimanapun. Di sisi lain, @JeffBezanson menunjukkan bahwa hal ini dapat mempersulit kompilasi, dan membuat perilaku deklarasi seperti function Base.sum(...) sulit dilakukan. Preferensi saya adalah membuat Module.foo non-overloadable, setidaknya untuk saat ini, dalam hal apapun di mana kompilator tahu itu bekerja dengan Module (bukan Vector{Any} ) ; sedikit ketidakkonsistenan tampaknya sepadan untuk menjadi konservatif tentang apa yang diubah.

+1 untuk tidak mengizinkan kelebihan Module.foo .

Untuk berpadu di sini, salah satu area komputasi ilmiah di mana pemrograman dan sintaksis OO sebenarnya lebih unggul daripada FP adalah pemodelan berbasis agen. Meskipun saya merindukan warisan konkret dan banyak untuk mengatur hierarki agen, abstraksi yang ringan dan cepat serta prototipe cepat Julia luar biasa- Sudah beberapa kerangka kerja ABM telah muncul.

Dalam ABM, notasi titik lebih disukai untuk mengekspresikan interaksi agen: Agent1.dosomething (Agent2) vs dosomething (Agent1, Agent2).

Ini jelas bukan kasus penggunaan terbesar, tetapi alangkah baiknya menyimpan gula sintaksis ini untuk berpikir dan membuat kode tentang ABM.

Saya juga sangat ingin sintaks ini tersedia di Julia. Sebagai
sama seperti saya menghargai pendekatan berorientasi fungsi dari sebuah desain
Perspektif, sintaks pemanggilan metode sangat berguna dan dapat dibaca di beberapa
domain. Akan lebih bagus jika Ab (C) setara dengan b (A, C).
Pada 22 Apr 2015 8:50 AM, "datnamer" [email protected] menulis:

Untuk berpadu di sini, salah satu bidang komputasi ilmiah tempat pemrograman OO
dan sintaks sebenarnya lebih unggul dari FP adalah pemodelan berbasis agen. Meskipun saya
kehilangan warisan konkret dan ganda untuk menyiapkan hierarki agen, file
abstraksi ringan dan cepat dan prototipe cepat Julia
luar biasa- Sudah ada beberapa kerangka kerja ABM yang muncul.

Di ABM, notasi titik lebih disukai untuk mengekspresikan interaksi agen:
Agent1.dosomething (Agent2) vs dosomething (Agent1, Agent2).

Ini jelas bukan kasus penggunaan terbesar, tapi alangkah baiknya menyimpan ini
gula sintaksis untuk pemikiran dan pengkodean tentang ABM.

-
Balas email ini secara langsung atau lihat di GitHub
https://github.com/JuliaLang/julia/issues/1974#issuecomment -95218555.

Dalam ABM, notasi titik lebih disukai untuk mengekspresikan interaksi agen: Agent1.dosomething (Agent2) vs dosomething (Agent1, Agent2).

Mengapa lebih baik? Edit: Maksud saya dalam konteks ABM ini secara khusus.

Tolong, jangan terjebak dalam perang agama karena ejaan. @ dbeach24 , tidak ada yang mengusulkan bahwa a.b(c) setara di Julia dengan b(a,c) ; Ini tidak akan terjadi.

Titik yang dapat dibebani sangat penting untuk interop alami dengan bahasa lain. Itu alasan yang cukup.

Subject.Verb (DirectObject)

Cukup alami dalam beberapa konteks. Banyak programmer OO yang terbiasa
itu, dan sementara itu hanyalah penataan ulang fungsi (A, B), penataan ulang itu
melakukan banyak hal untuk keterbacaan, IMO.
Pada 22 Apr 2015 10.32, "Andy Hayden" [email protected] menulis:

Di ABM, notasi titik lebih disukai untuk mengekspresikan interaksi agen:
Agent1.dosomething (Agent2) vs dosomething (Agent1, Agent2).

Mengapa lebih baik?

-
Balas email ini secara langsung atau lihat di GitHub
https://github.com/JuliaLang/julia/issues/1974#issuecomment -95256390.

Saya mengusulkannya. (Maaf, saya tidak bermaksud memulai perang, saya juga tidak tahu
saran itu tidak akan populer.) Saya pikir saya pernah melihat ini muncul sebelumnya
di forum, tetapi tidak menyadari bahwa itu sudah diberhentikan sebagai
ide buruk. Boleh saya tanya kenapa (Bisakah Anda mengarahkan saya ke sebuah utas?)

Terima kasih.
Pada tanggal 22 Apr 2015 11:09, "Steven G. Johnson" [email protected]
menulis:

Tolong, jangan terjebak dalam perang agama karena ejaan. @ dbeat
https://github.com/dbeach24 , tidak ada yang mengusulkan bahwa ab (c) menjadi
setara di Julia dengan b (a, c); Ini tidak akan terjadi.

Titik yang dapat diisi berlebih sangat penting untuk interop yang lancar dengan bahasa lain.
Itu alasan yang cukup.

-
Balas email ini secara langsung atau lihat di GitHub
https://github.com/JuliaLang/julia/issues/1974#issuecomment -95266671.

Salah satu alasan untuk tidak melakukan ini adalah karena a.b mencari b dalam lingkup a , sedangkan b saja mencari b dalam lingkup terlampir. Akan sangat membingungkan jika akses titik-titik terkadang tidak terlihat di objek sisi kiri.

Btw, itu dianggap sebagai fitur yang berfungsi di Julia tidak mencari di dalam objek tetapi dalam cakupan saat ini. Saya percaya ketakutan bahwa orang akan mulai menggunakan fungsi mencari di dalam objek adalah salah satu alasan yang menahan dot overloading kembali.

@toivoh , penerapan dot overloading apa pun akan menggunakan metode pengiriman yang sudah ada, sehingga tidak akan mengubah perilaku pelingkupan. @ dbeach24 , alasan dasar untuk tidak mendorong penggunaan a.b(c) sembarangan adalah jika Anda memiliki terlalu banyak sintaks untuk melakukan hal yang sama, bahasa dan pustaka menjadi berantakan. Lebih baik memilih satu ejaan dan tetap menggunakannya, dan pengiriman berganda Julia lebih menyukai b(a,c) karena lebih jelas bahwa b tidak "dimiliki" oleh a - b(a,c) metode ditentukan sama oleh _both_ a dan c .

Pengecualian terbesar, tentu saja, adalah untuk memanggil pustaka eksternal dalam bahasa seperti Python atau C ++, di mana bagus untuk dapat mencerminkan sintaks titik dari pustaka yang Anda panggil. (Sehingga misalnya orang dapat menerjemahkan dokumentasi dan contoh langsung ke Julia tanpa banyak berubah.)

Apakah saya semua basah, tetapi bukankah ab (arg) berarti mengambil fungsi anonim yang disimpan di bidang b dari a, dan kemudian mengevaluasinya dengan argumen yang diberikan?

dikirim dari iPhone saya

Pada 22 April 2015, pukul 17.06, Steven G. Johnson [email protected] menulis:

luar

@ScottPJones Yang saat ini berfungsi dengan baik, tetapi umumnya tidak dianggap gaya yang baik.

Saya tidak peduli tentang gaya atau tidak, saya hanya berpikir bahwa karena itu sudah memiliki makna, yang konsisten dengan cara kerja Julia (yaitu, dapat menyimpan fungsi anonim di bidang), itu adalah argumen yang baik _tidak_ untuk cobalah untuk memperlakukan ab (arg) seolah-olah itu adalah b (a, arg).
Saya _might_ memiliki kegunaan untuk memiliki struct (tipe) dengan anggota yang menyimpan fungsi anonim, di mana saya memuat fungsi yang ditulis dalam Julia dari database, dan kemudian melakukan parse pada mereka, dan menyimpan fungsi ke dalam objek ...
Apa gaya "Julian" yang lebih baik untuk melakukan sesuatu seperti itu?

Terima kasih!

@ScottPJones Saya rasa ada kesepakatan bahwa ini tidak boleh setara *.

Mungkin ada pengecualian untuk aturan "gaya", tetapi harus ada kasus yang menarik untuk digunakan, letakkan fungsi di bidang, sama seperti untuk kelebihan titik. Saya pikir masalahnya adalah orang tidak boleh melakukan ini secara sembarangan / demi itu / karena mereka bisa.

Itu mungkin sebuah contoh, tapi mungkin juga ada cara yang lebih baik (tentunya ini bukan satu-satunya cara) ...

99% + dari waktu lebih baik untuk mengirimkan pada tipe (a); tidak ada bidang fungsi, tidak ada titik yang berlebihan.

* _Namun, saya pikir semua orang tahu saat ini tanah akan ada paket yang melakukan hal itu ..._

Di D mereka bahkan memiliki nama "sintaks panggilan fungsi seragam" untuk a.b(arg) dan itu cukup populer, tapi saya pikir itu sangat bertentangan dengan fungsi generik, cara pengiriman ganda Julia bekerja. Jika fungsi yang dimaksud adalah anonim atau sepenuhnya diketik bebek, maka saya kira semuanya akan berfungsi, tetapi itu IMO yang cukup membatasi. Saya tidak berpikir ada banyak alasan untuk menyimpan bidang fungsi di dalam tipe komposit, kecuali karena kebiasaan dari bahasa OO tradisional berbasis kelas. Tempat yang lebih baik untuk menyimpan fungsi generik jika Anda memuatnya dari suatu tempat adalah di modul.

Tapi kita sudah cukup jauh dari "pertanyaan mendasar" sekarang. Saya juga mendukung untuk bersikap konservatif dengan cara kami mengizinkan getfield kelebihan beban, tidak mengizinkan kelebihan beban getfield pada modul, dan tidak terlalu mengkhawatirkan sintaks khusus untuk "getfield sejati".

@stevengj : Ya, dan seperti yang saya coba katakan, itulah salah satu alasan mendasar mengapa tidak akan pernah terjadi bahwa a.b(c) menjadi sama dengan b(a, c) , terlepas dari apa yang kita lakukan dengan dot overloading sebaliknya .

Ada beberapa diskusi di milis yang menyentuh ini. Dari sudut pandang saya, pos paling relevan (oleh @nalimilan) ke utas ini adalah: https://groups.google.com/d/msg/julia-users/yC-sw9ykZwM/-607E_FPtl0J

Menambahkan ke komentar @johnmyleswhite tentang kebijakan pribadi tentang kapan menggunakan fitur ini - menurut saya beberapa ide HTTP dapat berguna di sini, dan bahwa getfield() seharusnya tidak memiliki efek samping dan setfield!() harus idempoten (yaitu, memanggilnya beberapa kali dengan nilai yang sama harus memiliki efek yang sama seperti memanggilnya sekali). Tidak harus aturan keras dan cepat yang diberlakukan oleh kompiler, tetapi pedoman penggunaan untuk menjaga agar hal-hal tidak menjadi terlalu gila.

Saya telah memposting solusi menggunakan jenis parametrik dengan parameter penunjuk dan mengonversi untuk memanggil penyetel khusus saat menyetel bidang:
posting: https://groups.google.com/forum/#!topic/julia -users / _I0VosEGa8o
kode: https://github.com/barche/CppWrapper/blob/master/test/property.jl

Saya bertanya-tanya apakah saya harus menggunakan pendekatan ini dalam paket saya sebagai solusi hingga setfield! kelebihan beban tersedia, atau apakah terlalu banyak tekanan pada sistem tipe parametrik?

Saya ingin menyebutkan satu manfaat tambahan dari getfield / setfield! overloading, saya harap ini adalah tempat yang tepat untuk ini, maaf sebaliknya. (Topik terkait muncul di https://groups.google.com/forum/#!topic/julia-users/ThQyCUgWb_Q)

Julia dengan getfield / setfield! overloading akan memungkinkan implementasi fungsionalitas autoreload yang sangat elegan dalam paket _external_. (Lihat semua kerja keras yang harus dimasukkan ke dalam ekstensi autoreload IPython https://ipython.org/ipython-doc/3/config/extensions/autoreload.html untuk mendapatkan fungsi ini.) Ide dari autoreload adalah bahwa Anda dapat memodifikasi fungsi dan tipe dalam modul eksternal saat bekerja dengan REPL.

TLDR: getfield / setfield! overloading, kamus dan paket yang mirip dengan https://github.com/malmaud/Autoreload.jl harus melakukan triknya.


Untuk lebih spesifik, bayangkan sebuah paket yang mirip dengan Autoreload.jl yang melakukan hal berikut.

Anda pertama kali membuat modul M.jl:

module M
type Foo
  field1::Int64
end
bar(x::Foo) = x.field1 + 1.0
end

Di REPL, Anda mengetik

julia> using Autoreload2
julia> arequire("M")
julia> foo = Foo(42)

Kemudian Anda mengubah M.jl menjadi

module M
type Foo
  field1::Int64
  field2::Float64
end
bar(x::Foo) = x.field1+x.field2

Ini akan dimuat otomatis dan diubah menjadi

# type redefinition removed as already done by Autoreload.jl
const field2_dict = Dict{UInt64,Float64}()
setfield!(x::Foo, ::Field{:field2}, value) = field2_dict[object_id(x)] = value
getfield(x::Foo, ::Field{:field2}) = field2_dict[object_id(x)]
<strong i="25">@do_not_inline</strong> bar(x::Foo) = x.field1 + x.field2

dan kemudian di REPL Anda bisa melakukannya

julia> foo.field2 = 3.14
julia> println(bar(foo)) # prints 45.14

Performa tidak akan lebih buruk daripada dengan Python, jadi orang yang memigrasi alur kerja mereka dari autoreload IPython tidak akan kehilangan apa pun dalam hal performa. Dan setelah Anda memulai ulang REPL, Anda kembali ke kinerja penuh.

Saya bosan menulis a[:field][:field2][:morestuff](b[:random_stuff]) karena tidak benar-benar dapat dibaca. Jadi saya menulis makro kecil ini yang berfungsi untuk kasus penggunaan saya di 0.4 dan 0.5
https://github.com/sneusse/DotOverload.jl

TL; DR
Makro yang mengubah AST ekspresi a.b -> getMember(a, :b)

Menghapus dari 0,6, karena tidak ada konsensus bahwa ini adalah ide yang bagus dan ada proposal yang bertentangan tentang apa yang harus dilakukan dengan sintaks titik.

@ Keno : Apakah Anda memiliki link ke proposal yang bertentangan?

Jangan kira @Stefanarpun sudah menuliskannya, tapi saya berharap akan segera ada Julep tentangnya.

Saya menemukan object.fieldname lebih bagus daripada fungsi pengambil seperti fieldname(object) atau get_fieldname(object) . Mungkin memiliki object.fieldname (atau object$fieldname ) menjadi panggilan ke getpublicfield (mungkin dengan nama yang lebih baik) dan object..fieldname adalah getfield (pribadi) bisa menjadi pilihan yang baik. Dengan cara itu, tipe harus mendefinisikan getpublicfield alih-alih getter, dan mencoba melakukan object.fieldname harus memberikan id kesalahan, bidang tersebut bersifat pribadi (Ini akan menjadi pribadi jika tidak memiliki definisi getpublicfield ).

Saya menambahkan label keputusan. Masalah ini telah dibahas panjang lebar dan harus dilakukan atau tidak. Saat membaca ke # 5848, tampaknya @JeffBezanson @StefanKarpinski dan @stevengj menginginkan ini. Jika ya maka masalah ini harus mendapatkan tonggak sejarah agar tidak dilupakan. Jika tidak, tutup. Bagaimanapun saya pikir ini adalah perubahan yang harus dilakukan sebelum 1.0.

@JeffBezanson dan saya baru saja membahas ini kemarin. Kesimpulan tentatif: (i) ya, kita harus memiliki ini; (ii) jangan izinkan dot overloading untuk Module (yang akan ditangani secara khusus); (iii) tidak menyediakan sintaks khusus untuk Core.getfield (karena tidak ada kebutuhan mendesak untuk getfield kelebihan beban untuk memiliki nama yang sama dengan bidang "nyata"; yang terakhir dapat dimulai dengan garis bawah).

@stevengj : Kedengarannya seperti rencana yang masuk akal. Bisakah Anda menunjukkan apakah ini akan dibatasi untuk satu argumen atau jika versi multi argumen a.fieldname(b) juga harus didukung? Ini akan menarik kesimpulan dari pembahasan di atas. Selain itu, akan sangat bagus untuk memberi label pencapaian yang sesuai untuk ini (1.0?). Terima kasih!

Jeff dan saya tidak membahas kasus multi-argumen. Perasaan saya adalah bahwa kami mungkin juga mendukungnya, karena Anda tetap dapat mensimulasikannya dengan mengembalikan fungsi dari kasus no-arg (tetapi tidak penting untuk segera melakukannya karena alasan yang sama).

Saya menggunakan konverter untuk memberikan nilai dan memvalidasi data.
seperti ini:

abstract AbstractAge{T}
abstract AbstractPerson
type PersonAge <: AbstractAge{AbstractPerson} 
    value::Int64
end

Base.convert(t::Type{AbstractAge{AbstractPerson}}, value::Int64) =  begin
  if value < 140 && value > 0
    PersonAge(value) 
  else
     throw(ErrorException("ValueError"))
  end
end

type Person <: AbstractPerson
  age::AbstractAge{AbstractPerson}
end 

a = Person(32)
a.age = 67

Berikut implementasi 3 baris yang menyenangkan dari ini:

diff --git a/base/boot.jl b/base/boot.jl
index cd3ae8b..a58bb7e 100644
--- a/base/boot.jl
+++ b/base/boot.jl
@@ -266,6 +266,9 @@ Void() = nothing

 (::Type{Tuple{}})() = ()

+struct Field{name} end
+(::Field{f})(x) where {f} = getfield(x, f)
+
 struct VecElement{T}
     value::T
     VecElement{T}(value::T) where {T} = new(value) # disable converting constructor in Core
diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm
index b4cb4b5..59c9762 100644
--- a/src/julia-syntax.scm
+++ b/src/julia-syntax.scm
@@ -1685,7 +1685,7 @@
     (if (and (pair? e) (eq? (car e) '|.|))
         (let ((f (cadr e)) (x (caddr e)))
           (if (or (eq? (car x) 'quote) (eq? (car x) 'inert) (eq? (car x) '$))
-              `(call (core getfield) ,f ,x)
+              `(call (new (call (core apply_type) (core Field) ,x)) ,f)
               (make-fuse f (cdr x))))
         (if (and (pair? e) (eq? (car e) 'call) (dotop? (cadr e)))
             (make-fuse (undotop (cadr e)) (cddr e))

Pemikiran saya adalah bahwa a.b seharusnya benar-benar memanggil fungsi proyeksi Field{:b}() daripada getfield , sehingga Anda mendapatkan fungsi seperti x->x.a secara gratis. Itu juga memungkinkan getfield selalu berarti akses bidang tingkat rendah.

Implementasi di atas benar-benar berfungsi tetapi cukup sulit pada kompiler (sysimg + 5%, yang sebenarnya merupakan kejutan yang menyenangkan). Jadi ini akan memerlukan beberapa heuristik spesialisasi dan beberapa pengoptimalan awal perlu diperbarui, tetapi semoga ini akan dapat dilakukan.

Saya terkejut tes bisa lulus dengan implementasi itu. Bukankah semantik ini membuat codegen mustahil untuk mengoptimalkan referensi modul? Sepertinya itu juga akan membuat variabel global jauh lebih mahal (kecepatan dan memori). Tampaknya tidak terlalu mungkin hal ini muncul di sysimg. Meskipun degradasi inferensi sepertinya seharusnya membuat sysimg lebih kecil di sini, jadi 5% lebih buruk tidak terdengar seperti awal yang baik)

Ya, ada kode dalam jl_resolve_globals untuk mengubahnya menjadi GlobalRefs yang perlu diperbarui. Selain itu, ini harus disisipkan sehingga codegen dapat menanganinya. Sebagai kesimpulan, kita mungkin hanya membutuhkan kasus khusus yang mirip dengan tuple getindex .

kita mungkin hanya membutuhkan case khusus yang mirip dengan tuple getindex

Kasus khusus itu berharap dan mengasumsikan bahwa tidak ada metode yang akan didefinisikan yang berpotongan dengan tanda tangan metode getindex bawaan. Saya tidak melihat bagaimana kasus itu dapat diterapkan secara khusus di sini.

Saya akan menambahkan metode untuk ::Module dan mengatakan Anda tidak diizinkan untuk membayangi itu --- mungkin diberlakukan dengan kesalahan sebenarnya.

@JeffBezanson Apakah Anda memiliki cabang dengan tiga jalur tersebut? Saya mencoba menambahkannya sendiri ke bangunan lokal, tetapi julia tampaknya sudah pindah sekarang dan saya tidak bisa membuatnya berfungsi.

Saya harus mengatakan bahwa jika saya hanya memiliki satu keinginan untuk fitur di julia 1.0, ini saja. Ini akan menyelesaikan dua masalah lama yang saya miliki di Mimi dan Query .

Kasus untuk Query menurut saya cukup umum. Saya percaya seseorang dapat menulis versi NamedTuples dengan ini yang tidak harus menghasilkan tipe baru di makro, dan itu akan memungkinkan saya untuk menulis fungsi yang dihasilkan yang mengembalikan NamedTuple dengan satu set bidang yang dihitung dalam fungsi yang dihasilkan. Saya pikir itu akan memungkinkan saya untuk menulis versi tipe stabil dari blackrock / NamedTuples.jl # 4, yang sejauh ini merupakan batu sandungan terbesar saya di Query.jl saat ini.

Singkat cerita, ini akan menjadi super, super berharga untuk keseluruhan cerita penanganan data di Julia.

Apakah sintaks di bawah tersedia?

function (obj::MyType).plus(n::Int)
       return obj.val + n
end

Mengapa Anda menginginkan sintaks itu?

Ingin menulis obj.plus(n) alih-alih obj + n adalah beberapa sindrom Stockholm yang serius.

Ok, contoh yang diberikan adalah yang sepele (dan yang buruk).
Itu hanya sebuah ide untuk menggunakan sintaks ini daripada membebani getfield dan dapat menggunakan argumen.

Saya sangat memperhatikan fungsionalitasnya. Memiliki sintaks yang disingkat untuk membebani getfield tampaknya jauh lebih penting dan tidak layak untuk dibantah. Lebih lanjut, getfield dan setfield! langsung mencerminkan getindex dan setindex! dan begitu juga wajar di Julia. Akhirnya, penggunaan ekstensif gaya OO tidak benar-benar cocok untuk idiom Julia, dan saya rasa kami tidak ingin mendorongnya dengan sintaks definisi metode mirip-OO (tetapi lihat komentar saya di atas mengenai argumen).

Satu hal yang terpikir oleh saya adalah bahwa operator $ sudah tidak digunakan lagi. Sekarang, ini jelas membuat melakukan sesuatu seperti $(x, sym::Symbol) = ... sudah tersedia, tetapi kita juga bisa menghibur penulisan ulang sintaksis yang lebih bagus seperti:

x$y          => $(x, ::Type{Val{:y}})
x$z(args...) => $(x, ::Type{Val{:z}}, args...)

Saya pikir itu sudah mencakup sebagian besar kasus yang disebutkan dalam masalah ini tanpa melakukan overloading getfield penuh. Terus terang, operator . cukup jenuh secara semantik di Julia, jadi sesuatu seperti ini terasa lebih mudah untuk dicerna dan cukup nyaman untuk tetap berguna.

@quinnj , sudah diusulkan di # 18696.

Karena kita sudah memiliki . untuk akses lapangan, bagaimanapun, tampaknya tidak elegan untuk memiliki dua operator yang mirip dengan akses lapangan, satu dapat kelebihan beban dan satu tidak. Dan akan sedikit tidak wajar untuk panggilan antar-bahasa ke Python dan bahasa OO lainnya, yang hampir secara universal menggunakan . .

Saya tidak melihat interoperabilitas dengan bahasa lain sebagai argumen yang valid untuk memperkenalkan sesuatu seperti ini. Ini seperti mengatakan, "Kode Python terlihat seperti ini, jadi untuk bisa berpura-pura menjadi Python kita harus melakukan ini juga." Saya belum melihat argumen untuk ini yang akan membuat Julia sendiri lebih baik dan / atau lebih konsisten. Sudah pasti bahwa Julia tidak menyediakan sintaks x.f() gaya OOP; membiarkan hal-hal seperti itu meminta ketidakkonsistenan.

@stevengj , bagian dari mana saya berasal adalah kenyataan bahwa x.f _isn't_ field access. Tidak ada anggota lapangan sebenarnya f . Seluruh masalah ini adalah tentang membebani getfield dan saya pikir kekhawatiran utamanya adalah potensi kebingungan apakah x.f benar-benar mengacu pada suatu bidang atau melakukan sesuatu yang lain di bawah tenda.

Keuntungan dari penulisan ulang sintaksis yang saya usulkan adalah memenuhi cerita interop bahasa tanpa menimbulkan kebingungan tambahan dengan getfield; itulah yang saya maksud ketika saya menyebutkan . akan terlalu jenuh. Apakah x$f benar-benar jauh lebih rumit? Saya hanya tidak mengerti mengapa _has_ ini menggunakan operator titik.

Seluruh masalah ini adalah tentang membebani getfield dan saya pikir kekhawatiran utamanya adalah potensi kebingungan apakah x.f benar-benar mengacu pada suatu bidang atau melakukan sesuatu yang lain di bawah tenda.

Saya tidak berpikir ada potensi kebingungan dalam proposal tiga baris a.b selalu diturunkan menjadi panggilan fungsi proyeksi, dan tidak pernah menjadi panggilan getfield . Dan kemudian ada metode default / fallback di dasar untuk fungsi proyeksi yang memanggil getfield . Pengguna bisa menambahkan metode untuk tipenya sendiri ke fungsi proyeksi jika mereka mau. getfield selalu berarti akses bidang tingkat rendah dalam proposal itu, dan pengguna tidak akan menambahkan metode ke getfield (Saya berasumsi itulah yang Anda maksud dengan "membebani getfield"). Itu terlihat sangat bersih bagi saya. Karena itu, tidak jelas bagi saya apakah proposal itu masih ada atau tidak, sepertinya ada beberapa masalah compiler yang saya tidak mengerti :)

Saya sangat senang memiliki fitur ini (saya selalu menyukainya di Scala). Bidang pengaksesan IMHO secara langsung menjadi sangat alami di Julia (dibandingkan dengan, misalnya, cara Java dalam mendefinisikan getter dan setter secara defensif untuk semuanya). Tetapi tanpa kemampuan untuk menambahkan field "virtual", akan menjadi sangat sulit untuk mengembangkan / refactor struktur tanpa merusak banyak kode.

Saya tahu ini telah diberi tag v2.0, tapi saya ingin membahasnya lagi dengan harapan dapat mempertimbangkan kembali ini untuk v1.0. Fitur ini akan sangat berharga bagi pengembang paket / pustaka.

Saat membuat paket untuk pengguna, kelebihan pengakses atribut akan memungkinkan pengelola paket menyajikan antarmuka pengguna yang jauh lebih bersih. Saya berpendapat bahwa,

complex_data_structure.attribute

mudah untuk mengetik daripada

get(complex_data_structure, :attribute)

dimana get adalah fungsi yang dibutuhkan untuk mendapatkan data yang dijelaskan oleh :attribute .

Ini berpotensi juga meningkatkan kemampuan untuk ditemukan di REPL, dan akan menguntungkan kode bahasa dan eksplorasi data.

Selain itu, penyerang atribut penyetel juga akan sangat berharga. Perhatikan contoh berikut (sudah ditemukan di banyak skrip saya saat ini :)),

set(complex_data_structure, :attribute, modify_attribute(get(complex_data_structure, :attribute), additional_arguments))

Jika pengambil dan penyetel atribut disertakan sebagai bagian dari Julia, hal di atas akan menjadi berikut.

complex_data_structure.attribute = modify_attribute(complex_data_structure.attribute, additional_arguments)

Ini menurut pendapat saya jauh lebih mudah dibaca. (Catatan kaki: Saya bisa menggunakan komposisi pipa agar lebih mudah dibaca, tetapi additional_arguments lagi akan mempersulitnya lagi. Dan argumen di sini akan tetap berlaku.)

Lebih jauh, nilai-nilai pembatas yang diberikan oleh pengguna adalah kasus penggunaan yang sangat umum, dan ini akan sangat bagus untuk dimiliki di v1.0. Ini akan sangat meningkatkan antarmuka pengguna untuk banyak paket. Saat ini, sejauh pengetahuan saya, tampaknya tidak ada cara untuk melakukan hal berikut (dapatkah seseorang mengoreksi saya jika saya salah)?

module point

mutable struct Point
    x::Int
    y::Int
    function Point(x, y)
        if x < 0 || y < 0
            throw(error("Only non-negative values allowed"))
        end
        this = new(x, y)
    end
end

end
# point

p1 = point.Point(-1, 0)
# Only non-negative values allowed

# Stacktrace:
# [1] point.Point(::Int64, ::Int64) at ./In[30]:8
# [2] include_string(::String, ::String) at ./loading.jl:515

p1 = point.Point(0, 0);
p1.x = -1;
p1
# point.Point(-1, 0)

Konstruktor dapat membatasi input domain ke struktur yang tidak dapat diubah, namun pengguna masih dapat memasukkan nilai yang tidak valid. Pengambil dan penyetel atribut akan membantu di sini, karena mereka akan membuat umpan balik tentang validitas data di sini lebih cepat, dan ini akan membuat antarmuka yang jauh lebih bersih sebagai pengguna bahasa.

Ini juga akan sangat menguntungkan paket lain seperti PyCall.jl dan DataFrames.jl untuk membangun antarmuka yang bisa dibilang lebih baik dan lebih intuitif bagi pengguna. Penulis paket ini juga telah menyatakan minatnya di atas untuk memiliki fitur ini.

Pikiran? Membaca utas, tampaknya ini sudah cukup dekat dengan final. Saya penasaran apa pendapat penulis tentang ini?

Diimplementasikan dalam makro opsional tipe stabil di sini: https://github.com/bramtayl/DotOverloading.jl. Saya pikir itu memiliki potensi untuk Base.

@bramtayl , saya pikir harus meletakkan @overload_dots sebelum a.b ekspresi mengalahkan poin di sini.

Saya tidak menyarankan makro ditambahkan ke basis; Saya menyarankan strategi yang digunakan oleh makro untuk dimasukkan ke dalam parser. Namun, ini adalah makro yang dapat dijalankan di seluruh file atau bahkan diretas menjadi IDE.

Itu berarti a.b diurai menjadi Expr(:., :a, :(Val{:b}()) dan diturunkan menjadi get_field_overloadable(a, Val{:b}())

@bramtayl , lihat implementasi Jeff di

Satu masalah dengan overloading getfield adalah bahwa beberapa perpustakaan tidak memiliki antarmuka yang wajar ke internal struktur data. Ketika yang saya inginkan hanyalah mengubah titik data dalam suatu struktur sehingga saya dapat menjalankan kode saya dan membuat laporan saya untuk besok, saya tidak terlalu berharap untuk mengedit kode di perpustakaan tiga tingkat jauh ke dalam ketergantungan saya sehingga saya bisa untuk mengakses titik data secara langsung dengan cara yang wajar dan cepat. Saya ingin merasa memiliki kendali atas struktur data yang saya gunakan.

Poin kedua adalah ketika saya menggunakan getfield dan setfield, saya menginginkan harapan yang masuk akal bahwa saya mengakses struktur data secara langsung alih-alih beberapa mekanisme fungsi yang besar. Selain itu, kata kunci struct digunakan untuk mendefinisikan tipe, mengingatkan Anda tentang C, dan saya rasa ini memberi Anda harapan bahwa akses getfield harus langsung.

Jadi, saya pikir menggunakan operator $ sebagai gantinya adalah kompromi yang masuk akal karena tidak ada harapan bahwa akses langsung seperti di getfield, itu sudah menjadi karakter khusus dalam konteks lain dan karenanya tidak akan terlalu mengejutkan ketika digunakan dengan cara ini, akan mudah untuk tidak digunakan lagi karena hanya sedikit orang yang menggunakannya dan karena tidak akan berfungsi lagi, dan digunakan dengan cara yang sama di R.

Satu masalah dengan overloading getfield adalah bahwa beberapa perpustakaan tidak memiliki antarmuka yang wajar ke internal struktur data.

Implementasi @JeffBezanson dari atas tidak membebani getfield (atau setfield ).

Saya juga ingin memohon agar ini menjadi 1.0. Saya sering menemukan diri saya terbelah antara menulis getter / pengaturan untuk menjaga kode saya agak fleksibel (dan kemudian bertanya-tanya bagaimana menamainya), atau "mengizinkan / mendorong" pengguna kode saya untuk menggunakan akses lapangan langsung.

Menulis banyak getter dan setter di depan secara defensif tampaknya tidak terlalu Julian bagi saya - lagipula, Julia tidak memiliki bidang pribadi / terlindungi dan mendorong pengguna untuk mengakses bidang secara langsung. Lalu ada pertanyaan tentang bagaimana memberi nama banyak getter- / setter-functions tanpa mempertaruhkan banyak konflik dengan paket lain. Dan alternatifnya, menambahkan metode untuk getfield dan setfield! (atau yang serupa), yang dikirim dengan Val{:fieldname} atau lebih, juga tidak tampak ramah pengguna.

Di sisi lain, jika akses bidang langsung pada dasarnya mengunci semua struct selamanya, itu jelas tidak boleh didorong - terutama untuk kode berumur panjang. Penerapan @JeffBezanson tampaknya menjadi jalan keluar yang rapi dari dilema ini.

Benar, @davidanthoff. Saya harus menggabungkan kelebihan getfield dan kelebihan sintaks akses bidang . . Maksud saya overloading sintaks akses lapangan.

Ada beberapa cara yang kompatibel untuk menambahkan fitur ini, sehingga bisa ditambahkan dalam rilis 1.x, tetapi tidak terjadi di 1.0. Kami tidak memiliki desain yang selesai atau waktu untuk menerapkannya bahkan jika kami memilikinya.

Akan sangat mudah untuk menggunakan sintaks titik pada pointer ke struct (C-).

Sintaks pilihan saya adalah menggunakan titik untuk memindahkan dan mengonversi pointer saja, dan menggunakan [] untuk deref.

Jadi, struct somepair a :: Int b :: Int end, dan p :: Ptr {somepair}, lalu pa adalah penunjuk ke bidang, dan saya menuliskannya dengan pa [] = 3, atau membaca dengan x = pa [].

Selanjutnya kita hanya perlu meneruskan deklarasi tipe, dan mungkin cara untuk mengimpor header C, dan kemudian membungkus C menjadi sangat mudah.

Ps, untuk memperjelas, seperti:

function getfield(p::Ptr{T}, fn::Symbol) # dispatch on values of T and fieldname
ftype = fieldtype(T, fn)
offset = fieldoffset(T,fn)
return convert(Ptr{ftype}, p+offset)
end

getindex(p::Ptr{T}) where T = unsafe_load(p)
setindex!(p::Ptr{T}, v) where T = unsafe_store!(p, convert(T,v))

Untuk poin bonus, tipe dari runtime lib julia bisa diekspor, dan pointer_from_objref sebenarnya bisa memberi kita pointer yang diketik dengan baik untuk introspeksi yang lebih nyaman.

Saya merasa bahwa serikat pekerja harus bekerja secara otomatis, hanya dengan memiliki dua bidang dengan offset yang sama?

@chethega Saya rasa Anda sedang mencari https://github.com/JuliaLang/julia/pull/21912 , yang menyediakan fitur yang kira-kira sama, tanpa masalah seputar adopsi model memori C (pelanggaran jenis pukulan, di luar -bounds access, aliasing of interior pointers, dll, yang membatasi kemungkinan pengoptimalan kinerja dalam bahasa tersebut).

Apakah propagasi konstan membuat ini lebih bisa dilakukan?

Tolong ubah pencapaian menjadi 1.0! :)

@ Liso77 Apa maksud Anda? Ini sudah diterapkan pada git master, seperti yang dicatat dalam status saat menutup.

@nalimilan maaf kalau saya salah paham! Tapi saya pikir karena 1.x diberi label hal-hal yang ditunda yang akan diselesaikan setelah 1.0. Dan ini diselesaikan sekarang ...

Open source adalah komunitas yang terdesentralisasi. Tonggak pencapaian menetapkan apa yang harus diselesaikan dengan 1.0, tetapi kontributor dan mengerjakan apa pun yang mereka inginkan. Dalam hal ini seseorang menginginkan ini di 1.0, jadi mereka menyumbangkan kode untuk mendapatkannya di sana.

@ Liso77 Seperti yang saya pahami, ini tidak akan di v1.0. Tanggal pembekuan fitur Julia v1.0 ditetapkan ke 15 Desember, tetapi masalah ini ditutup pada tanggal 17 Desember jadi saya rasa kita dapat mengharapkannya dalam rilis 1.x. Pengembang inti dapat mengoreksi saya jika interpretasi saya salah.

Tidak, ini digabungkan menjadi master dan akan ada di rilis berikutnya.

:) Nah! Saya hanya berpikir bahwa baik untuk memberi label 1.0 apa yang akan keluar di 1.0. Jika tidak diinginkan maka maaf telah mengganggu! :)

Saya pikir file NEWS adalah cara yang lebih baik untuk melihat apa yang terjadi pada 1.0.

Tonggak pencapaian ditambahkan dengan arti "dapat diimplementasikan tanpa merusak kompatibilitas di seri rilis 1.x", tetapi karena kode sudah siap, kode itu tetap digabungkan sebelum fitur 1.0 dibekukan. Saya telah menghapus tonggak sejarah untuk kejelasan, tetapi pada titik ini semua yang digabungkan dalam master akan menjadi 1.0.

Terimakasih atas klarifikasinya! Ini menyenangkan! File NEWS juga sangat mencerahkan.

Terima kasih telah menghapus! Saya juga sangat senang ini akan hadir di 1.0! :)

Saya ingin tahu apakah ada cara untuk mendukung "bidang yang ditentukan secara dinamis" baru ini dalam pelengkapan otomatis, misalnya dengan membolehkan membebani fieldnames ?. Ini bisa sangat berguna, untuk penggunaan interaktif, misalnya ketika berhadapan dengan DataFrame s (dengan asumsi mereka akan mendukung df.column_name di masa mendatang) dengan banyak kolom dan / atau nama kolom yang panjang.

Saya kira saat ini REPL (tab-expansion), IJulia, dll. Lihat definisi tipe, bukan contohnya? Tapi mungkin itu bisa diubah, untuk penggunaan interaktif. Mungkin tidak mungkin untuk mendukung dalam IDE seperti Juno, karena instance tidak tersedia selama pengkodean.

@oschulz , Anda sudah dapat membebani fieldnames :


julia> struct Foo; foo; end

julia> fieldnames(Foo)
1-element Array{Symbol,1}:
 :foo

julia> Base.fieldnames(::Type{Foo}) = [:bar, :baz]

julia> fieldnames(Foo)
2-element Array{Symbol,1}:
 :bar
 :baz

Dan sepertinya fieldnames adalah tampilan REPL:

julia> x = Foo(3)
Foo(3)

julia> x.ba<tab>
bar baz

@yurivish benar - tetapi apakah "aman" untuk melakukannya, saat ini? Saya tidak yakin apa lagi yang mengandalkan fieldnames .

Jika tidak aman, seharusnya mungkin untuk menentukan complete_fieldnames(x) = fieldnames(x) , menggunakan complete_fieldnames untuk penyelesaian REPL, dan membebani itu untuk penyelesaian kustom.

Ya, inilah mengapa saya mengemukakan hal ini - jika ada sesuatu yang perlu diubah / ditambahkan di Base, sehingga REPL dan teman-teman dapat memanfaatkannya nanti. Mengingat fitur 0,7 membekukan ...

Inilah mengapa saya senang kita memanggil fungsi getproperty ; ini membantu menjelaskan bahwa itu tidak merujuk ke hal yang sama seperti fieldnames . Mengganggu fieldnames pasti bisa menyebabkan masalah serius.

Kita mungkin membutuhkan propertynames untuk memberikan nilai properti yang valid. Tentu saja, itu mungkin bukan konsep yang terdefinisi dengan baik, jadi mungkin kita menginginkan sesuatu yang lebih spesifik untuk diselesaikan.

Saya merasa ini mungkin memerlukan sedikit analisis dan masukan yang lebih dalam dari para ahli (terima kasih!) - haruskah kita membuka edisi baru untuk ini?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat