modularml / mojo

The Mojo Programming Language
https://docs.modular.com/mojo/manual/
Other
23.35k stars 2.6k forks source link

[Feature Request] [mojo-lang] More meta-programming tooling #3756

Open martinvuyk opened 2 weeks ago

martinvuyk commented 2 weeks ago

Review Mojo's priorities

What is your request?

Several requests to make this possible:

alias AnyTrait = __mlir_type[`!lit.anytrait<`, AnyType, `>`]

@value
struct Type[type_def: AnyType, traits: AnyTrait = __trait_of(type_def)]:
    """This represents a Type which implements a composition of traits.

    Parameters:
        type_def: The struct type definition.
        traits: The composition of traits the type conforms to.
    """
    var value: type_def

    @staticmethod
    fn __type__() -> AnyType:
        return Self.type_def

    @staticmethod
    fn __trait__() -> AnyTrait:
        return Self.traits

    @uses_arguments_statically
    @staticmethod
    fn __is__[T: AnyType, //](other: __type_of(Type[T, _])) -> Bool:
        return _type_is_eq[Self.type_def, T]()

    @uses_arguments_statically
    @staticmethod
    fn rebind[T: AnyType, //](
        new_type: __type_of(T)
    ) -> __type_of(Type[T, __trait_of(T)]):
        return Type[rebind[T](Self.type_def), __trait_of(T)]

    fn rebind[T: AnyType, //](
        self, new_type: __type_of(T)
    ) -> Type[T, __trait_of(T)] as result:
        result = __type_of(result)(rebind[T](self.value))

    @uses_arguments_statically
    @staticmethod
    fn rebind[T: AnyTrait, //](
        new_trait: __type_of(T)
    ) -> __type_of(Type[rebind_trait[T](Self.type_def), T]):
        return Type[rebind_trait[T](Self.type_def), T]

    fn rebind[T: AnyTrait, //](
        self, new_trait: __type_of(T)
    ) -> Type[rebind_trait[T](Self.type_def), T] as result:
        return __type_of(result)(rebind[__type_of(result).type_def](self.value))

    @uses_arguments_statically
    @staticmethod
    fn implements(some_trait: AnyTrait) -> Bool:
        return _implements[Self.type_def, some_trait]()

@uses_arguments_statically
fn type[T: AnyType, //](v: T) -> __type_of(Type[T]):
    return Type[T]

@uses_arguments_statically
fn isinstance[
    T0: AnyType, T1: AnyType, //
](v: T0, some_type: __type_of(T1)) -> Bool:
    return Type[T0] is Type[T1]

struct Origin[is_mutable: Bool]:
    """This represents an origin reference of potentially parametric type.

    Parameters:
        is_mutable: Whether the origin reference is mutable.
    """

    @staticmethod
    fn __type__() -> AnyType:
        return Self

    @staticmethod
    fn __trait__() -> AnyTrait:
        return __mlir_type[`!lit.origin<`, is_mutable.value, `>`]

struct _is_mut[
    is_mutable: Bool,
    //,
    origin_trait: __mlir_type[`!lit.origin<`, is_mutable.value, `>`],
]:
    ...

@uses_arguments_statically
fn origin[T: AnyType, //](
    v: T
) -> __type_of(Origin[_is_mut[__origin_of(v)].is_mutable, __origin_of(v)]):
    return Origin[_is_mut[__origin_of(v)].is_mutable, __origin_of(v)]

What is your motivation for this change?

More pythonic code when writing generics. And overall a better experience. This will potentially become the compile time version of Variant

Any other details?

This potentially helps by indirectly solving/being a workaround for

Sidenote: we might also want to add the ability for types to control how to be implicitly cast between mutable and immutable references. This will make generic code for non owning types much better (or even enabling Read Write locks to be embedded into the type system in an awesome way).

soraros commented 2 weeks ago

Would be a very nice workaround. Also helps with length of Tuple.

owenhilyard commented 1 week ago

I think I'd rather move metaprogramming in the direction of Zig's comptime. For instance, Foo.__member_vars Foo.__traits and Foo.__member_functions. Zig has one of the most battle tested reflection systems out of the languages I know of, and @parameter already gives us a lot of the tools it would need. I think that if we leave the "normal" metaprogramming to the current system and then only have something like this for a reflection API, we'll be in a better place. I think that Type will probably have to be a compiler builtin anyway, especially if we want to allow modifying/copying the type (which we do for things generating like SoA and AoSoA).