🐰

【MoonBit】アクセス修飾子の使い分け

に公開

MoonBitの可視性はほかの言語と比較するとやや複雑な仕様になっており、初見では混乱しがちです。そこで今回の記事では、MoonBitのアクセス修飾子をどのように使い分ければよいかを解説します。

なお、現在のMoonBitは未だ1.0に到達していないため、仕様が変更される可能性があることに注意してください。(執筆時における最新バージョン: v0.8.0)

また、この記事が扱う内容はドキュメントの以下のページに記載されています。

https://docs.moonbitlang.com/en/latest/language/packages.html#access-control

関数・変数の可視性

トップレベルの関数・変数における可視性は非常にシンプルです。pubをつけると外部モジュールに公開され、そうでない場合は公開されません。

// これは外部からアクセスできる
pub fn foo() -> String {
  "foo!"
}

// これはアクセスできない
fn bar() -> String {
  "bar!"
}

なお、MoonBitのモジュールの単位はファイルではなくディレクトリであることに注意してください。pubがなくとも同一ディレクトリにあるファイルであればアクセスが可能です。

型の可視性

型の可視性は4種類あり、関数に比べるとやや複雑です。順番に見ていきましょう。

priv (Private type)

privは最も厳しいアクセス修飾子です。これをつけられた型は外部モジュールからアクセスすることができなくなります。

// この型は外部から完全に見えなくなる
priv struct Foo {
  foo : Int
}

修飾子なし (Abstract type)

修飾子がつけられていない型は抽象型(Abstract type)として扱われます。抽象型は名前のみが外部から参照可能で、型の内部表現は隠蔽されます。

例えばfooモジュールに以下のような定義があった場合を考えてみます。

// module foo
struct Foo {
  field : Int
}

pub fn new(value : Int) -> Foo {
  { field: value }
}

pub fn Foo::get_field(self : Foo) -> Int {
  self.field
}

これを外部から参照する場合、Fooという型自体は見えますが、中のフィールドに関する情報にアクセスすることはできません。フィールドの読み書きを行う場合には、pubで公開されたメソッドなどを通じて扱う必要があります。

fn main {
  // 型自体にはアクセスできる
  let f: @foo.Foo = @foo.new(42);

  // ただし、中のフィールドには一切アクセスできない
  // println(f.field) // これをコメントアウトするとエラー

  println(f.get_field()) // これはOK
}

pub (Readonly type)

pubをつけた型は読み取り専用型(Readonly type)として扱われます。これは外部から内部のフィールドにアクセスすることは可能ですが、フィールドを書き換えたり、{ field: value }のような形で新しく構造体をコンストラクトすることはできなくなります。

こちらも例を挙げて見てみましょう。

// module Foo
pub struct Foo {
  mut field : Int // 書き換え可能なフィールドにはmutキーワードが必要
}

pub fn new(value : Int) -> Foo {
  { field: value }
}

pub fn Foo::set_field(self : Foo, value : Int) -> Unit {
  self.field = value
}

これに外部からアクセスしてみましょう。

fn main {
  // 型自体にはアクセスできる
  let f: @foo.Foo = @foo.new(42);

  // 中のフィールドを読み取ることもできる
  println(f.field)

  // ただし、フィールドがmutであっても書き換えることはできない
  // f.field = 100 // これをコメントアウトするとエラー

  f.set_field(100)
  println(f.field)
}

ただし、フィールドにprivをつけることで特定のフィールドを隠蔽することが可能です。これはpub(all)でも同様です。

pub struct Foo {
  priv field : Int // privをつけると外部からフィールドが見えなくなる
}

pub(all) (Fully public types)

pub(all)をつけた型はフィールドも含めて完全に公開され、mutがあれば外部から書き換えることも可能になります。もちろん、外部から{ field: value }のようにコンストラクトすることも許可されます。

// module Foo
pub(all) struct Foo {
  mut field : Int
}

// 別モジュールのmain
fn main {
  let f = @foo.Foo::{ field: 42 }
  f.field = 100
  println(f.field)
}

Trait

traitの可視性も型と同様に4種類に分類されます。その内容も型の場合とほぼ同じなので、箇条書きでの軽い説明に留めます。

  • privをつけたtraitは完全に隠蔽され、外部からアクセスできなくなります。
  • 修飾子のないtraitは名前のみ外部からアクセスできますが、中のメソッドにはアクセスできません。
  • pubをつけたtraitは外部からアクセスでき、trait境界を通してメソッドの呼び出しも行えますが、新たに実装を追加することはできません。
  • pub(open)をつけたtraitは完全に公開され、メソッド呼び出しや新たな実装の追加を行うことができます。

まとめ

というわけでMoonBitのアクセス修飾子についてでした。MoonBitは概ねRustを書いているときの雰囲気で書いていけますが、ところどころ仕様が違って引っかかる部分があるので、その辺りは備忘録として記事に残しておきます。この記事が何らかの役に立てば幸いです。

Discussion