metals-poly 0.1.1

Poly library provides the interface and interface type abstractions that form the foundations of the meta programming in Metals
Documentation
/*
 * Copyright 2022 Weavers @ Eternal Loom. All rights reserved.
 * Use of this software is governed by the license that can be
 * found in LICENSE file in the source repository.
 */

//! Contains [`Interface`] and [`InterfaceType`] and their essential
//! implementations used to model behaviors and states of systems.
//!
//! An [`Interface`] specifies the observable behavior of a system. By
//! generalizing the idea of interfaces and combining with [`InterfaceType`],
//! we will be able to model all aspects of complex dynamic system.
//!
//! ## Interfaces
//!
//! In its simplest form, an interface specifies how to the system converts a
//! set of inputs into a set of outputs. We can write this as
//!
//! ```text
//!     f: (I) → O
//! ```
//!
//! This conforms to our notion of functions in programming. This describes
//! the observable behavior of a system we are modeling, a _unit of
//! computation_, if we may.
//!
//! While it is simplistic to think of _f_ as a function, it is, in fact, far
//! more general. It can represent an event, a process or even a complete
//! system. It is the _f interface_ that contractually guarantees that the  
//! system will return an output _O_ given an input _I_. It captures the notion
//! of some computation, an _f computation_ or an _f system_.
//!
//! In _Rust_, this this is captured by the [`call`][`Interface::call`] method on
//! the [`Interface`] trait.
//!
//! ```ignore
//!    fn call(input: I) -> O;
//! ```
//!
//! [`metals`]: https://docs.rs/metals/

/// An `Interface` specifies the behavior of a system as a map from _input_ to
/// _output_.
///
/// It is easy to think of an `interface` as a _function_. But it is far more
/// general and can represent a function, a network call, an event, a process,
/// an event or a complete system. The essence of `Interface` is captured by
/// the `call` method.
///
/// ```ignore
///   fn call(input: I) -> O;
/// ```
///
/// ## A Few Examples
///
/// ### A _Function_ as `Interface`
///
/// TBD
///
/// ### A _Type_ implementing `Interface`
///
/// A simple increment interface. This is not very interesting, but just
/// to start building familiarity with `Interface`s.
///
/// ```
/// use metals_poly::interface::Interface;
/// /// The types that will implement the necessary increment behavior
/// struct Increment;
///
/// /// Increment represents a computation that turns i32 into some other i32.
/// impl Interface<i32, i32> for Increment {
///    /// Increment the input by 1.
///    fn call(input: i32) -> i32 {
///       input + 1
///     }
/// }
///
/// assert_eq!(Increment::call(1), 2);
/// ```
///
/// There are no `self` (read it as `no state`) or multiple arguments or
/// anything interesting here at all. Looks very boring, to be honest. While
/// it may not appear terribly exciting, let us build a few more interfaces
/// for Increment.
///
/// ```
/// # use metals_poly::interface::Interface;
/// #
/// struct Increment;
/// // x-- hiding previous lines for brevity
/// #
/// # impl Interface<i32, i32> for Increment {
/// #    fn call(input: i32) -> i32 {
/// #       input + 1
/// #     }
/// # }
/// #
///
/// // Increment interface for u32 -> u32
/// impl Interface<u32, u32> for Increment {
///    fn call(input: u32) -> u32 {
///       input + 1
///     }
/// }
///
/// // We have to tell the input type
/// assert_eq!(Increment::call(1u32), 2u32);
/// // This calls previous i32 -> i32
/// assert_eq!(Increment::call(1), 2);
/// ```
///
/// But if we add another interface say, say to increment by 10, for same
/// input and output types, we get compiler error.
///
/// ```compile_fail
/// # use metals_poly::interface::Interface;
/// #
/// struct Increment;
///
/// impl Interface<i32, i32> for Increment {
///    fn call(input: i32) -> i32 {
///       input + 1
///     }
/// }
///
/// // This will lead to compiler error for reimplementing the same function
/// impl Interface<i32, i32> for Increment {
///    fn call(input: i32) -> i32 {
///       input + 10
///     }
/// }
/// ```
///
/// But we can mix input and output types. For e.g., we can have the same
/// increment by 1 method defined for i32 -> u32 (or vice versa).
///
/// ```
/// # use metals_poly::interface::Interface;
/// #
/// struct Increment;
/// // x-- hiding previous code lines for brevity
/// #
/// // Increment interface for i32 -> i32
/// impl Interface<i32, i32> for Increment {
/// // x-- snip implementation code
/// #    fn call(input: i32) -> i32 {
/// #       input + 1
/// #     }
/// }
/// #
/// // Increment interface for u32 -> u32
/// impl Interface<u32, u32> for Increment {
/// // x-- snip implementation code
/// #    fn call(input: u32) -> u32 {
/// #       input + 1
/// #    }
/// }
/// // Increment interface for i32 -> u32
/// impl Interface<i32, u32> for Increment {
///    fn call(input: i32) -> u32 {
/// // x-- snip implementation code
///       u32::try_from(input).unwrap() + 1u32
///     }
/// }
///
/// // Now we have to provide a lot of type information to both at calling
/// // and at assert
///
/// // Calling i32 -> i32. Needs type info even for i32 -> i32
/// let o: i32 = Increment::call(1i32);
/// assert_eq!(o, 2i32);
/// // Calling i32 -> u32
/// let o: u32 = Increment::call(1i32);
/// assert_eq!(o, 2u32);
/// // Calling u32 -> u32, not different from last time
/// assert_eq!(Increment::call(1u32), 2u32);
/// ```
///
pub trait Interface<Input, Output> {
    /// An interface is called with an input.
    ///
    /// Please note that we are passing a mutable reference to self which
    /// is an indication that the internal state may be muted.
    ///
    /// # Example
    ///
    fn call(input: Input) -> Output;
}

/// An `InterfaceType`
///
/// Just placeholders
pub trait InterfaceType {
    /// The input type of the interface
    type Input;
    /// The output type of the interface
    type Output;
    /// The type that implements the interface
    type Interface: Interface<Self::Input, Self::Output> + ?Sized;

    /// Return the instance of interface for this type
    fn interface(&mut self) -> &mut Self::Interface;

    // /// Call the interface
    // fn call(&mut self, input: Self::Input) -> Self::Output {
    //     self.interface().call(input)
    // }
}

// Some blanket implementations for mutable references to interfaces.

// impl<'a, T, Input, Output> Interface<Input, Output> for &'a mut T
// where
//     T: Interface<Input, Output> + 'a,
// {
//     fn call(&mut self, input: Input) -> Output {
//         (**self).call(input)
//     }
// }

// impl<'a, T, Input, Output> Interface<Input, Output> for Box<T>
// where
//     T: Interface<Input, Output> + ?Sized,
// {
//     fn call(&mut self, input: Input) -> Output {
//         (**self).call(input)
//     }
// }

#[cfg(test)]
mod tests {
    use super::*;
    use core::fmt::Debug;
    use core::ops::Sub;

    // Two interfaces, one to increment and other to decrement the input
    struct Increment;
    struct Decrement;

    // Here the interfaces we want to implement
    //
    // 1. Increment by 1 from i32 -> i32
    // 2. Increment by 1 from u32 -> u32
    // 3. Increment by 1 from i32 -> u32 (different output type)
    // 4. Increment by 1 from u32 -> i32 (different output type)
    // 5. Increment by 10 from i32 -> i32
    // 6. Decrement by 1 from i32 -> i32
    // 7. Decrement by 1 from u32 -> u32
    // 8. Decrement by 1 from i32 -> u32 (different output type)
    // 9. Decrement by 1 from u32 -> i32 (different output type)
    // 10. Decrement by 10 from i32 -> i32

    // 1. Increment by 1 from i32 -> i32
    impl Interface<i32, i32> for Increment {
        // The add interface
        fn call(input: i32) -> i32 {
            input + 1
        }
    }

    // 2. Increment by 1 from u32 -> u32
    impl Interface<u32, u32> for Increment {
        // The add interface
        fn call(input: u32) -> u32 {
            input + 1
        }
    }

    // 3. Increment by 1 from i32 -> u32 (different output type)
    impl Interface<i32, u32> for Increment {
        // The add interface
        fn call(input: i32) -> u32 {
            u32::try_from(input).unwrap() + 1u32
        }
    }

    // 4. Increment by 1 from u32 -> i32 (different output type)
    impl Interface<u32, i32> for Increment {
        // The add interface
        fn call(input: u32) -> i32 {
            i32::try_from(input).unwrap() + 1i32
        }
    }

    // 5. Increment by 10 from i32 -> i32
    // Compiler error
    // We can't implement a second interface with the same input/output types
    // impl Interface<i32, i32> for Increment {
    //     // The add interface
    //     fn call(input: i32) -> i32 {
    //         input + 10
    //     }
    // }

    // In Rust we could do this by generic function, but we are not trying to
    // implement increment or decrement functions in Rust, but to build out
    // the interface model.
    // Here Decrement implemented using generics in Rust (just for the heck
    // of it)
    fn decrement_one<I, O>(input: I) -> O
    where
        O: TryFrom<I>,
        <O as TryFrom<I>>::Error: Debug,
        O: Sub<Output = O> + From<u8>,
    {
        <O>::try_from(input).unwrap() - <O>::from(1u8)
    }

    #[allow(dead_code)]
    fn decrement_ten<I, O>(input: I) -> O
    where
        O: TryFrom<I>,
        <O as TryFrom<I>>::Error: Debug,
        O: Sub<Output = O> + From<u8>,
    {
        <O>::try_from(input).unwrap() - 10u8.into()
    }

    // Decrement by 1 from i32 -> i32
    impl Interface<i32, i32> for Decrement {
        // The add interface
        fn call(input: i32) -> i32 {
            decrement_one(input)
        }
    }

    // Decrement by 1 from u32 -> u32
    impl Interface<u32, u32> for Decrement {
        // The add interface
        fn call(input: u32) -> u32 {
            decrement_one(input)
        }
    }

    // Decrement by 1 from i32 -> u32 (different output type)
    impl Interface<i32, u32> for Decrement {
        // The add interface
        fn call(input: i32) -> u32 {
            decrement_one(input)
        }
    }

    // Decrement by 1 from u32 -> i32 (different output type)
    impl Interface<u32, i32> for Decrement {
        // The add interface
        fn call(input: u32) -> i32 {
            decrement_one(input)
        }
    }

    // Decrement by 10 from i32 -> i32
    // Compiler error
    // We can't implement a second interface with the same input/output types
    // impl Interface<i32, i32> for Decrement {
    //     // The add interface
    //     fn call(input: i32) -> i32 {
    //         decrement_ten(input)
    //     }
    // }

    #[test]
    fn it_checks_incr() {
        // Calls interface 1: i32 -> i32.
        // Input 1 is defaulted to i32, o needs explicit type annotation
        let o: i32 = Increment::call(1);
        assert_eq!(o, 2);
        // Calls interface 2: u32 -> u32.
        // i and o needs explicit type
        let o: u32 = Increment::call(1u32);
        assert_eq!(o, 2u32);
        // Calls interface 3: i32 -> u32
        // Output o needs explicit type
        let o: u32 = Increment::call(1);
        assert_eq!(o, 2u32);
        // Calls interface 4: u32 -> i32
        // i and o needs explicit type
        let o: i32 = Increment::call(1u32);
        assert_eq!(o, 2);
    }
}