Closed8
関数型

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

オブジェクト指向
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);

余談
クラスを使っても、こう書けば、良さそうじゃない?
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);

めちゃくちゃ余談
上記の書き方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 })
}
}

これはもうクラス使う必要はないけどね

副作用
- グローバル変数の変更
- オブジェクトのプロパティ変更
- 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"

参照透過性
- ある式が常に同じ結果を返す性質
fn add(a: i32, b: i32) -> i32 {
a + b
}
let result = add(2, 3);
↑みたいなのを純粋関数(Pure Function)という
fn get_random_number() -> i32 {
rand::random()
}
こういうのは違う

関数型
mut
基本的にデフォルトでは、更新できない
let num = 10;
num = 20; // error
更新するためには mut (mutable) をつける必要ある
式
Rustだと = は数学の = と同じで、左辺と右辺の等価を表す
let num = 10
TypeScript は違う
変数は箱のイメージでそこに値を入れている
let num = 10
num = num + 10
イコールではない
このスクラップは2024/10/21にクローズされました