Re: Advice for novice programmers thread
Posted: Wed May 05, 2021 9:39 pm
ECMA/JavaScript sounds like a lame idea for a //first language.
The Place to Start for Operating System Developers
https://f.osdev.org/
What an utterly well-reasoned and constructive opinion. Would you care to expand upon that or are you just here to insult people? (Hint: The tone makes the music.)Seasoft wrote:ECMA/JavaScript sounds like a lame idea for a //first language.
The problem with C++ is that it requires a really good instructor. Done right, you can get all the way through an introduction course and halfway into an intermediate course without even mentioning pointers, or indeed arrays. And today, "manual memory management" is something for library implementors, if even that. Unless you are programming an allocator, you should never have to write "new" or "delete".vvaltchev wrote:* The problem with C++ is that, while is all-powerful, is too complicated and expert-friendly to start with.
That's the modern mainstream approach towards teaching C++, pushed by most C++ evangelists and by Stroustrup himself. I oppose this approach, despite its high-profile advocates. It's all about teaching modern C++ AS IF it was a managed language like Java or C#. It goes like: "use vector<T>, map<T>, make_unique(), make_shared(), etc. follow the _rules_ and it will work". The problem with that is: what happens when we do make a mistake? In managed languages, mistakes have a much limited effect compared to unmanaged ones. Null pointer? Exception. Out of bounds? Exception. Use after free / double free? Cannot have that with GC. Uninitialized memory? There's no such thing in managed languages I know.Solar wrote:The problem with C++ is that it requires a really good instructor. Done right, you can get all the way through an introduction course and halfway into an intermediate course without even mentioning pointers, or indeed arrays. And today, "manual memory management" is something for library implementors, if even that. Unless you are programming an allocator, you should never have to write "new" or "delete".
The thing is maximum power is what has brought people to C/C++ for decades! If I want a memory safe language, I'll use Java or Rust. I like old fashioned C++, and quite honestly, I greatly dislike C++11 and later. The reason why is because they are trying to hide what has C++ has thrived on! C++ is trying to become a safe language, but, it has become a bloated mess instead!Solar wrote:The problem with C++ is that it requires a really good instructor. Done right, you can get all the way through an introduction course and halfway into an intermediate course without even mentioning pointers, or indeed arrays. And today, "manual memory management" is something for library implementors, if even that. Unless you are programming an allocator, you should never have to write "new" or "delete".
That is very true. Same goes for C. C really only started clicking for me when I joined a Usenet group discussing the subject, and the standard came up (frequently). I had previously never heard of ISO C, but immersing myself in the subject really helped my C. And then I went to Uni afterwards, enrolled in CS, and got a Programming professor that did not care one lick about portable code, and basically wrote C like he had learned to do in Turbo C for DOS. Teaching tons of wrong things. Thankfully I only had to endure it for half a semester, but I can't imagine the damage a more impressionable mind would take on such upbringing.Solar wrote:The problem with C++ is that it requires a really good instructor.
Java can have dangling references, or unreachable objects still referenced, leading to resource leaks. Java claims it is free of pointers, but does throw a literal NullPointerException on occasion. You have a String object that might not actually be a String object because it is actually NULL pointers? In its way, Java is more backward than C++. And it's not without pointers, it is all pointers, it's just lying through its teeth about it.vvaltchev wrote:That's the modern mainstream approach towards teaching C++, pushed by most C++ evangelists and by Stroustrup himself. I oppose this approach, despite its high-profile advocates. It's all about teaching modern C++ AS IF it was a managed language like Java or C#. It goes like: "use vector<T>, map<T>, make_unique(), make_shared(), etc. follow the _rules_ and it will work". The problem with that is: what happens when we do make a mistake? In managed languages, mistakes have a much limited effect compared to unmanaged ones. Null pointer? Exception. Out of bounds? Exception. Use after free / double free? Cannot have that with GC. Uninitialized memory? There's no such thing in managed languages I know.
As far as I know, that's not possible unless you mess with JNI or use weird things like sun.misc.unsafe#allocateMemory().Solar wrote:Java can have dangling references, or unreachable objects still referenced, leading to resource leaks.
Nobody talking about Java has ever hidden the fact that all objects are pointers. Having NULL pointers is absolutely natural a concept for most programming languages. It's not a defect. Forcing an object to always have a value is bad, and would also lead to many limitations. How could you create a linked list? You cannot have an infinite amount of objects. Having a NULL "next" pointer is exactly what we want. Null pointers are absolutely simple to use in Java and the rest of managed languages. No UB, nothing. Just, you obviously cannot attempt to de-reference it. But if you do, you'll get a clear exception, no surprises.Solar wrote:Java claims it is free of pointers, but does throw a literal NullPointerException on occasion. You have a String object that might not actually be a String object because it is actually NULL pointers? In its way, Java is more backward than C++. And it's not without pointers, it is all pointers, it's just lying through its teeth about it.
I disagree, but I just wanna let go. Answering here would lead to an endless discussion like the one about the history of UB in C. We'll be both happier by just skipping this. Obviously, we're light years apart and there's no way to change that.Solar wrote:C++ can be used as a language without manual memory management, without C arrays, and yes, to a large degree without using pointers. And you know what? Your code will be shorter, more expressive, and more robust that way.
And it's, IMHO, the right way to learn C++. Obviously, things can be skipped along the way. For example? Well, the classic "auto" that nobody used, along with auto_ptr<T> and a bunch of other stuff that has been replaced by better features. No need to know the limitations of C++11's lambdas. No need to know C++11's limitations of constexpr etc. The long way to learn C++ does not include learning each C++ standard. It's all about exploring all the features of the latest standard, step by step.Solar wrote:Going from C to C++ in tiny steps is hard.
Oh.. I believe that such path will be easy for people, while they learn. The problem is they won't know/understand a ton of super important stuff and will be very limited in what can do in terms of practical stuff. And they will suffer when they hit non-trivial problems.Solar wrote:Starting with C++, the real C++ taught by an instructor who knows that all the C stuff is for backward compatibility only -- that is such an easy learning experience you wouldn't believe it.
With interfaces.vvaltchev wrote:How could you create a linked list?
Code: Select all
public class List<T> {
interface Node {
bool isEnd();
Node getNext();
T getVal();
}
private Node first;
class EmptyNode implements Node {
EmptyNode() {}
bool isEnd() { return true; }
Node getNext() { throw new NotImplementedException(); }
T getVal() { throw new NotImplementedException(); }
}
class FullNode implements Node {
private Node n;
private T val;
FullNode(T v, Node nx) { n = nx; val = v; }
bool isEnd() { return false; }
Node getNext() { return n; }
T getVal() { return val; }
}
class ListIterator implements Iterator {
private Node n;
ListIterator(Node node) { n = node; }
bool hasNext() { return !n.isEmpty(); }
T next() {
T val = n.getVal();
n = n.getNext();
return val;
}
}
List() { first = new EmptyNode(); }
void PushFront(T v) { first = new FullNode(v, first); }
Iterator iterator() { return new ListIterator(first); }
}
It's not a war; it really isn't, not for me. This is relaxed chat, for me. If it isn't for you, sorry, and this is my last post on the matter as well.vvaltchev wrote:Anyway, I'm forcing myself to stop here. No intention in fighting another war. I've expressed my opinion, you have expressed yours, that's enough. Readers will make up their own mind.
Not a defect, but a crutch, dating back to a time when pointers were all we had. You don't need pointers when you can have references -- which, incidentially, can never be NULL. References are simply superior to pointers in every conceivable way.Nobody talking about Java has ever hidden the fact that all objects are pointers. Having NULL pointers is absolutely natural a concept for most programming languages. It's not a defect.
See? You think "linked list", and immediately think about its innards, about the "next pointer" in there. I think "linked list" and immediately know that that's a standard container I can just use.How could you create a linked list? You cannot have an infinite amount of objects. Having a NULL "next" pointer is exactly what we want.
Code: Select all
my_class x{ 42 };
std::forward_list< my_class > list;
list.push_back( x );
nullplan wrote:With interfaces.vvaltchev wrote:How could you create a linked list?
Well, C++ references are good and I use them all the time, but they cannot replace pointers, simply because they cannot change the object they refer to and also because they cannot be set to nullptr. Yes, in Haskell everything is different, but that's a purely functional programming language and pure (non-nullable) references are a regular thing. Sure, I agree that the concept of a nullable attribute is interesting and usable outside of Haskell. For example, in the latest version of Dart, I believe all Java-style pointers will be non-nullable by default and a @nullable attribute will be supported.nullplan wrote:Excuse syntax errors in the above code, I haven't done this in a while. In any case, as you can see, not a single null reference is present or desired. Object oriented programming allows you to do things very differently from pure procedural programming. I do not think having all references be nullable is a good thing. In C++, references are never NULL. There is a separate type to denote the possibility for an object to not be filled (called Nullable, similar to Haskell's Maybe), and so you cannot pass a reference that may be NULL to a function that can't deal with it.
Well, I agree: if you can use the type system to your advantage for free, than there's no reason to not do it. I do have a similar example too. Tilck's linked list is inspired by Linux's intrusive linked list implementation but I don't like having using list_head for everything. That's why I have:nullplan wrote:Using the type system to detect such errors is one of the hallmarks of higher level programming, so it is a bit of a shame to see people not take advantage of it. In C, I have started using small structs all over the place to keep several low-level things apart from each other. I have a struct for 4-byte little endian values, and 4-byte big endian values, and functions to translate between those and actual integers. I never have to deal with byte swapping ever again!
Code: Select all
struct list_node {
struct list_node *next;
struct list_node *prev;
};
struct list {
struct list_node *first;
struct list_node *last;
};
I don't want discussion to turn in to war either but.. it's super easy to get heated when opinions diverge so much and we do the mistake of trying to convince the other person.Solar wrote:It's not a war; it really isn't, not for me. This is relaxed chat, for me. If it isn't for you, sorry, and this is my last post on the matter as well.
OK, I appreciate the good intentions. I'd just say that almost everybody out there believes in what you're saying and all the conferences about C++ repeat the same point over and over again. So, I honestly believe that my approach is in the minority here.Solar wrote:This is something I have experience in, and if you and I had the time (I don't, though, sorry) I'd love to show you what I mean in practice. I completely understand your POV, I've been there not that long ago myself. I even started an online blog series to teach C++ "ground up", "oldschool", which was how I learned it myself after all.
Which was exactly when I realized how many problems you create that way.
I disagree, but I believe that, in your world-view about software development that makes completely sense. Again, we don't disagree in details, we have a fundamentally different system of values. We're both right in our own system of values. Unfortunately for me, most people in the industry are closer to yours than mine.Solar wrote:Not a defect, but a crutch, dating back to a time when pointers were all we had. You don't need pointers when you can have references -- which, incidentially, can never be NULL. References are simply superior to pointers in every conceivable way.
Yep, I know that position. You don't want to teach people a deep understanding of software. You want them to just use pre-built blocks without thinking. Alright.Solar wrote:See? You think "linked list", and immediately think about its innards, about the "next pointer" in there. I think "linked list" and immediately know that that's a standard container I can just use.
If you don't know how it works internally, you won't be able to use it well. You'd use it mechanically, often in the wrong place or in the wrong way. When I was studying CS, I remember how many students before my eyes didn't pass the test because the professor noticed that they used Java's LinkedList's get(int index) method inside a for loop, leading to a quadratic complexity. They should have used ArrayList instead.Solar wrote:Thinking about innards, thus breaking the abstraction, is exactly the kind of thinking that perpetuates problem of people approaching C++ bottom-up. It absolutely does not matter how a linked list works internally, because we have a complete implementation available.
Well, how can we be further than this? You want people to use things without knowing how they work. That's a very efficient way to write bad software IMHO. I believe in the opposite: before using something, you need to have a decent understanding of how it works. The best way of having a good understanding of something is to re-implement it, for educational purposes.Solar wrote:The most aggravating thing I know is instructors asking their students to re-implement standard functionality (like writing their own list class, or implementing bubblesort). It's wasted time; chances are you never will have to actually write anything like that in the field -- not if you did spend that time learning what is already there, and how to use it, instead on how to re-implement it.