組込型にメソッドチェーンっぽいものを繋げてみるその2(D, C#, VB.Net, JavaScript, Python3 + Clojure, Ruby)

[これ](https://qiita.com/yosgspec/items/560b9f80397a66e2d671)の続き。

## 概要
前回で組み込み型にメソッドチェーンを組み込むことができることを確認した。
そこで今回はどこまで何をチェーンできるのか試してみた。
また、グローバルを汚染することになるjsに対し対応策を考えてみた。

## 実装要件
下記のモジュールを実装し、

```csharp
10
.buy()
.buy()
.puts()
.gets("> ")
.insertStr("echo> ")
.puts()
.gets()
```
のように処理を行う。

### ExtendObject モジュール
```csharp
int object.put()
```
インスタンスの内容を改行無しで標準出力する。
戻り値は0を返す。

```csharp
int object.puts()
```
インスタンスの内容を改行有りで標準出力する。
戻り値は0を返す。

```csharp
string string.insertStr(string)
```
Stringインスタンスにの手前に文字列を結合して返す。

```csharp
double number.buy()
```
Numberインスタンスの値を2倍にして返す。

```csharp
string int.gets(string)
Promise<string> int.gets(string)
```
標準入力を返す。
インスタンスの値は使用しない。
JavaScriptのみ戻り値の方はPromise型とする。

```csharp
Promise<void> object.gets(string)
```
プログラムを終了させる。
JavaScriptのみ実装。

# 実装
## D言語
```d:Program.d
import ExtendObject;
mixin ExtendObject;

void main(){
	10
	.buy
	.buy
	.puts
	.gets("> ")
	.insertStr("echo> ")
	.puts
	.gets;
}
```
```d:ExtendObject
module ExtendObject;

template ExtendObject(){
	import std.stdio;
	template put(T){
		int put(T self){
			write(self);
			return 0;
		}
	}

	template puts(T){
		int puts(T self){
			writeln(self);
			return 0;
		}
	}

	string insertStr(string self,string s){
		return s~self;
	}

	template buy(T){
		T buy(T self){
			return self*2;
		}
	}

	string gets(int self){
		return readln();
	}

	string gets(int self,string s){
		write(s);
		return readln();
	}
}
```
名前空間代わりにtemplate使っていること以外は至って順当な実装。

## <span>C#</span>
```csharp:Program.cs
using ExtendObject;

static class Program{
	static void Main(){
		10
		.buy()
		.buy()
		.puts()
		.gets("> ")
		.insertStr("echo> ")
		.puts()
		.gets();
	}
}
```
```csharp:ExtendObject
using System;
using static System.Console;

namespace ExtendObject{
	static class ObjectChain{
		static public int put(this object self){
			Write(self);
			return 0;
		}

		static public int puts(this object self){
			WriteLine(self);
			return 0;
		}

		static public string insertStr(this string self,string s){
			return s+self;
		}

		static public double buy(this object self){
			return Convert.ToDouble(self)*2;
		}

		static public string gets(this object self){
			return ReadLine();
		}

		static public string gets(this object self,string s){
			Write(s);
			return ReadLine();
		}
	}
}
```
数値型をObject型としていい加減に扱っていること以外は普通。前回からそのまま拡張。

## VB.Net
```vbnet:Program
Option Infer On
Option Strict On
Imports project.ExtendObject

Module Program
	Sub Main
		Call 10 _
		.buy _
		.buy _
		.puts _
		.gets("> ") _
		.insertStr("echo> ") _
		.puts _
		.gets
	End Sub
End Module
```
```vbnet:ExtendObject
Option Infer On
Option Strict On
Imports System.Console
Imports System.Runtime.CompilerServices

NameSpace ExtendObject
	Module ObjectChain
		<Extension()>
		Function put(self As Object) As Integer
			Write(self)
			Return 0
		End Function

		<Extension()>
		Function puts(self As Object) As Integer
			WriteLine(self)
			Return 0
		End Function

		<Extension()>
		Function insertStr(self As String,s As String) As String
			Return s & self
		End Function

		<Extension()>
		Function buy(self As Object) As Double
			Return CDbl(self)*2
		End Function

		<Extension()>
		Function gets(self As Integer) As String
			Return ReadLine
		End Function

		<Extension()>
		Function gets(self As Integer,myPrompt As String) As String
			Write(myPrompt)
			Return ReadLine
		End Function
	End Module
end NameSpace
```
プログラムについては大体C#と一緒。
しかし、数字から書き始めると行数がどうとか怒られたのはびっくり。
あとはビルドする環境(MSBuild, .Net Core他)次第でモジュールのImports名が変わるらしいので注意。

```vbnet:
Imports ExportObject
```
だったり、

```vbnet:
Imports project.ExportObject
```
だったりする。
ちなみにこの時の"projectはプロジェクト(.vbploj)名(ファイル名?)を指す。

## JavaScript(Node.js)
```js:Program.js
"use strict";
const ExtendObject=require("./ExtendObject");

new class Program{
	constructor(){
		ExtendObject.using(async()=>{
			await 10.
			.buy()
			.buy()
			.puts()
			.gets("> ").then(v=>v
			.insertStr("echo> ")
			.puts()
			.gets()).then(v=>v
			.exit());
		});
	}
};
```
```js:ExtendObject
"use strict";

module.exports=(()=>{
	const rl=require("readline").createInterface(process.stdin,process.stdout);
	const util=require("util");

	const ObjectChain={
		put:[Object,function(){
			process.stdout.write(util.format(this.valueOf()));
			return 0;
		}],

		puts:[Object,function(){
			console.log(this.valueOf());
			return 0;
		}],

		insertStr:[String,function(s){
			return s+this.valueOf();
		}],

		buy:[Number,function(){
			return this.valueOf()*2;
		}],

		gets:[Number,(myPrompt="")=>new Promise(resolve=>{
			rl.setPrompt(myPrompt);
			rl.prompt();
			rl.once("line",resolve);
		})],

		exit:[Object,()=>{
			process.exit();
			return Promise.resolve();
		}]
	};

	return class{
		static open(){
			for(var v in ObjectChain){
				if(ObjectChain[v][0].prototype[v]!=null){
					throw new RangeError("Property already exists.");
				}
				ObjectChain[v][0].prototype[v]=ObjectChain[v][1];
			}
		}

		static close(){
			for(var v in ObjectChain){
				delete ObjectChain[v][0].prototype[v];
			}
		}

		static using(fn){
			if(typeof fn=="function"){
				this.open();
				const fnCall=fn();
				if(toString.call(fnCall)=="[object Promise]"){
					return fnCall.then(()=>
						this.close()
					);
				}
				else this.close();
			}
			else throw new TypeError("Type requires function.");
		}
	};
})();
```
前回から最も手を加えたのがこの実装。
結局、グローバルオブジェクトの汚染は防げないものの、
拡張メソッドの存在確認と処理後のメソッド破棄で安全にしたつもり。
usingメソッドにコールバック関数投げ込むかopen・closeメソッドの間で使用可能。
getsメソッドの戻り値はNode.jsのため致し方なくPromiseを返却。

## Python3
```py3:Program.py
from ExtendObject import *

class Program:
	@staticmethod
	def main():
		(
			f_(10)
			.buy()
			.buy()
			.puts()
			.gets("> ")
			.insertStr("echo> ")
			.puts()
			.gets()
		)

if __name__=="__main__":
	Program.main()
```
```py3:ExtendObject.py
class o_:
	def put(self):
		print(self,end="")
		return i_(0)

	def puts(self):
		print(self)
		return i_(0)

class s_(str,o_):
	def insertStr(self,s):
		return s_(s+self)

class n_(o_):
	def buy(self):
		return f_(self*2)

class f_(float,n_):
	pass

class i_(int,n_):
	def gets(self,myPrompt=None):
		return s_(
			input() 
		if myPrompt is None else 
			input(myPrompt)
		)
```

前回同様にプリミティブ型を継承した拡張型を使用。
値を返す時は必ず拡張型のコンストラクタを使用することだけ注意した。
ここからは余り関係ないことだけど、setup.pyでプロジェクトを作成したとき、

```py3
from ExtendObject import *
```
じゃなくて、

```py3:
from .ExtendObject import *
```
としなきゃいけなかったのはなかなかハマってしまった。


以下、おまけ。

## Ruby
```rb:
class ExtendObject
	private
	module ObjectChain
		def put
			print self
			return 0
		end

		def _puts
			puts self
			return 0
		end
	end

	module StringChain
		def insertStr(s)
			return s+self
		end
	end

	module NumericChain
		def buy
			return self*2
		end
	end

	module IntegerChain
		def _gets(myPrompt="")
			print myPrompt
			return gets
		end
	end

	def self.exclude(obj,mod)
		mod.instance_methods(false).each{|v| 
			obj.class_eval{undef_method v}
		}
	end

	public
	def self.open
		Object.include ObjectChain
		String.include StringChain
		Numeric.include NumericChain
		Integer.include IntegerChain
	end

	def self.close
		exclude Object,ObjectChain
		exclude String,StringChain
		exclude Numeric,NumericChain
		exclude Integer,IntegerChain
	end

	def self.using(fn)
		open
		fn.call
		close
	end
end

Class.new{
	def initialize
		ExtendObject.using lambda{
			10
			.buy
			.buy
			._puts
			._gets("> ")
			.insertStr("echo> ")
			._puts
			._gets
		}
	end
}.new()
```
クラスベースかプロトタイプベースかの違いはあるけどアプローチはJavaScriptと一緒。
それゆえにJavaScriptと同様の懸念がありそう。

#### 追記:
コメントに@raccyさんよりご意見頂きました。
Rubyには[refine](https://blog.fenrir-inc.com/jp/2014/07/refine.html)という機能があり、これを使うことで安全にオブジェクトの拡張ができるそうです。

## Clojure
```clj:
(defn put[self]
	(print self)
	0
)

(defn puts[self]
	(println self)
	0
)

(defn insertStr[self,s]
	(str s self)
)

(defn buy[self]
	(* self 2)
)

(defn gets
	([self]
		(read-line)
	)
	([self s]
		(print s)
		(read-line)
	)
)

((fn[]
	(->
		10
		(buy)
		(buy)
		(puts)
		(gets "> ")
		(insertStr "echo> ")
		(puts)
		(gets)
	)
))
```
Lisp系言語でチェーンはどうするのかなぁと思っていたけどこんな解決策があるんだね。
面白い。こういう機能がデフォルトで組み込まれているのもCommonLispやSchemeに比べて新しい言語ならではって感じ。


以上、ここまで。