カリー化とは

編集

カリー化(Currying)は、複数の引数を取る関数を、単一の引数を取る関数の連続した呼び出しに変換する関数型プログラミングの技法です。名前の由来は、論理学者ハスケル・カリー(Haskell Curry)にちなんでいます。

カリー化の主な特徴

編集
  • 関数の部分適用: 一部の引数を固定し、新しい関数を生成
  • 関数の柔軟性向上: 引数を段階的に渡すことが可能
  • 関数合成の容易さ: 関数を柔軟に組み合わせることができる
  • 関数型プログラミングの基本的な技法

カリー化の基本的な実装

編集

JavaScript

編集
// 通常の関数
function add(x, y, z) {
    return x + y + z;
}

// カリー化された関数
function curriedAdd(x) {
    return function(y) {
        return function(z) {
            return x + y + z;
        };
    };
}

console.log(add(1, 2, 3));          // 6
console.log(curriedAdd(1)(2)(3));   // 6

// 汎用的なカリー化関数
function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        } else {
            return function(...moreArgs) {
                return curried.apply(this, args.concat(moreArgs));
            };
        }
    };
}

const curriedMultiply = curry((x, y, z) => x * y * z);
console.log(curriedMultiply(2)(3)(4));  // 24
console.log(curriedMultiply(2, 3)(4));  // 24

Haskell

編集
-- デフォルトでカリー化された関数
add :: Int -> Int -> Int -> Int
add x y z = x + y + z

-- 部分適用の例
addFive = add 5  -- 最初の引数を固定
result = addFive 2 3  -- 14

-- 高階関数でのカリー化
map' :: (a -> b) -> [a] -> [b]
map' f [] = []
map' f (x:xs) = f x : map' f xs
object CurryingExample {
    // カリー化された関数
    def multiply(x: Int)(y: Int)(z: Int): Int = x * y * z

    // 部分適用の例
    val multiplyByTwo = multiply(2)(_: Int)(_: Int)
    val multiplyByTwoAndThree = multiplyByTwo(3)(_: Int)

    def main(args: Array[String]): Unit = {
        println(multiply(2)(3)(4))        // 24
        println(multiplyByTwo(3)(4))      // 24
        println(multiplyByTwoAndThree(5)) // 30
    }
}
func curriedAdd(_ x: Int) -> (Int) -> (Int) -> Int {
    return { y in
        return { z in
            return x + y + z
        }
    }
}

let result = curriedAdd(1)(2)(3)  // 6
let addFive = curriedAdd(5)
let addFiveAndTwo = addFive(2)
print(addFiveAndTwo(3))  // 10

カリー化の実践的な利用

編集

ロガーの実装

編集

Kotlin

編集
class Logger {
    fun log(level: String) = { message: String ->
        { context: String ->
            println("[$level] $context: $message")
        }
    }
}

fun main() {
    val logger = Logger()
    val errorLogger = logger.log("ERROR")
    val databaseError = errorLogger("Database connection failed")
    
    databaseError("UserService")  // [ERROR] UserService: Database connection failed
}

バリデーションの柔軟な実装

編集
fn validate(min_length: usize) -> impl Fn(&str) -> impl Fn(&str) -> bool {
    move |field_name: &str| {
        move |value: &str| {
            if value.len() >= min_length {
                true
            } else {
                println!("{} は {} 文字以上である必要があります", field_name, min_length);
                false
            }
        }
    }
}

fn main() {
    let validate_username = validate(3)("ユーザー名");
    let validate_password = validate(8)("パスワード");

    println!("{}", validate_username("ab"));   // false
    println!("{}", validate_username("abc"));  // true
    println!("{}", validate_password("short")); // false
}

イベントハンドラの動的生成

編集

TypeScript

編集
type EventHandler = (event: string) => (data: any) => void;

const createEventLogger: EventHandler = (eventType) => {
    return (data) => {
        console.log(`[${eventType}] ${JSON.stringify(data)}`);
    };
};

const logUserEvent = createEventLogger("USER");
const logSystemEvent = createEventLogger("SYSTEM");

logUserEvent({ action: "login", userId: 123 });
logSystemEvent({ status: "startup", timestamp: Date.now() });

カリー化の高度なテクニック

編集

関数の合成

編集
type Fn func(int) int

func compose(f, g Fn) Fn {
    return func(x int) int {
        return f(g(x))
    }
}

func double(x int) int { return x * 2 }
func increment(x int) int { return x + 1 }

func main() {
    doubleAndIncrement := compose(increment, double)
    result := doubleAndIncrement(5)  // (5 * 2) + 1 = 11
    fmt.Println(result)
}

パーシャルアプリケーション

編集
let multiply x y z = x * y * z

(* 部分適用の例 *)
let double = multiply 2
let doubleAndTriple = double 3
let result = doubleAndTriple 4  (* 2 * 3 * 4 = 24 *)

カリー化の利点と注意点

編集

利点

編集
  • 関数の再利用性の向上
  • より柔軟な関数合成
  • 関数型プログラミングパラダイムの強化

注意点

編集
  • パフォーマンスオーバーヘッド
  • 可読性の低下の可能性
  • 過度な抽象化の回避

カリー化のベストプラクティス

編集
  • シンプルで明確な関数設計
  • 部分適用の戦略的な利用
  • パフォーマンスとの適切なバランス
  • 読みやすさの重視

まとめ

編集

カリー化は、関数型プログラミングにおける強力で洗練された技法です。関数の柔軟性を高め、コードをより表現力豊かにする重要な概念です。