Skip to content

Commit 82c85e4

Browse files
author
Darin Morrison
committed
Remove additional examples
1 parent bf3201d commit 82c85e4

File tree

1 file changed

+2
-234
lines changed

1 file changed

+2
-234
lines changed

text/0000-type-macros.md

Lines changed: 2 additions & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@ case for the `Ty_` enum so that the parser can indicate a macro
4343
invocation in a type position. In other words, `TyMac` is added to the
4444
ast and handled analogously to `ExprMac`, `ItemMac`, and `PatMac`.
4545

46-
## Examples
47-
48-
### Heterogeneous Lists
46+
## Example: Heterogeneous Lists
4947

5048
Heterogeneous lists are one example where the ability to express
5149
recursion via type macros is very useful. They can be used as an
@@ -136,7 +134,7 @@ Operations on HLists can be defined by recursion, using traits with
136134
associated type outputs at the type-level and implementation methods
137135
at the term-level.
138136

139-
The HList append operation is provided as an example. type macros are
137+
The HList append operation is provided as an example. Type macros are
140138
used to make writing append at the type level (see `Expr!`) more
141139
convenient than specifying the associated type projection manually:
142140

@@ -191,236 +189,6 @@ fn test_append() {
191189
}
192190
```
193191

194-
### Additional Examples ###
195-
196-
#### Type-level numerics
197-
198-
Type-level numerics are another area where type macros can be
199-
useful. The more common unary encodings (Peano numerals) are not
200-
efficient enough to use in practice so we present an example
201-
demonstrating binary natural numbers instead:
202-
203-
```rust
204-
struct _0; // 0 bit
205-
struct _1; // 1 bit
206-
207-
// classify valid bits
208-
trait Bit: MarkerTrait {}
209-
impl Bit for _0 {}
210-
impl Bit for _1 {}
211-
212-
// classify positive binary naturals
213-
trait Pos: MarkerTrait {}
214-
impl Pos for _1 {}
215-
impl<B: Bit, P: Pos> Pos for (P, B) {}
216-
217-
// classify binary naturals with 0
218-
trait Nat: MarkerTrait {}
219-
impl Nat for _0 {}
220-
impl Nat for _1 {}
221-
impl<B: Bit, P: Pos> Nat for (P, B) {}
222-
```
223-
224-
These can be used to index into tuples or HLists generically, either
225-
by specifying the path explicitly (e.g., `(a, b, c).at::<(_1, _0)>()
226-
==> c`) or by providing a singleton term with the appropriate type
227-
`(a, b, c).at((_1, _0)) ==> c`. Indexing is linear time in the general
228-
case due to recursion, but can be made constant time for a fixed
229-
number of specialized implementations.
230-
231-
Type-level numbers can also be used to define "sized" or "bounded"
232-
data, such as a vector indexed by its length:
233-
234-
```rust
235-
struct LengthVec<A, N: Nat>(Vec<A>);
236-
```
237-
238-
Similar to the indexing example, the parameter `N` can either serve as
239-
phantom data, or such a struct could also include a term-level
240-
representation of N as another field.
241-
242-
In either case, a length-safe API could be defined for container types
243-
like `Vec`. "Unsafe" indexing (without bounds checking) into the
244-
underlying container would be safe in general because the length of
245-
the container would be known statically and reflected in the type of
246-
the length-indexed wrapper.
247-
248-
We could imagine an idealized API in the following fashion:
249-
250-
```rust
251-
// push, adding one to the length
252-
fn push<A, N: Nat>(xs: LengthVec<A, N>, x: A) -> LengthVec<A, N + 1>;
253-
254-
// pop, subtracting one from the length
255-
fn pop<A, N: Nat>(xs: LengthVec<A, N + 1>, store: &mut A) -> LengthVec<A, N>;
256-
257-
// look up an element at an index
258-
fn at<A, M: Nat, N: Nat, P: M < N>(xs: LengthVec<A, N>, index: M) -> A;
259-
260-
// append, adding the individual lengths
261-
fn append<A, N: Nat, M: Nat>(xs: LengthVec<A, N>, ys: LengthVec<A, M>) -> LengthVec<A, N + M>;
262-
263-
// produce a length respecting iterator from an indexed vector
264-
fn iter<A, N: Nat>(xs: LengthVec<A, N>) -> LengthIterator<A, N>;
265-
```
266-
267-
We can't write code like the above directly in Rust but we could
268-
approximate it through type-level macros:
269-
270-
```rust
271-
// Expr! would expand + to Add::Output and integer constants to Nat!; see
272-
// the HList append earlier in the RFC for a concrete example
273-
Expr!(N + M)
274-
==> <N as Add<M>>::Output
275-
276-
// Nat! would expand integer literals to type-level binary naturals
277-
// and be implemented as a plugin for efficiency; see the following
278-
// section for a concrete example
279-
Nat!(4)
280-
==> ((_1, _0), _0)
281-
282-
// `Expr!` and `Nat!` used for the LengthVec type:
283-
LengthVec<A, Expr!(N + 3)>
284-
==> LengthVec<A, <N as Add< Nat!(3)>>::Output>
285-
==> LengthVec<A, <N as Add<(_1, _1)>>::Output>
286-
```
287-
288-
##### Implementation of `Nat!` as a plugin
289-
290-
The following code demonstrates concretely how `Nat!` can be
291-
implemented as a plugin. As with the `HList!` example, this code (with
292-
some additions) compiles and is usable with the type macros prototype
293-
in the branch referenced earlier.
294-
295-
For efficiency, the binary representation is first constructed as a
296-
string via iteration rather than recursively using `quote` macros. The
297-
string is then parsed as a type, returning an ast fragment.
298-
299-
```rust
300-
// Convert a u64 to a string representation of a type-level binary natural, e.g.,
301-
// ast_as_str(1024)
302-
// ==> "(((((((((_1, _0), _0), _0), _0), _0), _0), _0), _0), _0)"
303-
fn ast_as_str<'cx>(
304-
ecx: &'cx base::ExtCtxt,
305-
mut num: u64,
306-
mode: Mode,
307-
) -> String {
308-
let path = "_";
309-
let mut res: String;
310-
if num < 2 {
311-
res = String::from_str(path);
312-
res.push_str(num.to_string().as_slice());
313-
} else {
314-
let mut bin = vec![];
315-
while num > 0 {
316-
bin.push(num % 2);
317-
num >>= 1;
318-
}
319-
res = ::std::iter::repeat('(').take(bin.len() - 1).collect();
320-
res.push_str(path);
321-
res.push_str(bin.pop().unwrap().to_string().as_slice());
322-
for b in bin.iter().rev() {
323-
res.push_str(", ");
324-
res.push_str(path);
325-
res.push_str(b.to_string().as_slice());
326-
res.push_str(")");
327-
}
328-
}
329-
res
330-
}
331-
332-
// Generate a parser which uses the nat's ast-as-string as its input
333-
fn ast_parser<'cx>(
334-
ecx: &'cx base::ExtCtxt,
335-
num: u64,
336-
mode: Mode,
337-
) -> parse::parser::Parser<'cx> {
338-
let filemap = ecx
339-
.codemap()
340-
.new_filemap(String::from_str("<nat!>"), ast_as_str(ecx, num, mode));
341-
let reader = lexer::StringReader::new(
342-
&ecx.parse_sess().span_diagnostic,
343-
filemap);
344-
parser::Parser::new(
345-
ecx.parse_sess(),
346-
ecx.cfg(),
347-
Box::new(reader))
348-
}
349-
350-
// Try to parse an integer literal and return a new parser which uses
351-
// the nat's ast-as-string as its input
352-
pub fn lit_parser<'cx>(
353-
ecx: &'cx base::ExtCtxt,
354-
args: &[ast::TokenTree],
355-
mode: Mode,
356-
) -> Option<parse::parser::Parser<'cx>> {
357-
let mut lit_parser = ecx.new_parser_from_tts(args);
358-
if let ast::Lit_::LitInt(lit, _) = lit_parser.parse_lit().node {
359-
Some(ast_parser(ecx, lit, mode))
360-
} else {
361-
None
362-
}
363-
}
364-
365-
// Expand Nat!(n) to a type-level binary nat where n is an int literal, e.g.,
366-
// Nat!(1024)
367-
// ==> (((((((((_1, _0), _0), _0), _0), _0), _0), _0), _0), _0)
368-
pub fn expand_ty<'cx>(
369-
ecx: &'cx mut base::ExtCtxt,
370-
span: codemap::Span,
371-
args: &[ast::TokenTree],
372-
) -> Box<base::MacResult + 'cx> {
373-
{
374-
lit_parser(ecx, args, Mode::Ty)
375-
}.and_then(|mut ast_parser| {
376-
Some(base::MacEager::ty(ast_parser.parse_ty()))
377-
}).unwrap_or_else(|| {
378-
ecx.span_err(span, "Nat!: expected an integer literal argument");
379-
base::DummyResult::any(span)
380-
})
381-
}
382-
383-
// Expand nat!(n) to a term-level binary nat where n is an int literal, e.g.,
384-
// nat!(1024)
385-
// ==> (((((((((_1, _0), _0), _0), _0), _0), _0), _0), _0), _0)
386-
pub fn expand_tm<'cx>(
387-
ecx: &'cx mut base::ExtCtxt,
388-
span: codemap::Span,
389-
args: &[ast::TokenTree],
390-
) -> Box<base::MacResult + 'cx> {
391-
{
392-
lit_parser(ecx, args, Mode::Tm)
393-
}.and_then(|mut ast_parser| {
394-
Some(base::MacEager::expr(ast_parser.parse_expr()))
395-
}).unwrap_or_else(|| {
396-
ecx.span_err(span, "nat!: expected an integer literal argument");
397-
base::DummyResult::any(span)
398-
})
399-
}
400-
401-
#[test]
402-
fn nats() {
403-
let _: Nat!(42) = nat!(42);
404-
}
405-
```
406-
407-
##### Optimization of `Expr`!
408-
409-
Defining `Expr!` as a plugin would provide an opportunity to perform
410-
various optimizations of more complex type-level expressions during
411-
expansion. Partial evaluation would be one way to achieve
412-
this. Furthermore, expansion-time optimizations wouldn't be limited to
413-
arithmetic expressions but could be used for other data like HLists.
414-
415-
##### Builtin alternatives: types parameterized by constant values
416-
417-
The example with type-level naturals serves to illustrate some of the
418-
patterns type macros enable. This RFC is not intended to address the
419-
lack of constant value type parameterization and type-level numerics
420-
specifically. There is
421-
[another RFC here](https://github.com/rust-lang/rfcs/pull/884) which
422-
proposes extending the type system to address those issue.
423-
424192
# Drawbacks
425193

426194
There seem to be few drawbacks to implementing this feature as an

0 commit comments

Comments
 (0)