Conformance to traits and type equality semantics

The current way to check if a type is equal in mojo is using an intrinsic:

_type_is_eq[T1, T2]()

This could be hidden behind a struct and create a library solution for the ugly function use

trait SomeTrait: ...

struct SomeType(SomeTrait): ...
struct SomeOtherType: ...

@fieldwise_init
struct Type[T: AnyType]:
    fn __init__(out self, value: T):
        self = {}

    fn is[A: AnyType](self) -> Bool:
        return _type_is_eq[T, A]()

fn main():
    print(Type[SomeType]().is[SomeType]()) # True

If we get an intrinsic function to check whether a type implements a trait we could also add:

@fieldwise_init
struct Domain[Trait: AnyTrivialRegType]:
  fn __contains__[T: AnyType](self, other: T) -> Bool:
    return _implements[T, Trait]()

fn main():
    alias a = SomeType in Domain[SomeTrait]() # True
    alias b = SomeOtherType in Domain[SomeTrait]() # False
    print(a, b)

This could all be made easier and more readable IMO if we introduced some first class support for two simple operators:

fn some_fn[T: AnyType](v: T):
  @parameter
  if T == SomeType:
    ... # something
  elif T == SomeOtherType:
    ... # something else
  ...

#  (assuming we have where clauses)
fn print[T: AnyType](v: T) where (T is Writable or T is Stringable):
  @parameter
  if T is Writable:
    ... # something
  else:
    ... # something else

Even though this might be an abuse of the operator is, it reads well with many other traits: Intable, Iterable, Defaultable, etc.

3 Likes

My preference would also be to use is for _implements (ex: T is Stringable) and == for _type_is_eq. Whether that == needs to be fully compiler magic or we can implement that on the type type is something we can discussion. I think the readability benefits are substantial and using them this way generally makes sense.

3 Likes