use fields; と my TYPE EXPR;

my TYPE EXPR;という書式を初めて知ったので調べた。

追記

御叱り元


Re: use fields; と my TYPE EXPR; - Islands in the byte stream

fields::new()が遅いからですね。fields::new()の意味は、5.10.0より前のバージョンとの互換性のためと、実行時チェックのためのHash::Util::lock_keys()の適用です。5.10.0以上限定で実行時チェックが不要なら使う必要はありません。

field::newが遅いだけでした!
ベンチマークも取り直してみました。

use fields;

fieldsを使うと、フィールドに不正な代入があったら、"コンパイル時"にはじくことが出来る。

ハッシュキーをよくtypoする人にはおすすめだが、実行速度は遅い。
AUTOLOADを使って動的にgetter/setterを作って実行時エラーを出すようにしても、fieldsはそれ以上に遅い。

ただ、ハッシュをblessしたものとそっくり置き換えることが可能なので、
開発中はfields, リリース時は高速なハッシュをblessしたものを使うようにすると良いかもしれない...?



Hash::Utilのrestricted hashesの選択肢もあるが、また別のお話。

my TYPE EXPR;

動的型付けなのに、TYPEとか意味あるのか、とか思っていたが、fieldsと一緒に使うとかなんとか。
スーパークラスを実装したサブクラスであれば使えるよ、と明示するためのものといった印象。

 
package Hoge;
use strict;
use warnings;
use fields qw(XXX);

sub new{
	my $class = shift;
	my $self = fields::new($class);
	return $self;
}

package Fuga;
use strict;
use warnings;
use base qw(Hoge);
use fields qw(YYY);

sub new{
	my $class = shift;
	my $self = fields::new($class);
	$self->SUPER::new;
	return $self;
}

package main;

my Hoge $hoge = new Hoge();

$hoge->{XXX} = 100; # OK
# $hoge->{YYY} = 100; # Hogeでは未定義のフィールドなのでNG

my Fuga $fuga = new Fuga();

$fuga->{XXX} = 100; # OK
$fuga->{YYY} = 100; # OK

my Hoge $hogefuga = $fuga; # アップキャスト的な感じ
$hogefuga->{XXX} = 100; # OK
# $hogefuga->{YYY} = 100; # TYPE==Hoge のため、FugaのフィールドであるYYYへアクセスや変更はできない。

bless {},__PACKAGE__;とuse fields;とAUTOLOADを使った3種類のgetter/setterのベンチマーク

#!/usr/bin/perl

package Hoge;
use strict;
use warnings;
use fields qw(XXX YYY);

sub new{
	my Hoge $class = shift;
	return fields::new($class);
}

package Fuga;
use strict;
use warnings;

sub new{
	my Fuga $class = shift;
	return bless {
		XXX => 0,
		YYY => 0
	}, $class;
}

package Moge;
use strict;
use warnings;

sub new{
	my Moge $class = shift;
	return bless {
		XXX => 0,
		YYY => 0
	}, $class;
}

sub DESTROY{
	
}

sub AUTOLOAD{
	my $self = shift;
	our $AUTOLOAD;
	my $method = $AUTOLOAD;
	$method =~s/.+:://;
	if(exists $self->{$method}){
		eval qq{
			sub ${method}{my \$s=shift; \$s->{$method} = shift if \@_; return \$s->{$method};}
		};
		return $self->$method(@_);
	}
	die "Error";
}

package main;

use strict;
use warnings;
use Benchmark qw(timethese) ;


timethese(1000000, {
	'use fields' => sub{
		my $obj = new Hoge();
		$obj->{XXX} = 100;
		$obj->{YYY} = 200;
		my $x = $obj->{XXX};
		my $y = $obj->{YYY};
		die if $x * 2 != $y;
	},
	'sub AUTOLOAD' => sub{
		my $obj = new Moge();
		$obj->XXX(100);
		$obj->YYY(200);
		my $x = $obj->XXX;
		my $y = $obj->YYY;
		die if $x * 2 != $y;
	},
	'bless hash' =>  sub{
		my $obj = new Fuga();
		$obj->{XXX} = 100 if exists $obj->{XXX};;
		$obj->{YYY} = 200 if exists $obj->{YYY};
		my $x = $obj->{XXX} if exists $obj->{XXX};
		my $y = $obj->{YYY} if exists $obj->{YYY};
		die if $x * 2 != $y;
	}
});

Benchmark: timing 1000000 iterations of bless hash, sub AUTOLOAD, use fields...
bless hash: 3 wallclock secs ( 2.64 usr + 0.00 sys = 2.64 CPU) @ 379218.81/s
(n=1000000)
sub AUTOLOAD: 5 wallclock secs ( 4.82 usr + 0.00 sys = 4.82 CPU) @ 207468.88/
s (n=1000000)
use fields: 12 wallclock secs (13.52 usr + 0.00 sys = 13.52 CPU) @ 73937.15/s (
n=1000000)

ぜんぜん違いますね。



再計測(インスタンス生成は1回のみ)

#!/usr/bin/perl

package Hoge;
use strict;
use warnings;
use fields qw(XXX YYY);

sub new{
	my Hoge $class = shift;
	return fields::new($class);
}

package Fuga;
use strict;
use warnings;

sub new{
	my Fuga $class = shift;
	return bless {
		XXX => 0,
		YYY => 0
	}, $class;
}

package Moge;
use strict;
use warnings;

sub new{
	my Moge $class = shift;
	return bless {
		XXX => 0,
		YYY => 0
	}, $class;
}

sub DESTROY{	
	
}
sub AUTOLOAD{
	my $self = shift;
	our $AUTOLOAD;
	my $method = $AUTOLOAD;
	$method =~s/.+:://;
	if(exists $self->{$method}){
		eval qq{
			sub ${method}{my \$s=shift; \$s->{$method} = shift if \@_; return \$s->{$method};}
		};
		return $self->$method(@_);
	}
	die "Error";
}

package main;

use strict;
use warnings;
use Benchmark qw(timethese) ;


my $f = new Fuga();
my $m = new Moge();
my $h = new Hoge();


timethese(1000000, {
	'use fields' => sub{
		$h->{XXX} = 100;
		$h->{YYY} = 200;
		my $x = $h->{XXX};
		my $y = $h->{YYY};
		die if $x * 2 != $y;
	},
	'sub AUTOLOAD' => sub{
		$m->XXX(100);
		$m->YYY(200);
		my $x = $m->XXX;
		my $y = $m->YYY;
		die if $x * 2 != $y;
	},
	'bless hash' =>  sub{
		$f->{XXX} = 100 if exists $f->{XXX};
		$f->{YYY} = 200 if exists $f->{YYY};
		my $x = $f->{XXX} if exists $f->{XXX};
		my $y = $f->{YYY} if exists $f->{YYY};
		die if $x * 2 != $y;
	}
});

Benchmark: timing 1000000 iterations of bless hash, sub AUTOLOAD, use fields...
bless hash: 1 wallclock secs ( 1.14 usr + 0.00 sys = 1.14 CPU) @ 877963.13/s (n=1000000)
sub AUTOLOAD: 2 wallclock secs ( 2.89 usr + 0.00 sys = 2.89 CPU) @ 346500.35/s (n=1000000)
use fields: 1 wallclock secs ( 0.72 usr + 0.00 sys = 0.72 CPU) @ 1392757.66/s (n=1000000)

ぜんぜん違いますね!!