「(本)すごい Haskell たのしく学ぼう」 の7章に出ているデータ型の例を TypeScript に移植する。
環境はこれ:
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 9.0.2
例として出ていた Shape データ型の定義:
data Shape
= Circle Float Float Float
| Rectangle Float Float Float Float
deriving (Show)
面積を計算する area 関数を定義:
area :: Shape -> Float
area (Circle _ _ r) = pi * r ^ 2
area (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)
これらを使って書いたコード main.hs 全体:
data Shape
= Circle Float Float Float
| Rectangle Float Float Float Float
deriving (Show)
area :: Shape -> Float
area (Circle _ _ r) = pi * r ^ 2
area (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)
main :: IO ()
main =
putStrLn $
(show $ area (Circle 0 0 100)) ++
", " ++ (show $ area (Rectangle 0 0 100 100))
それでは実行:
$ ghc main.hs
$ ./main
31415.928, 10000.0
data Shape を TypeScript に:
type Circle = {
x: number
y: number
r: number
}
type Rectangle = {
x1: number
y1: number
x2: number
y2: number
}
type Shape = Circle | Rectangle
ただ area 関数を移植しようとしてわかったのですが、 この定義では多少不都合があります。 あとで修正します。
area 関数を定義(これは作動しない):
const area = (s: Shape): number => {
//switch ( typeof s) {
switch (s) {
case Circle:
return Math.PI * Math.pow(s.r, 2)
case Rectangle:
return Math.abs(s.x2 - s.x1) * Math.abs(s.y2 - s.y1)
}
}
Shape を受け取ってそれが Circle か Rectangle かを判別して、 それぞれの面積計算をして結果を返す関数です。
これで作動すればうれしいのですが、TypeScript では このような記述はできないようです。
TypeScript の実行環境はこれ:
$ deno --version deno 2.5.2 (stable, release, aarch64-apple-darwin) v8 14.0.365.5-rusty typescript 5.9.2
Gemni にたずねたところ、 in を使って Circle か Rectangle か どちらかを調べる方法もあるけど、非推奨だと言われた。 そのかわり、Circle と Rectangle の定義に kind を追加して、 それで判別せよ、とのこと。
つまり、このように定義する:
type Circle = {
kind: 'circle'
x: number
y: number
r: number
}
type Rectangle = {
kind: 'rectangle'
x1: number
y1: number
x2: number
y2: number
}
そうしておけば、 area 関数では kind を使って判定できる、このように:
const area = (s: Shape): number => {
switch (s.kind) {
case 'circle':
return Math.PI * Math.pow(s.r, 2)
case 'rectangle':
return Math.abs(s.x2 - s.x1) * Math.abs(s.y2 - s.y1)
}
}
自明な定義を手動で追加するのは面倒ですが、致し方ない。
コード全体はこうなりました:
// main.ts
type Circle = {
kind: 'circle'
x: number
y: number
r: number
}
type Rectangle = {
kind: 'rectangle'
x1: number
y1: number
x2: number
y2: number
}
type Shape = Circle | Rectangle
const area = (s: Shape): number => {
switch (s.kind) {
case 'circle':
return Math.PI * Math.pow(s.r, 2)
case 'rectangle':
return Math.abs(s.x2 - s.x1) * Math.abs(s.y2 - s.y1)
}
}
const c: Circle = {
kind: 'circle',
x: 0,
y: 0,
r: 100
}
const r: Rectangle = {
kind: 'rectangle',
x1: 0,
y1: 0,
x2: 100,
y2: 100
}
console.log(`${area(c)}, ${area(r)}`)
実行します:
$ deno run --check main.ts
Check file:///path/to/main.ts
31415.926535897932, 10000
できました。