Smalltalk 風 if

今回は、筆者の趣味に偏っています。

# そもそもオブジェクト指向はこうだった

以下は、Smalltalk での真偽値の実装の一部です。

`trueAlternativeBlock` や `falseAlternativeBlock` は Perl 6 ならそれぞれ `&true-alternative-block`、`&false-alternative-block` と表現されるものです。そして、`trueAlternativeBlock value` という式は、Perl 6 なら `&true-alternative-block()` に当たります。`^` は `return` です。

Smalltalk には条件分岐のための構文はありません。Smalltalk で真偽値を得る式を評価すると、`True` か `False` のシングルトンインスタンスが返ります。これらのインスタンスは、以下に定義されたように振る舞い、結果的に条件分岐が実現されます。

```smalltalk
True>>ifFalse: alternativeBlock
	^nil
```
```smalltalk
True>>ifFalse: falseAlternativeBlock ifTrue: trueAlternativeBlock
	^trueAlternativeBlock value
```
```smalltalk
True>>ifTrue: alternativeBlock
	^alternativeBlock value
```
```smalltalk
True>>ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock
	^trueAlternativeBlock value
```
```smalltalk
True>>not
	^false
```
```smalltalk
False>>ifFalse: alternativeBlock
	^alternativeBlock value
```
```smalltalk
False>>ifFalse: falseAlternativeBlock ifTrue: trueAlternativeBlock
	^falseAlternativeBlock value
```
```smalltalk
False>>ifTrue: alternativeBlock
	^nil
```
```smalltalk
False>>ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock
	^falseAlternativeBlock value
```
```smalltalk
False>>not
	^true
```

# Perl も負けていない

同じことは Perl で (Perl 5 でも)実現できます。Perl にはクロージャーがあるからです。

## 真クラス (T)、偽クラス (F)、両者の定義 (B.pm6)

```perl6:B.pm6
use v6;

class T { ... }
class F { ... }

my ($t, $f) = (T.new(), F.new());

class T
{
	my $._ = $t;

	method _if_(:&t = { self; }, :&f = { self; })
	{
		&t();
	}

	method not() { return $f; }
}

class F
{
	my $._ = $f;

	method _if_(:&t = { self; }, :&f = { self; })
	{
		&f();
	}

	method not() { return $t; }
}
```

## 真偽値クラスのテスト

```perl6:a.basic.t6
#!/usr/bin/env perl6

use v6;
use B;
use Test;
plan *;

is T._._if_, T._;
is T._._if_(:f({ 'ng' })), T._;
is T._._if_(:t({ 'ok' })), 'ok';
is T._._if_(:t({ 'ok' }), :f({ 'ng' })), 'ok';

is F._._if_, F._;
is F._._if_(:t({ 'ng' })), F._;
is F._._if_(:f({ 'ok' })), 'ok';
is F._._if_(:f({ 'ok' }), :t({ 'ng' })), 'ok';
```

```
$ perl6 -I. a.basic.t6
ok 1 - 
ok 2 - 
ok 3 - 
ok 4 - 
ok 5 - 
ok 6 - 
ok 7 - 
ok 8 - 
```

## 整数クラス

```perl6:I.pm6
use v6;
use B;

class I;

has Int $.Int;

my $._0 = I.new(0);
my $._1 = I.new(1);

method new($native)
{
	self.bless(*, :Int($native));
}

method pred()
{
	self.WHAT.new($.Int.pred);
}

method add(I $with)
{
	self.WHAT.new($.Int + $with.Int);
}

method times(I $by)
{
	self.WHAT.new($.Int * $by.Int);
}

multi method equals(I $cmp)
{
	self.equals($cmp.Int);
}

multi method equals(Int $cmp)
{
	($.Int == $cmp ?? T !! F)._;
}
```

# 例によって階乗

これらが本当に機能するか、おなじみの I.factorial を定義して試してみましょう。I クラスに次を追加します。

```perl6
method factorial()
{
	my $one = I._1; # レキシカルスコープのデモ。
	self.equals(0)\
		._if_(
			:t({ $one; }),
			:f({ self.times(self.pred.factorial); })
		)
	;
}
```

## テスト

```perl6:b.factorial.t6
#!/usr/bin/env perl6

use v6;
use I;
use B;
use Test;
plan *;

is I._0.factorial.equals(1), T._;
is I._1.factorial.equals(I._1), T._;
is I.new(2).factorial.equals(I.new(2)), T._;
is I.new(4).factorial.Int, 24;
```

```
$ perl6 -I. b.factorial.t6
ok 1 - 
ok 2 - 
ok 3 - 
ok 4 - 
```

次回は、以上のプリミティブを使ってループを定義してみます。

https://github.com/x19290/2012advent.p6/tree/master/d.st-if