Hacker News new | ask | show | jobs
by adastra22 277 days ago
Traits have methods. How is that not adding new functions?
2 comments

> Traits have methods. How is that not adding new functions?

If you add a method to a trait, every implementation of that trait has to be modified in the original source code. The point of the expression problem is that you want to be able to add new operations in a way that is type safe and checked for completeness, even if you don't have access to the original source code.

Rust can probably do this, because its traits are equivalent to Haskell type classes and those have a solution to the expression problem, but it's not trivial. See the link in the article.

In rust you would add a NEW trait, which is brought in scope by those who need it. Or add a new method to an existing trait, with a default implementation.

The problem you describe makes no sense. It sounds like, for example, wanting to add a new variant to an enum while also not wanting to modify match statements which now fail exhaustive testing. That’s a direct contradiction.

The only sensible charitable interpretation I can come up with is that you want to add new methods to a type without having to update any existing type definitions. This is what traits do.

> In rust you would add a NEW trait, which is brought in scope by those who need it.

No, that's not sufficient if done naively as I think you're describing. You seem to be missing the context of this discussion as described by the article. Other code already depends on the current compiled abstraction, and you want to extend it in various ways, safely, so that existing code continues to work without recompilation, but you can extend the abstraction that code is already safely handling with new types and new operations without modifying the original source code.

If you want to add a new node type to an AST, with algebraic data types you would have to modify the original source code of the original functions that match on those types, so types are closed to extension. You can leave the type open to extension via traits, but now the operations are closed to extension without modifying the original source code.

> It sounds like, for example, wanting to add a new variant to an enum while also not wanting to modify match statements which now fail exhaustive testing. That’s a direct contradiction.

No it's not, if enums and match statements were safely open to extension rather than closed. That's exactly what would solve the expression problem. This can and has been done [1] as the article described, via multimethods or via lifting data constructors to the type class level. It's obtuse and awkward, but in theory it doesn't have to be.

The ultimate goal is that the new type and/or pattern matching cases have to be provided together to cover all of missing combinations, but importantly "together" means by the time the final program is assembled and not in the source code that defined the original types or operations.

[1] open pattern matching and open algebraic data types have also been done in research languages

> No, that's not sufficient if done naively as I think you're describing. You seem to be missing the context of this discussion as described by the article. Other code already depends on the current compiled abstraction, and you want to extend it in various ways, safely, so that existing code continues to work without recompilation, but you can extend the abstraction that code is already safely handling with new types and new operations without modifying the original source code.

That is exactly what a new trait would accomplish. I remain confused as to the distinction you are making.

Read the Haskell-specific follow-up which I mentioned, type classes have a correspondance with traits so the problems with this approach are the same:

https://eli.thegreenplace.net/2018/more-thoughts-on-the-expr...

Unfortunately I don’t speak Haskell. As none of the terminology used by Haskell folks align with languages I do know, I really can’t make head or tails of that article.
Classes in C++ have methods too.

The problem is that you can't add new methods to an existing class.

More specifically, you cannot add new abstract (aka "pure virtual") methods to an existing class/interface/trait when that class/interface/trait is already extended/implemented by code you don't control.
Rust lets you combine multiple traits per type.
C++ lets you inherit from multiple classes as well. I don't see how this has anything to do with being able to add new methods to existing types.
There is an important difference: in Rust you can write a new trait, with new methods, and impl that trait for a type without having to change the existing structs/enums which comprise that type at all. You can also do this (with some restrictions, "the orphan rule") for types defined in another library.

In C++ classes, you have to be able to modify a class definition to extend it with new methods (or a new ancestor to multiply inherit from).

You can add traits (with their methods) to existing types, without modification.
C++ classes are types. Rust traits are applied to types.
And what exactly do you think traits apply to types exactly?

If your answer doesn't start with an "m" and end with a "ethod", then you may need to re-read the Rust book found here:

https://doc.rust-lang.org/book/ch10-02-traits.html

So hypothetically, if you could add new methods to an existing class, it would solve the problem?
New virtual methods, yes.