4 ms·
I think a lot of the confusion is just that it's not what people are used to. e.g., an anytime parameter is checked by the compiler, just a like a parameter of
by jmull 2y ago
I think a lot of the confusion is just that it's not what people are used to.
e.g., an anytime parameter is checked by the compiler, just a like a parameter of an interface type in a language with interfaces. In either case, if you try to pass a non-conforming type you get a compiler error.
Now, with an interface, you get a symbol name you can hang your hat on. That can really help when you're in the "I'm not exactly sure what I'm doing, I'm trying to figure out this API" phase of development. API documentation and design need to compensate. But you need to be past that phase before you're writing actually useful code anyway, so this isn't that big a deal. Zig's overall simplicity really mitigates this too. You're probably going to be spending less overall time in the confusion stage.
(re "writer: anytype" the article says, "You either have to go through the source code and see how writer is used, or let the compiler tell you which function is expected." which is true, but also true of writer: IWriter. It also leaves off the option of reading the docs. doc comments are a first-class construct in zig, so they are pretty accessible.)
- latch 2y agoA couple comments have mentioned that whatever issue my post raises, are minor compared to the simplicity of the language. But the current implementation of GenericWriter + AnyWriter (with some performance pitfalls) seems more, not less, complicated. Also, neither of these, nor anytype, lend themselves to _simple_ documentation, so that seems like another strike against the simplicity-argument. As for anytype specifically, in simple cases where it's being used in a single function, you can quickly figure out what it needs. But in non trivial cases, the parameter can be passed all over the place, including into different packages. For example `std.json.stringify`. Not only does its own usage of `out_stream: anytype` take more than a glance, it passes it into any custom `jsonStringify` function. So you don't just need to know what `std.json.stringify` needs, but also any what any custom serialization needs.
- jmull 2y ago> But in non trivial cases, the parameter can be passed all over the place Like pretty much any parameter in any language? That's abstraction. Re jsonStringfy, that's a way for a type to control how its own instances are serialized as JSON. std.json.stringify doesn't depend on the details of a jsonStringfy implementation (nor a caller of std.json.stringify). A type being able to implement a method to customize how instances of the type are serialized is a common/typical feature of a lot of JSON serializers.
- SkiFire13 2y ago> Like pretty much any parameter in any language? That's abstraction. This is only an issue in languages with duck-typing though. You're not really abstracting something if you need to go look how it's used to understand what to pass.
- josephg 2y ago> Like pretty much any parameter in any language? That's abstraction. It’s crappy abstraction. Why make every user of the interface figure out how to use it properly? And guess what functions are required and which are always available? Rust traits seem like a strictly better tool here. You get exactly the same emitted code as anytype, but the expected interface is explicit and well documented. Any time a trait name appears, you can cmd+click the trait name and see what functions are parts of the trait. It’s clean and explicit. And traits can also have associated types (eg for an error type). They can have default implementations of functions. You can store a trait object in a struct. And you can use &dyn Trait if you want dynamic dispatch instead of monomorphisation. If this article is anything to go by, Zig makes all of this stuff difficult to impossible. Anytype only solves the problem for the compiler. Traits solve the problem for the compiler and the programmer at the same time.
- jmull 2y ago> If this article is anything to go by, Zig makes all of this stuff difficult to impossible. Well, having done it, it’s not.
- josephg 2y agoOh go on then - how do you make a zig interface that has equivalent behaviour to a rust trait? I want something that has a documented interface, default function implementations, can be stored in other structs, and allows both monomorphization and dynamic dispatch. If the authors of the zig standard library can't do it, I assume its impossible. And hence this article talking about 3 different definitions of a "writer" appearing in the standard library, each trying to make up for shortcomings of the others. You have anytype, AnyWriter and GenericWriter. All with weird and complex tradeoffs and interactions. Not only do people need to learn 3 different interfaces, they also need to figure out how to convert from one kind of writer to another! I talked to Rob Pike, back before Go hit version 1.0. I told him I really thought he should have not only enums, but parametric enums in Go. Of course, he disagreed. He said enums don't add enough value. Honestly I'm not sure if he'd ever used a language with parametric enums - and he didn't understand their value. Having used typescript, and now rust and swift, going back to C or Go and needing to build my own enums out of union structs and so on is awful. It feels like I'm trying to build a house using a rock and a piece of flint. I see rust's trait system as just like that. What a pity that Zig doesn't have something similar. To me, traits seem like an obvious thing to copy into the language.