Perl 6 に colon pairs という構文がある。名前の通りコロンから始まる Pair のコンストラクタで、例えば次のように使う:
:foo(42) # foo => 42
:bar<yay> # bar => 'yay'; 値が括弧類を使うリテラルの場合は小括弧は不要
:baz # baz => True; 値を省略すると True になる
:!quux # quux => False; 実に Perl らしい否定形
:$var # var => $var
言う間でもないがこれは Hash を生成する場合のみならず、名前つき引数を渡す際にも便利である。 :$var
という形式は変数の (sigil を除いた) 名前がそのままキーになるので、Perl 5 でよくある f(foo => $foo, bar => $bar, ...)
といった繰り言をひとまとめにできる。 こういう構文を Punning (語呂合わせ、地口) と言ったりする。
対称的に関数のシグネチャも同様に書ける:
sub f(:$x, :$y) { $x - $y }
my $x = 42;
say f(:$x, :y(1)); # 41
say f(y => 1, :$x); # ditto; 名前つき引数なので順番は可変
JavaScript の場合
JavaScript は冗長な用語を好むので Punning より Destructuring Assignment (の省略構文) と言った方が通りが良いだろう。 Destructuring Assignment の正確な構文は複雑だが、「右辺のオブジェクトを構築するときに値を置くべき場所に識別子を置くと対応する値が束縛される」程度の認識で概ね正しい:
const { x: value } = { x: 42 }; // value === 42
そして代入しようとする識別子名がオブジェクトのキーと同じ場合は識別子を省略できる:
const { x: x } = { x: 42 } // x === 42
const { x } = { x: 42 }; // ditto; 上記の省略形
このようにオブジェクトを分解して代入できるのだから、オブジェクトの構築も対称にできるのが自然である:
const foo = 42;
const bar = 'yay';
const baz = true;
{ foo, bar, baz }; // { foo: foo, bar: bar, baz: baz } 即ち { foo: 42, bar: 'yay', baz: true }
OCaml の場合
Punning という用語を公式に使っているのは OCaml が有名だろう。対象はレコード型のフィールドと名前付き引数である。前二者の言語と違い静的型検査があるので安全である。
type record =
{ foo: int
; bar: string
; baz: bool
}
let f { foo; bar; baz } =
Printf.printf "foo: %d, bar: %s, baz: %b\n" foo bar baz
let () =
let foo = 42 and bar = "yay" and baz = true in
let r = { foo; bar; baz } (* { foo = foo; bar = bar; baz = baz } *) in
f r (* foo: 42, bar: yay, baz: true *)
let f ~x ~y = x - y
let () =
let x = 42 and y = 1 in
print_int (f ~x ~y); print_newline (); (* f ~x:x ~y:y = 41 *)
print_int (f ~y ~x); print_newline () (* ditto *)
フィールド名はモジュールに所属しているので、モジュール外からレコードを生成しようとすると微妙な構文になる:
module M = struct
type record =
{ foo: int
; bar: string
; baz: bool
}
end
(*
M.record 型の M.foo フィールドの値を変数 foo に束縛する。
bar, baz も同様だが、既に M に所属していることが明らかなのでモジュール名を省略可能。
*)
let f { M.foo; bar; baz } =
Printf.printf "foo: %d, bar: %s, baz: %b\n" foo bar baz
let () =
let foo = 42 and bar = "yay" and baz = true in
let r = { M.foo; bar; baz } in (* { M.foo = foo; M.bar = bar; M.baz = baz } *)
f r
コメント
コメントを投稿