重要なのは、正しいタイプを考え出すことです。
のようなものが必要な場合は、すべてのリスト要素が同じ型でなければならないため[1, [2, [3, [4]]]]
、それを正確に行うことはできません。
これは理にかなっています。なぜなら、リストから要素を取得するとき、それを使って何かを行う前に、それがどのような型であるかを知る必要があるからです (これが型の要点のようなもので、何ができるか、何ができるかを教えてくれます。)物とは関係ありません)。
しかし、Haskell の型システムは静的であるため、リストのどの要素であるかを知らなくても、どの型であるかを知る必要があります。取得しているリスト インデックスは、プログラムが実行されるまでわからない可能性があるからです。したがって、使用するインデックスが何であれ、同じタイプのものを取得する必要があります。
ただし、あなたが望むものと非常によく似た何かをすることは可能です: 整数であるかもしれないデータ型、またはリストであるかもしれないデータ型が欲しいです:
type IntegerOrList a = Either Integer [a]
タイプに慣れていない場合Either
、値はsomeまたはsomeのEither l r
いずれかになります。値が整数または何かのリストのいずれかである型も同様です。したがって、それらのリストを作成できます: 以下は type の値です:Left x
x :: l
Right y
y :: r
IntegerOrList a
[IntegerOrList Bool]
[Left 7, Left 4, Right [True, False], Left 8, Right [], Right [False]]
オーケー、それでリスト内のリストの 1 つのレベルですが、まだリスト内のリスト内にリストを入れることはできません。内部のリストにはBool
s が含まれており、リストにすることはできません。代わりに があれば[IntegerOrList (IntegerOrList Bool)]
、リスト内のリスト内にリストを含めることができますが、それでもそれ以上のことはできません。この例では、整数またはリストのいずれかである値を含むリストがあり、リストは整数またはリストである値を含むリストであり、... 本当に必要なのは のようなものIntegerOrList (IntegerOrList (IntegerOrList ...
、またはもっと単純に何かお気に入り:
type IntegerOrLists = Either Integer [IntegerOrLists]
しかし、それは許可されていません。型シノニムを再帰的にすることはできません。これは、貧弱なコンパイラを混乱させる無限に大きな型を生成するためです。ただし、適切なデータ型は再帰的である可能性があります。
data IntegerOrLists = I Integer | L [IntegerOrLists]
これで、整数とタイプのリストを組み合わせて、次のようなリストを作成できます。
L [I 1, L [I 2, L [I 3, L [I 4]]]]
重要なのは、各項目が整数であるかリストであるかは、I
またはL
コンストラクターを使用してフラグを立てる必要があるということです。これで、リストの各要素は typeIntegerOrLists
になり、そのコンストラクターを見ることでそれがどれであるかを区別できます。したがって、タイプチェッカーはついに満足しています。