Upgrade to Pro — share decks privately, control downloads, hide ads and more …

関数型プログラミングの考え方を取り入れて予測しやすいコードを書く

ken7253
October 02, 2023

 関数型プログラミングの考え方を取り入れて予測しやすいコードを書く

社内LT会にて発表した資料です。

ken7253

October 02, 2023
Tweet

More Decks by ken7253

Other Decks in Programming

Transcript

  1. 「引数のみを利用する」とは下記のようなこと グローバル変数を参照したり、時刻に依存するコードなど。 純粋関数の条件 // 🙆‍♂ 純粋関数 const pureFunc = (x:number,

    y:number): number => { return x * y; }; // 🙅‍♂ 純粋関数ではない let x = 0; let y = 0; const notPureFunc = (): number => { return x * y; };
  2. 既存の変数を変更しない Array#push() や Array#reverse() などのメソッド、 var / let に対する再代入 などを関数の内部で呼び出して、既存の値を変更している場合など

    純粋関数の条件 let num = 0; // 🙆‍♂ 純粋関数 const pureFunc = (n:number): number => { return n + 1; }; pureFunc(num); // num => 0 let num = 0; // 🙅‍♂ 純粋関数ではない const notPureFunc = (n:number): number => { return n++; }; notPureFunc(num); // num => 1
  3. シグニチャーをより正確にしてみる これにテストを追加してみる。 こうすることで(テストが無くても)ある程度挙動が推測できる。 シグニチャーの情報が少ない場合 export const sum = (first: number,

    second: number): number | TypeError => { // 外部からは見えない何らかの処理 } import { sum } from "./sum" describe('与えられた引数を足し算して返却するsum関数', () => { test('自然数同士の足し算が正しく実行されること', () => {/* 省略 */}) test('引数のどちらかにNaNが渡された場合TypeErrorを返却すること', () => {/* 省略 */}) test('引数のどちらかにInfinityが渡された場合TypeErrorを返却すること', () => {/* 省略 */}) })
  4. 例としてこの関数を実際に使ってみる。 このようにシグニチャーから関数の使い方が理解できる。 情報の多い関数は適切に使用できる import { sum } from "./sum"; import

    { sendError } from "./sendError"; const [x,y] = [10, 20]; const sumResult = sum(x,y); // number | TypeError // そのままだと型が合わないので型ガードを利用する。 if (sumResult instanceof TypeError) { // sendError = (error: Error) => void; sendError(sumResult); // sendError(); はエラー情報をサーバーに送る処理として考える } else { console.log(sumResult); }
  5. 具体的なコードで見てみる 複数の要素の中から一番高さを持つ要素を探してコンソールに出力する処理 一つの関数にいろいろやらせている例 このようなコードがあった場合、次のように変えてみる。 // 入出力の情報がないので説明のための関数名が長くなりがち const displayHighestElementByElementList = ()

    => { // 要素の取得 const elements = document.querySelectorAll('.some-class'); const elementList = Array.from(elements); // 比較とソート const sortedFromClientHeight = [...elementList].sort((prev, next) => { return next.clientHeight - prev.clientHeight; }); // コンソールへの出力 console.log(sortedFromClientHeight[0]); }; displayHighestElementByElementList();
  6. 具体的なコードで見てみる 複数の要素の中から一番高さを持つ要素を探してコンソールに出力する処理 要素の取得 -> 比較関数 -> 出力 という値の流れが掴みやすい。 入出力の型情報があることで挙動が推測しやすい関数になる。 //

    与えられた要素の配列から一番高さを持つ要素を返す関数 const getHighestElement = (elementList: Element[]): Element => { const sorted = [...elementList].sort((prev, next) => { return next.clientHeight - prev.clientHeight }); return sorted[0]; }; // 要素の取得 const elementList = Array.from(document.querySelectorAll('.some-class')); // コンソールへの出力 console.log(getHighestElement(elementList));
  7. 具体的なコードで見てみる 複数の要素の中から一番高さを持つ要素を探してコンソールに出力する処理 もっと関数型っぽい書き方だとこう。 配列のメソッドをうまく使って無駄なく宣言的に記述する。 // 与えられた要素の配列から一番高さを持つ要素を返す関数 const getHighestElement = (elementList:

    Element[]): Element => elementList.reduce((acc, current) => acc.clientHeight >= current.clientHeight ? acc : current ); // 要素の取得 const elementList = Array.from(document.querySelectorAll(".some-class")); // コンソールへの出力 console.log(getHighestElement(elementList));