# 🚀 Feature Request
## Motivation
Interface/trait is a key for creating ab…stractions for computer code. Historically ethereum was able to came up a number of well known interfaces such as erc20, erc72 that eventually became the de-facto standard for lots of applications in the blockchain community. As of now, Move doesn’t support any form of interfaces, neither statically nor dynmaically. However, this does imposed a huge problem for the Move ecosystem for smart contract authors. This document aims to go over the problem, and tries to propose a few solutions that could tackle the problem without compromising the core safety properties of Move.
## What’s the current Problem?
As of now, Move only supports passing simple generic parameters without any trait bounds/interfaces. This implies that when a callee function is invoked with a generic parameter, the only thing that callee can do is to move the value of that generic type around, and there’s no way to dispatch different function calls based on the calling generic types.
This imposes a significant limitation for the modularity of Move. For example, when we want to write a generic uniswap contract for Move, it’s hard for the uniswap contract to invoke the corresponding transfer function for the buyer type and seller type.
### Case Study: Token standard and uniswap contract
A uniswap smart contract is a piece of code that exchange one token type with another token type where relative token price is a function of the supply of both token types in the pool. We have a reference implementation of an example of this contract [here](https://github.com/move-language/move/blob/main/language/documentation/examples/experimental/coin-swap/sources/CoinSwap.move). In this implementation, we used the [witness pattern](https://github.com/move-language/move/blob/f982b265a232e29b87b3d5935971c947b3b99a0b/language/documentation/examples/experimental/coin-swap/sources/CoinSwap.move#L63) in order to transfer tokens in a generic way. However, I'm seeing at least two downsides here:
1. The uniswap contract has a direct dependency over `BaseCoin` module. This implies that every token type in the ecosystem will need to use `BaseCoin` for their dependency. In other words, `BaseCoin` is not only the `ERC20` standard in the Move world but we also enforce that there's exactly one implementation of such standard on chain. To me this seems to be a huge constraint for the ecosystem as even if other developers want to develop an alternative for `BaseCoin`, they won't be able to use the `CoinSwap` contract at all even though the `AltCoin` have the exact same type signature as `BaseCoin`.
3. It could be tricky to provide witness to the uniswap function: the constructor of witness will be controlled by the parent coin module, e.g: `GoldCoin`. It could be hard to get the witness for `GoldCoin` and `SilverCoin` together in one module as the point of the witness is to provide access control separately.
Instead, I suggest what we really need here is like in rust, we can say `CoinType1` and `CoinType2` both implement a `Coin` trait and in the coin trait, there could be a transfer function so that we can simply return the Coin as an output.
### How is that mitigated now?
There are two major ways to get around such limitations from my observation:
The more “idomatic Move” way is to use marker type for type parameters. So instead of having concrete type of `WrappedEth` and `WrappedSolana` as type argument, the contract will take a `Coin<WrappedEth>` and `Coin<WrappedSolana>` as argument. By doing so, the callee smart contract can invoke the generic transfer function that is defined under `Coin` module, and the token metadata can be found using borrow global on the marker type, ala `WrappedEth` and `WrappedSolana`. This is similar to what we did in diem-framework where we need to deal with different types of currencies.
Another way of doing this is a more brute force one: contract authors will simply try to bypass the limitation via reflection or runtime value. In both the aptos-framework and starcoin, there are functions that can return the name of the type. This allows caller to dispatch different calling methods by doing if statements on the identifier either returned from the runtime reflection or from the passed in type id.
### What’s the flaw in the current mitigation?
Both mitigations has some significant flaws. For the “idomatic Move” way, `Coin` module is acting like the interface that provides generic functionality for token transfer. However, this is also implying that the implementations of `Coin` will be the one and only implementation of the token transfer and if some alternative party want to come up with an implementation of `AltCoin` module that has exact same interface as `Coin` module but with some slightly different implementations, there’s no easy way for other defi contracts that calls into `Coin` module to swap the underlying implementation to `AltCoin` easily. The defi contract will need to recompile and republish the module that calls into `AltCoin` module instead.
Reflections and type id instead of types is also not a suggested approach. Firstly, there needs to be an exhaustive list of type ids that a callee can accept. This not only means a big block of if code in order to perform transfer based on the value of the type id, this also means that for every new type that the callee want to support, recompilation/rewritten of module is needed. Secondly, reflection/type_id can make verification so much harder as it is effectively introducing dynmaic dispatch and can potentially introduce other unexepected bugs as it bypasses the type system of Move.
## Why don’t we have it now?
Interfaces are not implemented in Move intensionally, for multiple good reasons. Firstly, dynamic dispatching is not preferrable, as dynamic dispatching will complicate prover significantly, as you will no longer know the properties of the callee. Moreover, dynamic dispatching can potentially introduce reentrancy issues as you now might be able to invoke into yourself, which is the source of multiple notorious bugs in the solidity community.
However, static dispatch is also not easy to achieve in the blockchain setup. The main reason is that we publish codes by modules. Thus when a module gets published, it doesn’t necessarily know all the callers that might call into this module. Moreoever, it’s hard to do monomorphization for the exact same reason.
## Pitch
The design here is simply an RFC now and the design principle I used here is to bring as minimal changes with how modules are organized in Move and aiming it to be a feature that is fully compatible Move code. This may not be the best/most ergonomic solution IMO and we should talk about the alternatives there.
The following idea was inspired mostly from ML’s module system. The idea is to introduce the concept of signature and functors into Move’s ecosystem. Signature is the type constraint for a module. Signature will describe the functions and types a module will need to implement. Functor is a special type of module that can have modules as its argument.
For example, a signature will look like following:
```jsx
signature COIN =
sig
struct T
fun value(coin: &T): u64;
fun split(coin: &mut T, value: u64): T;
fun deposit(coin: &mut T, other: T);
end
```
A module can implements a signature. The syntax might look like following:
```jsx
module 0xa::FooCoin: COIN {
struct T {
val: u64,
}
fun value(coin: &T): u64 { coin.val }
fun split(coin: &mut T, value: u64): T {
assert(coin.val > value);
coin.val -= value;
T { val: value }
}
fun deposit(coin: &mut T, other: T) {
let T { val } = other;
coin.val += val;
}
}
```
Lastly we have functors that is essentially modules that can take modules as an argument:
```jsx
functor 0xa::WalletMaker(C: COIN): WALLET {
struct T {
balance: C::T,
}
fun deposit(self: &mut T, coin: C::T) {
C::deposit(&self.balance, coin)
}
}
```
And other modules can call functions implemented functor only if all the modules has been passed in:
```jsx
module 0xa::Bar {
use WalletMaker(FooCoin) as W;
fun bar(..) {
W::deposit(..)
}
}
```
There will be two ways to implement functors. One possibility would be that functor will only be a front end language support in move-lang and no extra support is needed in bytecode. In this setup, the compiler will need to do a monomorphization when a functor is instantiated with all of its arguments and a new module will need to be published as a result of this monomorphization. Functor also will become a completely off chain construct and there would be no ways to publish functors to chain directly, only the monomorphized module, which could be against the code reusing strategy for Move. A downside of this implementation is that name mangling would be required for the monomorphized module. In this sense, functor is just an example of a macro system for modules.
Another possibility is to introduce the concept of signature and functor into bytecode. This means that we will be able to publish functors into the on chain storage. Bytecode verifier will need to thus check if a module implements a specific signature. This would allow full on-chain dependency of functor and would make reusing on chain bytecode a lot easier. I would argue that using this approach would allow us to have full ethereum-like interface support in Move and allow us to create standards like ERC20/ERC721 inside Move.
## Unknowns
However, how the functor/signature system could affect Move prover remains unknown to me. In the schema I just layed out, we still statically know all the instantiation of functors. However, it could be hard to verify the functor on its own as the implementation of the function is missing. We may want to add some spec to the signature in order to verify properties of functor.
## [WIP] More examples/use cases of interfaces
Still working on this. Will update this section when I have more concrete examples.
### Coin module and Wallet
### Signature verification polymorphism
### Container and Iterators