◆ パイプライン演算子があればメソッドいらない
◆ オブジェクト指向唯一の良いところだと思ってたメソッドいらないならオブジェクト指向じゃなくていいかも

オブジェクト指向っているのかな

オブジェクト言語を使う上で困らない程度にはオブジェクト指向がなにかは知ってますが 特別好きってほどではないです
Java 使いの人たちみたいにオブジェクト志向を崇拝する気は全く無いです
どっちかと言えば関数型言語のほうが興味があるけどいろいろ難しすぎるし適度にいいとこ取りできる系言語が好きです
そういった言語性とその他いろいろの結果 JavaScript が一番スキです

オブジェクト指向のいいところ

メソッドが使える

以上です


実際のところこれくらいしかメリットを感じません
オブジェクトというところにキーバリュー形式でデータを保持できるというのも好きですが JavaScript だとオブジェクトと呼ぶだけで Map とか Dictionary でもいいわけで 構造体とかレコードとかオブジェクト指向言語ですらない言語でも同様のものはあります
むしろクラス定義しないと好きに追加削除できないのが普通なので一般的には私の好きな特徴はオブジェクト指向のオブジェクトのものじゃない気もします

なのでやっぱりメソッドが使えるというところです

文字列の部分文字列を取り出したり 長さを取得するときに JavaScript はこう書けます

"foobarbaz".substr(2, 5).length
// 5

PHP だとこうなります

strlen(substr("foobarbaz", 2, 5))
// 5

JavaScript のほうが見やすいですよね
実行順に書いていけますし ネストせずフラットに書けます

オブジェクト指向言語だからこそオブジェクトがメソッドを持っていて こういう書き方ができるもので 非オブジェクト指向言語では関数にデータを渡して結果を受け取るものなので PHP のようなネストをするしか無いと思っていました
Lisp なんてカッコばかりで PHP での書き方をインデントで見やすくしただけのように見えますし

ですが 関数型の言語ではカリー化とか関数合成などが使いやすい形になっていて PHP で関数書くのとは全然違います
(※そこまで使ってるわけではありません 軽く使った感想です)

その中でも Elm や F# にあるパイプラインオペレータはかなり感動しました
これがあればメソッドなんていらなくなります

Pipeline Operator

パイプライン演算子は
|>

という記号の演算子です
左辺に値と右辺に関数を指定すると右辺の関数の引数に左辺を渡して実行されます

演算子 + を
function plus(left, plus){
return left + right
}
plus(1, 2) // 1 + 2
と書くなら

|> 演算子は
function pipe(left, right){
return right(left)
}

pipe(10, console.log) // 10 |> console.log
// 10

となります


F# だとこういう感じです
> [1; 2; 3; 4; 5] |> List.filter (fun n -> n % 2 = 0) |> List.map (fun n -> n * 5);;

val it : int list = [10; 20]

「;」 区切りですがこれがリストで JavaScript でいう配列です
これを List.filter に渡して 結果を List.map に渡します
JavaScript で書くと
;[1, 2, 3, 4, 5].filter(n => n % 2  === 0).map(n => n * 5)
// [10, 20]
となります

メソッドチェーンです
ほぼ同じことが出来ます

パイプライン演算子とメソッドチェーン

同じことができるならメソッドチェーンで良いとも言えるのですが パイプライン演算子で指定するのはは型とは関係ないただの関数です
型に関数を紐づけておくとメソッドだと

  • その型で出来ること一覧があるので探しやすい
  • その型のメソッドしか使えないので型を意識する

というメリットがありました

例えば Date 型だと getFullYear や toDateString がメソッドにありますが メソッドでなくただの関数だと parseInt も encodeURI も Uint8Array も全部が候補です
F# の例の List.map みたいに List は List.*** と決まっていればいいですがそうでないと何を使えるか機能一覧がわかりづらいです
JavaScript でいうと Date.prototype で一覧があるのでメソッド形式で呼び出しているものの あまり変わらないと言えます

const getFullYear = Function.prototype.call.bind(Date.prototype.getFullYear)
getFullYear(new Date("2018-04-20"))
// 2018

F# の例で言うと List.*** 以外にも List を引数にするものはあると思います
それらのすべてがどこにあるかはわかりませんが それは JavaScript でも同じことです

String.fromCharCode(102)
// f

これは Number 型を引数にしますが String に属しています
パイプライン演算子を使えれば こういう関数は関数呼び出しで Numer.prototype にあるものはメソッド呼び出しと形を変えなくても同じ書き方ができます

また ES5 までの JavaScript でよく使われていた
Array.protoype.slice.call(document.querySelectorAll("*"))

こういう呼び出し方があります
配列じゃないけど配列のメソッドを使う方法です
メソッドという考え方をせず全部がただの関数なら こういったことを当たり前にできます
型自体が何かじゃなくて中で持ってる値が関数が必要としているものに一致してれば何でも使えたほうがいいですからね

メソッドを取り出して指定の this で実行というと他言語だと Reflection 使えばできそうというものですが  Reflection が必要な言語は呼び出すだけで一苦労ですしパフォーマンスも落ちます
JavaScript はメソッドもただの関数としてプロパティに存在するので 場所指定して関数を呼び出すだけです
パイプライン演算子はないものの 値と関数ベースな感じがしますね

ただ関数を呼び出すとなると 上で書いた PHP 形式のネスト風になるのでパイプライン演算子が欲しくなります
現状だと メソッドを追加すればフラットで左から右に読めるように書けはします
しかしメソッドに入れるほどでも無い関数もあります
fetch の第一引数が文字列ですが文字列のメソッドに fetch があるのは違和感があります
また String.fromCharCode や Promise.all はそれによって作られる型を元に String や Promise のプロパティとなっているので Number や Array 型のメソッドにするのもなんか違う気がします
そういった事を考えずに済むところが メソッドチェーンではなくパイプライン演算子のほうが良いなと思うところです


またもうひとつのメソッドのメリットの型を意識するというものですが ゆるい動的型付けの JavaScript と PHP を比較すると JavaScript だと数値型に substr メソッドを使おうとするとエラーになるのでそこでどの型かを意識しますが PHP だと substr に数値型を入れても動きます
そういうゆるさもあって PHP ではその変数にどんな型が入ってるかを考えてないコードが多いです
動的型付け言語は好きですが そういう使い方は嫌いなのでメソッドのおかげで PHP みたいなひどいコードが減るのはメリットだと思うのです
あくまで初心者レベルの話なのでパイプライン演算子のメリットのほうが大きいです


他には パイプライン演算子に限らずメソッドではなくただの関数として扱うメリットにすると コールバック関数に渡しやすいということがあります
const trimmed= arr.map(e => e.trim())

const trim = Function.prototype.call.bind(String.prototype.trim)
const trimmed = arr.map(trim)
になります
下の 1 行目で trim は自分で作っていますが メソッドじゃなく関数ベースだったらなくていい部分です

そう考えると オブジェクト指向でメソッド使えるより関数型言語風にして パイプライン演算子などの関数を使いやすくする機能がある方が良いんじゃないかなと思いました

オブジェクトが良いと思える唯一のメソッドの存在価値がなくなりましたからね

パイプライン演算子:JavaScript の場合

まだ Stage1 ですが Proposal はあるので将来的に JavaScript で実装されそうです
新記法で既存の互換性問題もないですし 他言語にすでにあるものなので途中で NG にはならないんじゃないかと思います
https://github.com/tc39/proposal-pipeline-operator

ただ 引数の bind が絡むと関数型言語で書かれるほど見やすい書き方にはならなそうです
多くの関数型言語は呼び出しにカッコが不要です
また引数が足りないなら部分適用となって 一部の引数がすでにセットされた関数になります
あとから残りを渡せば実行されます

つまり
value
|> func arg
|> func arg
のように書けるわけです

JavaScript だと既存関数はカリー化されていないので

value
|> _ => func(arg, _)
|> func.bind(null, arg)

のようにしないといけないです

自作の関数なら
const func = x => y => x + y
value
|> func(arg)
|> func(arg)
とできますが カッコの無い見た目のシンプルさは勝てません
ここでは関数実行というより引数コレを設定するみたいなイメージでカッコないほうが好みなのですが関数実行している以上仕方ないです

ところで 見直してみると F# の例がカッコで囲まれて同じように見えてますが 関数実行のカッコじゃなくてラムダ式部分をまとめてるだけです
arg のような変数に代入されてれば不要です

パイプライン演算子:PHP にも

そういえば PHP にも標準と言われる古めのテンプレートエンジンの Smarty でパイプライン演算子風な関数呼び出しができました

{$var|json_encode}
{$str|substr:1:2|strlen}

みたいな使い方です

PHP 自体ではできません
また PHP の関数を呼び出せるのですが PHP の関数の引数順がダメ仕様なので

{foreach ","|explode:$array as $value}{$value}{/foreach}

こういうパイプになることも少なくないです

ところで PHP で派生の Hack にはパイプライン演算子があります
PHP の関数呼び出しが辛いのはよく感じることなのでパイプライン演算子が使えるのはすごいメリットだと思います
PHP7 で速度が改善されたのでいらない言語かと思ってたのですが こういう面を考えると PHP より優れた言語なのかもしれません
アロー関数もありますし

JavaScript でパイプライン演算子と関数結合する

新構文なので polyfill にはならないものの パイプライン演算子や関数結合 (>>, <<) 風なことをメソッドでできるようにしてみます
といっても これまでも「全部メソッドチェーンで済ませたい」みたいな感じで作ったものと大差ないです

Function.placeholder = Function._ = Symbol("bind-placeholder")
Object.assign(Function.prototype, {
after(fn, ...bind_args) {
const self = this
return function(...args) {
return fn(...resolveBind(bind_args, [self(...args)]))
}
},
before(fn, ...bind_args) {
const self = this
return function(...args) {
return self(fn(...resolveBind(bind_args, args)))
}
},
trunc(n) {
const self = this
return function(...args) {
return self(...args.slice(0, n))
}
},
pbind(...bind_args) {
const self = this
return function(...args) {
return self(...resolveBind(bind_args, args))
}
},
})
Object.assign(Object.prototype, {
pipe(fn, ...args) {
return fn(...resolveBind(args, [this]))
},
})

function resolveBind(a, b){
return a.map(e => e === Function.placeholder ? b.shift() : e).concat(b)
}

これがライブラリ部分です
関数の after/before メソッドでその関数の後に実行する関数や 前に実行する関数を設定します
after をチェインすると左から右に書いた関数を順に実行する関数ができます
trunc は引数を切り捨てます
map の引数に parseInt を渡したら 2 つ目の引数として渡されるインデックスが基数として扱われてちゃんと変換できません
こういうのを防ぐ目的です
pbind はパラメータバインドです
this というキーワードは使わない前提で通常の引数のみを bind します

つかってみた例
const call = Function.prototype.call
const map = call.bind(Array.prototype.map)
const substr = call.bind(String.prototype.substr)
const join = call.bind(Array.prototype.join)
const mul = (a, b) => a * b

const fn = map
.pbind(
Function._,
mul.pbind(7).after(String).after(substr, Function._, 0, 1)
)
.after(join, Function._, "")
.after(parseInt)
fn([1,2,3])
// 712

;[1, 2, 3]
.pipe(map, Function._, mul.pbind(7).after(String).after(substr, Function._, 0, 1))
.pipe(join, Function._, "")
.pipe(parseInt.truc(1))
// 712

map(["1", "2", "3"], parseInt.trunc(1))
// [1, 2, 3]

最初の部分は関数の準備です
その後 fn を定義して実行しているのと [1, 2, 3] をベースにメソッドチェーンしているのは実行内容は同じで 結合した関数を用意しておくか その場でパイプライン演算子代わりのメソッドで実行しているかです

あとはせっかく作る以上ちょっと便利にしようと思って placeholder に対応しています
Function._ を pbind や pipe で指定するとそこに後から渡される引数が入ります
map は配列→関数の順番の引数ですが 関数の方を固定として配列を受け取ったものにしたいときなどに使います