KDOC 252: 『月刊ラムダノート 4-3-9-2024』

この文書のステータス

  • 作成
    • 2024-11-01 貴島
  • レビュー
    • 2024-11-04 貴島

概要

n月刊ラムダノート Vol.4, No.3(2024)は技術解説情報誌。以下の章で構成される。

  1. TypeScriptではじめる型システム(遠藤侑介)
  2. 型を活用した安全なアプリケーション開発(佐藤有斗)
  3. 「インターネットのカタチ」のその後(小川晃通)

メモ

  • たとえば処理系ではプログラムを実行するたびに「値がnullでないか」をチェックする。こうしたチェックには、計算機性能や用途次第では無視できないほどのオーバーヘッドがかかる。そこで歴史的に、動作保証しようとしたら実行時オーバーヘッドがかかってしまうような動作は「未定義」であるとし、その対処を言語の責任ではなくプログラマの責任としたCやC++といったプログラミング言語が、特に実行速度が求められるプログラムでは使われてきた。しかし未定義動作は、その回避に対して責任があるプログラマにとっては不便であったり間違いの原因になったりする。このように未定義動作に陥る問題を解決する方法のひとつが型システムである。未定義動作に陥る可能性のあるプログラムを実行前に型検査器で判定し、OKとされたプログラムは未定義動作にならない、ということが保証できる(p5)
  • JavaScriptのプログラムは未定義動作に陥ることがない。なのでTypeScriptの型システムは型安全性を厳密に保証する必要がない(p5)
  • “argument” と “parameter” を使い分けることもある。“argument” を実引数、 “parameter” を仮引数として使い分けたりする。言語やコミュニティによって異なる(p19)
  • 型の等価判定をするための関数と、変数がどういう型を持っているかの情報を保持する仕組みを実装する(p22)
  • TAPLの11.3節のタイトルにある「派生」は、型システムにおいて逐次実行や変数参照が「関数という基本的な操作から派生的に得られるもの」ということを意味している、という(p35)

以下のTypeScriptのコードは型エラーにならないが、実行時に失敗する。

const f = () => x;
f(); // 初期化前の x を参照する
const x = 1;
  • ビジネスロジックを代数的データ型により表現するという考え方や、状態遷移を全域関数として記述することで異常な処理を防ぐ手法がある、という(p39)
class ConfirmedOrder(
  val orderId: OrderId,
  val customerId: CustomerId,
  val shippingAddress: Address,
  val lines: List[OrderLine],
  val confirmedAt: LocalDateTime,
)

class CancelledOrder(
  val orderId: OrderId,
  val customerId: CustomerId,
  val shippingAddress: Address,
  val lines: List[OrderLine],
  val confirmedAt: LocalDateTime,
  val cancelledAt: LocalDateTime,
  val cancelReason: String,
)
  • ↑その代わりにorderとして共通の処理が書きにくくなるので、各ステータスのクラスをサブクラスとする(p46)
  • 直積型と直和型を組み合わせる「代数的データ型」
  • 直和型はinterfaceとかtraitなど、異なる型を同じように扱うもの
    • Order は、 ConfirmedOrder + CancelOrder + ShippingOrder
  • 直積型は各フィールド
    • CanceledOrder は、OrderID x DateTime(キャンセル日時) x String(キャンセル日時)
  • 状態遷移をそれぞれの型にメソッドとして実装すれば、そもそもメソッドが定義されていない型に対して処理を呼び出すことはできなくなる(p56)
  • 全域関数(total function)は、「すべての入力値に対して必ず出力値が1つ存在する関数」である。いっぽう、一部の入力値に対して出力値が存在しない関数のことを部分関数という(p56)
class NonZeroInt(val value: Int) {
  require(value != 0, "0 以外の整数を入力してください ")
}

def divideTwelveBy(n: NonZeroInt): Int = 12 / n.value
  • divideTwelveByは、NonZeroInt型に対して全域関数である。いっぽう、nの型をintにすると0が入る可能性があるので部分関数となっている(p57)
  • 開発において何かしら例外を投げる関数を書く必要が生じたときは、その仕様を型で表現することにより全域関数として定義できないかを意識するとよい、という(p59)
  • コンストラクタの可視性をコントロールすることで、「あるインスタンスは特定の箇所からのみ作成可能」という状態を作り出せる。不正な状態のインスタンスを作るのを防げる(p63)
  • リレーショナルDBなど外部システムは、アプリケーション上の型より「広い」型を持つ。そのため外部から内部の変換(DBからは読み込み)では失敗することがある。いっぽう内部から外部の変換(DBからは書き込み)は「狭い」型からの変換なので失敗することがないケースが多い(p72)
  • 本稿の執筆に際し、「2011年当時はどうだったか?」を確認するためにさまざまな情報源を参照した、という。昔書いた原稿や当時の記事などを読み返すうちに、非常に多くの記事がインターネット上から消えてしまっている事実をあらためて思い知らされた、という(p92)

関連

なし。