😸

Verse言語の設計思想を読み解きたい(17) 型システム② 部分型(Subtyping)

2023/05/01に公開

前回はこちら。
https://zenn.dev/t_tutiya/articles/a762bb727e255f
ここから数回はVerseの「型システム(Type System)」まわりを見ていきます(非同期処理の続編を予告していましたが、検証したら思っていた挙動と違ったのでその記事はボツになりました)。
それに伴い前々回のクラスの初期化の記事を型システムの第1回に変更しました。今回が第2回になります。

部分型(Subtyping)

今回は「部分型(Subtyping)」について。C#では、継承関係にあるクラス同士での代入が可能です。一方Verseでは「部分型」という仕組みによって、継承関係にない型同士での代入が可能です[1]

部分型や、次回以降に説明する「共変/反変」などの型システムの概念は、C++/C#のユーザーには馴染みが薄い[2]かと思います。正直土屋も説明が正しいか自信が無いので、ご意見ありましたら是非コメントにお願いします。

注意:型システム関連の訳語について(★このブログだけの話★)

例によって型システムの関連用語について、このブログで用いる訳語の一覧を示します。

原文 このブログ
functional language 関数型言語
function type function型
parameter type パラメータの型
type parameter 型パラメータ
subtyping 部分型[3]
subtype サブタイプ
supertype スーパータイプ
nominal type system 名前的型システム[4]
structual type system 構造的型システム
Type safe 型安全
Type safety 型安全性
covariance (名)共変性
contravariance (名)反変性
covariant (形)共変の
contravariant (形)反変の
covariantly (副)共変的に
contravariantly (副)反変的に
Explicit Type Arguments 明示的な型引数
Implicit Type Arguments 暗示的な型引数

公式ドキュメントと異なる訳語を使う物は以下になります。型システム関連の用語は訳語が安定しているとも言えず、公式訳が間違っているという訳ではありませんのでご注意ください。

原文 公式訳 このブログ
parametric type パラメトリック型 パラメータ型
positive ポジティブ/出力
negative ネガティブ/入力
positive position 正の位置 ポジティブ位置/出力位置
negative position 負の位置 ネガティブ位置/入力位置

「positive/negative」を「出力/入力」としているのは明らかに意訳であり、正確ではありません[5][6]。この訳語を使った方が理解が捗る場合に使用します。

前提:クラス型の互換性(サブクラスとスーパークラス)

まずはクラス継承のおさらいから。

C#では、クラスAを継承してクラスBを定義した場合、継承元のクラスAを「スーパークラス(superclass)」、継承先のクラスBを「サブクラス(subclass)」と呼びます。

サブクラスのオブジェクトは、アップキャストによりスーパークラスのオブジェクトに代入して、スーパークラスとして振る舞うことができます。これは、常に安全に実行されます。

また、このアップキャストされたオブジェクトを、ダウンキャストにより元のサブクラスに代入し直すこともできます。こちらは、想定とは異なるサブクラスにダウンキャストされる可能性がある為、常に安全とは限りません。

近年の言語であれば、アップキャストの際にサブクラスの型を記録しておき、ダウンキャストの際に安全に型変換ができるかを確認し、出来ない場合には例外を送出するようになっているでしょう[7]

「名前的型システム(Nominal Type System)」と「構造的型システム(Structual Type System)」

サブクラスのオブジェクトをスーパークラスに代入できるのは、両者が明示的に「継承」を宣言している為です。このように、型同士の互換性を明示的に示す方式を「名前的型システム(Nominal Type System)」と言います。クラス継承はこちらに相当します。

一方、プログラミング言語では「明示的な宣言がなくても、型の構造に互換性が保証されるなら、型同士の代入を許す」という形式を採用しているものがあります。これを「構造的型システム(Structual Type System)」と言います[8]

以下、構造的型システムによって互換性が保証される型同士の関係の事を「部分型(subtyping)」と呼ぶことにします[9]

サブタイプ(subtype)とスーパータイプ(supertype)

部分型では、型Bが型Aに代入可能な場合、型Bを「サブタイプ(subtype)」、型Aを「スーパータイプ(supertype)」と呼びます(「クラス」ではなく「タイプ(型)」です)。とはいえ、考え方はクラス継承のサブクラス、スーパークラスと同じです。違いは、継承関係がなくても関係が成立すると言う点です。

例えばVerseでは、同じ型のみで構成されたタプルは、その型の配列のサブタイプになります[10]。そのため、以下のような代入が可能です。

argTuple: tuple(int,int) = (0,1)
argInt: []int = argTuple

タプルと配列自体には継承関係が無いにも関わらず、このコードはコンパイルエラーにならず、想定通りに機能します。

一方、逆は出来ません。型変換を使ってもダメです。

argTuple: []int = (0,1)
# 以下はコンパイルエラー
argInt: tuple(int,int) = argTuple
# こちらもコンパイルエラー^[というかそもそもどんな型でもtupleへの型変換は許されていないと思われる。]
if(argInt: tuple(int,int) = tuple(int,int)[argTuple])

このように、部分型では型の構造に互換性がある場合にのみ、安全に代入が出来る仕組みです。

Verseの組み込み型における部分型

Verseの組み込み型の中では、既に説明したタプルと配列の関係の他にも、以下のような部分型の関係を持つ物があります。

any型

Verseで定義される全ての型はany型のサブタイプになります。

これはつまり、どのような型であっても、any型に代入出来る事を意味します。ただし、any型に代入した値を元の型に戻すことは出来ません。any型を継承している訳では無いからです。

例えば、ある関数がint型と失敗(fail)の2種類の型を返す必要がある場合、関数の戻り型はany型になります。ただし、返ってきた値を使う事はできません。一般に、anyは使わない(使う事になった時点でコードに問題がある)と考えるべきだと思われます。

なお、any型は「引数として受け取るが参照しない型」を表す場合に用いられます(これは型エイリアスか型マクロの時に説明します)。

comparable型

互いに比較が可能な型はすべてcomparable型のサブタイプになります。
組み込み型では、以下がcomparable型のサブタイプになります。

  • int型
  • logic型
  • float型
  • char型
  • char32型

以下のコンテナ型については、格納する型が全てcomparable型のサブタイプで構成されている場合のみ、comparable型のサブタイプなります。

  • 配列型
  • option型
  • タプル型
  • マップ型

なお、comparable型自身もany型のサブタイプです。

空(false)

仕様上、option型によって導入される「空の状態(値が無い状態)」は、全ての型のサブタイプの型とみなされるようです。ただし、この事が現時点で役に立つケースはありません[11]

補足:現時点では許されない部分型

部分型のルールでは以下の様な代入も出来る筈ですが、現時点ではこのようなコードはコンパイルエラーになります。

#※コンパイルは通りません
class1 := struct:
    X:int
    Y:int

class2 := struct:
    X:int
    Y:int
    Z:int

instance1 := class2{X:=0,Y:=1,Z:=2}
instance2 :class1 = instance1

将来的にはclass2がclassのサブタイプである事を明示することで代入できるようになると思われます(正式に予告されている訳ではありません)。

#Fortnite #Verse #VerseLang #UEFN

続き

https://zenn.dev/t_tutiya/articles/29abe385eba3a5

脚注
  1. C#にも部分型の考え方はあります ↩︎

  2. 気がする ↩︎

  3. 型システム関連の代表的な書籍である「型システム入門」ではsubtypingを「部分型付け」、subtypeを「部分型」としてます ↩︎

  4. 「公称的型システム」と訳される場合もあります ↩︎

  5. 「型システム入門」での訳は公式訳と同じ「正/負」 ↩︎

  6. 「正の位置」「負の位置」を「陽性位置」「陰性位置」と訳す場合もあるようです ↩︎

  7. Verseの場合は処理が失敗扱いになります ↩︎

  8. 同じ意味で「名前的部分型付け(nominal subtyping)」「構造的部分型付け(strcutual subtyping)」という用語もありますが、分かりにくいので今回は使いません ↩︎

  9. 厳密には、名前的型システムと構造型システムを包括した物を「部分型付け(subtyping)」と呼ぶのだと思われますが、ここでは「クラス継承ではない互換性のある型」の意味で使います ↩︎

  10. 恐らく最適化によってデータ構造が同じになっているのだと思うけど良く知らない ↩︎

  11. 多分 ↩︎

Discussion