Mengapa beberapa pengguna mengutip nama kelas di Perl?

12

Melihat Type::Tiny, saya melihat bahwa nama kelas dalam panggilan ke Type::Tiny->newdikutip dalam dokumen resmi,

my $NUM = "Type::Tiny"->new(
   name       => "Number",
   constraint => sub { looks_like_number($_) },
   message    => sub { "$_ ain't a number" },
);

Kenapa ini? Apakah ini sekadar gaya? Apakah ada konsekuensi kinerja untuk praktik ini?

Evan Carroll
sumber

Jawaban:

7

Ambil contoh yang lebih sederhana

package Foo { sub new { die 7  } };
package Bar { sub new { die 42 } };
sub Foo { "Bar" }
Foo->new();

Dalam contoh di atas, konstanta Foomenyelesaikan ke "Bar", jadi ini panggilan "Bar"->newtidak "Foo"->new. Bagaimana Anda menghentikan penyelesaian subrutin? Anda bisa mengutipnya.

"Foo"->new();

Adapun implikasi kinerja, hal-hal tidak menjadi lebih buruk dengan menggunakan string daripada kata pengantar. Saya telah mengkonfirmasi bahwa pilihan yang dihasilkan O=Deparseadalah sama. Jadi sebagai aturan umum, tampaknya akan lebih baik untuk mengutip Classnames jika Anda menghargai kebenaran.

Ini disebutkan dalam Programming Perl, (sayangnya dalam konteks doa metode tidak langsung )

... jadi kami akan memberi tahu Anda bahwa Anda hampir selalu bisa lolos dengan nama kelas yang kosong, asalkan ada dua hal yang benar. Pertama, tidak ada subrutin dengan nama yang sama dengan kelas. (Jika Anda mengikuti konvensi bahwa nama subrutin seperti newmulai huruf kecil dan nama kelas seperti ElvenRingmulai huruf besar , ini tidak pernah menjadi masalah). Kedua, kelas telah dimuat dengan salah satu dari

use ElvenRing;
require ElvenRing;

Salah satu deklarasi ini memastikan Perl tahu ElvenRingadalah nama modul, yang memaksa setiap nama telanjang seperti newsebelum nama kelas ElvenRingdiinterpretasikan sebagai pemanggilan metode, bahkan jika Anda telah mendeklarasikan newsubrutin Anda sendiri dalam paket saat ini.

Dan, itu masuk akal: kebingungan di sini hanya dapat terjadi jika subrutin Anda (biasanya huruf kecil) memiliki nama yang sama dengan kelas (biasanya huruf besar). Ini hanya dapat terjadi jika Anda melanggar konvensi penamaan di atas.

tldr; mungkin merupakan ide yang bagus untuk mengutip nama kelas Anda, jika Anda mengetahuinya dan Anda menghargai kebenaran dari kekacauan.

Catatan: sebagai alternatif Anda dapat menghentikan resolusi sebuah kata kunci pada suatu fungsi dengan menempelkannya ::pada akhir kata tersebut, misalnya di atas Foo::->new.


Terima kasih kepada Ginnz pada reddit karena menunjukkan hal ini kepada saya , dan untuk Toby Inkster atas komentarnya (meskipun itu tidak masuk akal bagi saya saat pertama kali membaca).

Evan Carroll
sumber
2
Atau Foo::->new, seperti yang saya pelajari dari ikegami.
mob
@mob ya, buku Perl menyebutkan itu juga (secara eksplisit), tidak yakin apakah saya harus meletakkannya di sana atau tidak? lol.
Evan Carroll
@ mob saya menambahkan itu juga, sebagai catatan kaki. Meskipun saya tidak yakin saya menyukainya.
Evan Carroll
1
Foo::memiliki keunggulan yang diperingatkan jika Foobukan paket yang ada
ikegami
1
Coba :: Tiny adalah contoh yang lebih baik daripada Foo. Dalam menggunakan kode Foo->Anda, Anda dapat diharapkan untuk mengetahui bahwa tidak ada subrutin seperti itu dalam paket Anda. Tetapi jika Anda menggunakan Try :: Tiny dan sejumlah cpan lain / modul eksternal lainnya, siapa bilang jika salah satunya menggunakan paket Try, dan jika demikian, jika ada sub Tiny di dalamnya?
ysth
0

Secara eksplisit mengutip nama kelas daripada menggunakan kata kunci (yang diperlakukan sebagai string) adalah salah satu dari tiga cara untuk menghindari ambiguitas sintaksis. Itu Metode Kelas Meminta dari dokumentasi perlobj menjelaskan.

Karena Perl memungkinkan Anda menggunakan kata kunci untuk nama paket dan nama subrutin, kadang-kadang kata tersebut diartikan salah. Misalnya, konstruk Class->new()dapat diartikan sebagai salah satu 'Class'->new()atau Class()->new(). Dalam bahasa Inggris, interpretasi kedua dibaca sebagai "panggil subrutin bernama Class(), lalu panggil new()sebagai metode pada nilai balik dari Class()." Jika ada subrutin bernama Class()dalam namespace saat ini, Perl akan selalu menafsirkanClass->new() sebagai alternatif kedua: panggilan ke new()objek yang dikembalikan oleh panggilan ke Class().

Lihat kasus aneh ini dalam aksi dengan demo di bawah ini.

#! /usr/bin/env perl

use strict;
use warnings;

sub Type::Tiny { print "Returning Bogus\n" ; return "Bogus" }

sub Type::Tiny::new { print "Type::Tiny::new\n" }

sub Bogus::new { print "Bogus::new\n" }

my $class = "Type::Tiny";

Type::Tiny->new;
Type::Tiny::->new;
"Type::Tiny"->new;
$class->new;

Outputnya adalah

Bogus yang kembali
Bogus :: baru
Ketik :: Kecil :: baru
Ketik :: Kecil :: baru
Ketik :: Kecil :: baru

Bagian dokumentasi yang disebutkan di atas menunjukkan bagaimana melindungi dari perilaku yang mengejutkan atau kesalahan yang tidak disengaja.

Anda bisa memaksa Perl untuk menggunakan interpretasi pertama ( yaitu , sebagai pemanggilan metode pada kelas bernama "Class") dalam dua cara. Pertama, Anda dapat menambahkan ::ke nama kelas:

Class::->new()

Perl akan selalu menafsirkan ini sebagai pemanggilan metode.

Atau, Anda dapat mengutip nama kelas:

'Class'->new()

Tentu saja, jika nama kelas dalam skalar Perl akan melakukan hal yang benar juga:

my $class = 'Class';
$class->new();

Menerapkan pertanyaan Anda, semua panggilan di bawah ini setara.

Type::Tiny::->new(  );

"Type::Tiny"->new(  );

my $class = "Type::Tiny";
$class->new(  );

Menambahkan ::sampai akhir memiliki keuntungan menghasilkan peringatan yang bermanfaat. Katakanlah Anda tidak sengaja mengetik

Type::Tinny::->new;

Itu menghasilkan

Bareword "Type :: Tinny ::" mengacu pada paket yang tidak ada di ./try line 15.
Tidak dapat menemukan metode objek "baru" melalui paket "Type :: Tinny" (mungkin Anda lupa memuat "Type :: Tinny"?) Di ./try line 15.
Greg Bacon
sumber