Skip to content

Commit bc1b75d

Browse files
committed
proposal(tuple-struct)
1 parent 81f4b23 commit bc1b75d

File tree

1 file changed

+172
-0
lines changed

1 file changed

+172
-0
lines changed

proposals/0008-tuple-struct.mbt.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Replace type with tuple struct
2+
3+
* Proposal: [ME-0008](https://github.com/moonbitlang/moonbit-evolution/blob/0008-tuple-struct/proposals/0008-tuple-struct.mbt.md)
4+
* Author: [whitepie](https://github.com/whitepie)
5+
* Status: Draft
6+
* Review and discussion: [GitHub issue](https://github.com/moonbitlang/moonbit-evolution/issues/)
7+
8+
## Introduction
9+
10+
Currently, MoonBit uses the `type` syntax for implementing the newtype idiom, which creates a new type that wraps an existing type. However, the current syntax has several issues, particularly when dealing with tuple types. We propose to replace the `type` syntax with a more intuitive `tuple struct` syntax that provides clearer semantics and better ergonomics while maintaining the newtype pattern.
11+
12+
## Motivation
13+
14+
### Problems with current type syntax
15+
16+
The current `type` syntax for implementing the newtype idiom with tuple types creates ambiguity and inconsistency:
17+
18+
```moonbit
19+
type Newtype Int
20+
type NewtypeTuple (Int, Int)
21+
```
22+
23+
For tuple types, the semantics are unclear and there are multiple ways to construct and access elements:
24+
25+
#### Construction ambiguity
26+
```moonbit
27+
fn make_newtype_tuple1(x: Int, y: Int) -> NewtypeTuple {
28+
(x, y) // Direct tuple conversion
29+
}
30+
31+
fn make_newtype_tuple2(x: Int, y: Int) -> NewtypeTuple {
32+
NewtypeTuple((x, y)) // Constructor with extra parentheses
33+
}
34+
```
35+
36+
#### Access ambiguity
37+
```moonbit
38+
fn get_x1(t: NewtypeTuple) -> Int {
39+
t.0 // Direct field access
40+
}
41+
42+
fn get_x2(t: NewtypeTuple) -> Int {
43+
t.inner().0 // Using .inner() method then field access
44+
}
45+
46+
fn get_x3(t: NewtypeTuple) -> Int {
47+
match t {
48+
NewtypeTuple((x, _)) => x // Pattern matching
49+
}
50+
}
51+
```
52+
53+
These inconsistencies become even more problematic when dealing with value types and create cognitive overhead for developers. The newtype pattern should provide clear, unambiguous semantics.
54+
55+
## Proposed solution
56+
57+
We propose introducing `tuple struct` syntax as a replacement for the `type` syntax when implementing the newtype idiom:
58+
59+
```moonbit
60+
struct Single(Int)
61+
struct Multiple(Int, String, Char)
62+
```
63+
64+
### Benefits of tuple struct syntax
65+
66+
1. **Clearer semantics**: Tuple structs have unambiguous construction and access patterns
67+
2. **Consistent access**: Use `.0`, `.1`, etc. for all tuple structs
68+
3. **Simpler construction**: Constructor takes the same number of arguments as the struct definition
69+
4. **Better value type support**: Easier to implement and reason about
70+
5. **Maintains newtype pattern**: Still provides type safety and distinct semantics from the underlying type
71+
72+
### Construction and access
73+
74+
```moonbit
75+
struct Multiple(Int, String, Char)
76+
77+
fn make_multiple(a: Int, b: String, c: Char) -> Multiple {
78+
Multiple(a, b, c) // Clear constructor syntax
79+
}
80+
81+
fn use_multiple(x: Multiple) -> Unit {
82+
println(x.0) // Access first element
83+
println(x.1) // Access second element
84+
println(x.2) // Access third element
85+
}
86+
```
87+
88+
## Special cases and features
89+
90+
### Single-element tuple structs
91+
92+
Single-element tuple structs are guaranteed to be unboxed at runtime and provide several convenient shorthands, maintaining the efficiency benefits of the newtype pattern:
93+
94+
```moonbit
95+
struct S {
96+
a: Int
97+
}
98+
99+
struct R(S)
100+
101+
fn get_a(r: R) -> Int {
102+
r.a // Automatic field forwarding
103+
}
104+
```
105+
106+
### Function type support
107+
108+
When the element type is a function, tuple structs provide seamless function application:
109+
110+
```moonbit
111+
struct F((Int) -> Int)
112+
113+
fn apply(f: F, x: Int) -> Int {
114+
f(x) // No need for .0 when applying
115+
}
116+
```
117+
118+
## Migration strategy
119+
120+
### Single-element types
121+
122+
For single-element types where the underlying type is not a tuple, the formatter automatically migrates to the new syntax:
123+
124+
```moonbit
125+
// Old syntax
126+
type A1 Int
127+
fn A1::get(a: A1) -> Int {
128+
a.inner()
129+
}
130+
131+
// New syntax (automatically migrated)
132+
struct A2(Int)
133+
fn A2::get(a: A2) -> Int {
134+
a.0
135+
}
136+
```
137+
138+
To facilitate migration, single-element tuple structs temporarily provide a `.inner()` method, which will be deprecated and removed in future versions.
139+
140+
### Multi-element types
141+
142+
Multi-element tuple structs differ from the old tuple type syntax in important ways:
143+
144+
1. **No direct tuple construction**: Tuple structs cannot be constructed directly from tuples
145+
2. **No .inner() method**: Tuple structs do not provide access to the underlying tuple
146+
147+
### Compatibility with tuple conversion
148+
149+
If you need a tuple struct that can be directly converted to and from tuples while maintaining the newtype pattern, you can wrap the tuple type:
150+
151+
```moonbit
152+
struct T((Int, Int))
153+
154+
fn make_t(x: Int, y: Int) -> T {
155+
(x, y) // Direct tuple construction
156+
}
157+
158+
fn use_t(t: T) -> (Int, Int) {
159+
t.0 // Access the wrapped tuple
160+
}
161+
162+
// Access individual elements requires nested access
163+
fn get_x(t: T) -> Int {
164+
t.0.0 // t.0 gets the tuple, .0 gets the first element
165+
}
166+
```
167+
168+
## Conclusion
169+
170+
The tuple struct syntax provides a cleaner, more intuitive alternative to the current `type` syntax while maintaining the newtype idiom's benefits. It eliminates ambiguity in construction and access patterns while preserving type safety and distinct semantics from the underlying types. The migration strategy ensures backward compatibility while encouraging adoption of the new syntax.
171+
172+
This proposal aligns with MoonBit's goal of providing a clear, consistent language design that reduces cognitive overhead for developers while maintaining the powerful newtype pattern for type safety and domain modeling.

0 commit comments

Comments
 (0)