Closed8

関数型

katayama8000katayama8000

紛らわしい点

  • 関数型言語という言葉
    • 一般に関数型言語(F# Haskell)と呼ばれる言語を使わなくても、関数型プログラミングはできる
    • TypeScript Java でもできると思う
    • 混乱した
  • オブジェクト指向との対比
    • 別に対になっているわけではない
    • クラスは内部に状態を保つため、関数型プログラミングと思考が合わない
    • 関数型は、型で状態を表現する
katayama8000katayama8000

オブジェクト指向

class User {
    private name: string;
    private age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    public updateName(newName: string): void {
        if (newName.trim() === '') {
            throw new Error("Name cannot be empty.");
        }
        this.name = newName;
    }

    public updateAge(newAge: number): void {
        if (newAge <= 0) {
            throw new Error("Age must be a positive number.");
        }
        this.age = newAge;
    }

    public getInfo(): string {
        return `${this.name} (${this.age} years old)`;
    }
}

const user = new User("John", 30);
user.updateName("Jane");
user.updateAge(25);

関数型

type User = {
    name: string;
    age: number;
};

const createUser = (name: string, age: number): User => {
    if (name.trim() === '') {
        throw new Error("Name cannot be empty.");
    }
    if (age <= 0) {
        throw new Error("Age must be a positive number.");
    }
    return { name, age };
};

const updateUserName = (user: User, newName: string): User => {
    if (newName.trim() === '') {
        throw new Error("Name cannot be empty.");
    }
    return { ...user, name: newName };
};

const updateUserAge = (user: User, newAge: number): User => {
    if (newAge <= 0) {
        throw new Error("Age must be a positive number.");
    }
    return { ...user, age: newAge };
};

const getUserInfo = (user: User): string => {
    return `${user.name} (${user.age} years old)`;
};

let user = createUser("John", 30);
user = updateUserName(user, "Jane");
user = updateUserAge(user, 25);
katayama8000katayama8000

余談

クラスを使っても、こう書けば、良さそうじゃない?

class User {
    private name: string;
    private age: number;
    private constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    public static create = (name: string, age: number): User => {
        return new User(name, age);
    }

    public static update = (_user: User, name: string, age: number): User => {
        if (user.name === name && user.age === age) {
            throw new Error();
        }
        return new User(name, age);
    }
}

const user = User.create("taro", 20);
const updatedUser = User.update(user, "jiro", 30);
katayama8000katayama8000

めちゃくちゃ余談
上記の書き方Rustに見えてきた

#[derive(Debug)]
struct User {
    name: String,
    age: u32,
}

impl User {
    // Userを作成するためのコンストラクタ
    fn create(name: String, age: u32) -> Self {
        User { name, age }
    }

    // Userを更新するためのメソッド
    fn update(&self, name: String, age: u32) -> Result<Self, &'static str> {
        if self.name == name && self.age == age {
            return Err();
        }
        
        Ok(User { name, age })
    }
}
katayama8000katayama8000

副作用

  1. グローバル変数の変更
  2. オブジェクトのプロパティ変更
  3. I/O 操作
    • console への出力
    • エラー
    • DBに書き込み

クラスを使うと、一般的に副作用が発生しやすい
セッターは大体副作用を伴う

class User {
    private name: string;

    constructor(name: string) {
        this.name = name;
    }

    public setName(newName: string): void {
        this.name = newName; // クラスの内部状態を変更 -> 副作用
    }

    public logName(): void {
        console.log(this.name); // コンソールに出力 -> 副作用
    }
}

const user = new User("John");
user.setName("Jane");
user.logName(); // "Jane"
katayama8000katayama8000

参照透過性

  • ある式が常に同じ結果を返す性質
fn add(a: i32, b: i32) -> i32 {
    a + b
}
let result = add(2, 3);

↑みたいなのを純粋関数(Pure Function)という

fn get_random_number() -> i32 {
    rand::random()
}

こういうのは違う

katayama8000katayama8000

関数型

mut

基本的にデフォルトでは、更新できない

let num = 10;
num = 20; // error

更新するためには mut (mutable) をつける必要ある

Rustだと = は数学の = と同じで、左辺と右辺の等価を表す

let num = 10

TypeScript は違う
変数は箱のイメージでそこに値を入れている

let num = 10
num = num + 10

イコールではない

このスクラップは2024/10/21にクローズされました