2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FixLangで遊ぼう (2) 基礎編: モジュールと型

Last updated at Posted at 2024-04-17

この記事では「FixLangで遊ぼう」の第2回として、FixLangのモジュールと型について説明したいと思います。前回の記事はこちらをご覧ください。

本記事はFixLangの公式ドキュメントを日本語に翻訳し、初学者向けに一部を書き改めたものです。詳しく知りたい方は公式ドキュメントをご覧ください。

FixLangを試しに使ってみたい方はぜひfix playgroundにアクセスしてみてください。

また、FixLangの非公式Dockerイメージを公開していますので、Dockerに慣れている方はこちらの方が便利かも知れません。

ソースファイル

ソースコードを記述するファイルのことを、ソースファイルと呼びます。
FixLangのソースファイルは、.fix という拡張子にします。(例: main.fix)

FixLangのソースファイルを実行するには、シェルプロンプトで以下のコマンドを実行します。

$ fix run -f main.fix

複数のソースファイルがある場合は、以下のように -f の後に複数のソースファイルを指定します。

$ fix run -f main.fix sub.fix

モジュール

module Main;                       // モジュール定義

main: IO ();                       // mainの型定義
main = println("Hello World!");    // mainの値定義

ソースファイルの先頭では、モジュールを定義する必要があります。
モジュールとは、ソースファイル内部の型定義や値定義などをまとめたものです。

上記の例では、Mainという名前のモジュールを定義しています。
モジュール名の先頭文字は大文字にする必要があります。

なお、モジュール階層を表現するため、Main.Model.Implのようなモジュール名にすることも可能です。この場合、. 区切りの名前の先頭文字は大文字にする必要があります。

グローバル値

上記の例では、mainというグローバル値の型と値を定義しています。
FixLangでは、グローバル値の型を明示的に定義する必要があります。

FixLangのプログラムを実行すると、Mainモジュールのmainという名前の関数を実行します。

名前空間

module Main;                           // モジュール定義

namespace Test {                       // 名前空間の定義
    test: I64 -> I64 -> String;        // testの型定義
    test = |x, y| (x + y).to_string;   // testの値定義
}

main: IO ();                           // mainの型定義
main = println(Test::test(3,5));       // mainの値定義

モジュール内部では、名前空間を定義可能です。
名前空間の役割は、以下の通りです。

  • 関連した機能をひとまとめにできる
  • 名前の衝突を防ぐことができる

上記の例のTest::testは、名前空間を明示的に指定した名前です。
名前が他の名前と衝突していない場合や、前後の文脈から推定できる場合は、名前空間を省略可能です。従って、上記の例のTest::testは単にtestと書くことも可能です。

FixLangの組み込みライブラリでは、Array, Iteratorなどの名前空間が定義されています。詳しくは、組み込みライブラリのリファレンスをご覧ください。

FixLangでは値はすべて型を持ちます。型は数学の集合で、値は集合の要素と見なすことができます。

数値型

8ビット 16ビット 32ビット 64ビット
符号なし整数 U8 U16 U32 U64
符号あり整数 I8 I16 I32 I64
浮動小数点数 - - F32 F64

数値リテラルは、{数値を表す文字列}_{型} という形式です。
型を省略すると、ピリオドがない場合は I64、ピリオドがある場合は F64 になります。

123              // 64ビット符号あり整数(I64)
123_U32          // 32ビット符号なし整数(U32)

123.45           // 64ビット浮動小数点数(F64)
123.45_F32       // 32ビット浮動小数点数(F32)

また、0x, 0o, 0b というプレフィックスを付けることで、16進数/8進数/2進数での表記が可能です。

0xdeadbeef_U32    // 32ビット符号なし整数(U32), 16進数表記
0o000755_U16      // 16ビット符号なし整数(U16), 8進数表記
0b00010111_U8     // 8ビット符号なし整数(U8), 2進数表記

真偽値型

Bool型は真偽値を表します。true, falseは真偽値を表すリテラルです。

true             // 
false            // 

配列型

Array aは配列型を表します。例えば文字列(String)の配列は Array String になります。また、64ビット符号あり整数(I64)の配列は Array I64 になります。
[] の間に要素を , で区切って書くと、配列のリテラルになります。なお、配列の要素はすべて同じ型にする必要があります。

[1, 2, 3]                  // Array I64 
[65_U8, 66_U8, 67_U8]      // Array U8 
["Hello", "World"]         // Array String 

最後の要素の後にある,は無視されます。

// 以下の2つは同じ配列を表す
[1, 2, 3]
[1, 2, 3, ]

要素の型が異なる場合はエラーになります。

[1, 2, "Hello"]            // 要素の型が異なるためエラー

文字列型

String型は文字列を表します。文字列のリテラルは "Hello"のように書くことができます。

"Hello"              // "Hello" という文字列
"Foo\nbar\nbaz"      // Foo, bar, baz を改行で連結した文字列

文字列にはヌル文字(0_U8)を含めることはできません。

FixLangの内部では文字列はバイト配列として表現されています。エンコーディングは特に決まっていませんが、アプリケーションではUTF-8として扱うことが多いと思います。

タプル型

() の間に2個以上の要素を , で区切ったものはタプルになります。
タプルの各要素は異なる型であっても問題ありません。

(1, 2)                    // (I64, I64)
(3, "Hello")              // (I64, String)
(4.5_F32, 6_U8, [7, 8])   // (F32, U8, Array I64)

() の間に1個の要素があるものは、1個の要素をカッコで囲んだものとして解釈されます。

(1)                       // I64 
("Hello")                 // String

() の間に1個の要素と,があるものは、長さ1のタプルとして解釈されます。

(1,)                       // (I64,) 
("Hello",)                 // (String,) 

ユニット型

() の間に何も無いものはユニット型になります。ユニット型は()というただ一つの値しか持ちません。

()                        // ()

IO a 型

画面入出力やファイル入出力などのI/O処理は IO a型になります。
ただし a はI/O処理結果の型です。
画面出力のように処理結果がとくにない場合は IO ()型になります。
Mainモジュールのmain関数はIO ()型にする必要があります。

input_line                // IO String 
println("Hello")          // IO () 

関数型

関数型は 引数の型 -> 戻り値の型 のように表します。

下記の例は、I64 -> String という型を持つ関数定義です。(関数定義については、次回の記事で詳しく説明する予定です)

make_item: I64 -> String;
make_item = |id| (
  "item-" + id.to_string
);

また、2個の引数を持つ関数の型は 1番目の引数の型 -> 2番目の引数の型 -> 戻り値の型 のように表します。

下記の例は、String -> I64 -> String という型を持つ関数定義です。

make_item2: String -> I64 -> String
make_item2 = |name, id| (
  name + "-" + id.to_string
); 

ちなみに、-> 演算子は右結合のため、String -> I64 -> StringString -> (I64 -> String) として解釈されます。

FixLangには、「2変数関数」という概念はありません。引数は1個ずつ順番に適用されます。

例えば、上記の make_item2 関数に "button"という文字列を適用すると、I64 -> String 型の関数になります。
その関数に 123を適用すると、String型の値になります。(上記の例では、"button-123"という文字列になります)

関数定義と関数適用については、次回の記事で詳しく説明する予定です。

終わりに

本記事では、FixLangのモジュールと型について説明しました。
次回の記事で、FixLangの基本的な文法について説明する予定です。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?