基本的な型
コンパイラ版WapLは静的型付き言語であり、コンパイル時にすべての変数の型が判明している必要があります。基本的に型は変数や関数の宣言時に明示的に書く必要があり、それ以降は型推論が効きます。
スカラー型
スカラー型は、単独の値を表します。WapLにおいては主に4つのスカラー型があります: 整数、浮動小数点数、論理値、文字、です。
整数型
WapLでは2種類の整数型があります:i32とi64とisizeです。それぞれ32-bit、64-bitの符号ありの整数、ビルド時に何ビットアーキテクチャをターゲットにしているかに合わせたサイズになる符号あり整数です。符号ありなので例えばi32だと$-(2^{31})$から$2^{31}-1$まで表現できます。
整数リテラルは小数点を含まないただの整数はi64になり、末尾にsをつけるとi32になります。また末尾に_をつけるとisizeになります
| 型 | 例 |
|---|---|
| i64 | -123 |
| i32 | 42s |
| isize | 10_ |
浮動小数点型
整数型と同様に浮動小数点型にも32-bitと64-bitのものがあります:f32とf64です。
浮動小数点リテラルは小数点を持つ数値です。また、浮動小数点リテラルも整数リテラル同様に32-bitならsを末尾につけ、つけなければf64として扱われます。
| 型 | 例 |
|---|---|
| f64 | -123.45 |
| f32 | 42.0s |
数値演算
WapLにも全数値型に期待されうる標準的な数学演算が用意されています: 足し算、引き算、掛け算、割り算、余りです。WapLで特徴的なのは異なる型で数値演算を行った際には左側の型に合わせて自動でキャストされるということです。
fn main():i32{
// 足し算
#=(sum, +(5,20.2), i64);// 25
// 引き算
#=(difference, -(10.5,3), f64);// 7.5
// 掛け算
#=(product, *(42,5), i64);// 210
// 割り算
#=(quotient, /(49.54,10.0), f64);// 4.954
#=(floored, /(2,3), i64);// 0
// 余り
#=(remainder, %(43,5), i64);// 3
return 0s;
}
これらの文の各式は、数学演算子を使用しており、一つの値に評価され、そして、変数に束縛されます。
論理値型
WapLの論理値型:boolはtrueとfalseの二つの値をとることができます。
fn main():i32{
#=(t, true, bool);
#=(f, false, bool);
return 0s;
}
文字型
WapLで文字はchar型で扱います。charは8-bitであるためASCIIまでしか扱えません。また、文字リテラルはシングルクォーテーションで囲みます。
fn main():i32{
#=(c1, 'A', char);
#=(c2, '\x41', char);
#=(c3, '\u{41}',char);
println(format("%c",c1));
println(format("%c",c2));
println(format("%c",c3));
return 0s;
}
A
A
A
これらはすべてAを表します。
ポインタ型
WapLでは配列や文字列などもポインタ型であらわします。ポインタ型には4種類あり、ptr:T *:T &:T &mut:Tがあり、Tのところにポインタが指す型が入ります。ポインタのポインタだったりする場合はptr:ptr:Tのようになります。それぞれのポインタ型の詳しい説明は第3章 簡易的な所有権/借用でします。
fn main():i32{
#=(str, "hello", ptr:char);
println(format("%s",str));
#=(arr, salloc(i64,5), ptr:i64);
=(arr, Array(3,1,4,1,5));
#=(i,0,i64);
loopif:(<(i,5)){
println(format("%d", [](arr,i)));
=(i,+(i,1));
}
#=(x, 10, i64);
#=(p, &_(x), ptr:i64);
println(format("%d", *_(p)));
return 0s;
}
hello
3
1
4
1
5
10
このように、文字列や配列もポインタ型として扱います。文字列リテラルはダブルクォーテーションで囲みます。また、配列は先に型と要素数を指定してsallocでスタック上にメモリを確保してArrayで値を列挙して配列に格納していき、[](ポインタ変数, インデックス)で値を取り出せます。[]では多次元配列でも[](ポインタ変数, インデックス1, インデックス2, ...)のようにして簡単に値を取り出すことができます。
固定長配列型(0.2.16以降)
固定長配列型はすべての要素が同じ型である必要があり、また配列のサイズを変更することもできません。これは型名自体がサイズと要素の型の情報を持っているためです。例えば要素の型がi64でサイズが10の固定長配列型の型名はarray_10:i64となるように、要素の型T、サイズNに対してarray_N:Tという型名になります。またarray(1,2,3)のようにして値を作ることができます。要素へのアクセスはポインタでは[]を用いましたが、固定長配列型では[array]を用います。先ほどのポインタ型による配列の処理を固定長配列型でも同じように書いてみましょう。
fn main():i32{
#=(str, array('h','e','l','l','o'), array_5:char);
println(format("%s",as(str,ptr:char)));
#=(arr,array(3,1,4,1,5) , array_5:i64);
#=(i,0,i64);
loopif:(<(i,5)){
println(format("%d", [array](arr,i)));
=(i,+(i,1));
}
return 0s;
}
hello
3
1
4
1
5
固定長配列型のとポインタ配列の最も大きな違いは固定長配列型は要素そのものが値であるという点です。ポインタ配列の値は0個目の要素の値があるメモリ上のアドレスであり、それを他の変数に渡しても参照するアドレスが渡されるだけで要素の値が格納されている場所は同じです。一方固定長配列型では要素そのものがすべて渡されるため要素が格納されている場所は変数によって異なります。
fn print_array(ptr:i64 arr){
#=(i,0,i64);
loopif:(<(i,7)){
print(format("%d,", [](arr,i)));
=(i,+(i,1));
}
println("");
}
fn main():i32{
// ====固定長配列型====
println("固定長配列型");
#=(arr1,array(0,1,1,2,3,5,8) , array_7:i64);
#=(arr2, arr1, array_7:i64);
// arr2のインデックス3の値を99に書き変える
=([array](arr2,3),99);
// ----arr1を表示----
print_array(as(arr1,ptr:i64));
// ----arr2を表示----
print_array(as(arr2,ptr:i64));
// ====ポインタ配列型====
println("ポインタ配列");
#=(ptr1, salloc(i64,7), ptr:i64);
=(ptr1,Array(0,1,1,2,3,5,8));
#=(ptr2, ptr1, ptr:i64);
// ptr2のインデックス3の値を99に書き変える
=([](ptr2,3),99);
// ----ptr1を表示----
print_array(ptr1);
// ----ptr2を表示----
print_array(ptr2);
return 0s;
}
固定長配列型
0,1,1,2,3,5,8,
0,1,1,99,3,5,8,
ポインタ配列
0,1,1,99,3,5,8,
0,1,1,99,3,5,8,
そのため、このようにしたときに固定長配列型ではarr2に加えた変化はarr1には影響がありませんが、ポインタ配列ではptr2に加えた変化がptr1にも反映されます。