modular icon indicating copy to clipboard operation
modular copied to clipboard

[Feature Request] Allow to declare methods only if a type conforms to a specific trait

Open gabrieldemarmiesse opened this issue 1 year ago • 5 comments

Review Mojo's priorities

What is your request?

DynamicVector doesn't have an __eq__ method currently because of this. We'd need something like this to do it:

trait Equalable:
    fn __ne__(self, other: Self) -> Bool: ...

@value
struct CustomList[T: CollectionElement]:
    var internal_vector: DynamicVector[T]

    if hastrait(T, Equalable):
        def __eq__(self, other: Self) -> Bool:
            if len(self.internal_vector) != len(other.internal_vector):
                return False
            for i in range(len(self.internal_vector)):
                if self.internal_vector[i] != other.internal_vector[i]:
                    return False
            return True

@value
struct NotEqualable:
    var i: Int

def main():
    x = CustomList(DynamicVector[Int]()) # supports ==
    y = CustomList(DynamicVector[NotEqualable]()) # doesn't support ==

The method would be available conditionally, if the type T has the right trait. hastrait would work like isinstance.

What is your motivation for this change?

Otherwise we need to define the function for each type know, that's very painful and leads to ugly apis. The fact that DynamicVector and Dict don't have __eq__ or __str__ shows this.

Any other details?

No response

gabrieldemarmiesse avatar Mar 04 '24 10:03 gabrieldemarmiesse

+1 for conditional conformance, -20 for using hastrait + if as the means to support it.

I believe this problem is better addressed with a more structural approach (consider impl ... for). The pros and cons of these two approaches align closely with the two schools of compile-time branching discussed in #1731. To put it simply, we likely don't want C++ concepts but something more akin to Rust, Swift, or Haskell.

soraros avatar Mar 04 '24 12:03 soraros

Thanks for filing the issue. This was actually one of the first things I asked for when starting to experiment with traits in the library when they initially got implemented in the compiler. Conditional conformance is on the compiler roadmap, but I don't have a clear timeline when it will be started right now. We haven't decided on specific syntax for it; we're still working on a design for implementing the feature itself.

The library is intentionally not providing __eq__ and friends right now in DynamicVector because of limitations in using traits at this time.

Stay tuned as conditional conformance is important from my perspective as we start pushing on traits more in the library.

JoeLoser avatar Mar 04 '24 14:03 JoeLoser

@JoeLoser Could you comment more about the approach Mojo may take? Like, leaning more towards the "Turing complete" if trait or Rust style "very static" conditional conformance?

soraros avatar Mar 04 '24 15:03 soraros

If I may, I'd recommend using a syntax that doesn't require an if statement in the struct level scope

Moosems avatar Apr 16 '24 12:04 Moosems

An idea:

@value
struct SpecialStruct[T, L]:
    var x: T
    var y: L
    @conditional(T, Stringable)
    fn __str__(self) -> String raises:
        # Raises in case T does not conform
        return self.x

    @conditional(T, Intable)
    @conditional(L, Intable)
    # Allow multiple conditionals
    fn add_vals(self) -> Int raises:
        return self.x + self.y

    @conditional((T, Intable), (L, Intable))
    fn mult_vals(self) -> Int raises:
        # Tuples allowed so you can condense multiple conditionals
        return self.x * self.y

Moosems avatar Apr 16 '24 12:04 Moosems