関数型プログラミングの専門用語


関数型プログラミングには多くの利点があり、その人気は常に高まっています。 しかし、プログラミングパラダイムのように、FPには独自の専門用語があります。 FPに精通しているすべての人のために小さな辞書を作成することにしました。


例ではJavaScript ES2015を使用しています。 ( なぜJavaScriptなのか?


素材の作業は継続されます。 プルリクエストを元の英語のリポジトリに送信します。


このドキュメントでは、必要に応じてFantasy Land仕様の用語を使用しています。


アリティ


関数の引数の数。 単項、二項、三項(単項、二項、三項)などの単語から。 これは、「-ary」と「-ity」の2つの接尾辞で構成されるため、通常とは異なる単語です。 たとえば、加算は2つの引数を取るため、これはバイナリ関数、またはアリティが2に等しい関数です。 ギリシャ語のルートがラテン語よりも優先される場合、「ダイアディック」という用語が時々使用されます。 任意の数の引数をとる関数は、それぞれvariadicと呼ばれます。 しかし、バイナリ関数は、カリー化または部分的な適用を除いて、2つだけの引数を取ることができます。


const sum = (a, b) => a + b const arity = sum.length console.log(arity) // 2 // The arity of sum is 2 

高階関数


関数を引数として受け取り、関数を返す関数。


 const filter = (predicate, xs) => { const result = [] for (let idx = 0; idx < xs.length; idx++) { if (predicate(xs[idx])) { result.push(xs[idx]) } } return result } 

 const is = (type) => (x) => Object(x) instanceof type 

 filter(is(Number), [0, '1', 2, null]) // [0, 2] 

部分適用


関数の部分的な使用とは、元の関数のいくつかの引数を事前に入力して新しい関数を作成することを意味します。


 // Helper to create partially applied functions // Takes a function and some arguments const partial = (f, ...args) => // returns a function that takes the rest of the arguments (...moreArgs) => // and calls the original function with all of them f(...args, ...moreArgs) // Something to apply const add3 = (a, b, c) => a + b + c // Partially applying `2` and `3` to `add3` gives you a one-argument function const fivePlus = partial(add3, 2, 3) // (c) => 2 + 3 + c fivePlus(4) // 9 

また、JSでは、 Function.prototype.bindを使用してFunction.prototype.bindを部分的に適用できます。


 const add1More = add3.bind(null, 2, 3) // (c) => 2 + 3 + c 

予備的なデータ準備のおかげで、部分的なアプリケーションはより複雑な機能からより簡単な機能を作成するのに役立ちます。 カリー化を伴う関数は、部分的な使用を自動的に実行します。


カレー


複数の引数を取る関数を、一度に1つの引数を取る関数に変換するプロセス。


関数が呼び出されるたびに、1つの引数を取り、すべての引数が処理されるまで1つの引数を取る関数を返します。


 const sum = (a, b) => a + b const curriedSum = (a) => (b) => a + b curriedSum(40)(2) // 42. const add2 = curriedSum(2) // (b) => 2 + b add2(10) // 12 

自動カレー


複数の引数をとる関数の新しい関数への変換。 指定した数よりも少ない数の引数を新しい関数に渡すと、残りの引数を取る関数が返されます。 関数が正しい数の引数を受け取ると、実行されます。


アンダースコア、lodash、およびramdaにはcurry機能があります。


 const add = (x, y) => x + y const curriedAdd = _.curry(add) curriedAdd(1, 2) // 3 curriedAdd(1) // (y) => 1 + y curriedAdd(1)(2) // 3 

追加資料



機能構成


新しい関数を形成する2つの関数の組み合わせ。最初の関数の出力が2番目の関数の入力になります。


 const compose = (f, g) => (a) => f(g(a)) // Definition const floorAndToString = compose((val) => val.toString(), Math.floor) // Usage floorAndToString(121.212121) // '121' 

純度


返される値が入力値のみによって決定される場合、関数は純粋であり、関数には副作用がありません。


 const greet = (name) => 'Hi, ' + name greet('Brianne') // 'Hi, Brianne' 

とは対照的に:


 let greeting const greet = () => { greeting = 'Hi, ' + window.name } greet() // "Hi, Brianne" 

副作用


関数は、値を返すことに加えて、外部の可変状態と対話(読み取りまたは書き込み)する場合、副作用があります。


 const differentEveryTime = new Date() 

 console.log('IO is a side effect!') 

べき等(べき等)


繰り返し実行しても同じ結果が得られる場合、関数はべき等です。


 f(f(x))f(x) 

 Math.abs(Math.abs(10)) 

 sort(sort(sort([2, 1]))) 

ポイントフリースタイル(無意味表記)


定義が使用される引数の数を明示的に示さないような方法で関数を書く。 このスタイルには、通常、カリー化または別の高次関数(または一般的な暗黙のプログラミング)が必要です。


 // Given const map = (fn) => (list) => list.map(fn) const add = (a) => (b) => a + b // Then // Not points-free - `numbers` is an explicit argument const incrementAll = (numbers) => map(add(1))(numbers) // Points-free - The list is an implicit argument const incrementAll2 = map(add(1)) 

incrementAll関数は、 numbersパラメーターを定義および使用するため、ドット表記を使用しません。 incrementAll2は、引数に言及せずに関数と値を単に組み合わせます。 無意味な表記法を使用します。


無意味な表記の定義は、 functionまたは=>ない通常の割り当てのように見えます。


述語(述語)


述語は、渡された値に応じてtrueまたはfalseを返す関数です。 述語の一般的な使用例は、配列フィルターのコールバック関数です。


 const predicate = (a) => a > 2 ;[1, 2, 3, 4].filter(predicate) // [3, 4] 

カテゴリー


特定のルールに従う機能を持つオブジェクト。 たとえば、モノイド。


価値


変数に割り当てることができるすべてのもの。


 5 Object.freeze({name: 'John', age: 30}) // The `freeze` function enforces immutability. ;(a) => a ;[1] undefined 

定数


定義後に再割り当てできない変数。


 const five = 5 const john = {name: 'John', age: 30} 

定数には、参照透過性またはリンク透過性があります。 つまり、それらが表す値で置き換えることができ、これは結果に影響しません。


前のリストの定数を使用すると、上記の次の式は常にtrue返しtrue


 john.age + five === ({name: 'John', age: 30}).age + (5) 

ファンクター(functor)


map関数を実装するオブジェクト。オブジェクト内のすべての値を渡すと、新しいオブジェクトが作成され、次の2つの規則に従います。


 //    (identity) object.map(x => x) === object 

そして


 //   object.map(x => f(g(x))) === object.map(g).map(f) 

fgは任意の関数です)


JavaScriptにはArrayファンクターがあります。これは、次の規則に従うためです。


 [1, 2, 3].map(x => x) // = [1, 2, 3] 

そして


 const f = x => x + 1 const g = x => x * 2 ;[1, 2, 3].map(x => f(g(x))) // = [3, 5, 7] ;[1, 2, 3].map(g).map(f) // = [3, 5, 7] 

Pointed Functor(ポインティングファンクター)


任意の値を持つ関数を持つオブジェクト。 ES2015にはArray.ofがあり、配列をポインティングファンクターにします。


 Array.of(1) // [1] 

リフト


リフティングとは、値がファンクターのようなオブジェクトに配置されることです。 関数を適用可能なファンクターに「持ち上げる」と、ファンクターにも存在する値で機能させることができます。


実装によっては、関数liftまたはliftA2 、ファンクターでの関数の起動を簡素化するために使用されます。


 const liftA2 = (f) => (a, b) => a.map(f).ap(b) const mult = a => b => a * b const liftedMult = liftA2(mult) // this function now works on functors like array liftedMult([1, 2], [3]) // [3, 6] liftA2((a, b) => a + b)([1, 2], [3, 4]) // [4, 5, 5, 6] 

1つの引数で関数を呼び出して適用すると、 mapと同じことが行われmap


 const increment = (x) => x + 1 lift(increment)([2]) // [3] ;[2].map(increment) // [3] 

参照の透明度(リンクの透明度)


プログラムの動作に影響を与えずに式をその値に置き換えることができる場合、リンクの透明性があります。


たとえば、 greet関数があります。


 const greet = () => 'Hello World!' 

greet()呼び出しは、 Hello World!置き換えることができますHello World! そのため、この関数は透過的です(参照透過的)。


ラムダ


値として使用できる匿名関数。


 ;(function (a) { return a + 1 }) ;(a) => a + 1 

ラムダは多くの場合、引数として高階関数に渡されます。


 [1, 2].map((a) => a + 1) // [2, 3] 

ラムダは変数に割り当てることができます。


 const add1 = (a) => a + 1 

ラムダ計算(ラムダ計算)


関数を使用して汎用計算モデルを作成するコンピューターサイエンスの分野。


遅延評価(遅延計算)


必要に応じて、値が必要になるまで式の評価が遅れる計算メカニズム。 これにより、関数型言語では、コマンドの順序が重要な命令型プログラミング言語では通常の状態では不可能な、無限リストのような構造を作成できます。


 const rand = function*() { while (1 < 2) { yield Math.random() } } 

 const randIter = rand() randIter.next() //     ,    . 

モノイド


オブジェクトを同じタイプの別のオブジェクトと「結合」する機能を持つオブジェクト。 モノイドの簡単な例は、数字の追加です。


 1 + 1 // 2 

この場合、数値はオブジェクトであり、 +は関数です。


中立的な要素(アイデンティティ)がなければならないので、値を組み合わせても値は変わりません。 加算の場合、この要素は0です。


 1 + 0 // 1 

操作のグループ化が結果に影響しないことも必要です(結合性)。


 1 + (2 + 3) === (1 + 2) + 3 // true 

配列の連結もモノイドです。


 ;[1, 2].concat([3, 4]) // [1, 2, 3, 4] 

ニュートラル要素は空の配列[]


 ;[1, 2].concat([]) // [1, 2] 

中立的な要素と構成の機能がある場合、機能は全体としてモノイドを形成します。


 const identity = (a) => a const compose = (f, g) => (x) => f(g(x)) 

fooは、1つの引数を持つ任意の関数です。


 compose(foo, identity) ≍ compose(identity, foo) ≍ foo 

モナド


モナドはofおよびchain関数of持つオブジェクトです。 chainmapに似ていmapが、結果としてネストされたオブジェクトを分解します。


 // Implementation Array.prototype.chain = function (f) { return this.reduce((acc, it) => acc.concat(f(it)), []) } // Usage ;Array.of('cat,dog', 'fish,bird').chain((a) => a.split(',')) // ['cat', 'dog', 'fish', 'bird'] // Contrast to map ;Array.of('cat,dog', 'fish,bird').map((a) => a.split(',')) // [['cat', 'dog'], ['fish', 'bird']] 

of 、他の関数型言語of return ofも呼ばれます。
chain flatmapとしても知られ、他の言語ではbindします。


コモナド


extractおよびextend機能を備えたオブジェクト。


 const CoIdentity = (v) => ({ val: v, extract () { return this.val }, extend (f) { return CoIdentity(f(this)) } }) 

Extractはファンクターから値を取得します。


 CoIdentity(1).extract() // 1 

Extendはチームで機能を実行します。 関数は、コマンドと同じ型を返す必要があります。


 CoIdentity(1).extend((co) => co.extract() + 1) // CoIdentity(2) 

応用ファンクター


ap関数を持つオブジェクト。 apは、オブジェクトの関数を同じタイプの別のオブジェクトの値に適用します。


 // Implementation Array.prototype.ap = function (xs) { return this.reduce((acc, f) => acc.concat(xs.map(f)), []) } // Example usage ;[(a) => a + 1].ap([1]) // [2] 

これは、2つのオブジェクトがあり、それらのコンテンツにバイナリ演算を適用する必要がある場合に便利です。


 // Arrays that you want to combine const arg1 = [1, 3] const arg2 = [4, 5] // combining function - must be curried for this to work const add = (x) => (y) => x + y const partiallyAppliedAdds = [add].ap(arg1) // [(y) => 1 + y, (y) => 3 + y] 

結果として、結果を取得するためにapで呼び出すことができる関数の配列を取得します。


 partiallyAppliedAdds.ap(arg2) // [5, 6, 7, 8] 

形態


変換機能。


エンドモルフィズム(エンドモルフィズム)


同じタイプの入力と出力を持つ関数。


 // uppercase :: String -> String const uppercase = (str) => str.toUpperCase() // decrement :: Number -> Number const decrement = (x) => x - 1 

同型(同型)


データを失うことのない2種類のオブジェクト間の構造変換のペア。


たとえば、2次元座標は配列[2,3]またはオブジェクト{x: 2, y: 3}格納できます。


 // Providing functions to convert in both directions makes them isomorphic. const pairToCoords = (pair) => ({x: pair[0], y: pair[1]}) const coordsToPair = (coords) => [coords.x, coords.y] coordsToPair(pairToCoords([1, 2])) // [1, 2] pairToCoords(coordsToPair({x: 1, y: 2})) // {x: 1, y: 2} 

セトイド


同じタイプのオブジェクトを比較するために使用できるequals関数を持つオブジェクト。


配列をsetoidに設定します。


 Array.prototype.equals = (arr) => { const len = this.length if (len !== arr.length) { return false } for (let i = 0; i < len; i++) { if (this[i] !== arr[i]) { return false } } return true } ;[1, 2].equals([1, 2]) // true ;[1, 2].equals([0]) // false 

セミグループ(セミグループ)


同じタイプの別のオブジェクトと結合するconcat関数を持つオブジェクト。


 ;[1].concat([2]) // [1, 2] 

折り畳み可能


オブジェクトを別のタイプに変換するreduce機能を持つオブジェクト。


 const sum = (list) => list.reduce((acc, val) => acc + val, 0) sum([1, 2, 3]) // 6 

タイプ署名


多くの場合、JavaScriptの関数には、引数のタイプと戻り値を示すコメントが含まれています。 コミュニティにはさまざまなアプローチがありますが、それらはすべて類似しています。


 // functionName :: firstArgType -> secondArgType -> returnType // add :: Number -> Number -> Number const add = (x) => (y) => x + y // increment :: Number -> Number const increment = (x) => x + 1 

関数が別の関数を引数として使用する場合、括弧内に配置されます。


 // call :: (a -> b) -> a -> b const call = (f) => (x) => f(x) 

文字abcdは、引数が任意のタイプであることを示します。 map関数の次のバージョンは次を受け入れます。


  1. タイプb値を別のタイプb変換する関数
  2. タイプaの値の配列、

タイプbの値の配列を返します。


 // map :: (a -> b) -> [a] -> [b] const map = (f) => (list) => list.map(f) 

追加資料



ユニオンタイプ


2つのタイプを1つに組み合わせた新しいタイプ。


JavaScriptには静的型はありませんが、 StringNumber追加したNumOrString型を発明したと想像してください。


JavaScriptの+操作は文字列と数字で機能するため、新しいタイプを使用して入力と出力を記述することができます。


 // add :: (NumOrString, NumOrString) -> NumOrString const add = (a, b) => a + b add(1, 2) //   3 add('Foo', 2) //   "Foo2" add('Foo', 'Bar') //   "FooBar" 

ユニオン型は、代数型、ラベル付きユニオン、および和型とも呼ばれます。


JavaScriptには、これらのタイプを定義して使用するためのライブラリいくつかあります。


製品の種類


タイプワークは、最もよく知っている方法でタイプを組み合わせます。


 // point :: (Number, Number) -> {x: Number, y: Number} const point = (x, y) => ({x: x, y: y}) 

データ構造の可能な値は異なる値の積であるため、製品と呼ばれます。


参照: 集合論


オプション


SomeNone 2つのケースを持つ共用体タイプ。 値を返さない可能性のある関数を作成するのに便利です。


 // Naive definition const Some = (v) => ({ val: v, map (f) { return Some(f(this.val)) }, chain (f) { return f(this.val) } }) const None = () => ({ map (f) { return this }, chain (f) { return this } }) // maybeProp :: (String, {a}) -> Option a const maybeProp = (key, obj) => typeof obj[key] === 'undefined' ? None() : Some(obj[key]) 

chainを使用して、 Optionを返す一連の関数を作成します。


 // getItem :: Cart -> Option CartItem const getItem = (cart) => maybeProp('item', cart) // getPrice :: Item -> Option Number const getPrice = (item) => maybeProp('price', item) // getNestedPrice :: cart -> Option a const getNestedPrice = (cart) => getItem(obj).chain(getPrice) getNestedPrice({}) // None() getNestedPrice({item: {foo: 1}}) // None() getNestedPrice({item: {price: 9.99}}) // Some(9.99) 

Option Maybe Optionも呼ばれます。 Some Justと呼ばれることもあります。 None Nothingと呼ばれることNothingます。


JavaScriptの関数型プログラミングライブラリ




Source: https://habr.com/ru/post/J310172/


All Articles