4 ms·
An aside: Swift’s generics have a pain point I bump into nearly daily, where type aliases are just that-aliases of the underlying type. This means that protocol
by wwalexander 4y ago
An aside: Swift’s generics have a pain point I bump into nearly daily, where type aliases are just that-aliases of the underlying type. This means that protocol conformance/method implementation is applied to the underlying type as well as any aliases.
So if I have two types, both of which can be represented by an underlying Int, but have entirely different semantics or methods (for instance, LosslessStringConvertible or various computed fields based on the underlying Int), I have to wrap the underlying type in either an enum with a single case or a struct with a single field, obscuring the meaning of the type and requiring awkward duplicate naming of the struct field/enum case.
The ExpressibleBy*Literal protocols are some help here, allowing Int/String/Array literals to be directly assigned to the type, but this only helps for assignment and not for retrieving the underlying value (and only
applies when the type can be initialized from the literal without any failure cases).
It’s not a huge deal, but a simple newtype statement would make the generics system far more general and easy to use imo.
- andrepd 4y agoIn rust the canonical way to get "newtypes" afaiu is to have a 1-member tuple struct, like struct Id(u64); then if `foo` is an `Id`, `foo.0` is the u64.
- wtetzner 4y agoAnd if you want to ensure the underlying representation is preserved, you can use #[repr(transparent)]
- wwalexander 4y agoYep, I remember thinking this was a great approach. Unfortunately, Swift doesn’t allow you to extend “non-nominal types”, which includes tuples. I often find myself thinking of Swift as a not-quite-fully-baked attempt at making a Rust, and this reinforces my impression.
- pjmlp 4y agoOnly of the design goals of Swift is precisely not to be as low level as Rust, unless required to for high performance code, and had a few big names from C++ community involved in its initial versions.
- wwalexander 4y agoTrue. I wouldn’t want to think about the borrow checker writing UI code, and I don’t need the performance or ultra-optimized machine code of Rust when I write Swift. I moreso meant in terms of the high-level features of Rust and how they integrate FP-style patterns into a C-style imperative language. Like tuple-based structs as discussed above, or control flow as expressions, like if and match. The alternatives to the latter in Swift are A) defining a dedicated function for the control flow, using return statements B) defining but not initializing a let constant before the control flow statement C) using the ternary operator, which is concise but doesn’t really allow for destructuring complex types all of which are more verbose, less clear in intent, and IMO don’t embody’s Swift’s mantra of “clarity at the site of use” as well as Rust’s approach.
- slavapestov 4y agoHow is it different from a one-field struct in Swift, other than that Swift makes you name the field while Rust allows it to have the name “.0”?
- wwalexander 4y agoPractically, there’s little difference. But as they say, naming things is one of the two hard problems in computer science and I’d rather avoid it wherever possible when the name inevitably doesn’t add any meaning, and arguably obscures it. I could just use rawValue as the field name every time, but foo.rawValue is a lot less readable, IMO, than foo.0
- antifa 4y agoDoes wrapping a single member with a single member struct like this have any runtime costs?
- slavapestov 4y agoYou can define a struct with a single field in Swift, which is guaranteed to have the same representation as the field's type (but yeah, you have to "forward" the desired subset of protocol conformances manually to the underlying type).