I am a strong proponent of not using anything that forces you to use OOP as "baby's 1st language". Well I see OOP as a heresy in general tho. To me programming is about algorithms and data structures and data flow. Not some strange concept of OOP that does not even have an exact definition and you can see some extremists claiming that literally everything is OOP.
My first true language was learning C in college, I'd definitely do that again. When I switched to CS everything was scheme (lisp) based which I actually preferred to Python and later on was mostly Java based. I still hate Java but it's gotten me through interview / interview screens so I'm okay with it now.
For me, first language should be strict and not too loose with the rules (so not python). Once you've got the rules down and understand why they are there / why certain languages use those constructs - build from there.
But take all of this with a grain of salt because I was a very distracted student more interested in working for startups during college than graduating with a nice GPA ;)
As someone who had a Clojure phase about 9 years ago: the actual problem with modern Lisps is that they don't really offer a big set of unique good features. The basic functional map-filter-reduce "meat grinder" is everywhere (and has been almost everywhere for a long time, Java not having lambdas for ages is what clouded everyone's vision into thinking that's not the case). The other "Lisp feature" is macro metaprogramming based on homoiconicity (Code Is Data™) and we have it in some non-Lisps too now (hello Elixir) and… is it really the best way to do metaprogramming? Ehh. Well, many compiled&typed languages actually do similar-ish things: using syn in Rust proc macros / haskell-src-exts in Template Haskell you can work with code "as data". And that isn't as nice to work with as D's `static foreach/if` + reflection/introspecion thingies like allMembers/getMember.
Clojure is a sort of baby’s first lisp. That’s not a bad thing by the way. Its relative simplicity and restrictiveness are usually beneficial for corporate environments and I like working in it.
That said, if you want to talk about lisp you should really talk about Common Lisp. And there are a lot of solid criticisms of Common Lisp, but not enough features isn’t one I’ve heard before. The LOOP facility alone has an absurd number of features, to say nothing of the Meta Object Protocol.
It’s fair to say that a proficient Lisper can write performant code in any set of paradigms you like using a modern Common Lisp implementation. That comes with some considerable trade-offs though.
If people feel regularly compelled to redo the syntax, there's something wrong with it. "Many" means balkanization, which is another problem. (D has some carefully designed points which discourage balkanization.)
It goes back to too many parentheses. So I'm only half joking about it.
To that end, I’ve left out a lot of the advanced features (so far), such as:
Templates
Type inference
Operator overloading
Overloading in general!
__traits
Classes/Interfaces/OOP
Pointers/references (mostly)
Memory management in general (thank you GC!)
I have no teaching experience to back it up yet I've always felt this approach of intentionally leaving out very nifty, useful, and sometimes important bits of knowledge is bad. For sure, in the scope of one class there's not enough time to thoroughly teach everything and test on it, tradeoffs have to be made, but I'd still rather instructors explicitly mention such things as briefly as they can get away with (but three times) just with the expectation that not everyone is going to really even 'get' them and it won't be on any tests. People very much go with what they know (you'll find bubble sort in production software, alas) and if you don't even give them a sign for "here's something you might want to explore on your own or come back to if you run into it again later" most won't set foot beyond their present understanding. The worst offender in this sort of omission I think comes from the excellent SICP book -- it's not the book's fault, it's not even really trying to "teach Lisp", but people nevertheless use the book and as a side effect learn a bit of Scheme, which they confuse (because no one told them otherwise) for thinking that they now know Lisp. But they don't. In order to know Lisp one needs to learn Common Lisp with its macros, its OOP, its condition system, its types (which SBCL can check at compile time and use for more optimized assembly), its packages, its approach to interactive development... It's the same issue as high school C++ courses (I assume some of them are still around!) teaching it as C with classes, and never hearing about (let alone touching), say, templates, let alone any of the modern C++0x and on features.
I don't know what "heresy in general" means regarding a programming language feature, but I dislike OOP in Java and C++ code where I have encountered it.
The largest code base I love and feel productive in is Linux kernel which is mostly C and assembly, medium sized project of 30 million lines, probably less than ten thousand active developers (although difficult to judge with the way development on certain features or functions can occur for years on private repositories before being merged), I would say relatively high rate of change for a project of the size.
It frequently has structures containing objects of different types that have to identify themselves so they can be processed in different ways. This general pattern, and function pointers in C predates formal "OOP" of course, so I don't call it manually-written vtables. But yes it has this and it is a good technique.
OOP languages which do this automatically for you is AFAIKS just a thin and often clunky layer of syntactic sugar on top of it.
> just a thin and often clunky layer of syntactic sugar on top of it.
That sugar does help.
C allows adding another function pointer to that structure, but not set that pointer. C++ compiler forces programmers to implement all interface methods. Forget to override a method, the code which instantiates the class won’t compile complaining about not being able to instantiate an abstract class.
Code navigation is another thing. In visual studio, while the cursor is over an abstract method, F12 key looks up all implementations of the abstract class, and populates the “find symbol results” panel with a clickable list of the implementations of the method. With visual assist addon installed, Alt+G key does the same only presents the results in a popup menu instead of a separate panel.
Both things are borderline useless for small projects, but IMO they help a lot for medium to large ones, especially developed by multiple people.
I've never found it very compelling. As I said there are also downsides, inflexible implementation that is implementation specific (so it can be difficult to manipulate with low level assembly).
> C allows adding another function pointer to that structure, but not set that pointer. C++ compiler forces programmers to implement all interface methods. Forget to override a method, the code which instantiates the class won’t compile complaining about not being able to instantiate an abstract class.
Never found that particularly helpful if the code is structured well. You'd either allow for NULL implementations to be default or error not implemented at least until all subsystems are converted, or the initialization functions that all object allocations should call (because the code is well written) can verify all required fields are set. If you want to be even cleverer, you can probably do static initialization checks at least where your fields are constant and have those compile down to nothing just checked at compile time.
> Code navigation is another thing. In visual studio, while the cursor is over an abstract method, F12 key looks up all implementations of the abstract class, and populates the “find symbol results” panel with a clickable list of the implementations of the method. With visual assist addon installed, Alt+G key does the same only presents the results in a popup menu instead of a separate panel.
I can see how that might help a little, although surely with some minimal scripting a symbol browsing tool should be able to be taught about similar patterns like find all functions that are assigned to this particular member of a structure of function pointers. Although I don't use IDEs or any symbol tagging tools just grep usually, so maybe I'm a luddite.
With a nice code base that follows reasonable conventions and naming, it's pretty easy to find e.g., if you have a structure-of-function-pointers style of thing then you can find all definitions of "struct address_space_operations" or if a function pointer member is called page_mkwrite, then you search for *_page_mkwrite and get ext4_page_mkwrite, xfs_page_mkwrite, btrfs_page_mkwrite, etc. (which are not always strictly enforced in Linux but at least if you are searching for \.page_mkwrite you can usually easily see non-confirming names).
> Both things are borderline useless for small projects, but IMO they help a lot for medium to large ones, especially developed by multiple people.
I don't see that it helps a lot, and even as syntactic sugar I don't see it being a big advancement in the scheme of things.
> Never found that particularly helpful if the code is structured well
IMO, all else being equal, errors detectable at compile-time should be detected at compile-time. Following reasons. (1) No matter the circumstances like time pressure, compiler errors are impossible to ignore, because they fail the build (2) Compiler errors are easier to detect automatically, e.g. many projects have automatic build systems. Even if the project has automatically run unit tests, people still need to write these tests first, i.e. the process is not completely automatic. (3) Runtime checks have runtime costs. Even if the checks are in static initializers or similar, that’s still runtime cost at startup and/or first use. Compile-time checks are free at runtime, makes the code faster.
> although surely with some minimal scripting a symbol browsing tool should be able to be taught about similar patterns
Technically, yes. Practically, two things. If you’re part of a team of developers, in particular if that team is remote, people are using their own computers, and people have varying levels of programming experience, such scripting tools are hard to implement. Another thing, the time spent making and supporting these tools is the time not spent improving the product being built.
> IMO, all else being equal, errors detectable at compile-time should be detected at compile-time.
I agree and you can do that with C. See compiletime_assert in Linux. An ops table definition or registration macro can do compile time checking that such fields are populated.
> Technically, yes. Practically, two things. If you’re part of a team of developers, in particular if that team is remote, people are using their own computers, and people have varying levels of programming experience, such scripting tools are hard to implement. Another thing, the time spent making and supporting these tools is the time not spent improving the product being built.
In all teams I've been part of including the loosely coupled and highly distributed open source side of kernel development, tools are widely shared and developed together. People don't just get left out in the wilderness. The bigger the project the more true this is.
I get that standard IDE niceness exists for some OOP language features, so it might be some small practical advantage, it just isn't an inherent advantage of the OOP paradigm. We don't need to use C++ to get nice rich and context aware editing search/display.
> An ops table definition or registration macro can do compile time checking
If programmers writes that check, yes. Same applies to a unit test. C++ compiler gonna verify that thing automatically, no extra work required, neither initially, not over the lifetime of the project.
> tools are widely shared and developed together
I prefer when a freshly cloned repository builds without any extra tools on a freshly installed computer, with either F7 in the correct version of Visual Studio, or something like cmake ../ && make in Linux shell, after installing the required dependencies from the official package repository of that Linux. In my experience, custom tools are often a pain in the long run.
> it just isn't an inherent advantage of the OOP paradigm
The paradigm is orthogonal to programming languages. I think OOP is merely a high-level design pattern where objects keeping their private state only accessible/modifiable by calling methods of these objects.
In this sense, the source code linked above in this thread is 100% OOP, despite plain C. Just like the majority of Linux APIs which operate on opaque handles: open/close/read/write for files, snd_pcm_* functions for ALSA, all these IOCTL requests for V4L2, various handles for Vulkan. All these APIs are implementing an abstraction over external devices with very complicated internal mutable state, OOP is pretty much the only way to go for these things.
> If programmers writes that check, yes. Same applies to a unit test. C++ compiler gonna verify that thing automatically, no extra work required, neither initially, not over the lifetime of the project.
Yes. Programmers have to write these checks, that's all about what a well structured and maintained codebase is about. They have to write many checks no matter what the language, because missing initializers is one tiny little aspect of things you might want to check for.
> I prefer when a freshly cloned repository builds without any extra tools on a freshly installed computer, with either F7 in the correct version of Visual Studio, or something like cmake ../ && make in Linux shell, after installing the required dependencies from the official package repository of that Linux. In my experience, custom tools are often a pain in the long run.
I didn't suggest otherwise, I was talking about editing and searching scripts and commands.
> The paradigm is orthogonal to programming languages.
Right, C can do it. As I said in the beginning, I don't like the OOP features of C++ because they're clunky inflexible and hardly any benefit in terms of easier syntax.
> I think OOP is merely a high-level design pattern where objects keeping their private state only accessible/modifiable by calling methods of these objects.
No "OOP" is definitely considered to be language features too.
File descriptors are fundamentally polymorphic, they can be files, pipes, sockets, signalfd, eventfd and the list is ever growing, everything is a file.
Well, "is" is quite the load-bearing word in those claims. I think it is kinda true in the sense that lots of things can be represented as OOP. A great example is how Scala represents ML-style ADTs as sealed class hierarchies.
To be frank, why would one choose a language with a small community over any of the major languages? Beginners have to learn -how- to program on top of the language's syntax and toolchain itself, so it helps tremendously to be able to search online when they get stuck. And boy do they get stuck a lot, in my experience.
If you pick something like python, any common beginner problem should have questions answered on stackoverflow, blog posts, etc. With a more boutique language that's not the case. Not to mention the ecosystems are much larger so chances are a library already exists to do something instead of making the beginner do it themselves and overloading them. There will be more tutorials, so people can get unstuck. More documentation around the tooling and just documentation in general exists, and so forth.
---
Of course, you should teach "what you know best", but how hard is it to learn e.g. python (even just for the scope of the course) and just teach that? My favorite languages are Rust and Go, but I'm not going to foist those upon newbies just because I'm more familiar with those than javascript, python, ruby, etc.
The secondary lesson here is that teaching (well) is actually quite difficult. Coming up with a curriculum is hard. Keeping students engaged and unstuck is hard. Knowing how to program doesn't prepare you for any of that.
1. D is an easy language to learn. Frankly, one will probably learn better by having to explore a bit here and there rather than having every answer instantly available, no effort required. A big part of effective learning is working to solve a problem yourself.
2. D supports many styles of programming - imperative, meta, OOP, functional, machine code - so one can quickly learn these many styles without having to learn another language. With D's inline assembler, one can even use D to learn assembler without needing to deal with all the boilerplate of setting up an assembler program.
3. Interpreted languages, for example, do a good job of hiding what is actually going on. This leaves gaps in a programmer's knowledge.
4. D has a active and large learn forum. Help is available as needed.
5. Being a D programmer marks one as innovative and willing to try new things. Many employers consider D experience on the resume as a nice plus.
6. D includes a C compiler (ImportC) so if you must use a solution in C, it's easy to access.
1. People learn more than 1 language. What I'm referring to here is complete beginners who need to learn -how- to program. Once they grok that, it's not difficult to move to other languages, obviously with some exceptions.
2. It's not the job of a -language- to teach everything about programming or networking or computers or various protocols. That's the job of a curriculum.
3. There are way more $major_lang jobs than jobs for D, Clojure, Lisp, Crystal, or any of the other languages that are significantly less used. The sky is the limit for how many languages people want to play around with and learn, once they know enough to start learning other languages by themselves. No recruiter is gonna be like "oh, you learned $major_lang... Even though you likely applied to this role because our stack involves $major_lang and that's a language that you know well..."
4. Help, resources, meetups etc for $major_lang are orders of magnitude more plentiful than for D, etc. Imagine telling a student "no, no, you need to prove yourself by making forum posts for <niche language that I personally like and use> instead of having stackoverflow questions or tutorials to follow" in the case of getting stuck. Of course students should strive to do as much as they can themselves, but you would be surprised to see what beginners get stuck on and just need a gentle push to get going again.
5. Being able to be independent is more engaging than having to constantly pester a teacher or a forum. If a student wants to be a go-getter with e.g. python or lua, there are communities and resources to build games or websites, etc. And those already have had common stumbling block questions answered because they have large communities around them.
For the goal of teaching someone how to program, any language other than the major ones like python, javascript, ruby, c++ strictly make it more difficult for students because of the smaller ecosystem, lesser amount of documentation, and smaller community in general. I have nothing against D or other niche languages, but I'm against picking languages just because I'm familiar with them.
It can be difficult for experienced programmers & power users to remember that there's a sheer amount of information that needs to be learned in. There is no reason to make that more difficult for students.
I don't buy learning D is more difficult. Everything needed is available, for free. Once a certain threshold is met, more doesn't add much of anything.
It's easier to transition from D to C++, or D to Python, than to transition from C++ to Python or vice versa.
You're right, it's not the job of a language to teach everything. But since D supports multi-paradigm, it's easier to use D to teach it.
I mean this with all due respect, but you are far too biased. It's fair for you to state that D makes solid efforts to be approachable and easy to learn, but you cannot claim it is easy to learn:
you don't have the data, and you invented the language.
> Everything needed is available, for free.
This is a moot point. I struggle to think of another language for which this is not true.
> But since D supports multi-paradigm, it's easier to use D to teach it.
You've confused learning to program and learning a paradigm with learning a language. They are not the same thing.
For one, multi-paradigm languages hinder comprehension for newcomers as there are too many ways to do things. It's too easy and convenient to be impure with respect to a paradigm. Multi-paradigm languages are mostly useful after you understand the individual paradigms well, but you will surely disagree with me here. We're all entitled to be wrong from time to time :)
I respect you and your work but you're missing the forest for the trees here.
When I've taught people to program, the simplest environments are the most effective. Basic, C, Lua, Processing, Lisp, etc. By comparison, C++ and Python are full of difficult-to-grasp footguns, I'd strongly avoid them (or comparisons to them) in this day and age.
Keep in mind that I created D because I wanted to program in it. It is based on my decades of experience with other languages and supporting those languages and knowing where people go wrong with them and where the problems are.
> C
C is a simple language, but grasping how it works is not simple. To get C you need to understand assembler, which is not simple at all. The misunderstandings novices have about pointers, for example, are misunderstandings about how computers work.
C is loaded with legacy problems, too, like uninitialized variables containing garbage. There's all that confusion between prototypes and non-prototypes. Implicit coercion of 2 to a pointer:
int *p = 2;
The C preprocessor is nowhere near as simple as it appears. Arrays are determined to decay to uncontrolled pointers. Buffer overflows are pervasive, even among the most professional programs.
D (as Better C) is a very C-like language, but minus those problems. It's reasonable to say it is much more easily learned. It has far fewer footguns.
> You've confused
I may have been unclear, but I am not confused.
I've written two C compilers from scratch, and 3 C preprocessors. I know every last dirty detail of C.
Basic, however, we can agree is a simple language. But it teaches bad habits, which have to be unlearned.
Look, you and I know this is horrible and should probably be a war crime, but you have to admit: it's elegant. The ability to pierce the abstraction with C is the only reason it's on that list. I can teach people how computers actually work without getting to assembly (though it's usually a good idea to go there). It all depends on the goal. If your goal is to understand how computers execute programs, then a little C doesn't hurt.
> But it teaches bad habits, which have to be unlearned.
Apologies, but you're confused again: you've conflated learning how to code, with learning to be a software engineer. They're related, but less so than you think.
In my experience, it is impossible to teach a student a lesson that requires experience without providing them the opportunity to experience it. Teach someone Basic, let them do bad things, then show them the light.
Telling me again that I'm confused and telling me what I think and what I have to admit is rather presumptuous. You can make all your points just fine without that.
When I see `int *p = 2;`, "elegant" is not at all what comes to mind. "Bug" is what comes to mind.
Reminds me of my wood shop teacher. He showed me the right way to use hand tools, from the start, which pays off every time since that I use one. He also showed how to use power tools, and never failed to show us the missing finger on his hand - the price for taking shortcuts.
Whenever I use the table saw, I think of his finger stub and slow down, take my time, and do it right.
Another issue with C being an allegedly good beginner language is strings. C is simply terrible at handling strings. Let's say I want to concatenate two strings:
I get that C tries to be incredibly barebones, but operations on strings are so common that it's crazy how bad the string handling in C is. It seems like it was designed under the assumption that all of your strings are stored in fixed-size buffers.
I might not pick D as a beginner language simply because it unfortunately isn't very popular, but I certainly wouldn't pick C as a beginner language.
I've tried D after programming in Perl and wish it had existed when I was in highschool and in my first uni year, so that I could have been taught D instead of Borland C.
It's too bad it didn't make its way into embedded programming where we still have to use C or C++ or dumbed down high level languages, even if it was just for the BetterC part. D-style OOP would be nice as well, though. Maybe LDC and GDC would change that by targeting more platforms and ARM eating the world.
And python: https://stackoverflow.com/questions/50510291/ (this one got downvoted, awesome experience for a novice, some people argue abusive nature of stackoverflow is intended so that people learn hard all its ins and outs before touching it or get abused)
Hiding what's going on (e.g. details related to real hardware like machine integers) is arguably pretty good for teaching a complete beginner about what this "code" thing even is.
Though I guess the classic workaround for that is to carefully construct the course such that these details are revealed gradually, without being overwhelming.
Go is a fantastic first language imo. It is simple and yet still exposes the concept of pointers without having to worry about being safe with them. Ive taught a course with it which was well received
I find that searching for help with popular languages will more often than not bring up heaps of clickbait articles with trash code. Do you want newbies to learn from that?
> Beginners have to learn -how- to program on top of the language's syntax and toolchain itself, so it helps tremendously to be able to search online when they get stuck. And boy do they get stuck a lot, in my experience.
While this is not a crazy argument to make, it applies to people teaching themselves to program. If you're teaching someone else how to program, you're very carefully introducing new ideas, you're teaching them what they need to know, and you're answering the questions when they get stuck.
You are referring more to university classes that "teach" programming. That's a little different from this, where the author was actually teaching.
Because it is harder. Why do you have brutal bootcamps when you join the military? To eliminate your assumptions and break you, and show yourself your limits and form a foundation upon which you can build something that lasts.
One's experience of adopting to new hardships and challenges can either be an uphill battle or downhill. You start with a hard language with manual memory management and small community you are forced to figure things out on your own, learn by helping others and contributing libraries. Then you can appreciate easier languages and learning them would not be as steep of a curve compared to starting off easy.
While I 100% agree with your overarching point, I think it's interesting that learning programming myself in the late 90s meant that I had far fewer resources. I got very very stuck, for sure, but it meant that I wasn't just copy-pasting solutions I didn't understand.
This was exacerbated by GML (the Game Maker Language) being the first real programming language I learned and used a lot. Idiosyncratic to say the least, but taught me "OOP" in terms of how scripts attach to objects to define behaviour. Fascinating stuff. Game Maker 4 is the reason I'm a programmer two decades on!
> "The features that I found the kids handled well were basic types like ints and strings (though what a funny name “string” is, that took a while to sink in)"
I have so ingrained in my brain the association of "string" to a text variable type that I didn't even consider the fact the name is bizarre.
It really doesn't make sense at first sight, and this is something I probably would not have realized until someone asked me "Why do they call it a 'string'?"
Well, if you learn about chars and then realise a string is a "string" of chars, then it's not so bizarre. If we're starting with ints before introducing floats, then why not start with chars before introducing strings.
Well, not all languages have chars. And even in those that do, strings are not always made up of them (e.g. in Rust, char is 32 bits and strings are utf8).
I am writing an l-system evaluator, and have explained the concept to non-programmer friends of mine several times now. I've found myself explaining the concept of "strings" over and over, so I've switched to calling it "text" in those situations.
As a reasonably experienced programmer, however, this terminology makes me uncomfortable. To me, "text" implies a sequence of words written in a human language, rather than a sequence of arbitrary characters. It hints at structure which may not be present in a string.
- A Byte (AKA Word8) is 8 bits. It has no inherent meaning.
- A Character (AKA Char) is a Unicode code point.
- A ByteString is a sequence of Bytes. It has no inherent meaning.
- A Text (AKA String) is a sequence of Chars.
In particular, Char != Byte and String != ByteString; we can juggle bytes without having to care about encodings; and we can manipulate text without having to care about its representation.
We only apply that to strings though. float[] isn't a string of floats. And lots of languages have strings that aren't arrays of chars too, so I don't really think that's it either.
You do still hear people speaking of a string of bits, as well as a string of characters. I think the word "array" tends to be preferred for lists of numbers, since in its original sense in mathematics it was (and is) a synonym for "matrix". For lists of arbitrary objects I guess "array" won out by random chance, more or less.
RE: Debugging D in VS Code, this hasn't been my experience at all
> "Another challenge I’ve faced is the lack of a good debugging experience for VS Code on Windows. The C debugger for VS Code displays, for example, a string as a length and a C string (which might have trailing garbage). I’d rather not confuse them with this. I have read that Visual D has a better debugging experience, but I need dub support for these projects, and Visual D focuses on Visual Studio integration, something I don’t necessarily want to deal with in teaching these kids. "
You need to use the Microsoft C/C++ "cppvsdbg" option in your "launch.json" config as the "type", and also to use Webfreak's custom Natvis file for how to display D native types in the debugger:
This makes associative arrays display properly, and strings, etc.
I think this has been integrated in latest code-d. You want to compile with "-gc" flag though (not sure if this is the same flag name on DMD as it is on GDC/LDC).
The LLDB debugging is worse than the "cppvsdbg" mode IME.
Depend on what is your target audience. If they are beginner comp sci students then D is ok. Preferably something lispy like Racket. If they are more of "learn to code" and get high paying job, D is not recommended. Python, Java/Kotlin, R, C# or C/C++ would be better choices. If you are focusing on embedded students, C is a must and maybe Rust. If you are focusing on web development, then Javascript/Node is a must. Some people would argue that learning any languages is ok as you learn the concept. But this argument is the same as learning Japanese or Korean so you have "upper hand understanding" of Chinese. Why not just go straight learn Chinese than beating around the bush learning something "similar". There are concepts in C/C++ that are hard to replicate in C# or Python such as pointers and low level memory management. Or, data science community on Python platform that are scarce in others. Same with r for statistics, C# for .Net/Microsoft, Java/Kotlin for Android stuff. Most people have limited ability to learn 1 or 2 languages over their lifetime. Just focus on that most useful and accessible. Good one will always go on and create their own languages.
All I can say is they haven't had any trouble getting those jobs.
> are they well-paid because they are D experts, or is being a D expert merely correlated with other traits that explain their high pay?
Both. As related to me by their employers. D experience looks good on the resume. It strongly suggests that the person is not one of those do as little as possible people - that they actually like programming.
If you tell me you work in a dev role of some description, I assume you at least like programming somewhat.
If you then tell me you write Java or C#, that gives me very little additional information. They’re super mainstream languages used by whole armies of “drones” who have no particular passion for the craft, but there’s also some truly passionate people out there, and I don’t know which bucket you fall into.
If you tell me you write D, or Lisp, or Haskell, that’s different. You don’t end up writing any of those by accident, so you either sought them out deliberately or you saw an oddball language and weren’t scared off. That’s definitely strong signal for “actually likes programming”.
Please check the previous discussions on HN regarding seven deadly sins and seven desirable traits of introductory language design [1]. Not sure whether Walter read the paper before designing D because the reference paper is written before D was started, but interestingly D avoids most of the sins and does follow the recommendations. The paper, however, did not mention not having GC as one of the deadly sins but most of modern languages specifically designed for new learners namely ABC and Scratch have GC. The fact that D has GC by default make it very suitable for new introductory programming language (see the 7th recommendation for choosing the suitable level of abstraction).
[1]Seven Deadly Sins of Introductory Programming Language Design (1996) [pdf]:
75 comments
[ 2.8 ms ] story [ 147 ms ] threadFor me, first language should be strict and not too loose with the rules (so not python). Once you've got the rules down and understand why they are there / why certain languages use those constructs - build from there.
But take all of this with a grain of salt because I was a very distracted student more interested in working for startups during college than graduating with a nice GPA ;)
As someone who had a Clojure phase about 9 years ago: the actual problem with modern Lisps is that they don't really offer a big set of unique good features. The basic functional map-filter-reduce "meat grinder" is everywhere (and has been almost everywhere for a long time, Java not having lambdas for ages is what clouded everyone's vision into thinking that's not the case). The other "Lisp feature" is macro metaprogramming based on homoiconicity (Code Is Data™) and we have it in some non-Lisps too now (hello Elixir) and… is it really the best way to do metaprogramming? Ehh. Well, many compiled&typed languages actually do similar-ish things: using syn in Rust proc macros / haskell-src-exts in Template Haskell you can work with code "as data". And that isn't as nice to work with as D's `static foreach/if` + reflection/introspecion thingies like allMembers/getMember.
That said, if you want to talk about lisp you should really talk about Common Lisp. And there are a lot of solid criticisms of Common Lisp, but not enough features isn’t one I’ve heard before. The LOOP facility alone has an absurd number of features, to say nothing of the Meta Object Protocol.
It’s fair to say that a proficient Lisper can write performant code in any set of paradigms you like using a modern Common Lisp implementation. That comes with some considerable trade-offs though.
If people feel regularly compelled to redo the syntax, there's something wrong with it. "Many" means balkanization, which is another problem. (D has some carefully designed points which discourage balkanization.)
It goes back to too many parentheses. So I'm only half joking about it.
So yes, perfectly usable without OOP or classes.
(It's impractical to program in C without using the preprocessor, and the preprocessor requires use of a lot of very outdated programming practices.)
D can be used in "BetterC" mode, where it operates as what C could be with modern sensibilities.
Rndm_access, I know everyone is entitled to their opinion, but can I probe a bit on this?
Can you tell us about the largest code base you feel productive in? How about the largest code base you love?
How many lines of code are they? How many active developers do those code bases have (i.e. developers with weekly commits)?
Do you have trouble staffing new engineers?
Thanks for any reply given.
The largest code base I love and feel productive in is Linux kernel which is mostly C and assembly, medium sized project of 30 million lines, probably less than ten thousand active developers (although difficult to judge with the way development on certain features or functions can occur for years on private repositories before being merged), I would say relatively high rate of change for a project of the size.
OOP languages which do this automatically for you is AFAIKS just a thin and often clunky layer of syntactic sugar on top of it.
That sugar does help.
C allows adding another function pointer to that structure, but not set that pointer. C++ compiler forces programmers to implement all interface methods. Forget to override a method, the code which instantiates the class won’t compile complaining about not being able to instantiate an abstract class.
Code navigation is another thing. In visual studio, while the cursor is over an abstract method, F12 key looks up all implementations of the abstract class, and populates the “find symbol results” panel with a clickable list of the implementations of the method. With visual assist addon installed, Alt+G key does the same only presents the results in a popup menu instead of a separate panel.
Both things are borderline useless for small projects, but IMO they help a lot for medium to large ones, especially developed by multiple people.
I've never found it very compelling. As I said there are also downsides, inflexible implementation that is implementation specific (so it can be difficult to manipulate with low level assembly).
> C allows adding another function pointer to that structure, but not set that pointer. C++ compiler forces programmers to implement all interface methods. Forget to override a method, the code which instantiates the class won’t compile complaining about not being able to instantiate an abstract class.
Never found that particularly helpful if the code is structured well. You'd either allow for NULL implementations to be default or error not implemented at least until all subsystems are converted, or the initialization functions that all object allocations should call (because the code is well written) can verify all required fields are set. If you want to be even cleverer, you can probably do static initialization checks at least where your fields are constant and have those compile down to nothing just checked at compile time.
> Code navigation is another thing. In visual studio, while the cursor is over an abstract method, F12 key looks up all implementations of the abstract class, and populates the “find symbol results” panel with a clickable list of the implementations of the method. With visual assist addon installed, Alt+G key does the same only presents the results in a popup menu instead of a separate panel.
I can see how that might help a little, although surely with some minimal scripting a symbol browsing tool should be able to be taught about similar patterns like find all functions that are assigned to this particular member of a structure of function pointers. Although I don't use IDEs or any symbol tagging tools just grep usually, so maybe I'm a luddite.
With a nice code base that follows reasonable conventions and naming, it's pretty easy to find e.g., if you have a structure-of-function-pointers style of thing then you can find all definitions of "struct address_space_operations" or if a function pointer member is called page_mkwrite, then you search for *_page_mkwrite and get ext4_page_mkwrite, xfs_page_mkwrite, btrfs_page_mkwrite, etc. (which are not always strictly enforced in Linux but at least if you are searching for \.page_mkwrite you can usually easily see non-confirming names).
> Both things are borderline useless for small projects, but IMO they help a lot for medium to large ones, especially developed by multiple people.
I don't see that it helps a lot, and even as syntactic sugar I don't see it being a big advancement in the scheme of things.
IMO, all else being equal, errors detectable at compile-time should be detected at compile-time. Following reasons. (1) No matter the circumstances like time pressure, compiler errors are impossible to ignore, because they fail the build (2) Compiler errors are easier to detect automatically, e.g. many projects have automatic build systems. Even if the project has automatically run unit tests, people still need to write these tests first, i.e. the process is not completely automatic. (3) Runtime checks have runtime costs. Even if the checks are in static initializers or similar, that’s still runtime cost at startup and/or first use. Compile-time checks are free at runtime, makes the code faster.
> although surely with some minimal scripting a symbol browsing tool should be able to be taught about similar patterns
Technically, yes. Practically, two things. If you’re part of a team of developers, in particular if that team is remote, people are using their own computers, and people have varying levels of programming experience, such scripting tools are hard to implement. Another thing, the time spent making and supporting these tools is the time not spent improving the product being built.
I agree and you can do that with C. See compiletime_assert in Linux. An ops table definition or registration macro can do compile time checking that such fields are populated.
> Technically, yes. Practically, two things. If you’re part of a team of developers, in particular if that team is remote, people are using their own computers, and people have varying levels of programming experience, such scripting tools are hard to implement. Another thing, the time spent making and supporting these tools is the time not spent improving the product being built.
In all teams I've been part of including the loosely coupled and highly distributed open source side of kernel development, tools are widely shared and developed together. People don't just get left out in the wilderness. The bigger the project the more true this is.
I get that standard IDE niceness exists for some OOP language features, so it might be some small practical advantage, it just isn't an inherent advantage of the OOP paradigm. We don't need to use C++ to get nice rich and context aware editing search/display.
If programmers writes that check, yes. Same applies to a unit test. C++ compiler gonna verify that thing automatically, no extra work required, neither initially, not over the lifetime of the project.
> tools are widely shared and developed together
I prefer when a freshly cloned repository builds without any extra tools on a freshly installed computer, with either F7 in the correct version of Visual Studio, or something like cmake ../ && make in Linux shell, after installing the required dependencies from the official package repository of that Linux. In my experience, custom tools are often a pain in the long run.
> it just isn't an inherent advantage of the OOP paradigm
The paradigm is orthogonal to programming languages. I think OOP is merely a high-level design pattern where objects keeping their private state only accessible/modifiable by calling methods of these objects.
In this sense, the source code linked above in this thread is 100% OOP, despite plain C. Just like the majority of Linux APIs which operate on opaque handles: open/close/read/write for files, snd_pcm_* functions for ALSA, all these IOCTL requests for V4L2, various handles for Vulkan. All these APIs are implementing an abstraction over external devices with very complicated internal mutable state, OOP is pretty much the only way to go for these things.
Yes. Programmers have to write these checks, that's all about what a well structured and maintained codebase is about. They have to write many checks no matter what the language, because missing initializers is one tiny little aspect of things you might want to check for.
> I prefer when a freshly cloned repository builds without any extra tools on a freshly installed computer, with either F7 in the correct version of Visual Studio, or something like cmake ../ && make in Linux shell, after installing the required dependencies from the official package repository of that Linux. In my experience, custom tools are often a pain in the long run.
I didn't suggest otherwise, I was talking about editing and searching scripts and commands.
> The paradigm is orthogonal to programming languages.
Right, C can do it. As I said in the beginning, I don't like the OOP features of C++ because they're clunky inflexible and hardly any benefit in terms of easier syntax.
> I think OOP is merely a high-level design pattern where objects keeping their private state only accessible/modifiable by calling methods of these objects.
No "OOP" is definitely considered to be language features too.
Well, "is" is quite the load-bearing word in those claims. I think it is kinda true in the sense that lots of things can be represented as OOP. A great example is how Scala represents ML-style ADTs as sealed class hierarchies.
:D
If you pick something like python, any common beginner problem should have questions answered on stackoverflow, blog posts, etc. With a more boutique language that's not the case. Not to mention the ecosystems are much larger so chances are a library already exists to do something instead of making the beginner do it themselves and overloading them. There will be more tutorials, so people can get unstuck. More documentation around the tooling and just documentation in general exists, and so forth.
---
Of course, you should teach "what you know best", but how hard is it to learn e.g. python (even just for the scope of the course) and just teach that? My favorite languages are Rust and Go, but I'm not going to foist those upon newbies just because I'm more familiar with those than javascript, python, ruby, etc.
The secondary lesson here is that teaching (well) is actually quite difficult. Coming up with a curriculum is hard. Keeping students engaged and unstuck is hard. Knowing how to program doesn't prepare you for any of that.
2. D supports many styles of programming - imperative, meta, OOP, functional, machine code - so one can quickly learn these many styles without having to learn another language. With D's inline assembler, one can even use D to learn assembler without needing to deal with all the boilerplate of setting up an assembler program.
3. Interpreted languages, for example, do a good job of hiding what is actually going on. This leaves gaps in a programmer's knowledge.
4. D has a active and large learn forum. Help is available as needed.
5. Being a D programmer marks one as innovative and willing to try new things. Many employers consider D experience on the resume as a nice plus.
6. D includes a C compiler (ImportC) so if you must use a solution in C, it's easy to access.
1. People learn more than 1 language. What I'm referring to here is complete beginners who need to learn -how- to program. Once they grok that, it's not difficult to move to other languages, obviously with some exceptions.
2. It's not the job of a -language- to teach everything about programming or networking or computers or various protocols. That's the job of a curriculum.
3. There are way more $major_lang jobs than jobs for D, Clojure, Lisp, Crystal, or any of the other languages that are significantly less used. The sky is the limit for how many languages people want to play around with and learn, once they know enough to start learning other languages by themselves. No recruiter is gonna be like "oh, you learned $major_lang... Even though you likely applied to this role because our stack involves $major_lang and that's a language that you know well..."
4. Help, resources, meetups etc for $major_lang are orders of magnitude more plentiful than for D, etc. Imagine telling a student "no, no, you need to prove yourself by making forum posts for <niche language that I personally like and use> instead of having stackoverflow questions or tutorials to follow" in the case of getting stuck. Of course students should strive to do as much as they can themselves, but you would be surprised to see what beginners get stuck on and just need a gentle push to get going again.
5. Being able to be independent is more engaging than having to constantly pester a teacher or a forum. If a student wants to be a go-getter with e.g. python or lua, there are communities and resources to build games or websites, etc. And those already have had common stumbling block questions answered because they have large communities around them.
For the goal of teaching someone how to program, any language other than the major ones like python, javascript, ruby, c++ strictly make it more difficult for students because of the smaller ecosystem, lesser amount of documentation, and smaller community in general. I have nothing against D or other niche languages, but I'm against picking languages just because I'm familiar with them.
It can be difficult for experienced programmers & power users to remember that there's a sheer amount of information that needs to be learned in. There is no reason to make that more difficult for students.
It's easier to transition from D to C++, or D to Python, than to transition from C++ to Python or vice versa.
You're right, it's not the job of a language to teach everything. But since D supports multi-paradigm, it's easier to use D to teach it.
you don't have the data, and you invented the language.
> Everything needed is available, for free.
This is a moot point. I struggle to think of another language for which this is not true.
> But since D supports multi-paradigm, it's easier to use D to teach it.
You've confused learning to program and learning a paradigm with learning a language. They are not the same thing.
For one, multi-paradigm languages hinder comprehension for newcomers as there are too many ways to do things. It's too easy and convenient to be impure with respect to a paradigm. Multi-paradigm languages are mostly useful after you understand the individual paradigms well, but you will surely disagree with me here. We're all entitled to be wrong from time to time :)
I respect you and your work but you're missing the forest for the trees here.
When I've taught people to program, the simplest environments are the most effective. Basic, C, Lua, Processing, Lisp, etc. By comparison, C++ and Python are full of difficult-to-grasp footguns, I'd strongly avoid them (or comparisons to them) in this day and age.
Keep in mind that I created D because I wanted to program in it. It is based on my decades of experience with other languages and supporting those languages and knowing where people go wrong with them and where the problems are.
> C
C is a simple language, but grasping how it works is not simple. To get C you need to understand assembler, which is not simple at all. The misunderstandings novices have about pointers, for example, are misunderstandings about how computers work.
C is loaded with legacy problems, too, like uninitialized variables containing garbage. There's all that confusion between prototypes and non-prototypes. Implicit coercion of 2 to a pointer:
The C preprocessor is nowhere near as simple as it appears. Arrays are determined to decay to uncontrolled pointers. Buffer overflows are pervasive, even among the most professional programs.D (as Better C) is a very C-like language, but minus those problems. It's reasonable to say it is much more easily learned. It has far fewer footguns.
> You've confused
I may have been unclear, but I am not confused.
I've written two C compilers from scratch, and 3 C preprocessors. I know every last dirty detail of C.
Basic, however, we can agree is a simple language. But it teaches bad habits, which have to be unlearned.
> But it teaches bad habits, which have to be unlearned.
Apologies, but you're confused again: you've conflated learning how to code, with learning to be a software engineer. They're related, but less so than you think.
In my experience, it is impossible to teach a student a lesson that requires experience without providing them the opportunity to experience it. Teach someone Basic, let them do bad things, then show them the light.
"If you give a man a fish" and all that.
When I see `int *p = 2;`, "elegant" is not at all what comes to mind. "Bug" is what comes to mind.
Reminds me of my wood shop teacher. He showed me the right way to use hand tools, from the start, which pays off every time since that I use one. He also showed how to use power tools, and never failed to show us the missing finger on his hand - the price for taking shortcuts.
Whenever I use the table saw, I think of his finger stub and slow down, take my time, and do it right.
I might not pick D as a beginner language simply because it unfortunately isn't very popular, but I certainly wouldn't pick C as a beginner language.
It's too bad it didn't make its way into embedded programming where we still have to use C or C++ or dumbed down high level languages, even if it was just for the BetterC part. D-style OOP would be nice as well, though. Maybe LDC and GDC would change that by targeting more platforms and ARM eating the world.
Thank you for creating the language.
A novice stands no chance to figure out anything with this.
Same result for javascript: https://stackoverflow.com/questions/8290710/
And python: https://stackoverflow.com/questions/50510291/ (this one got downvoted, awesome experience for a novice, some people argue abusive nature of stackoverflow is intended so that people learn hard all its ins and outs before touching it or get abused)
Though I guess the classic workaround for that is to carefully construct the course such that these details are revealed gradually, without being overwhelming.
While this is not a crazy argument to make, it applies to people teaching themselves to program. If you're teaching someone else how to program, you're very carefully introducing new ideas, you're teaching them what they need to know, and you're answering the questions when they get stuck.
You are referring more to university classes that "teach" programming. That's a little different from this, where the author was actually teaching.
One's experience of adopting to new hardships and challenges can either be an uphill battle or downhill. You start with a hard language with manual memory management and small community you are forced to figure things out on your own, learn by helping others and contributing libraries. Then you can appreciate easier languages and learning them would not be as steep of a curve compared to starting off easy.
This was exacerbated by GML (the Game Maker Language) being the first real programming language I learned and used a lot. Idiosyncratic to say the least, but taught me "OOP" in terms of how scripts attach to objects to define behaviour. Fascinating stuff. Game Maker 4 is the reason I'm a programmer two decades on!
It really doesn't make sense at first sight, and this is something I probably would not have realized until someone asked me "Why do they call it a 'string'?"
Fun read. D rocks.
As a reasonably experienced programmer, however, this terminology makes me uncomfortable. To me, "text" implies a sequence of words written in a human language, rather than a sequence of arbitrary characters. It hints at structure which may not be present in a string.
- A Byte (AKA Word8) is 8 bits. It has no inherent meaning.
- A Character (AKA Char) is a Unicode code point.
- A ByteString is a sequence of Bytes. It has no inherent meaning.
- A Text (AKA String) is a sequence of Chars.
In particular, Char != Byte and String != ByteString; we can juggle bytes without having to care about encodings; and we can manipulate text without having to care about its representation.
Where does string come from. How odd.
https://code.visualstudio.com/docs/cpp/launch-json-reference...
https://github.com/Pure-D/dlang-debug#natvis-with-vsdbg
This makes associative arrays display properly, and strings, etc.
I think this has been integrated in latest code-d. You want to compile with "-gc" flag though (not sure if this is the same flag name on DMD as it is on GDC/LDC).
The LLDB debugging is worse than the "cppvsdbg" mode IME.
Also: are they well-paid because they are D experts, or is being a D expert merely correlated with other traits that explain their high pay?
All I can say is they haven't had any trouble getting those jobs.
> are they well-paid because they are D experts, or is being a D expert merely correlated with other traits that explain their high pay?
Both. As related to me by their employers. D experience looks good on the resume. It strongly suggests that the person is not one of those do as little as possible people - that they actually like programming.
That has nothing to do with $LANGUAGE.
People who know languages to write CRUD may have different professional preferences to people who know Lisp.
If we are okay with stereotyping programming languages, is it not fair to stereotype people who are fluent in said programming languages?
If you then tell me you write Java or C#, that gives me very little additional information. They’re super mainstream languages used by whole armies of “drones” who have no particular passion for the craft, but there’s also some truly passionate people out there, and I don’t know which bucket you fall into.
If you tell me you write D, or Lisp, or Haskell, that’s different. You don’t end up writing any of those by accident, so you either sought them out deliberately or you saw an oddball language and weren’t scared off. That’s definitely strong signal for “actually likes programming”.
There, FTFY.
[1]Seven Deadly Sins of Introductory Programming Language Design (1996) [pdf]:
https://news.ycombinator.com/item?id=28859449