especially in embedded, where you might not get memory protection!
Or where there's perfectly valid memory mapped to address 0
HN user
especially in embedded, where you might not get memory protection!
Or where there's perfectly valid memory mapped to address 0
Phone calls are predominantly direct, 1:1 exchanges
Since the internet is instead a broadcast medium, it is much easier for big players to saturate the lines
The Internet is _not_ a broadcast medium. Data transfer over the Internet is by nature peer-to-peer.
ISPs naturally believe that _someone_ should be paying for what they interpret as the "extra load" from these big players
Someone _is_ paying for the load for big players: the consumers who are accessing their sites.
some households will use 30Mbit/s streaming content to 4 devices for 10 hours per day, and the next household, which pays the same monthly internet bill, uses 5Mbit/s or less to read predominantly-text based sites for 2 hours per day
30 Mb/s residential service already costs more than 5 Mb/s residential service.
Net neutrality is fundamentally large internet companies looking to keep their costs down by using government force to prevent ISPs from charging them usage-based "carriage fees" or similar.
Net neutrality is fundamentally a consumer protection preventing ISPs from leveraging their natural monopolies on last-mile service to enact rent-seeking policies. Whether ISPs charge consumers directly for access to, say, competing content providers ("Watch Turner Classic Movies for free with Spectrum Basic 10 Mb/s, or upgrade to Spectrum Premium 10 Mb/s for only $19.99 per month to access Netflix, Hulu, and HBO!"), or they charge those content providers extra to peer their traffic and those content providers fold that into subscription fees, we're the ones getting bent over the barrel.
The standard delta-epsilon type of reasoning crucially depends on existence of arbitrary reals.
What do you mean by "arbitrary?" Do you mean uncomputable? How does analysis require the existence of uncomputable reals?
All numbers ever written are rationals and thus countable
It is also possible to "write" computable irrational numbers, more or less by definition.
The fact that we can only write down only countably many expressions for numbers doesn't mean that there are numbers that we may never write expressions for
It depends, as they say, on what the definition of is is. What does it mean for a number to exist if it cannot be described? What does it mean for a construction to exist if it cannot be constructed?
if you confuse extant with useful you might end up believing that some random large integers aren't "there!"
The question isn't whether it's useful to claim the existence of uncomputable reals; the question is whether it's meaningful. A construction needn't be useful to be meaningful, but surely it must be meaningful to be useful.
Instead of complaining about it, you should be elevating AES Energy Storage and other similar companies.
No. I refuse to accept a standard of discourse where one cannot even point out a problem without having already solved it. There are many, many hard problems with complex systems in the world, and no person, much less a complete outsider, can hope even to begin to address more than a very, very small number of them even if they dedicate their life to the task. Saying "fix it or shut up" is asinine.
Then build a company if you think its so easy.
If I had a dollar for every time I saw a legitimate complaint about a complex system met with, "Well just reinvent it yourself from the ground up if you're so fucking smart," I'd be a rich man. It is not a constructive suggestion.
To expand on this, C++ fundamentally uses the same machine model as C (or at least one that's very, very close). Where C++ differs from C is the array of high-level language constructs, which map down to that common machine model (this is what is meant by "zero-cost abstractions"). The idea is that when you use a construct like templates or virtual functions, what it compiles down to is not significantly different from the analogous code you would write in C (hand-duplicated routines with different concrete types and structs-of-function-pointers, resp.).
There are exceptions (no pun intended) to this, namely exceptions, RTTI, global construction/destruction, and the intricacies of new/delete. As the article points out (as will anyone who advocates the use of C++ in constrained environments), none of these are really necessary to use C++ effectively and can be safely stubbed out.
Haskell is also garbage-collected, and lazily evaluated to boot. It's not really an apples-to-apples comparison.
if writing trivial data-structures in Rust is a great challenge, it shows that writing other, less trivial things in Rust will be much more challenging than might be preferred.
Data structures are exactly the abstractions built on top of raw memory. Rust is not really geared toward working with raw memory; it's geared toward working with abstractions that hide the raw memory (i.e., data structures). That's why writing data structures in Rust is hard, and it's also why that fact doesn't imply that "other things" (i.e., code that is not part of a data structure implementation) are hard.
In other words, the fundamental flaw with the assumption that there is a relationship between the difficulty of implementing data structures in Rust and the difficulty of writing applications in Rust is that the two involve very different programming models, and Rust has much more ergonomic support for one than the other.
The catch you're pointing out is that on x86 there are technically no 'invalid' addresses
Depending on what you mean precisely by "x86," there is such a thing as an invalid address: the IA32e architecture (or whatever you want to call Intel's flavour of 64-bit "x86") requires that the <n> high bits of an address match, where <n> is machine-dependent.
In some sense the language is the compiler and the compiler is the language; the language is much like a human language, used for its utility in expressing things (ideas, programs). You can tell if your human language words work by determining if people understand you. If people start being obtuse and refusing to understand you because of an arbitrary grammar rule that isn't really enforced, you'd be right to be upset with the people just as much as the grammar.
The shortcoming of this interpretation is that programs are not (only) consumed by humans; they're consumed by computers as well. Computers are not at all like humans: there is no such thing as "understanding" or "obtuseness" or even "ideas." You cannot reasonably rely on a computer program, in general, to take arbitrary (Turing-complete!) input and do something reasonable with it, at least not without making compromises on what constitutes "reasonable."
Along this line of thinking, the purpose of the standard is not to generate assembly code; it's to pin down exactly what compromises the compiler is allowed to make with regards to what "reasonable" means. It happens that C allows an implementation to eschew "reasonable" guarantees about behavior for things like "reasonable" guarantees about performance or "reasonable" ease of implementation.
Now, an implementation may choose to provide stronger guarantees for the benefit of its users. It may even be reasonable to expect that in many cases. But at that point you're no longer dealing with C; you're dealing with a derivative language and non-portable programs. I think that for a lot of developers, this is just as bad as a compiler that takes every liberty allowed to it by the standard. The solution, then, is not for GCC and LLVM to make guarantees that the C language standard doesn't strictly require; the solution is for the C language standard to require that GCC and LLVM make those guarantees.
Of course, it doesn't even have to be the C language standard; it could be a "Safe C" standard. The point is that if you want to simultaneously satisfy the constraints that programs be portable and that compilers provide useful guarantees about behavior, then you need to codify those guarantees into some standard. If you just implicitly assume that GCC is going to do something more or less "reasonable" and blame the GCC developers when it doesn't, neither you nor they are going to be happy.
That's not entirely true. Regarding portability, the layout of structs for example is implementation-defined to allow faster (by virtue of alignment) accesses or more compact storage depending on the system, but it's perfectly possible to write portable code that works with the layout using offsetof and sizeof (edit: and, of course, regular member access :) ).
That said, I would agree that, on the whole, C leans too heavily on under-specified behavior of every variety. It's just not an absolute.
The desire to reason about the precise behavior of a program and the desire to take advantage of different behavior on different platforms are fundamentally at odds. Like I said, there's a broad range of just how much of the specification you leave up to the implementation; it's an engineering trade-off like any other.
Which is exactly the motivation behind implementation-defined behavior. There's a broad range of "how much detail do you put in the specification" between the extremes of "this is exactly how the program should behave" and "this program fragment is ill-formed, therefore we make no guarantees about the behavior of the overall program whatsoever."
the problem is with compilers (and their developers) who think UB really means they can do anything
But that's exactly what undefined behavior means.
The actual problem is that programmers are surprised-- that is, programmers' expectations are not aligned with the actual behavior of the system. More precisely, the misalignment is not between the actual behavior and the specified behavior (any actual behavior is valid when the specified behavior is undefined, by definition), but between the specified behavior and the programmers' expectations.
In other words, the compiler is not at fault for doing surprising things in cases where the behavior is undefined; that's the entire point of undefined behavior. It's the language that's at fault for specifying the behavior as undefined.
In other other words, if programmers need to be able to rely on certain behaviors, then those behaviors should be part of the specification.
How difficult is it to add a "zero cost abstraction" like the ones used here? For example, what would it take to make this compile:
for window in buffer.sliding_window(coefficients.len()) {
let prediction = coefficients.iter()
.zip(window)
.map(|(&c, &s)| c * s as i64)
.sum::<i64>() >> qlp_shift;
let delta = buffer[i];
buffer[i] = prediction as i32 + delta;
}
with sliding_window returning an iterator over slices of the buffer?Given the world we live in, right now, why does mathematics continue to insist on minified expressions?
Because it is concise, precise, and widely accepted.
Given the option, most development teams would choose to read and write against verbose source code, rather than scrape obfuscated variables and method signatures out of a minified, transpiled, compressed package.
What's your point? Source code is not the same thing as mathematics, not for the majority of software and not for the majority of mathematics as practiced by mathematicians. Verbosity makes sense when your domain involves concrete entities like "customers" and "widgets" and "thermal sensors." When your domain involves abstract entities like "ring homomorphisms" and "clopen sets" and "vector spaces," it doesn't. If you're writing a database, a name like "transaction_mutex" is more descriptive than "m." If you're universally quantifying over the domain of an arbitrary continuous function on the reals, "x" is about as descriptive -- and as conventional -- as any name you can come up with.
As an aside, I really wish we would dispense with the non-word "transpile." We have a word for translating a program from one language to another: compile.
So why do we continue this archaic practice of obscure, inscrutable symbols in mathematics?
Because it works very well. I'm not sure what else to say. You spend maybe fifteen minutes learning about, say, the symbol "∂" when you're introduced to multivariate calculus, and then for the rest of time you have an extremely concise way of expressing a variety of combinations of partial derivatives that can be understood by anyone who has also been introduced to multivariate calculus.
Don't get me wrong, there are actual problems with mathematical notation -- overloading, "abuse of notation," and as often as not just plain omitting information -- but the use of non-ASCII symbols and short variable names are not among them.
Strictly speaking, it's possible for different sorting algorithms to produce different results if you sort using a weak order rather than a total order.
And people wonder why C++ programs compile so slowly.
People have been aware of this problem for some time. It's one of the reasons the C++ community is trying to develop a proper module system (or was the last time I checked, which was a while back).
Pointers are another example of something that is obvious to some but a huge mass of confusion and frustration to others. I've taught programming (mostly Asm and C), and this is quite common. You're not stupid, but you just aren't in the right perspective to understand it.
I understand pointers. I understand integer-based encoding. I'm a firmware programmer, I started with FORTRAN and C-with-classes-style C++, I get how computers work. And do you know what makes computers work? Abstractions. Integers are just an abstraction on top of bit vectors are just an abstraction on top of memory are just an abstraction on top of flip-flops, on top of gates, transistors, digital circuitry, analog circuitry, the laws of electromagnetism -- the only reason we are able to construct something as complex as a pocket calculator and have it work at all is clear, mathematical abstraction. The fact that, in 2016, using such a fundamental abstraction as equality of non-numerical values requires anything beyond the two characters "==" is patently absurd.
My "frustration" is not a result of having trouble grasping first-year C.S. concepts. Nor is it the result of the mild incovenience of specifically having to use "strcmp" instead of "==". It's the result of working in an industry where the value of anything beyond first-year C.S. concepts is completely unrecognized, because it's not "how computers fundamentally operate." Both low-level and high-level computational abstractions are useful, and trying to accomplish anything interesting in a system which completely eschews one class of abstraction in favor of the other, whether it's C or Haskell, is hell.
But hey, it beats the hell out of Javascript :)
I don't think the complaint is that the compiler won't accept a struct sockaddr_in* where the API expects a struct sockaddr* . I think the complaint is that the API is designed in such a way that obtaining the required struct sockaddr* involves casting between incompatible types. There are more robust ways of doing type erasure, even in C.
one of those things that divide programmers into two groups: one that instantly sees the elegance of this design and thinks it's so natural and obvious, and one that gets eternally confused by it.
Believe it or not, there are people who are capable of figuring out C's cute little "round trip through the integers" trick, but who would rather not deal with that bullshit every time they want to compare two values of non-numerical type. It's not elegant, it's not natural, it's only "obvious" to those of us who have had the standard workarounds for C's inexpressiveness drilled into our skulls, and if it were any different, we wouldn't bother with comparison operators in the first place!
I mean, I get that it's fun to squeeze useful semantics out of terse code, especially in an environment as constrained as C. But please don't act like the rest of us are stupid just because what you regard as elegant, we regard as more of a clever hack to be put up with for want of a simple equality operator.
Bit twiddling, ANDing, and ORing are specific and absolute
So what? You could implement addition using bitwise operators, but it makes for much more readable and less error-prone code to abstract it into an addition operator. Code should clearly express intent, and if your intent is to set, say, bits 5 though 9 of some register to some value, the clearest expression of that is something like (given a right-open range convention, which is well-established in this particular language):
register.set_bits(5..10, value);
If I look at the above statement, I know what the author of the code intended it to do and can rely on (or manually verify once) the correctness of the implementation of "set_bits." If instead I'm reading code like register = (register & 0xFC1F) | (value << 5);
I have to work out in my head what the bit fiddling is doing, and from that guess what the author meant the code to do. I can't know whether he or she made a mistake in translating the intent to bitwise operations (say I know from context that value is less than 8; did they really mean to clear bits 8 and 9?), because the intent isn't expressed anywhere.For example, what happens when I specify a range of 0..100 for a 32 bit int?
What happens is that you've violated a precondition of the API. Because this is Rust, I would expect the operation to be implemented in such a way as to panic (in debug mode), or just fail to compile, since selecting a bit range like this at run time is extremely uncommon.
what if the value is too big for the range?
Same thing as if you weren't using the abstraction around the bitwise operators: either you'd clobber higher bits, or the implementation would truncate the value.
Again, though, I would expect a debug-mode panic.
APIs around small operations like this introduce ambiguity that wasn't present before.
I'm not sure what "ambiguity" you see here. There's the issue of whether the range is right-open or -closed, but again right-open ranges are well-established in Rust.
It seems keywords are added to ensure serialization/atomicity, then compilers find a way to optimize it away/make it useless
No, what happens is keywords (or more broadly, semantics) are added which provide certain guarantees given certain preconditions. Then, developers write code that depends on those guarantees and fails to meet the preconditions, but happens to work anyway. At some point, the implementation adds some optimization which -- while still providing the appropriate guarantees when the preconditions are met -- breaks that code.
I know C and C++ can be a royal pain in the ass sometimes, but I have little sympathy for developers who flip the "I know what I'm doing, please assume my code is right and make it run fast" switch, then get upset with the compiler when it turns out that their code actually _isn't_ right and the compiler performs an optimization they weren't expecting. If you don't want the compiler to reorder your memory access, then don't fuck around with the memory_order flags.
Emacs gets a bad rap as a "kitchen sink" program, but it isn't really, any more than something like the Bourne shell or Python. The majority of Emacs' built-ins are things like an elisp interpreter, some text processing stuff, and system libraries. Conversely, most of the really interesting things you can do with Emacs, like web browsing (eww), git repo management (magit), terminal emulation (ansi-term), etc. are really just programs written in elisp. Some of them also happen to be distributed with Emacs.
Hardware doesn't have native saturating overflow operations
Uh, what hardware are you talking about? x86 and ARM at the least have saturating arithmetic instructions.
"saturating" behavior is something you want only rarely.
Well, yes, because what you almost always actually want is the mathematically accurate result. Failing that, though, I can think of a few cases where you would want overflow to saturate (hardware outputs, mostly); I haven't thought of any cases yet where wrapping would be remotely useful.
You may be able to use "greening" here, but at least AFAIK it is not common in English since it seems a bit strange to turn adjectives into nouns like that.
I think the equivalent in English would be "begreening." It looks weird written out, but I think your meaning would be clear if you said something like, "The city's begreening project will cost an estimated 1.5 million dollars."
NULL is a handy way to enforce that fundamentally, in that dereferencing a null pointer guarantees a crash
In the context of C and C++, dereferencing a null pointer does not guarantee a crash, or anything else for that matter. In fact, not only does it not give you any guarantees, it nullifies any guarantees you might otherwise have had, because the behaviour of a program that dereferences a null pointer is undefined. Now, an implementation is of course free to make guarantees about programs that have undefined behaviour, but none I know of does.
Part of programming is about memory management. You can hide it with a sophisticated compiler but it's always going to be there, and there are always going to be two types of memory: that which is available, and that which is not.
This is not true. It is perfectly possible to write useful programs that never have to deal with unavailable memory or even memory management at all past compile time, even in C. This is quite common (as are implementations that don't crash programs which dereference null pointers) when working with embedded systems, for example.
Why do people who write or talk about Lisp always have to start with something like this?
Well, the people who write or talk about Lisp generally probably think it's true, and it's the sort of thing that, if true, makes for a fantastic selling point.
you can add/remove 'language features' as you want, and the syntax is really simple, and code is data
I think this is the meat of it: that Lisp offers an exceptionally low barrier to entry to the world of, let's say, "experimental programming." It lets you do things you would never dream of if you've only ever mucked around in something like C or Java. Whether or not those things are actually useful in and of themselves, it's fun and rewarding to experiment not just with programs, but the way you write and even think about them.
Of course, there are people who will tell you that Lisp is also good for solid technical reasons, but I think this is the main appeal of Lisp to most people who describe it as "awesome."
Everyone tries to read programming languages as close as possible to plain english in their head
That's simply not true. You may do this, and it's a perfectly valid approach, but not everyone thinks that way. Personally, I think of code in terms of what it "does," rather than what it "says," if that makes any sense— it's difficult to explain.
As for readability, I firmly believe it ultimately comes down to familiarity. Yes, infix syntax is easier to read for a limited subset of small, operator-dense arithmetic expressions, but I find that kind of code is relatively rare. Broadly speaking, you're going to get most of your structural information about Lisp code from the indentation; the parentheses eventually fade into noise. And honestly, the same is true of just about every other programming language.