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

Scalaだったらこう書けるのに~Scalaが恋しくて~ (TypeScript編、Python編)

Scalaだったらこう書けるのに~Scalaが恋しくて~ (TypeScript編、Python編)

関数型まつり2025の登壇資料です。

Scalaでよく利用する機能を、TypeScriptやPythonの機能と比較した発表です。

Avatar for Yoshiteru Takeshita

Yoshiteru Takeshita

July 07, 2025
Tweet

More Decks by Yoshiteru Takeshita

Other Decks in Programming

Transcript

  1. レバレジーズ株式会社 竹下 義晃 テクノロジー戦略室室長 一般社団法人 TSKaigi Association 代表理事、一般社団法人 Japan Scala

    Association理事 2020年にレバレジーズに入社 フルスタックの技術力を背景に、レバレジーズ社の技術の向上とエンジニア組織文化の構築に取り組む。関数型 まつり:ScalaMatsuri時代の2014年から運営 TSKaigi:2024の立ち上げ 各種技術コミュニティを盛り上げる活動も行っている。 最近業務でコードは書けてない。もっぱら TypeScriptとPython書いている
  2. case class case class Presentation( title: String, speaker: String, isFinished:

    Boolean ) • コンストラクタに定義した変数が、 getterのみのプロパティとしても 自動で定義される • equalsが自動で実装される ◦ ==での比較が可能に • copyが自動で実装される ◦ 次のimmutableで説明 • printすると整形された形で出力 される
  3. TypeScript - TypeScriptには相当する機能は無し - プロパティ化だけなら左のコードで可能 - equalsの上書きやcopyの実装無し - copyにも難点あり class

    Presentation { constructor( public readonly title: string, public readonly speaker: string, public readonly isFinished: boolean ) {} }
  4. Python - Pythonはdataclassが存在 - または、namedtupleでも似たこ と実現可能 - ScalaだとRecordによるStructural Types import

    dataclasses @dataclasses.dataclass(frozen=True) class Presentation: title: str speaker: str is_finished: bool
  5. case classのcopy val p = Presentation( "Scala 3 Features", "John

    Doe", false ) val p2 = p.copy( isFinished = true ) • 更新したいフィールドのみが更新 された、新しいインスタンスを返す メソッドが自動で作られる
  6. TypeScript const p = { title: "Scala 3 Features", speaker:

    "John Doe", isFinished: false }; const p2 = { ...p, isFinished: true }; • 分割代入引数 (destructuring assignment parameter)で実現可能 • classの場合、この方法を使うとclassでは 無くなる
  7. Python p = Presentation( "Scala 3 Features", "John Doe", False

    ) p2 = p.replace(is_finished = True) • replaceというメソッドが自動で作られてい る
  8. 豊富な操作メソッド 当たり前のメソッドは割愛 groupBy( toKey: T => K): Map[K, List[T]] 要素を指定したKeyでグループ化

    partition( f: T => Boolean): (List[T],List[T]) 判定に従って、2つのリストに分ける sliding(size: Int, step: Int): Iterable[List[T]] stepずつずらしながら、size分の部分リストに 分割 intersect(l: List[T]), diff( l: List[T]), 集合を取る intersect A & B, diff: A & !B distinctBy(f: T => B): List[T] 要素をBに変換し、Bでユニークなリストを返 す permutations, combinations(size: Int) 全順列組み合わせ列挙、組み合わせ列挙
  9. TypeScript groupBy( toKey: T => K): Map[K, List[T]] なし partition(

    f: T => Boolean): (List[T],List[T]) なし sliding(size: Int, step: Int): Iterable[List[T]] なし intersect(l: List[T]), diff( l: List[T]), なし distinctBy(f: T => B): List[T] なし permutations, combinations(size: Int) なし ===演算子のオーバーロードができないので実現困難
  10. Python groupBy( toKey: T => K): Map[K, List[T]] itertools.groupby partition(

    f: T => Boolean): (List[T],List[T]) なし sliding(size: Int, step: Int): Iterable[List[T]] itertools.batched intersect(l: List[T]), diff( l: List[T]), set(a) & set(b), set(a) - set(b) distinctBy(f: T => B): List[T] なし permutations, combinations(size: Int) itertools.permutations, combinations itertoolsまたは、素直にpandasやPolarsを使うと良い
  11. Scalaではfor文は糖衣構文 val a = for { e <- list e2

    <- list2 e3 <- list3 if e + e2 == 2 } yield { e + e2 + e3 } val a = list.flatMap(e => { list2.flatMap(e2 => { list3 .withFilter(p => e + e2 == 2) .map(e3 => { e + e2 + e3 }) }) }) ネスト地獄からの開放 最後のループはmapまたはforeach,それ以外はflatMap,ガード式はwithFilterに 展開される
  12. TypeScript Effect.gen(function* () { const e = yield* list; const

    e2 = yield* list2; return e + e2; }) TypeScriptの標準では存在せず Effectが、Generatorで頑張っていたりする pipe演算子が入ると変わるかも?
  13. match obj match { case Some(v) => v case None

    => "None" case x :: left => x case Fullname(firstName, _) => firstName } - かなりスッキリ分岐をかける - 特にUnionTypeからの取り出し - 網羅チェックもしてくれる
  14. TypeScript narrowing(型ガード)で似たことは可能 またはts-patternなどライブラリを使用 type Speaker = { type: "Speaker", name:

    string } function isSpeaker(v: any): v is Speaker { return v.type === "Speaker" } const m: Speaker | string = { type: "Speaker", name: "Joe"}; if(isSpeaker(m)) { console.log(m.name) } if("name" in m) { console.log(m.name); } if(m?.type === "Speaker") { console.log(m.name); }
  15. 式のため、直接変数代入など可能 val z = if(op.isDefined) { op.get } else {

    "None" } val v = 233 val x = v match { case v if v % 15 == 0 => "FizzBuzz" case v if v % 5 == 0 => "Fizz" case v if v % 3 == 0 => "Buzz" case _ => v }
  16. TypeScript letを使うか、三項演算子のネストが必要 三項演算子は分岐が増えると可読性が一気に悪くなる const v = 22; let x; if(v

    % 2 === 0) { x = "even"; } else { x = "odd"; } const y = v % 15 === 0 ? "FizzBuzz" : v % 5 === 0 ? "Fizz" : v % 3 === 0 ? "Buzz" : v; // constにしたいときはこうすることも const x = (() => { if(v % 2 === 0) { return "even"; } else { return "odd"; }})();
  17. Python 変数をその場で定義 or 三項演算 v = 150 if v %

    15 == 0: x = "FizzBuzz" else: x = v x = "FizzBuzz" if v % 15 == 0 \ else "Fizz" if v % 5 == 0 \ else "Buzz" if v % 3 == 0 \ else v
  18. Contextの引き回し trait Connection { def query(sql: String): List[Map[String, Any]] }

    object MySQL extends Connection { def query(sql: String): List[Map[String, Any]] = … } def query(sql: String)(using conn: Connection) = { conn.query(sql) } def main() = { given conn: Connection = MySQL // これ以降はMySQLが使われる val result = query("SELECT * FROM users") println(result) }
  19. 型クラス object Asc extends Ordering[Int] { def compare(x: Int, y:

    Int): Int = x - y } object Desc extends Ordering[Int] { def compare(x: Int, y: Int): Int = y - x } def main() = { val list = List(3, 1, 2) given ord: Ordering[Int] = Desc // ここで切り替え println(list.sorted) // 3,2,1 }
  20. Union Type/Intersection Type Scala2までは、sealed traitで代用していた。 case class FirstLast(first: String, last:

    String) case class SeiMei(sei: String, mei: String) type Sei = { val sei: String } type Mei = { val mei: String } type Name = FirstLast | SeiMei | (Sei & Mei)
  21. Type pattern matching かなり直感的に型の計算が可能 type Extract[T] = T match {

    case List[t] => Extract[t] case Option[t] => Extract[t] case Either[_, r] => Extract[r] case _ => T }
  22. Type pattern matching in TypeScript わりと冗長 type Extract<T> = T

    extends T[] ? Extract<T> : T extends Map<any, infer V> ? Extract<V> : T;
  23. TypeScript/Python • TypeScriptは実行時に型が消滅 ◦ 自由なJavaScriptに型をつけるため、型機能はリッチ ◦ 実行のためのサポートはほぼ無い • Pythonは動的型付け(Type Hinting)

    ◦ データを扱うことが多いので、実行のためのサポート は比較的ある ◦ そもそもが動的型付けなので、型機能は弱い