Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

モジュール

💡 お知らせ: このドキュメントはAIによって翻訳されています。表現に違和感がある場合は、原文(英語)を参照するか、翻訳にご協力ください。

Flix は、他の多くのプログラミング言語で知られているような階層的モジュール(Hierarchical module)をサポートしています。

モジュールの宣言と使用

モジュールは、mod キーワードに続けてモジュールの名前空間と名前を書くことで宣言します。

例えば、次のようにモジュールを宣言できます:

mod Math {
    pub def sum(x: Int32, y: Int32): Int32 = x + y
}

ここでは、sum という関数を内部に持つ Math というモジュールを宣言しました。モジュールの外側からは、完全修飾名(Fully-qualified name)を使って sum 関数を参照できます:

def main(): Unit \ IO = 
    let result = Math.sum(123, 456);
    println(result)

あるいは、use を使って sum 関数をローカルスコープに持ち込むこともできます:

def main(): Unit \ IO = 
    use Math.sum;
    let result = sum(123, 456);
    println(result)

モジュール内の複数の宣言を使用する

モジュール内に複数の宣言がある場合:

mod Math {
    pub def sum(x: Int32, y: Int32): Int32 = x + y
    pub def mul(x: Int32, y: Int32): Int32 = x * y
}

もちろん、それぞれの宣言を個別に use することもできます:

use Math.sum;
use Math.mul;

def main(): Unit \ IO =
    mul(42, 84) |> sum(21) |> println

しかし、複数の use を 1 つにまとめる、より短い書き方もあります:

use Math.{sum, mul};

def main(): Unit \ IO =
    mul(42, 84) |> sum(21) |> println

注意: Flix はワイルドカードによる use をサポートしていません。これは、分かりにくいバグにつながる可能性があるためです。

リネームによる名前衝突の回避

同じ名前を持つ宣言同士の名前衝突は、リネーム(Renaming)を使って回避できます。

例えば、次の 2 つのモジュールがあるとします:

mod A {
    pub def concat(x: String, y: String): String = x + y
}

mod B {
    pub def concat(xs: List[Int32], ys: List[Int32]): List[Int32] = xs ::: ys
}

このとき、それぞれの concat 関数を一意な名前で use できます。例えば:

use A.{concat => concatStrings}
use B.{concat => concatLists}

def main(): Unit \ IO =
    concatStrings("Hello", " World!") |> println

この機能は強力ですが、多くの場合は完全修飾名を使う方が適切かもしれません。

モジュールと Enum

モジュールの内部で enum を定義できます。例えば:

mod Zoo {
    pub enum Animal {
        case Cat,
        case Dog,
        case Fox
    }
}

ここで Zoo モジュールは、CatDogFox という 3 つのケースを持つ Animal という enum 型を含んでいます。

型とケースには、完全修飾名を使ってアクセスできます:

def says(a: Zoo.Animal): String = match a {
    case Zoo.Animal.Cat => "Meow"
    case Zoo.Animal.Dog => "Woof"
    case Zoo.Animal.Fox => "Roar"
}

def main(): Unit \ IO = 
    println("A cat says ${says(Zoo.Animal.Cat)}!")

あるいは、Animal 型とそのケースの両方を use することもできます:

use Zoo.Animal
use Zoo.Animal.Cat
use Zoo.Animal.Dog
use Zoo.Animal.Fox

def says(a: Animal): String = match a {
    case Animal.Cat => "Meow"
    case Animal.Dog => "Woof"
    case Animal.Fox => "Roar"
}

def main(): Unit \ IO = 
    println("A cat says ${says(Cat)}!")

use Zoo.AnimalAnimalをスコープに持ち込むのに対し、use Zoo.Animal.CatCat というケースをスコープに持ち込むことに注意してください。

モジュールとトレイト

モジュールの内部でトレイトを定義することもできます。その仕組みは、モジュール内の enum と同様です。

例えば、次のように書けます:

mod Zoo {
    pub trait Speakable[t] {
        pub def say(x: t): String
    }
}

enum Animal with ToString {
    case Cat,
    case Dog,
    case Fox
}

instance Zoo.Speakable[Animal] {
    pub def say(a: Animal): String = match a {
        case Cat => "Meow"
        case Dog => "Woof"
        case Fox => "Roar"
    }
}

完全修飾名を使えば次のように書けます:

def speak(x: t): Unit \ IO with Zoo.Speakable[t], ToString[t] = 
    println("A ${x} says ${Zoo.Speakable.say(x)}!")

def main(): Unit \ IO = 
    speak(Animal.Cat)

あるいは、Zoo.Speakable トレイトと Zoo.Speakable.say 関数を use することもできます:

use Zoo.Speakable
use Zoo.Speakable.say

def speak(x: t): Unit \ IO with Speakable[t], ToString[t] = 
    println("A ${x} says ${say(x)}!")