행 다형성(Row Polymorphism)은 레코드(구조체)의 필드 집합을 타입 변수로 추상화하는 타입 시스템 기능이다. 구조적 서브타이핑을 타입 안전하게 표현하며 OCaml, PureScript 등에서 사용된다.
핵심 개념
일반 레코드 타입:
{ name: String, age: Int } -- 정확히 이 필드만
행 다형성 레코드:
{ name: String, age: Int | r } -- 추가 필드 r도 허용
행 변수 r은 "나머지 필드들"을 추상화
PureScript 예시
purescript
-- 행 다형성 함수
getName :: forall r. { name :: String | r } -> String
getName person = person.name
-- name 필드만 있으면 됨, 나머지는 무관
getName { name: "Alice", age: 30 } -- OK
getName { name: "Bob", email: "b@b" } -- OK
getName { age: 25 } -- 컴파일 에러!
-- 레코드 확장
addEmail :: forall r.
{ name :: String | r } ->
{ name :: String, email :: String | r }
addEmail rec = Record.insert (Proxy :: _ "email") "default" rec
OCaml 다형 변형 (Polymorphic Variants)
ocaml
(* 행 다형성을 활용한 변형 타입 *)
let stringify = function
| `Int n -> string_of_int n
| `Float f -> string_of_float f
| `String s -> s
(* 타입: [< `Float of float | `Int of int | `String of string] -> string *)
(* 다른 함수와 합성 가능 *)
let process = function
| `Bool b -> string_of_bool b
| other -> stringify other
(* 타입: [< `Bool of bool | `Float of float | `Int of int | `String of string] -> string *)
typescript
// TypeScript는 행 다형성과 유사한 구조적 타이핑 제공
interface HasName {
name: string;
}
// 함수는 name이 있는 모든 객체 수용
function greet(obj: HasName): string {
return `Hello, ${obj.name}!`;
}
greet({ name: "Alice", age: 30 }); // OK (추가 필드 허용)
greet({ name: "Bob", role: "admin" }); // OK
// Exact 타입은 추가 필드 금지
type ExactUser = { name: string } & { [K in Exclude<string, 'name'>]?: never };
행 다형성 vs 서브타이핑
| 비교 | 행 다형성 | 서브타이핑 |
|---|
| 타입 추론 | 주로 가능 | 어려움 |
| 합성 | 행 변수 결합 | 상속 계층 |
| 유연성 | 높음 | 중간 |
| 사용 언어 | OCaml, PureScript | Java, C++ |
관련 개념