5 ms·
Generic code is like nerd sniping. I look at this and think "why would you want to write generic code for all those ints?" The integer types may look similar
by shurcooL 12y ago
Generic code is like nerd sniping.
I look at this and think "why would you want to write generic code for all those ints?"
The integer types may look similar but they're different in more ways than they're similar. They have different bit sizes, different signedness. The CPU literally has to do different things depending if its `uint8` or `int64`. So why do you want or expect one piece of code that does it all?
It's just so much easier and faster to do it like Go, have non-generic functions that do exactly what you want and as a result, get meaningful work done. It's faster to write (because you don't need to figure out how to do in a generic way), faster and easier to read, and possible to make changes to one func but not others.
- Chinjut 12y agoWhy should I as the programmer have to do different things just because the CPU has to do different things? If the logic of what I want to do is the same in multiple cases, then I only want to write it once and let the compiler figure out what to do each time I call it. (It's the whole reason I write and call functions in the first place, instead of tediously manually inlining the corresponding code at each instance!)
- alricb 12y agoBut in this case, it isn't. Two's complement signed fixed-size integers are completely different from their unsigned brethren, and confusing them is an endless source of bugs.
- pcwalton 12y ago> But in this case, it isn't. Two's complement signed fixed-size integers are completely different from their unsigned brethren, and confusing them is an endless source of bugs. They both support a meaningful left-bitshift operation, which is what the author wanted to abstract over.
- tacos 12y agoBut they do not support a common right-bitshift operation, nor sign extend. Which is what both the compiler and the poster above you are trying to make clear.
- viraptor 12y agoInterface providing generic left-shift doesn't have to provide right-shift at the same time. And after the shift, what the compiler is complaining about is not the arithmetics but only type construction.
- __m 12y agohow would the compiler know what numbers to expect at runtime?
- pcwalton 12y agoEither by specializing the generic code to the specific types at compile time, or by using dynamic dispatch. (Note that this is the same tradeoff you get if you write the non-generic code by hand, so generics don't create a tradeoff where there wasn't one already.)
- tedajax 12y agoBecause you're writing code for a computer and sometimes it's expected that you actually care about what the computer is doing with your code.
- pcwalton 12y agoAgain, would you say the same thing about registers versus stack allocation (or heap allocation)? "The CPU handles it differently" isn't by itself a legitimate argument against abstraction.
- Nitramp 12y agoThis code is converting a bunch of bytes into an integer by bit shifting. So it's specific to the binary representation of ints in your language, and how types of various sizes and signedness interact. Note that this doesn't depend on your CPU, but rather on how integers and their byte representations are specified in the language. I think it's quite reasonable to expect the programmer to know what that means assuming they are writing bitfiddling code like this.
- pcwalton 12y agoNone of that argues against abstraction. It's perfectly reasonable to want to write code that uses left bitshift and works on unsigned and signed 8, 16, 32, and 64 bit integers.
- jmgao 12y agoBecause it should be trivial? template<typename T> T getValue(std::array<char, sizeof (T)> bytes) { return *reinterpret_cast<T *>(bytes.data()); }
- shurcooL 12y agoerr := binary.Read(bytes.NewReader(b), binary.LittleEndian, &i)
- codygman 12y agoThat's comparing Go's library call to a full implementation though.
- pcwalton 12y agoWell, it's not really fair to compare an implementation that throws out memory safety to a type-safe language. Swift has pointer casts too.
- jmgao 12y agoA memory safe implementation is only a bit longer: template<typename T> T getValue(std::array<char, sizeof (T)> bytes) { T result = 0; for (char byte : bytes) { result <<= 8; result |= byte; } return result; }
- barrkel 12y agoThe reason this works in C++ is because it doesn't have protocols. The operations that T is required to support in order to be a valid type argument to this function are not expressed symbolically; they need to be documented, otherwise you end up with compiler error messages that quickly get unwieldy in more complex scenarios. In large part, that's the problem the OP is confronted with. When you make these things symbolic and first-class, unless they're extremely complete, you find holes in the system. And when they're very complete, you find yourself overwhelmed by the number and apparent complexity of what should be simple. There's an inherent conflict.
- pcwalton 12y ago> The integer types may look similar but they're different in more ways than they're similar. They have different bit sizes, different signedness. The CPU literally has to do different things depending if its `uint8` or `int64`. So why do you want or expect one piece of code that does it all? > It's just so much easier and faster to do it like Go Would you say the same thing about registers versus stack-allocated memory? ISAs treat registers and stack-allocated memory very differently, yet virtually nobody wants the "register" keyword in C back and nobody is asking for a similar keyword in Go. (In fact, Go even abstracts over heap memory in the same way.) "The CPU does it differently" is not a valid argument per se against abstraction.
- shurcooL 12y agoYou're right. I should rephrase, I meant that in this case I see the abstraction not being worthwhile because it doesn't save enough time compared to not using it.
- ulber 12y agoThe cost of the abstraction is language dependent though and the programmer might desire the abstraction to be worth it. For example, this particular abstraction is much cheaper in C++ which is a (admittedly small) point in C++'s favor.
- stcredzero 12y agoGeneric code is like nerd sniping. (Remove "like?") You shouldn't use generics to magically get the compiler to magically do low level things for you. IMO, generics (specifically C++ style generics) should be used to manage things at fairly high levels of abstraction for fairly domain specific stuff. As Swift's Integers are built to prioritize performance and are fairly "close to the metal," this sort of problem is what you'd expect. If you want to create beautiful edifices of pure mathematics, go write in Haskell. That's not what Swift or its standard library were built for.
- dmritard96 12y agoWhile I agree that other languages can handle things in a cleaner way at the expense of performance, I also think that treating a smartphone with insanely powerful processors as if they are embedded devices is also a bit disappointing. Most of the worlds python code is probably running on commodity hardware with considerably less horsepower than todays smartphones. Even in python's case for instance, if you need to drop into something closer to the metal (most of the time probably unnecessary), you can quite easily.
- stcredzero 12y agoI also think that treating a smartphone with insanely powerful processors as if they are embedded devices is also a bit disappointing Computation is power, which is always going to be at a premium on mobiles in a competitive market. But if you want to have beautiful edifices of mathematics in Swift, you can always use Swift to write your own math library, and still get better performance than cpython. Expecting Swift's built in types to trivially be your math library is wishing the design goals were different.
- viraptor 12y ago> You shouldn't use generics to magically get the compiler to magically do low level things for you. Why not? > As Swift's Integers are built to prioritize performance and are fairly "close to the metal," this sort of problem is what you'd expect. I don't understand how one follows from the other. This is stuff that should be expanded at compilation stage, there's no runtime overhead. Likely that whole function should be specialised for each usage and be small enough to inline it. Since the loop has a known number of iterations, I even wouldn't be completely surprised if it was just a few basic instructions with array check skipped in some cases.
- viraptor 12y agoI don't understand why would you advocate almost-copy&paste in situations where you have to deal with types of similar structure. I mean you already use generic ints in almost every programming language out there. You don't write `a int32+ b`, or `a uint8+ b`. It's just `a+b`. > It's faster to write (because you don't need to figure out how to do in a generic way) And every time you copy/paste code, it's easier and faster to make stupid copy/paste mistakes either the first time, or when you change one version, but not the others.
- ubernostrum 12y agoTo be fair, I believe OCaml does use different infix operators for integral vs. floating-point arithmetic (or did, when last I looked at it years ago). But yes, the "problem" of just writing generic code involving things that are numbers, and letting your compiler or interpreter figure out the most efficient way to represent them in memory, is not really a problem; it's been solved by plenty of languages.
- duaneb 12y agoGenerics allow writing of algorithms—if you're writing a 'sort this array' function, why do you care whether the elements are integers or strings so long as you can order them. I do, however, agree with you in general—it seems like he's trying to re-implement generic serialization. Otherwise, it's difficult to see what a 'Bit' (for instance) has in common with an Int and not, for instance, a double.