Skip to content

Commit

Permalink
update doc for visibility of types and traits
Browse files Browse the repository at this point in the history
  • Loading branch information
Guest0x0 committed Nov 15, 2024
1 parent d8a49b8 commit f7fbed8
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 86 deletions.
106 changes: 66 additions & 40 deletions moonbit-docs/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1695,50 +1695,24 @@ fn reduce[S, T](self: List[S], op: (T, S) -> T, init: T) -> T {

## Access Control

By default, all function definitions and variable bindings are _invisible_ to other packages; types without modifiers are abstract data types, whose name is exported but the internals are invisible. This design prevents unintended exposure of implementation details. You can use the `pub` modifier before `type`/`enum`/`struct`/`let` or top-level function to make them fully visible, or put `priv` before `type`/`enum`/`struct` to make it fully invisible to other packages. You can also use `pub` or `priv` before field names to obtain finer-grained access control. However, it is important to note that:
By default, all function definitions and variable bindings are _invisible_ to other packages.
You can use the `pub` modifier before toplevel `let`/`fn` to make them public.

- Struct fields cannot be defined as `pub` within an abstract or private struct since it makes no sense.
- Enum constructors do not have individual visibility so you cannot use `pub` or `priv` before them.
There are four different kinds of visibility for types in MoonBit:

```moonbit
struct R1 { // abstract data type by default
x: Int // implicitly private field
pub y: Int // ERROR: `pub` field found in an abstract type!
priv z: Int // WARNING: `priv` is redundant!
}
- private type, declared with `priv`, completely invisible to the outside world
- abstract type, which is the default visibility for types. Only the name of an abstract type is visible outside, the internal representation of the type is hidden
- readonly types, declared with `pub(readonly)`. The internal representation of readonly types are visible outside,
but users can only read the values of these types from outside, construction and mutation are not allowed
- fully public types, declared with `pub(all)`. The outside world can freely construct, modify and read values of these types

pub struct R2 { // explicitly public struct
x: Int // implicitly public field
pub y: Int // WARNING: `pub` is redundant!
priv z: Int // explicitly private field
}
Currently, the semantic of `pub` is `pub(all)`. But in the future, the meaning of `pub` will be ported to `pub(readonly)`.
In addition to the visibility of the type itself, the fields of a public `struct` can be annotated with `priv`,
which will hide the field from the outside world completely.
Note that `struct`s with private fields cannot be constructed directly outside,
but you can update the public fields using the functional struct update syntax.

priv struct R3 { // explicitly private struct
x: Int // implicitly private field
pub y: Int // ERROR: `pub` field found in a private type!
priv z: Int // WARNING: `priv` is redundant!
}
enum T1 { // abstract data type by default
A(Int) // implicitly private variant
pub B(Int) // ERROR: no individual visibility!
priv C(Int) // ERROR: no individual visibility!
}
pub enum T2 { // explicitly public enum
A(Int) // implicitly public variant
pub B(Int) // ERROR: no individual visibility!
priv C(Int) // ERROR: no individual visibility!
}
priv enum T3 { // explicitly private enum
A(Int) // implicitly private variant
pub B(Int) // ERROR: no individual visibility!
priv C(Int) // ERROR: no individual visibility!
}
```

Another useful feature supported in MoonBit is `pub(readonly)` types, which are inspired by [private types](https://v2.ocaml.org/manual/privatetypes.html) in OCaml. In short, values of `pub(readonly)` types can be destructed by pattern matching and the dot syntax, but cannot be constructed or mutated in other packages. Note that there is no restriction within the same package where `pub(readonly)` types are defined.
Readonly types is a very useful feature, inspired by [private types](https://v2.ocaml.org/manual/privatetypes.html) in OCaml. In short, values of `pub(readonly)` types can be destructed by pattern matching and the dot syntax, but cannot be constructed or mutated in other packages. Note that there is no restriction within the same package where `pub(readonly)` types are defined.

```moonbit
// Package A
Expand Down Expand Up @@ -2056,6 +2030,58 @@ fn main {
}
```

## Visibility of traits and sealed traits
There are four visibility for traits, just like `struct` and `enum`: private, abstract, readonly and fully public.
Private traits are declared with `priv trait`, and they are completely invisible from outside.
Abstract trait is the default visibility. Only the name of the trait is visible from outside, and the methods in the trait are not exposed.
Readonly traits are declared with `pub(readonly) trait`, their methods can be involked from outside, but only the current package can add new implementation for readonly traits.
Finally, fully public traits are declared with `pub(open) trait`, they are open to new implementations outside current package, and their methods can be freely used.
Currently, `pub trait` defaults to `pub(open) trait`. But in the future, the semantic of `pub trait` will be ported to `pub(readonly)`.

Abstract and readonly traits are sealed, because only the package defining the trait can implement them.
Implementing a sealed (abstract or readonly) trait outside its package result in compiler error.
If you are the owner of a sealed trait, and you want to make some implementation available to users of your package,
make sure there is at least one declaration of the form `impl Trait for Type with ...` in your package.
Implementations with only regular method and default implementations will not be available outside.

Here's an example of abstract trait:

```moonbit
trait Number {
op_add(Self, Self) -> Self
op_sub(Self, Self) -> Self
}
fn add[N : Number](x : X, y: X) -> X {
Number::op_add(x, y)
}
fn sub[N : Number](x : X, y: X) -> X {
Number::op_sub(x, y)
}
impl Number for Int with op_add(x, y) { x + y }
impl Number for Int with op_sub(x, y) { x - y }
impl Number for Double with op_add(x, y) { x + y }
impl Number for Double with op_sub(x, y) { x - y }
```

From outside this package, users can only see the following:

```moonbit
trait Number
fn op_add[N : Number](x : N, y : N) -> N
fn op_sub[N : Number](x : N, y : N) -> N
impl Number for Int
impl Number for Double
```

The author of `Number` can make use of the fact that only `Int` and `Double` can ever implement `Number`,
because new implementations are not allowed outside.

## Automatically derive builtin traits

MoonBit can automatically derive implementations for some builtin traits:
Expand Down
109 changes: 63 additions & 46 deletions moonbit-docs/i18n/zh/docusaurus-plugin-content-docs/current/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1637,55 +1637,20 @@ fn reduce[S, T](self: List[S], op: (T, S) -> T, init: T) -> T {
## 访问控制

默认情况下,所有函数定义和变量绑定对其他包都是 _不可见_ 的;
没有修饰符的类型是抽象数据类型,其名称被导出,但内部是不可见的。
这种设计防止了意外暴露实现细节。
您可以在 `type`/`fn`/`let` 前使用 `pub` 修饰符使其完全可见,或在 `type`
前使用 `priv` 修饰符使其对其他包完全不可见。
您还可以在字段名前使用 `pub``priv` 获得更细粒度的访问控制。
但是,请注意:
您可以在 `fn`/`let` 前使用 `pub` 修饰符使其完全可见。
类型有四种不同的可见性:

- 在抽象或私有结构体内,所有字段都不能被定义为 `pub`,因为这样没有意义。
- 枚举类型的构造器没有单独的可见性,所以不能在它们前面使用 `pub``priv`
- 私有类型,用 `priv` 修饰,对外完全不可见
- 抽象类型,这是默认的可见性,无需额外修饰。对外只有名字可见,类型的内部表示不可见。这种设计防止了意外暴露实现细节
- 只读类型,用 `pub(readonly)` 修饰,其内部表示对外可见,但外部只能读取类型的值,不能构造或修改
- 公开类型,用 `pub(all)` 修饰,外部可以自由构造、修改、读取这些类型的值

```moonbit
struct R1 { // 默认为抽象数据类型
x: Int // 隐式的私有字段
pub y: Int // ERROR: 在抽象类型中找到了 `pub` 字段!
priv z: Int // WARNING: `priv` 是多余的!
}
目前,`pub` 修饰符的语义是 `pub(all)`,但未来 `pub` 的语义会调整为 `pub(readonly)`
除了类型自身的可见性,一个 `pub(readonly)``pub(all)` 的结构体的字段可以额外用 `priv` 修饰,
这样能对外完全隐藏这一字段的存在。
注意含有私有字段的结构体在外部无法直接构造,但可以用结构体更新语法更新公开的字段。

pub struct R2 { // 显式的公共结构
x: Int // 隐式的公共字段
pub y: Int // WARNING: `pub` 是多余的!
priv z: Int // 显式的私有字段
}
priv struct R3 { // 显式的私有结构
x: Int // 隐式的私有字段
pub y: Int // ERROR: `pub` 字段出现在了私有类型中!
priv z: Int // WARNING: `priv` 是多余的!
}
enum T1 { // 默认为抽象数据类型
A(Int) // 隐式的私有变体
pub B(Int) // ERROR: 无独立可见性!
priv C(Int) // ERROR: 无独立可见性!
}
pub enum T2 { // 显式的公共枚举
A(Int) // 隐式的公共变体
pub B(Int) // ERROR: 无独立可见性!
priv C(Int) // ERROR: 无独立可见性!
}
priv enum T3 { // 显式的私有枚举
A(Int) // 隐式的私有变体
pub B(Int) // ERROR: 无独立可见性!
priv C(Int) // ERROR: 无独立可见性!
}
```

MoonBit 中另一个有用的特性是 `pub(readonly)` 类型,其受到了 OCaml [private types](https://v2.ocaml.org/manual/privatetypes.html)的启发。简而言之,`pub(readonly)` 类型的值可以使用模式匹配或点语法析构,但在其他包中,不能被构造或改变。注意到在 `pub(readonly)` 类型定义的同一个包中,它没有任何限制。
只读类型是一个十分实用的特性,其受到了 OCaml [private types](https://v2.ocaml.org/manual/privatetypes.html)的启发。简而言之,`pub(readonly)` 类型的值可以使用模式匹配或点语法析构,但在其他包中,不能被构造或改变。注意到在 `pub(readonly)` 类型定义的同一个包中,它没有任何限制。

```moonbit
// Package A
Expand Down Expand Up @@ -2028,6 +1993,58 @@ fn main {
}
```

## 接口的可见性与封闭的接口
MoonBit 中,接口和类型一样有四种可见性:私有、抽象、只读和完全公开。
私有接口可以用 `priv trait` 声明,它们在外部是完全不可见的。
抽象接口是接口的默认可见性。只有接口的名字对外可见,接口中的方法对外是不可见的。
只读接口可以用 `pub(readonly) trait` 声明,外部可以调用这个接口中的方法,但只有定义这个接口的包可以实现这个接口,外部不能添加新的实现。
最后,完全公开的接口可以用 `pub(open) trait` 声明,外部可以给这种接口添加新的实现、也可以自由调用其中的方法。
目前,`pub trait` 默认的语义是 `pub(open) trait`。但未来 `pub trait` 的语义会迁移至 `pub(readonly) trait`

抽象和只读的接口是 **封闭** 的,因为只有定义接口的包可以为它们添加实现。
如果尝试在外部实现这些接口,就会产生编译错误。
如果你是一个封闭接口的所有者,并希望你的用户能够使用你提供的、这些接口的实现,
那么一定要保证你的包中至少有一处形如 `impl Trait for Type with ...` 的显式声明。
也就是说,只有普通方法和默认实现的情况下,封闭接口的实现在外部是不可用的。

下面是一个抽象接口的例子:

```moonbit
trait Number {
op_add(Self, Self) -> Self
op_sub(Self, Self) -> Self
}
fn add[N : Number](x : X, y: X) -> X {
Number::op_add(x, y)
}
fn sub[N : Number](x : X, y: X) -> X {
Number::op_sub(x, y)
}
impl Number for Int with op_add(x, y) { x + y }
impl Number for Int with op_sub(x, y) { x - y }
impl Number for Double with op_add(x, y) { x + y }
impl Number for Double with op_sub(x, y) { x - y }
```

在当前包外,用户只能看见下面的内容:

```moonbit
trait Number
fn op_add[N : Number](x : N, y : N) -> N
fn op_sub[N : Number](x : N, y : N) -> N
impl Number for Int
impl Number for Double
```

由于外部不能给 `Number` 添加新的实现。因此,只有 `Int``Double` 能够实现 `Number`
`Number` 的作者可以在编写程序时利用上这一事实。

## 自动实现内建接口

Moonbit 可以自动生成一些内建接口的实现:
Expand Down

0 comments on commit f7fbed8

Please sign in to comment.