# `@endo/exo` and `@endo/patterns` notes
### `makeExo()` param order
When I write an in-line exo object, I always write the behavior first and specify the interface guard second. This makes using `makeExo()` somewhat frustrating, because I have to first type out empty `M.interface()` boilerplate before I can get to what I really care about, i.e. the behavior of my object. Consider making the interface guard the last parameter.
### More succinct `defaultGuards: 'passable'` incantation for `makeExo()`
Specifying interface guards for `makeExo()` tends to become a verbose affair, even for trivial objects. If the interface guard is the last parameter, it could also be made optional, and default to what people are already defaulting it to in practice:
```js
const interfaceGuard = M.interface(
name,
{},
{ defaultGuards: 'passable' },
);
```
The `tag` param of `makeExo()` could perform double duty as the `interfaceName` of `M.interface()`:
```js
const myExo = makeExo('MyExo', {
foo: () => 'bar',
});
// Under the hood, makeExo creates the following interface:
M.interface(
'MyExo',
{},
{ defaultGuards: 'passable' },
);
```
Either way, some succinct way of specifying `defaultGuards: 'passable'` would be nice, and obviate the need for our [home-baked utility](https://github.com/MetaMask/ocap-kernel/blob/89f7f356f0344e157d40ef8b31d1a7572798cf66/packages/kernel-utils/src/exo.ts#L16-L30) that does exactly that. We have a number of objects for internal use, esp. in tests, where precise, method-level interface guards would be overkill.
### `M.interface()` "sloppy" option
The third and final parameter of `M.interface()` is an options bag of the form:
```ts
{ sloppy?: boolean, defaultGuards?: DefaultGuardType }
```
`sloppy` seems to control whether `defaultGuards` defaults to `'passable'` (if `true`) or `undefined` (if `false`). This may be needlessly complicated. It seems `defaultGuards` already defaults to `undefined`, forcing the user to specify guards for all methods. Can't we get rid of `sloppy` and just let the user specify whichever non-default guard they want?
### Merging / extending `M.interface()` values
`zod` offers an [.extend()](https://zod.dev/api#extend) operation that merges two different schemas. It would be very useful to be able to do this with `M.interface()` values. For instance, Ocap Kernel vats must export a root object with a method `bootstrap(): void`, but it may also contain any number of other methods. Currently, our best option is to do something like this:
```ts
const BootstrapMethod = M.call().returns(M.void());
// Then, in a vat:
const rootObject = makeExo(
'root',
M.interface('root', {
bootstrap: BootstrapMethod,
foo: M.call(/* ... */),
bar: M.call(/* ... */),
}),
{
bootstrap() { /* ... */ },
foo() { /* ... */ },
bar() { /* ... */ },
},
)
```
It would be nice to instead do something like:
```ts
const BaseRootObject = M.interface(
'rootWithBootstrap',
{ bootstrap: M.call().returns(M.void()) }
);
// Then, in a vat:
const rootObject = makeExo(
'root',
// The name specified to extend() overrides the name
// of the extended interface
BaseRootObject.extend('root', {
foo: M.call(/* ... */),
bar: M.call(/* ... */),
}),
{
bootstrap() { /* ... */ },
foo() { /* ... */ },
bar() { /* ... */ },
},
);
```
The benefits of doing this of course increases with the complexity of the interfaces you wish to extend.