パラメトリック型とは
パラメトリック型は、ジェネリック型やテンプレート型とも呼ばれ、汎用的なデータ構造や操作を定義するための仕組みです。
型引数を受け取ることができるため、さまざまなデータ型に対して再利用可能なコードを書くことができるのが特徴的。
プログラム内でパラメトリック型を使用する場合、型引数として具体的なデータ型を指定する必要があります。
これにより、同じコードを異なるデータ型に対して使用することができますね。
例えば、リストを表すパラメトリック型を考えてみましょう。
リストは様々なデータ型の要素を保持できるため、パラメトリック型を使用することで、整数のリストや文字列のリストなど、異なるデータ型のリストを作成できます。
これにより、リストに関連する操作やアルゴリズムを再利用することができます。

あすか
例
クリックすると開きます。
パラメトリック型は、パラメータを取り込むことができる型です。
たとえば、List
型は、T
というパラメータを取り込みます。
※T
には、任意の型を指定することができます。
そのため、List
型は、List
of int
, List
of string
, List
of object
など、さまざまな型のリストを表すことができるわけです。
一方、普通の型は、パラメータを受け取ることができません。
たとえば、int
型は、int
型の値しか格納できません。string
型は、string
型の値しか格納できません。
パラメトリック型は、普通の型よりも汎用的です。パラメトリック型は、さまざまな型の値を格納することができます。
明示的な型引数
明示的な型引数は、関数やクラスの定義時に指定される型の引数のことを指します。
これにより、特定のデータ型に対して関数やクラスを作成できますね。

あすか
box(first_item:type, second_item:type) := class:
ItemOne:first_item
ItemTwo:second_item
例えば、上記のコードでは、boxというクラスが定義されています。
このクラスは2つの引数を受け取り、それぞれの引数の型はtypeです。
first_itemはItemOneクラスのインスタンスを、second_itemはItemTwoクラスのインスタンスを初期化。
ここで、first_itemとsecond_itemは明示的な型引数であり、boxクラスを特定のデータ型に対して作成するために使用されます。
MakeOption(t:type):?t = false
IntOption := MakeOption(int)
FloatOption := MakeOption(float)
StringOption := MakeOption(string)
また、使用例として、string型の値を持つboxやint型の値を持つbox、またはstring型とint型の値を持つboxなど、異なるデータ型に対してboxクラスのインスタンスを作成することができます。
MakeOption関数は任意の型を引数として受け取り、その型のoptionを返すのが特徴的。
明示的な型引数があり、特定のデータ型に対してoptionを作成するのに使用されます。
例えば、int型のoptionを作成するためにはIntOption := MakeOption(int)というコードを使用します。
このようにすることで、MakeOption関数はint型に対してoptionを生成するための関数となります。同様に、float型のoptionやstring型のoptionを作成することもできますね。
メモ用
クリックすると開きます。
box(first_item:type, second_item:type) := class:
ItemOne:first_item
ItemTwo:second_item
上記のコードは、明示的な型引数を使用してクラスと関数を定義する例です。以下に解説します。
- クラス "box" の定義:
- "box" クラスは2つの引数を取りますが、それぞれの引数の型は「type」となっています。
- "box" クラスのインスタンスを作成する際には、具体的なデータ型を指定する必要があります。
- "FirstItem" と "SecondItem" はクラス内の変数で、それぞれ "first_item" と "second_item" のインスタンスへの参照です。
- 関数 "MakeOption()" の定義:
- "MakeOption()" 関数は引数として任意の型 "t" を取ります。
- 関数内部では "t" のオプション("?")を作成し、初期値は "false" です。
- この関数の型引数を指定することで、異なるデータ型に対してオプションを作成することができます。
クラス "IntOption"、"FloatOption"、"StringOption" の定義:
- "IntOption" は "MakeOption(int)" を呼び出して int 型のオプションを作成します。
- 同様に、"FloatOption" は float 型のオプション、"StringOption" は string 型のオプションを作成します。
これらのコードは、汎用的なクラスや関数を特定のデータ型に対して再利用するための手段として使用されます。例えば、"box" クラスを使って、異なるデータ型の値を格納する箱を作成したり、"MakeOption()" 関数を使って任意のデータ型のオプションを作成できます。
これにより、プログラミングにおいて柔軟性と再利用性が向上し、効率的なコードを書くことができます。初心者の方には、型引数を使うことでクラスや関数が特定のデータ型に応じてどのように動作するのかを指定できると理解すると良いでしょう。
typeについて
「type」という表記は、プログラミングにおいて一般的な用語ですが、言語や文脈によって意味が異なることがあります。以下では一般的な解釈を説明します。
「type」は、プログラミング言語における「型(type)」という概念を指します。型は、データの種類や性質を表現し、データの保存方法や操作方法を制約するために使用されます。例えば、整数型(int)、文字列型(string)、浮動小数点数型(float)など、データの種類に応じて異なる型を使用します。
「box」クラスの定義では、引数の型を「type」として指定しています。これは、具体的な型が指定されていないことを示しており、任意のデータ型を受け入れることができることを意味します。つまり、実際に「box」クラスのインスタンスを作成する際には、その場で具体的な型を指定する必要があるということです。
例えば、以下のように「box」クラスのインスタンスを作成する際に、具体的な型を指定します:
my_box = box(int, float)
この場合、最初の引数は整数型(int
)であり、2番目の引数は浮動小数点数型(float
)です。ただし、具体的なデータ型を指定する際には、使用しているプログラミング言語の構文や制約に従う必要があります。
「type」は、型引数や汎用プログラミングにおいて一般的な用語ですが、具体的な使用方法や振る舞いはプログラミング言語や文脈によって異なる場合があります。したがって、詳細な説明や具体的な使い方は使用しているプログラミング言語のドキュメントやリファレンスを参照することをおすすめします。
明示的な型引数は、プログラム内で特定のデータ型に対して再利用可能なコードを作成する際に便利です。

あすか

りゅう
具体的なデータ型を指定することで、関数やクラスを型に特化させることができるってことですね!
暗黙的な型引数
暗黙的な型引数は、関数やクラスの定義時に specific な型を指定せずに、任意の型に対して機能するコードを作成するために使われる仕組みです。
暗黙的な型引数は where
キーワードを使用して導入されます。
ReturnItem(Item:t where t:type):t = Item
例えば、上記のコードでは、関数 ReturnItem()
が定義されています。
この関数は Item
というパラメータを受け取り、その型を暗黙的な型引数 t
で制約し(where t:type
)、ReturnItem()
関数は受け取った引数 Item
をそのまま返します。
このとき、t
は関数 ReturnItem()
の暗黙的な型パラメータであり、どんな型でも受け取ることができます。
ただし、t
の型に基づいて、ReturnItem()
関数が受け取ることができる Item
の型が制約されます。
つまり、t
は type
という制約があるため、どんな型でも ReturnItem()
を呼び出すことができるわけです。

あすか
暗黙的な型引数を使用することで、ReturnItem()
を以下のように書く必要はありません。
ReturnInt(Item:int):int = Item
ReturnFloat(Item:float):float = Item
代わりに、1つの関数で済ませることができます。
ReturnItem(Item:t where t:type):t = Item
これにより、ReturnItem()
関数は実行する操作に関係なく、t
の型に基づいて正しく機能します。
ReturnItem("t") # t は文字列です
ReturnItem(0.0) # t は浮動小数値です
実際の使用例として、ReturnItem()
関数が引数として "hello"
や 0.0
を受け取った場合を示しました。この場合、t
はそれぞれ文字列型と浮動小数点型になります。
box(first_item:type, second_item:type) := class:
ItemOne:first_item
ItemTwo:second_item
MakeBox(ItemOneVal:ValOne, SecondItemVal:ValTwo where ValOne:type, ValTwo:type):box(ValOne, ValTwo) =
box(ValOne, ValTwo){ItemOne := ItemOneVal, ItemTwo := SecondItemVal}
Main():void =
MakeBox("A", "B")
MakeBox(1, "B")
MakeBox("A", 2)
MakeBox(1, 2)
同様に、MakeBox()
関数も暗黙的な型引数の例です。
この関数は2つの引数 ItemOneVal
と SecondItemVal
を受け取り、それぞれの引数の型を暗黙的な型引数 ValOne
と ValTwo
で制約(where ValOne:type, ValTwo:type
)。
そして、MakeBox()
関数は引数の型に基づいて決まる型のボックスを生成して返します。
box(first_item:type, se~の詳細
クリックすると開きます。
#2つの明示的な型パラメータを取るboxクラスを定義します。クラスにはItemOneとItemTwoという2つのプロパティがあります。
box(first_item: 型, second_item: 型) := class:
ItemOne: first_item
ItemTwo: second_item
#MakeBox()関数を定義します。この関数はValOneとValTwoという2つの明示的な引数を取ります。
#ただし、ValOneとValTwoは任意の型であることを示すために、型の指定が付いています ('where ValOne: 型, ValTwo: 型')。
#関数は指定された値を持つboxを返します。
MakeBox(ItemOneVal: ValOne, SecondItemVal: ValTwo where ValOne: 型, ValTwo: 型): box(ValOne, ValTwo) =
box(ValOne, ValTwo) {ItemOne := ItemOneVal, ItemTwo := SecondItemVal}
#Main()関数を定義します。
Main(): void =
#MakeBox()を呼び出し、引数として文字列"A"と文字列"B"を渡します。
MakeBox("A", "B")
#MakeBox()を呼び出し、引数として整数1と文字列"B"を渡します。
MakeBox(1, "B")
#MakeBox()を呼び出し、引数として文字列"A"と整数2を渡します。
MakeBox("A", 2)
#MakeBox()を呼び出し、引数として整数1と整数2を渡します。
MakeBox(1, 2)
提供されたコードは、パラメータ化された型と関数を使用して、ボックス(箱)を作成する例です。
以下に、各部分の解説を行います。
box
クラスの定義:
box(first_item:type, second_item:type) := class:
ItemOne: first_item
ItemTwo: second_item
これは、box
クラスの定義。
box
は2つの型引数 first_item
と second_item
を受け取ります。
ItemOne
と ItemTwo
インスタンス変数は、それぞれ first_item
と second_item
の型を持ちます。
これにより、box
クラスは任意の2つの異なる型のオブジェクトのボックスを表現します。

あすか
MakeBox
関数の定義:
MakeBox(ItemOneVal: ValOne, SecondItemVal: ValTwo where ValOne:type, ValTwo:type): box(ValOne, ValTwo) =
box(ValOne, ValTwo) {ItemOne := ItemOneVal, ItemTwo := SecondItemVal}
MakeBox
関数は2つの引数 ItemOneVal
と SecondItemVal
を受け取ります。
ValOne
と ValTwo
はそれぞれ型引数として宣言され、ValOne
が type
であり、ValTwo
も type
であることが制約されています(where ValOne:type, ValTwo:type
)。
関数の戻り値は box(ValOne, ValTwo)
型のボックスです。
これにより、返されるボックスは ValOne
型と ValTwo
型のオブジェクトの組み合わせです。
MakeBox
関数の本体では、ボックスを作成しています。
box(ValOne, ValTwo)
のコンストラクタを呼び出し、ItemOne
に ItemOneVal
、ItemTwo
に SecondItemVal
の値を設定しています。
つまり、MakeBox
関数は引数の値を使用して新しいボックスを作成し、それを返すわけですね。

あすか
Main
関数の定義:
Main(): void =
MakeBox("A", "B")
MakeBox(1, "B")
MakeBox("A", 2)
MakeBox(1, 2)
Main
関数では、MakeBox
関数を4回呼び出します。
それぞれの呼び出しでは、異なる値を引数として渡しており、異なる型の値を持つさまざまなボックスが作成されます。
最初の呼び出し MakeBox("A", "B")
では、ValOne
と ValTwo
の型が両方とも string
になります。
したがって、box(string, string)
の型のボックスが作成されるわけです。
同様に、MakeBox(1, "B")
では ValOne
の型が int
、ValTwo
の型が string
となりますので、box(int, string)
の型のボックスが作成されます。また、残りの2つの呼び出しについても同様にボックスが作成されます。
この例は、異なる型のオブジェクトの組み合わせを持つ汎用的なボックスを作成するために、パラメータ化された型と関数を使用しています。
暗黙的な型引数を使用することで、MakeBox
関数はどのような型のオブジェクトでも受け取ることができ、それを組み合わせてボックスを作成できる。と覚えておきましょう。

あすか
このように暗黙的な型引数を使用することで、特定のデータ型に依存せずに再利用可能なコードを作成できるのが特徴です。

あすか

みなみ
暗黙的な型引数によって、特定の型に制約されずに幅広いデータ型に対して機能する柔軟なコードを書くことができるわけですね🌸
型の制約
▼ 型の制約を指定することで、式の型に制約をかけることができます。
現在、サポートされている制約はサブタイプのみであり、暗黙的な型パラメータのみが対象となります。
これにより、与えられたクラスが特定のクラスのサブクラスである場合にのみ関数がコンパイルされるようにできます。
例えば以下のとおり。
int_box := class:
Item:int
MakeSubclassOfIntBox(NewBox:subtype_box where subtype_box:(subtype(int_box))) : tuple(subtype_box, int) = (NewBox, NewBox.Item)
クリックすると開きます。
# int_boxというクラスを定義しています。これは整数型のItemプロパティを持ちます。
int_box := class:
Item:int
# MakeSubclassOfIntBoxという関数を定義しています。この関数はsubtype_boxという型パラメータを持ちます。
# subtype_boxはint_boxのサブタイプである制約があります。
# 関数の戻り値の型は、subtype_boxとintのタプルです。
MakeSubclassOfIntBox(NewBox:subtype_box where subtype_box:(subtype(int_box))) : tuple(subtype_box, int) = (NewBox, NewBox.Item)
このコードは、型パラメータの制約を使用して、与えられたクラスが別のクラスのサブクラスである場合にのみ関数がコンパイルされるようにしています。
int_box := class:
Item:int
- この行では、
int_box
という名前のクラスを定義しています。 int_box
クラスには、Item
という整数型のプロパティがあります。Item:int
という表記は、Item
が整数型であることを示しています。
MakeSubclassOfIntBox(NewBox:subtype_box where subtype_box:(subtype(int_box))) : tuple(subtype_box, int) = (NewBox, NewBox.Item)
- この行では、
MakeSubclassOfIntBox
という関数を定義しています。 NewBox
という引数を受け取ります。subtype_box
はint_box
クラスのサブタイプ(派生クラス)である制約があります。- 関数の戻り値の型は、
tuple(subtype_box, int)
です。- つまり、
subtype_box
と整数型の値のペアを返します。
- 関数の実装では、
(NewBox, NewBox.Item)
というタプルを返しています。- これは、
NewBox
(クラスのインスタンス)とNewBox.Item
(NewBox
のItem
プロパティの値)のペアを含んでいます。
このコードは、以下のように使われることが想定されています。
int_box
クラスは、整数型のプロパティItem
を持つ新しいクラスです。- このクラスのインスタンスを作成することができます。
MakeSubclassOfIntBox
関数は、int_box
クラス(またはそのサブタイプ)のインスタンスであるNewBox
を引数として受け取ります。NewBox
を返すと同時に、NewBox
のItem
プロパティの値も返します。
タプルとは
クリックすると開きます。
タプルとは、複数の値を組み合わせて1つのデータ型として扱うことができるデータ構造です。タプルは、括弧で囲んで記述します。
たとえば、(1, 2, 3)は、3つの値を組み合わせたタプルですね。
タプルは、データの配列や関数の戻り値として使用することもできます。
タプルはデータの組み合わせを効率的に扱うことができるため、プログラミングでよく使用されているみたいです😌

あすか
タプルとマップの違い
クリックすると開きます。
タプルとマップは、どちらも複数の値を組み合わせて1つのデータ型として扱うことができるデータ構造です。しかし、タプルは順序付きのデータ構造であるのに対し、マップは順序付けされていないデータ構造です。
タプルは、値の順番が重要です。たとえば、(1, 2, 3)と(2, 1, 3)は、異なるタプルと見なされます。
一方、マップは、値の順番が重要ではありません。例えると、{1: "a", 2: "b", 3: "c"}と{2: "b", 1: "a", 3: "c"}は、同じマップと見なされます。
タプルは、データの配列として使用されたり、関数の戻り値として使用されたりします。
一方、マップは、データの連想配列として使用されたり、キーと値の組み合わせを効率的に検索するために使用したりすることもできます。
まとめると、タプルはデータの順序を保ちたい場合に、マップは、データのキーと値を効率的に検索したい場合に使用されるということです。

あすか
用語集
型マクロとは
クリックすると開きます。
型マクロは、式の型を取得するための特殊な機能です。
通常、式の型を取得するためには、その式を評価(実行)する必要がありますが、型マクロを使用すると、コンパイル時に式の型情報を取得することができます。
型マクロを使うことにより、プログラムの任意の箇所で式の型情報を利用できるわけですね。

あすか
これは、コードの柔軟性を高め、静的な型チェックやコンパイル時の最適化に役立ちます。
具体的な例としては、関数の引数や戻り値の型を表現する際に型マクロを使用することがあります。
また、特定の制約を持つ関数やオブジェクトを定義する際にも便利です。
型マクロの構文は言語によって異なる場合がありますが、一般的には特殊なキーワードや記号を用いて型情報を表現します。
型マクロを使うことで、より型安全なコードを書くことができます。
コンパイラが型情報を把握し、型に関するエラーや警告を提供することで、バグを事前に検出しやすくなります。
また、型マクロの利用により、コードの再利用性や保守性も向上する場合がありますね。
まとめ
2023/7/12 お疲れさま。