Mencampur Atribut Pribadi dan Publik dan Aksesor di Raku

12
#Private attribute example
class C { 
    has $!w;                            #private attribute
    multi method w { $!w }              #getter method
    multi method w ( $_ ) {                 #setter method
        warn “Don’t go changing my w!”;   #some side action
        $!w = $_
    }  
}
my $c = C.new
$c.w( 42 )
say $c.w #prints 42
$c.w: 43
say $c.w #prints 43

#but not
$c.w = 44
Cannot modify an immutable Int (43)

sejauh ini, sangat masuk akal, dan kemudian

#Public attribute example
class C { 
    has $.v is rw    #public attribute with automatic accessors
}
my $c = C.new
$c.v = 42
say $c.v #prints 42

#but not
$c.v( 43 ) #or $c.v: 43
Too many positionals passed; expected 1 argument but got 2

Saya menyukai kedekatan penugasan '=', tetapi saya membutuhkan kemudahan untuk melakukan tindakan sampingan yang disediakan oleh berbagai metode. Saya mengerti bahwa ini adalah dua dunia yang berbeda, dan mereka tidak bercampur.

TETAPI - Saya tidak mengerti mengapa saya tidak bisa hanya pergi $ cv (43) Untuk mengatur atribut publik

  1. Saya merasa bahwa raku membimbing saya untuk tidak mencampur dua mode ini - beberapa atribut pribadi dan beberapa publik dan bahwa tekanannya adalah pada metode metode (dengan beberapa: gula dari usus besar) - apakah ini maksud dari desain Raku?
  2. Apakah saya melewatkan sesuatu?
p6tinggal
sumber
Anda bisa menggunakan Proxy
user0721090601
Bagaimana Anda bisa menggunakan Proksi? Maksudku, pengakses sudah mengembalikan wadah ketika is rwditentukan. Mengembalikan Proxy tidak akan mengubah jumlah parameter yang diijinkan pada accessor.
Elizabeth Mattijsen
@ ElizabethMattijsen Mungkin saya salah paham pertanyaannya. Tapi ini tampaknya bekerja untuk apa yang dia inginkan (memungkinkan keduanya = foodan .(foo)untuk pengaturan) dan mengaktifkan efek samping yang harus dilakukan dalam kedua kasus (tetapi tidak ketika hanya diambil): tio.run/…
user0721090601

Jawaban:

13

apakah ini maksud dari desain Raku?

Wajar untuk mengatakan bahwa Raku tidak sepenuhnya tidak terbuka di bidang ini. Pertanyaan Anda menyentuh dua tema dalam desain Raku, yang sama-sama layak untuk dibahas.

Raku memiliki nilai l kelas satu

Raku memanfaatkan banyak nilai-l sebagai hal kelas satu. Ketika kita menulis:

has $.x is rw;

Metode yang dihasilkan adalah:

method x() is rw { $!x }

Di is rwsini menunjukkan bahwa metode mengembalikan nilai-l - yaitu, sesuatu yang dapat ditugaskan. Demikian ketika kita menulis:

$obj.x = 42;

Ini bukan gula sintaksis: itu benar-benar panggilan metode, dan kemudian operator penugasan diterapkan untuk hasilnya. Ini berhasil, karena pemanggilan metode mengembalikan Scalarwadah atribut, yang kemudian dapat ditugaskan ke. Seseorang dapat menggunakan binding untuk membagi ini menjadi dua langkah, untuk melihat itu bukan transformasi sintaksis sepele. Sebagai contoh, ini:

my $target := $obj.x;
$target = 42;

Akan menugaskan ke atribut objek. Mekanisme yang sama ini ada di balik berbagai fitur lain, termasuk penugasan daftar. Sebagai contoh, ini:

($x, $y) = "foo", "bar";

Bekerja dengan membangun Listwadah berisi $xdan $y, dan kemudian operator penugasan dalam hal ini iterates setiap sisi berpasangan untuk melakukan penugasan. Ini berarti kita dapat menggunakan rwpengakses objek di sana:

($obj.x, $obj.y) = "foo", "bar";

Dan itu semua secara alami bekerja. Ini juga merupakan mekanisme di balik penugasan pada irisan array dan hash.

Satu juga dapat digunakan Proxyuntuk membuat wadah bernilai-l di mana perilaku membaca dan menulis itu di bawah kendali Anda. Dengan demikian, Anda bisa memasukkan aksi samping ke dalamSTORE . Namun...

Raku mendorong metode semantik atas "setter"

Ketika kami menggambarkan OO, istilah seperti "enkapsulasi" dan "menyembunyikan data" sering muncul. Gagasan utama di sini adalah bahwa model keadaan di dalam objek - yaitu, cara yang dipilihnya untuk merepresentasikan data yang diperlukan untuk mengimplementasikan perilakunya (metode) - bebas untuk berevolusi, misalnya untuk menangani persyaratan baru. Semakin kompleks objek, semakin membebaskan ini menjadi.

Namun, getter dan setter adalah metode yang memiliki koneksi implisit dengan negara. Meskipun kami mungkin mengklaim bahwa kami mencapai penyembunyian data karena kami memanggil metode, tidak mengakses keadaan secara langsung, pengalaman saya adalah bahwa kami dengan cepat berakhir di tempat di mana kode luar membuat urutan panggilan setter untuk mencapai operasi - yang merupakan suatu bentuk iri fitur anti-pola. Dan jika kita melakukan itu , cukup yakin kita akan berakhir dengan logika di luar objek yang melakukan campuran antara pengambil dan penyetel operasi untuk mencapai operasi. Sungguh, operasi ini seharusnya diekspos sebagai metode dengan nama yang menggambarkan apa yang sedang dicapai. Ini menjadi lebih penting jika kita berada dalam pengaturan bersamaan; objek yang dirancang dengan baik seringkali cukup mudah untuk dilindungi pada batas metode.

Yang mengatakan, banyak penggunaan classbenar-benar jenis catatan / produk: mereka ada untuk hanya mengelompokkan sekelompok item data. Bukan kebetulan bahwa .sigil tidak hanya menghasilkan accessor, tetapi juga:

  • Memilih atribut agar diatur oleh logika inisialisasi objek default (yaitu, a class Point { has $.x; has $.y; }dapat dipakai sebagai Point.new(x => 1, y => 2)), dan juga menjadikannya dalam .rakumetode dumping.
  • Memilih atribut ke .Captureobjek default , yang berarti kita dapat menggunakannya dalam merusak (misalnya sub translated(Point (:$x, :$y)) { ... }).

Mana hal-hal yang Anda inginkan jika Anda menulis dengan gaya yang lebih prosedural atau fungsional dan menggunakan classsebagai sarana untuk menentukan jenis rekaman.

Desain Raku tidak dioptimalkan untuk melakukan hal-hal pintar di setter, karena itu dianggap hal yang buruk untuk dioptimalkan. Ini melampaui apa yang diperlukan untuk tipe rekaman; dalam beberapa bahasa kita bisa berdebat kita ingin melakukan validasi dari apa yang ditugaskan, tetapi di Raku kita dapat beralih ke subsettipe untuk itu. Pada saat yang sama, jika kita benar-benar melakukan desain OO, maka kita menginginkan API perilaku yang berarti yang menyembunyikan model negara bagian, daripada berpikir dalam kaitannya dengan pengambil / penentu, yang cenderung mengarah pada kegagalan untuk berkolokasi. data dan perilaku, yang merupakan inti dari melakukan OO.

Jonathan Worthington
sumber
Poin yang bagus untuk mengingatkan Proxys (meskipun saya menyarankan itu ha). Satu-satunya waktu saya menemukan mereka sangat berguna adalah untuk saya LanguageTag. Secara internal, $tag.regionmengembalikan objek tipe Region(karena disimpan secara internal), tetapi kenyataannya, itu jauh lebih nyaman bagi orang untuk mengatakan $tag.region = "JP"lebih $tag.region.code = "JP". Dan itu benar-benar hanya sementara sampai saya dapat mengekspresikan paksaan dari Strdalam jenis, misalnya, has Region(Str) $.region is rw(yang meminta dua fitur yang direncanakan tetapi prioritas rendah terpisah)
user0721090601
Terima kasih @Jonathan telah meluangkan waktu untuk menguraikan alasan desain - Saya telah mencurigai beberapa aspek minyak dan air dan untuk badan non-CS seperti saya, saya benar-benar mendapatkan perbedaan antara OO yang tepat dengan keadaan tersembunyi dan penerapan kelas sebagai cara yang ramah untuk membangun pemegang data esoteris yang akan mengambil gelar PhD dalam C ++. Dan untuk user072 ... atas pemikiran Anda tentang Proxy s. Saya memang tahu tentang Proxy sebelumnya tetapi menduga bahwa mereka (dan / atau sifat-sifat) sengaja sintaksis cukup berat untuk mencegah pencampuran minyak dan air ...
p6steve
p6steve: Raku benar-benar dirancang untuk membuat hal yang paling umum / mudah menjadi sangat mudah. Ketika Anda menyimpang dari model umum, itu selalu mungkin (saya pikir Anda telah melihat tentang tiga cara berbeda untuk melakukan apa yang Anda inginkan sejauh ini ... dan pasti ada lebih banyak lagi), tetapi itu membuat Anda bekerja sedikit - hanya cukup untuk membuat yakin apa yang Anda lakukan benar-benar ingin Anda inginkan. Tetapi melalui sifat, proxy, slangs, dll, Anda dapat membuatnya sehingga Anda hanya perlu beberapa karakter tambahan untuk benar-benar mengaktifkan beberapa hal keren saat Anda membutuhkannya.
user0721090601
7

TETAPI - Saya tidak mengerti mengapa saya tidak bisa hanya pergi $ cv (43) Untuk mengatur atribut publik

Nah, itu benar-benar terserah arsitek. Tapi serius, tidak, itu bukan cara standar Raku bekerja.

Sekarang, sangat mungkin untuk membuat Attributesifat dalam ruang modul, sesuatu seperti is settable, yang akan membuat metode pengakses alternatif atau yang akan menerima nilai tunggal untuk mengatur nilai. Masalah dengan melakukan ini pada intinya adalah, apakah saya pikir pada dasarnya ada 2 kubu di dunia tentang nilai kembalinya mutator: apakah akan mengembalikan nilai baru , atau nilai lama ?

Silakan hubungi saya jika Anda tertarik menerapkan sifat seperti itu dalam ruang modul.

Elizabeth Mattijsen
sumber
1
Terima kasih @ Elizabeth - ini adalah sudut yang menarik - saya hanya memukul pertanyaan ini di sana-sini dan tidak ada pengembalian yang cukup dalam membangun sifat untuk melakukan trik (atau keterampilan di pihak saya). Saya benar-benar mencoba mencari tahu tentang praktik pengkodean terbaik dan menyelaraskannya - dan berharap bahwa desain Raku akan diselaraskan dengan praktik terbaik, yang saya kumpulkan.
p6steve
6

Saat ini saya curiga Anda baru saja bingung. 1 Sebelum saya menyinggung itu, mari kita mulai lagi dengan apa yang Anda tidak bingung tentang:

Saya suka kesegeraan =penugasan, tetapi saya membutuhkan kemudahan untuk melakukan tindakan sampingan yang disediakan berbagai metode. ... Saya tidak mengerti mengapa saya tidak bisa langsung pergi $c.v( 43 )Untuk menetapkan atribut publik

Anda dapat melakukan semua hal ini. Artinya Anda menggunakan =penugasan, dan banyak metode, dan "langsung saja $c.v( 43 )", semua pada saat yang sama jika Anda ingin:

class C {
  has $!v;
  multi method v                is rw {                  $!v }
  multi method v ( :$trace! )   is rw { say 'trace';     $!v }
  multi method v ( $new-value )       { say 'new-value'; $!v = $new-value }
}
my $c = C.new;
$c.v = 41;
say $c.v;            # 41
$c.v(:trace) = 42;   # trace
say $c.v;            # 42
$c.v(43);            # new-value
say $c.v;            # 43

Kemungkinan sumber kebingungan 1

Di belakang layar, has $.foo is rwmenghasilkan atribut dan metode tunggal di sepanjang baris:

has $!foo;
method foo () is rw { $!foo }

Namun di atas tidak sepenuhnya benar. Mengingat perilaku yang kita lihat, foometode autogenerated kompiler entah bagaimana dideklarasikan sedemikian rupa sehingga setiap metode baru dengan nama yang sama secara diam-diam membayangi itu. 2

Jadi, jika Anda menginginkan satu atau beberapa metode khusus dengan nama yang sama dengan atribut, Anda harus melakukannya secara manual mereplikasi metode yang dibuat secara otomatis jika Anda ingin mempertahankan perilaku yang biasanya menjadi tanggung jawabnya.

Catatan kaki

1 Lihat jawaban jnthn untuk penghitungan yang jelas, menyeluruh, otoritatif dari pendapat Raku tentang pengambil / setter pribadi vs publik dan apa yang dilakukannya di belakang layar ketika Anda mendeklarasikan getter / setter publik (yaitu menulis has $.foo).

2 Jika metode accessor autogenerated untuk atribut dideklarasikan only, maka Raku akan, saya kira, melemparkan pengecualian jika metode dengan nama yang sama dideklarasikan. Jika dinyatakan multi, maka tidak boleh dibayangi jika metode baru juga dinyatakan multi, dan harus membuang pengecualian jika tidak. Jadi accessor autogenerated sedang dideklarasikan dengan tidak onlyatau tidak multimelainkan dalam beberapa cara yang memungkinkan bayangan diam.

raiph
sumber
Aha - terima kasih @raiph - itu hal yang saya rasa hilang. Sekarang masuk akal. Per Jnthn Saya mungkin akan mencoba menjadi pembuat kode OO sejati yang lebih baik dan mempertahankan gaya penyetel untuk wadah data murni. Tapi senang mengetahui bahwa ini ada di kotak alat!
p6steve