We have traits at home

54 points
1/21/1970
3 days ago
by Ygg2

Comments


Panzerschrek

I see little to no benefits of introducing such "traits" into a language having duck-typing in templates. It may sometimes improve code readability (slightly), but with more boilerplate code for traits handling in exchange. And the result machine code will be identical to code with no traits at all (with only duck-typing).

C++ is a good example of this. It had duck-typing in templates for decades. Then some sort of trait-like mechanism named concepts has been introduced. The only benefit of them seems to be slightly better error messages if someone misuses a template class/function.

2 days ago

Maxatar

>The only benefit of them seems to be slightly better error messages if someone misuses a template class/function.

The error messages are arguably worse with concepts than without them but it's really just a trade-off. Without concepts you got long "stack-traces" of raw syntax errors that could dig deep into nested functions. With concepts you get long stack-traces of concept failures where concept A fails because concept B fails because concept C isn't satisfied and then you need to dig through this chain of concepts to figure out what's actually needed.

Traits in more principled languages like Haskell/Rust don't have this issue whatsoever because traits are semantic requirements in those languages while concepts in C++ just perform a "will it compile" kind of check.

The primary benefit of concepts in C++ is overload resolution, they allow you to write multiple functions with the same name, and same number of parameters, but where the types of the parameters are required to satisfy certain properties.

For example:

    void serialize(std::ranges::contiguous_range auto&& range) {
      // Serialize the underlying memory directly
    }

    void serialize(std::ranges::range auto&& range) {
      // Serialize elements individually
    }
The compiler selects the first overload for contiguous ranges such as std::vector and the second for non-contiguous ranges such as std::list.
2 days ago

tialaramex

I'm not a Zig expert but it looks as though the intent is that duck-typing is not a core part of this idea. Having traits allows Carol to implement Ben's Teleport trait for Alice's Goose.

With duck-typing Carol needs to either prevail upon Alice to make you a teleporting Goose, or make her own "CarolsTeleportingGoose" wrapper type which implements the teleporting functions.

2 days ago

mrkeen

Traits/typeclasses tend to disallow your three-part scenario. It's called the orphan instance problem.

The "Goose teleports" code needs to go in Ben/Teleport.src, or Alice/Goose.src. Otherwise the compiler doesn't know which implementation to use if there are multiple.

Scala (and I think Ocaml) have mechanisms for selecting which one to use. In Haskell you'd use a newtype wrapper.

2 days ago

cowlevel

Concepts are predicates for types, that is, functions from types to booleans. Everyone was already defining templates like is_contiguous_range<T>::value so much that they decided to give it first-class status in the language.

The really cool part about concepts (unless you're writing the compiler) is actually requires expressions, which can replace a lot of SFINAE.

2 days ago

indiosmo

Concepts are great for overload sets where previously you needed enable_if gymnastics. Also useful for if constexpr.

If you do template metaprogramming it makes your life a lot easier.

2 days ago

dnautics

> The only benefit of them seems to be slightly better error messages if someone misuses a template class/function.

Seems like a pretty valuable benefit?

2 days ago

armchairhacker

The benefit to C++ is that it’s actively trying for as many features as possible, keeping out of reach full support from any compiler. Maybe GPT-8 will be the first true C++ expert, built by the first superintelligence GPT-7.

2 days ago

bikvfeee

I read these articles and am reminded how nice Julia is.

4 hours ago

Georgelemental

> Definition: a Trait is a compile-time struct which contains related functions over a given type.

There is actually a proposal to make Rust traits desugar behind the scenes to basically this: https://goals.rust-lang.org/2026/dictionary-passing-style-ex...

Rust's creator originally wanted an OCaml-style module system instead of traits (https://graydon2.dreamwidth.org/307291.html), and interest persists today (https://www.boxyuwu.blog/posts/an-incoherent-rust/).

a day ago

kooi

Coming from Rust:

Why is does implementation still have a the generic T? Should it not be concrete on i32?

/// i32 forms a sum reducer under 0 and addition const sum_trait = impl(Reducer(i32), struct { pub const empty = 0; pub fn combine(x: T, y: T) T { return x + y; } });

2 days ago

axelmagn

That was a mistake on my part. Thanks for catching. Fixed.

2 days ago

stock_toaster

While a well written article, and certainly intellectually interesting, I'm not sure I'd be excited to run across this level of indirection in a codebase if I were debugging an issue.

2 days ago

mrkeen

Complexity budget and all that. But I'd sooner throw out inheritance than traits.

2 days ago

detourdog

First I had heard of duck-typing.

https://en.wikipedia.org/wiki/Duck_typing

After reading the page I was left with the question of isn't that dynamic inheritance? The seems like dynamic runtime.

2 days ago

s_tec

In Zig's case, it's compile time. An expression like `foo.bar(1, 2)` can only compile if `foo` has a method named `bar` with the right number of arguments. In a duck-typed system, `foo` can be any type of value you like, as long as it "fits" the shape.

In contrast, many languages require you to describe the shape up-front, and then constrain `foo` to implement that shape. For instance, in Rust we would have to write something like `trait Bar { fn bar(a: u32, b: 32); }`, and then say something like `foo: T where T: Bar`, before we're allowed to call the `bar` method.

Both are compile-time safe, but one requires more ceremony (but is more explicit), while the other is simpler (but you might have to guess the requirements).

2 days ago

Jtsummers

> isn't that dynamic inheritance? The seems like dynamic runtime.

Duck-typing can be implemented with static checks, and doesn't require inheritance to be involved at all.

2 days ago

zahlman

It's ad-hoc inheritance (but really subtyping more generally).

The dynamically typed version is dynamic inheritance, or really just dynamic attribute lookup.

The statically typed version is "generic programming" with templates. See for example the way that C++ standard library sort works.

2 days ago

ZeroDayDreamer

[flagged]

2 days ago

no1guangming

Can't load the page

2 days ago