GDP Rust Retrospective 2018-09-10

By Max Woerner Chase

In this post:


After the last post, I read over The Little Book of Rust Macros some more, and figured out what I was doing wrong and how to make the macros properly expressive. Then, I re-did the documentation, and added examples. The result is... lengthy. I'm not sure I can properly convey it, so here's the file:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/// Create a named struct—that is, a struct that associates a Name to data.
/// This macro is invoked on any number of "items".
/// For the purposes of this macro, an "item" is one of:
/// a struct definition, or an impl block.
///
/// A struct definition consists of zero or more attributes,
/// followed by an optional visibility specifier,
/// followed by `struct`,
/// followed by a name,
/// followed by two type parameters with no bounds,
/// followed by a semicolon.
///
/// Note: the macro adds many derived trait impls.
/// `Default` is not one of them, and implementing `Default` is not recommended.
///
/// An impl block consists of `impl`,
/// followed by two type parameters,
/// still with no bounds,
/// followed by a name,
/// followed by two type parameters,
/// followed by a block.
/// The block contains method stubs.
///
/// Method stubs consist of a visibility specifier
/// (with allowable values constrained by the method name),
/// followed by `fn`,
/// followed by the name (one of a predefined set of choices),
/// followed by any required type parameters,
/// followed by a semicolon.
///
/// The allowed method names are:
///
/// * `new`: cannot have bare `pub` for visibility,
/// `pub(crate)` recommended,
/// takes no type parameters.
/// `new(value)` constructs a value of type `Self`, wrapping `value`.
/// * `as_mut`: cannot have bare `pub` for visibility,
/// `pub(crate)` recommended,
/// takes no type parameters.
/// `self.as_mut()` returns a mutable reference to the wrapped value.
/// * `into_inner`: can have any visibility,
/// `pub` recommended,
/// takes no type parameters.
/// `self.into_inner()` consumes `self` and returns the wrapped value.
/// * `as_ref`: can have any visibility,
/// `pub` recommended,
/// takes no type parameters.
/// `self.as_ref()` returns an immutable reference to the wrapped value.
///
/// # Examples
///
/// ```rust
/// # #[macro_use]
/// # extern crate gdp;
/// # fn main() {
/// named_struct! {
///     struct MyNamed<N, T>;
///
///     impl<N, T> MyNamed<N, T> {
///         pub(crate) fn new; // Define as needed.
///
///         pub(crate) fn as_mut; // Define as needed.
///
///         pub fn into_inner; // Definition recommended.
///
///         pub fn as_ref; // Definition recommended.
///     }
/// }
/// # }
/// ```
#[macro_export]
macro_rules! named_struct {
    ($vis:vis struct $name:ident<$N:ident, $T:ident $(,)*>; $($tail:tt)*) => {
        #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
        $vis struct $name<$N, $T>($crate::pd_wrapper::PDWrapper<$N>, $T);
        named_struct!{$($tail)*}
    };
    // Method block
    (@$param:ident pub fn new $($tail:tt)*) => {
        compile_error!("Generated `new` method cannot be public");
    };
    (@$param:ident pub fn as_mut $($tail:tt)*) => {
        compile_error!("Generated `as_mut` method cannot be public");
    };
    (@$param:ident $vis:vis fn new; $($tail:tt)*) => {
        $vis fn new(t: $param) -> Self {
            Self{0: $crate::pd_wrapper::PDWrapper::new(), 1: t}
        }
        named_struct!{@$param $($tail)*}
    };
    (@$param:ident $vis:vis fn into_inner; $($tail:tt)*) => {
        $vis fn into_inner(self) -> $param {
            self.1
        }
        named_struct!{@$param $($tail)*}
    };
    (@$param:ident $vis:vis fn as_ref; $($tail:tt)*) => {
        $vis fn as_ref(&self) -> &$param {
            &self.1
        }
        named_struct!{@$param $($tail)*}
    };
    (@$param:ident $vis:vis fn as_mut; $($tail:tt)*) => {
        $vis fn as_mut(&mut self) -> &mut $param {
            &mut self.1
        }
        named_struct!{@$param $($tail)*}
    };
    // End method block
    (impl<$N1:ident, $T1:ident $(,)*> $name:ident<$N2:ident, $T2:ident $(,)*> {$($tail1:tt)*} $($tail2:tt)*) => {
        impl<$N1, $T1> $name<$N2, $T2> {
            named_struct!{@$T2 $($tail1)*}
        }
        named_struct!{$($tail2)*}
    };
    ($(#[$outer:meta])+ $($tail:tt)*) => {$(#[$outer])* named_struct!{$($tail)*}};
    (@$param:ident $(#[$outer:meta])+ $($tail:tt)*) => {$(#[$outer])* named_struct!{@$param $($tail)*}};
    (@$_:ident) => {};
    () => {};
}

/// Create a name struct—that is, a struct that implements the `Name` trait.
/// This macro is invoked on any number of "items".
/// For the purposes of this macro, an "item" is one of:
/// a struct definition, or an impl block.
///
/// A struct definition consists of zero or more attributes,
/// followed by an optional visibility specifier,
/// followed by `struct`,
/// followed by a name,
/// followed by optional type parameters with no bounds,
/// followed by a semicolon.
///
/// Note: the macro adds many derived trait impls.
/// `Default` is not one of them, and implementing `Default` is not recommended.
///
/// An impl block consists of `impl`,
/// followed by the appropriate number of type parameters,
/// still with no bounds,
/// followed by a name,
/// followed by the appropriate number of type parameters,
/// followed by a block.
/// The block contains method stubs.
///
/// Method stubs consist of a visibility specifier
/// (with allowable values constrained by the method name),
/// followed by `fn`,
/// followed by the name (one of a predefined set of choices),
/// followed by any required type parameters,
/// followed by a semicolon.
///
/// The allowed method names are:
///
/// * `defn`: cannot have bare `pub` for visibility,
/// `pub(crate)` recommended,
/// takes one unbounded type parameter,
/// does not introduce bounds.
/// `defn<T>(value: T)` constructs an instance of `Named<Self, T>`,
/// wrapping `value`.
///
/// Note: all generated `impl`s, including the `Name` impl,
/// insert a bound of `Name` for `impl`-level type parameters.
///
/// # Examples
///
/// ```rust
/// # #[macro_use]
/// # extern crate gdp;
/// # fn main() {
/// name! {
///     struct Nullary;
///
///     impl Nullary {
///         pub(crate) fn defn<T>; // Define as needed.
///     }
///
///     struct Unary<A>;
///
///     impl<A> Unary<A> {
///         pub(crate) fn defn<T>; // Define as needed.
///     }
///
///     struct Binary<A, B>;
///
///     impl<A, B> Binary<A, B> {
///         pub(crate) fn defn<T>; // Define as needed.
///     }
/// }
/// # }
/// ```
#[macro_export]
macro_rules! name {
    ($vis:vis struct $name:ident<$($param:ident),* $(,)*>; $($tail:tt)*) => {
        #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
        $vis struct $name<$($param,)*>($crate::pd_wrapper::PDWrapper<($($param,)*)>);
        impl<$($param: $crate::named::Name,)*> $crate::named::Name for $name<$($param,)*> {}
        name!{$($tail)*}
    };
    ($vis:vis struct $name:ident; $($tail:tt)*) => {name!{$vis struct $name<>; $($tail)*}};
    // Method block
    (pub fn defn $($tail:tt)*) => {
        compile_error!("Generated `defn` method cannot be public");
    };
    ($vis:vis fn defn<$param:ident>; $($tail:tt)*) => {
        $vis fn defn<$param>(t: $param) -> $crate::named::Named<Self, $param> {
            $crate::named::defn(Self{0: $crate::pd_wrapper::PDWrapper::new()}, t)
        }
    };
    // End method block
    (impl<$($param1:ident),* $(,)*> $name:ident<$($param2:ident),* $(,)*> {$($tail1:tt)*} $($tail2:tt)*) => {
        impl<$($param1: $crate::named::Name),*> $name<$($param2),*> {
            name!{$($tail1)*}
        }
        name!{$($tail2)*}
    };
    (impl<$($param1:ident),* $(,)*> $name:ident $($tail:tt)*) => {name!{impl<$($param1),*> $name<> $($tail)*}};
    (impl $($tail:tt)*) => {name!{impl<> $($tail)*}};
    ($(#[$outer:meta])+ $($tail:tt)*) => {$(#[$outer])* name!{$($tail)*}};
    () => {};
}

EDIT: Ugh, something is wrong with the style sheet, I want to fix it...

I don't know how much of this length is How Macros Are, and how much is that these are the first big macros I've written, so of course it's probably longer than it could be.

Regardless, I'm past the big scary thing. Now I can focus on documenting everything else, made possible by following the API guidelines.

Anyway, so far as what I accomplished this week, I got these macros written with their cool input format, and documented them. Once I'd realized how bad my initial macro formats were, that was about all I could hope for.

Moving forward, I want to get the rest of the base crate up to standards, then go over sorted-by some more. Once I've hit the basic implementation goals for sorted-by, I want to see what kind of code it's generating. When I was messing around with a much earlier version of this code, it didn't look to me like the output was structured in a zero-cost way, relative to equivalent code.


Next week, I'm going to try to take it easy.