It's not what programming languages do, it's what they shepherd you to (2020)
nibblestew.blogspot.com
nibblestew.blogspot.com
Here goes my morning
Third-party frameworks are often used to establish one's self as a leader and establish abusive practices. Once the foothold is firm, it becomes literally "The framework made me do it." Cultural norms mandate the framework, and the framework forces the abuse. If anyone protests about the consequences of abuse, blame is redirected back at the language.
To any onlooker, the consensus seems clear: Your language sucks! Those same onlookers are happy to reinforce this perception because they are busy trying to establish competing cultures & leaders of their own. "You should try my language!" Of course their primary goal is not to help, but to improve their own status by rising the tide and lifting their own boat. If they wanted to really help, they would work on establishing better culture instead of jockeying for popular culture.
Recently I've been frustrated with the Python ecosystem and how many "object oriented" libraries there are which can't easily be tested because of all of the weird shit their constructors do (most recently, the class I'd like to test just builds JSON documents and sends them to an API, but the constructor hard-codes the HTTP client so you have to use magic to swap the hard-coded client with a mock).
Of course, someone will come riding in to defend OOP with "that's not true OOP" and another will come riding in to defend Python with "you can write bad code in any language!", both of which miss the point: this sort of thing is much more prevalent in the Python ecosystem than, say, the Go ecosystem or the Rust ecosystem precisely due to culture. And by the way, I'm sure there are lots of other languages that have a culture of poor programming practices--the intent here isn't to pick on Python (I've used it professionally for 15 years).
https://github.com/jupyterhub/kubespawner/blob/81e6059a14811...
^ I'm also curious to hear from people whether this kind of design is "OOP" or not, or if there are any good terms for talking about this particular antipattern.
That said, I would not consider default mock values in a constructor to be good OOP. In general, "new"ing things in a constructor IMO is a no-go and makes for harder testing and confusing behavior.
At the end of the day I'm not even sure why this class supports mock behavior. I'd expect two separate classes with divorced implementations, instead of an if statement on a boolean param.
The problem with Python and Ruby is that being dynamic it makes them real easy to do highly lightweight OOP. There's just a cultural disconnect in the OO world where once you get past objects wrapping data the design patterns tend to come from the Java world and quickly descend into explaining dependency injection containers that nobody uses in Python or Ruby. There's a bit of a cultural middle ground which is not well defined due to the history of how it all evolved.
Maybe a "shout-out" for Object Pascal in this regard too, as they have advanced records (structs with methods) and Free Pascal has simpler objects (but Delphi's dialect doesn't). Though I will say, Object Pascal's implementation of class-based OOP is a gentler version, versus the "harsher" implementations in other languages. Still, as with all the class-based OOP languages that I've seen, you are going to get forced feeding and forced to use plenty of OOP libraries.
Forcing or pushing class-based OOP is debatably how things can more easily and unnecessarily go bananas. Complexity traded for simplicity. Users are usually trapped in the paradigm, until they like it (programming version of Stockholm syndrome?), and are often scared to go outside of it or think the world might end if they do.
They are different phenomena, but every practical consequence is the same. Including what you must do to avoid the problems.
- LISP dialect devs insisting on using incredibly obscure technical abbreviations for important names, like car/cadr/cdr instead of first/second/rest or something else human readable.
- POSIX-ish shell standardizers and builders making it really difficult to deal with anything but ASCII alphanumeric filenames. Saying "You shouldn't put special characters in filenames!" isn't helpful if I'm trying to make a script to support other people's filenames.
- C-style language makers' insistence on terminating statements with semicolon, so that a tiny fraction of multi-line statements can be shortened to one line.
- Python devs treating time-zone-less datetimes as not an atrocity, for example in utcnow.
- Nix, well, being generally incredibly hard to read and learn, even after about 20 other languages. I hope somebody goes ham on that language and creates a thoroughly backwards incompatible implementation of the fantastic ideas behind it.
I guess I've pissed off the entire internet now, but remember that these are my gripes, not some absolute truths I'm trying to convince y'all of.
Line wrapping would be unacceptable without this.
Person A: "Programming language X is bad, code written in it is unreadable and horrible."
Person B: "No it's not. You can write good code in X, you just have to be disciplined."
What really ticks me off about those kinds of people is that they are extremely likely to just have been blind about their own mistakes when coding C, while loudly claiming that "you just need to be disciplined".
This is the most baffling part for me. I'm yet to figure out who these mythical non-idiots are that would not have fallen for any of the pitfalls that have led to exploits in the last several decades.
Transformation according to logical invariants is basically the goal of compilation. Eschewing compilation creates a treadmill whereby a programmer thinks they're getting a lot done because they're mechanistically spitting out a large volume of code, but really they're acting as an imperfect compiler.
See also the large crowd of people writing JVM assembly (aka "Java"), as opposed to using higher level languages like Clojure or Scala. Yes JVM is leaps and bounds ahead of C, just like C is leaps and bounds ahead of machine assembler. But unless you're often writing things that existing compilers cannot, then personally standing in for a compiler is mostly just a way of introducing bugs.
Take nuclear reactors as an example. Some people seem to legitimately think that its possible to eliminate all sources of errors in such an immensely complex system. If there's been an accident, the analysis is generally "we have to add sections in the manual for this specific scenario and we'll be safe", rather than accepting that it's impossible to prevent all errors and that it's merely a question of time before the next anomaly arises.
When building complex things anomalies will happen which means we should follow good engineering practices and add extra safety systems.
In programming, and especially systems programming, people love to pretend they are infallible, to the detriment of everyone suffering from permanent firmware bugs and critical security infrastructure leaking their secrets.
That mostly describes amateurs. Designers of modern reactors generally think along the "let us minimize possible negative effects of a failure" lines.
So, no positive void coefficient (a horrible feature of RBMK in Chernobyl), addition of core catchers [0] etc.
1. Physical
2. Social
On the physical side they try to establish controls that prevent or mitigate issues or minimize the consequences of them. For instance, a modern reactor (probably, not my domain) is going to be designed in a way that certain meltdown situations can't occur (physically designed to separate reactor materials at certain thresholds, resulting in an automatic shutdown versus manual) or are properly contained (smaller reactors where a runaway reaction is more easily contained with modern materials at a reasonable cost).
On the social side there is training, regulatory controls, and an emphasis on safety culture/discipline. Discipline doesn't scale, but you can't totally avoid having it. This would be things like not turning off an alarm because it disrupts your nap (looking at you, Homer J. Simpson), but actually responding to it to determine its legitimacy (related concept: normalization of deviance). They'd also want to look at it from an HCI perspective and minimize false alarms and overwhelming operators with alarms or information that seem to be at the same level of importance, but aren't. So true critical issues are brought to the front as high priority, but less critical issues (ones that should be addressed but are lower priority) won't overwhelm the operator. (I've worked with others on this wrt avionics systems, alarm fatigue is real so you have to be choosy in which things get audible alarms and which get visual alarms.)
> If there's been an accident, the analysis is generally "we have to add sections in the manual for this specific scenario and we'll be safe",
In my experience, that's a belief held by the prideful and the novices.
> rather than accepting that it's impossible to prevent all errors and that it's merely a question of time before the next anomaly arises.
Anyone with a modicum of humility or experience will accept this and act accordingly.
Why don't these ancient projects just move to C++ for vectors and strings, that do the buffer handling correctly for you? Surely they can run a C++ compiler on any workstation these days?
C++ runtime is hardly huge by modern standards, and the usual resolution to ABI issues is to provide C bindings.
> Careless use of C++ -- Lack of judgement --
C doesn't make you immune to any of this. What are buffer overruns if not careless use of C?
Edit: Considering that I suggested moving to C++ for vectors and strings, your bringing up of TMP and polymorphism sounds like a strawman argument.
Maybe, but it seems unreasonable to hold the world back because of those systems.
> When it comes to bad judgement, C++ gives a lot more rope and especially rope that's C developers are not familiar with.
I think one could just ban most of that rope to junior devs. "Don't try to roll your own classes or templates, for getting them right requires both skill and discipline." Also common guidelines ban rope that isn't needed but for implementing the superior alternatives, that are usually found in the standard library.
You can still program in a procedural way and use just the low hanging fruits of the STL, that was designed for correctness above all, for Alex Stepanov made it to demonstrate his ideas of generic programming: C++ just happened to make implementing it possible.
Mind you, I am of the firm belief that C is at fault for many of the security vulnerabilities, but this is not one of them.
You shouldn't. They're partly right.
You are correct that "just be 100% disciplined 100% of the time" will fail. But "just get a language/compiler smart enough that I don't need discipline" will also fail.
Speaking from the point of view of a senior dev however, certain practices become ultra tedious and annoying from one point and on. Literal soul crushers.
Using design systems (languages) that eliminate entire classes of mistakes, because they make them impossible is the rational choice. It is human to make mistakes, but how we learn from the past to prevent mistakes in the future is where you can tell between engineers who truly care and those who are in it for their own ego.
Additionally, I really want us to stop pretending we can program CPUs directly (and I include the higher-level languages here as well, not just machine code and assembly). We can't. We need more declarative languages, e.g. I want to be able to say "I want to open a huge file with scatter-gather mechanics and with these 3 functions piped that do mapping and reduction". Why should we always code this by hand? There are objective best ways to produce this code in most languages. Let's have a DSL library that allows us to express that intent and be done with it and move on to be productive on another task.
This is not done enough to this day. Various people and organizations are doing it here and there and to me they are the beacons of our future hope. Everybody else is just banging at their Emacs / VIM in the meantime.
IME C++ templates and generic programming help with that aim.
Who are these people and organizations?
- I am writing the thing you are asking someone to write
- I have performance/memory constraints that existing high-level abstractions can’t adequately deal with
- I have legal or other business constraints preventing me from using existing high level tools (eg company prefers to build in house to minimize dependency risk exposure)
- I want to learn how that stuff works
- I am working on new technology that does not fit cleanly into the prescribed mold of the high level implementationsExample: in Elixir you have the so-called streaming functions you can pipe to each other that can do e.g. filtering, mapping, chunking and what not. But they don't actually operate on the data you passed them; they generate further functions and produce chains that only start working when you put a certain command at the end of the command pipe.
This works really well if you work with a collection of 100_000 elements. Since Elixir is an FP language, you don't want to copy this huge list, say, 7 times one after another (assuming your transformation pipe has 7 stages). By using the streaming functions you only use the original list and produce a final list. Just one roundtrip to the memory.
But... it has a performance impact and it's definitely not worth doing if your collections are e.g. less than 1000 elements, maybe even 2000.
A declarative DSL can easily cover for such cases and have separate code branches addressing them.
RE: tinkering, obviously. I am not saying normal programming should be "banned". But it's about time we have higher-order programming, man. We're all collectively slacking off on that and should be really ashamed!
The rest of your points I don't see how to automate and that's OK. Like any tool, declarative programming should be used sparingly and only where it truly applies.
However data races (a small but important subset of race conditions in concurrent software) are preventable, and so you can use languages which do so. Unlike the cat race condition I described above, data races are often unfathomable in real systems, the ultimate cause of truly mysterious and hard to reproduce bugs. So they're very much worth preventing.
Anyway, while it is true that "unbounded complexity" is potentially necessary, tools capable of such power should not be the default. When I spread jam on toast, I don't use the butcher's cleaver, I use a butter knife that's too blunt to cut flesh let alone bone. Which means, if I screw up the worst case is maybe I get jam on the carpet instead of the toast, not I cut my own hand off.
That difference in tooling can exist both within a language, and also between languages. There are many programs you cannot write in WUFFS, whereas there are (almost) no programs you can't write in C++. But that actually means we should use WUFFS when we can, because we don't want those programs. The image viewer where PNG files with long rectangular orange areas cause the hard disk to get reformatted, easily possible in C++ and not WUFFS. We don't want that program. The icon maker where a JPEG text comment with a shell script in it executes the script, easily possible in C++, not in WUFFS. Don't want that either.
I personally haven't seen them simply anything yet, but your assumption that verifying things with a dependent type would necessarily result in more complexity than a non-verified program is not trivial and needs support.
But I do agree that it seems that people jump to use async instead of e.g. threads. I think there should be more literature around what situations work best for threads vs async, etc.
Then again, few programs are actually single-tasked in practice, and libraries often end up taking advantage of those async primitives for doing stuff like connection pooling even if your application code doesn't see it.
thing1_promise = get_thing(1)
thing2_promise = get_thing(2)
thing1 = thing1_promise.await
thing2 = thing1_promise.await
is almost always more difficult to do with threads. Sure, some threading APIs support joining nicely but it tends to be more awkward. Especially if you want to do something like handle the first request to finish first.Also the startup cost of threads affects how you write code. Does your library spawn a new thread for every HTTP request? Probably not, it is expensive and the server may be on the same host. So you end up with less consistent async support. Since async programming is a lot cheaper you tend to see better support which adds up.
Thus you only pay for it if it is required.
Minimal-cost threading support requires taking Send + Sync into account, essentially thread-safety: and the thread-safe primitives are more expensive than the thread-local primitives. Send + Sync matters become pervasive through the design of threaded code, even if they don’t need to be spelled out most of the time.
Minimal-cost async requires compiling the code into a state machine, which means you’ve got to restrict borrowing across suspend points, or mess around with Pin.
Threading and async are quite different in a language like Rust. They can even play quite nicely together. But you really can’t unify them.
Sure, you could do simple cases like single-threaded blocking-or-async, e.g. `async? fn foo() { bar().await; }` compiling to something like strawman `fn sync#foo() { sync#bar(); } async fn async#foo() { async#bar().await; }`, but this falls apart completely as soon as you want to do anything beyond immediate .await.
I guess I'm glad that Rust developers don't design CPUs, as we wouldn't have memory abstractions like cache coherent NUMA (which doesn't come for free).
Case in point: a CPU plus a discrete GPU is technically a NUMA system, and low-level APIs like Vulkan explicitly expose the ability to allocate non-coherent memory. I strongly assume that accessing that memory doesn't incur coherency overhead.
What are you even talking about? Rust gives you plenty of choice, it even lets you create as many "non-free" abstractions as you'd like.
As another example: compare "GC'd versus non-GC'd memory in Rust" to "coherent versus non-coherent memory in CPU architecture"; here Rust gives us just the bare machine, while CPU architects give us quite some abstraction plus a choice.
It is true that you could theoretically write a GC in Rust, but using it from the language itself would probably break ergonomics so much that you'd be better off with another language.
The only way to run code in another Thread is by spawning an `Isolate`. But once you have one, you can expose an API that is indistinguishable from an usual async one.
And the Dart 2.15 announcement (latest version as of today), which had a focus on making Isolates fast: https://medium.com/dartlang/dart-2-15-7e7a598e508a
EDIT: the main article about concurrency in Dart is actually this one: https://dart.dev/guides/language/concurrency
Try `dart compile exe app.dart` and then run `./app.exe`... it's really fast and the binary is fairly small!
The installation process is simple, documentation is excellent, the core library makes sense, the defacto package repository and package management is clean. And WRT this thread, the async model is dead simple to pick up.
As a scripting language it seems to be a perfect fit. But there is also a lot of first rate tooling available for Dart and I am starting to wonder if it wouldn’t be a good choice for larger projects as well.
Function is blocking and you want to use it in an async context without blocking the async worker thread? Use your runtime's equivalent of `spawn_blocking(|| sync_fn()).await` that will run the function call in a threadpool dedicated for blocking tasks.
(Both those examples are for tokio.)
There are no extra threads in the former case.
>(and in the second case, potentially undermine the whole point of using async at all)
If code is blocking for long periods of time, it shouldn't be running on the async worker thread in the first place. Marking such a function `async` would be wrong. If I write a tight loop that takes seconds to do its job, putting an `async` on the function is worse than not having it. Spawning such work in a different thread is correct. There's no "whole point of using async" that is being "undermined".
Right, you need to go right down and e.g. use a non-blocking I/O primitive instead of a blocking I/O primitive. Rust gives you no way to polymorphically do one or the other.
The examples they give to use the API using async, I really don't understand, and I can only assume I'm missing something: the example steps of going to a URL, finding a resultant element, entering text, then clicking the search button, then finding another element and then capturing a .png snapshot image for the result are in my mind all sequential, and depend on the previous step: I really struggle to understand how async helps in this case (at least the basic one), as I don't see how any of those steps can be parallelised.
I don't know anything about webdriver's API specifically, but as a general example, "do something else while waiting for a response" can include "stop waiting for a response after a timeout expires". Maybe you want to set a timeout on the "fetch a URL" function call. You can't do that with a synchronous function unless the function internally supports a timeout. With an asynchronous function you can just race it with a timeout future, and if the timeout future completes first you can stop waiting for the fetch future and fail your test.
Classic WebDriver, when used to control a single browser is indeed a synchronous API. So for example the server-side WebDriver implementation used in geckodriver is fully synchonous; it accepts a HTTP request, and does a bunch of blocking work to execute that command in Firefox. But there are a couple of reasons you might want an async client, even though the browser side is synchronous:
* You might want to drive multiple browsers at the same time. For example when running multiple tests in parallel you would have multiple concurrent requests going to different browser instances. Obviously you could implement that as multiple threads/processes/etc. but if async has the ergonomics you're looking for it is certainly a viable option.
* Modern extensions to WebDriver don't follow the command/response model. Many clients have additional functionality that's not implemented on top of WebDriver, but using browsers' built in debugging protocol. Even Selenium these days uses the Chrome Debug Protocol for some features (e.g. access to logs). Those protocols are event based and so not suitable for a fully blocking API. Of course one could still design something that doesn't require async for those use cases, but I think it's generally believed that async offers the best APIs in languages that support it.
Async/await as concept taints the call stack all the way up to main and is unmanageble on existing code.
While working on C++/WinRT I am seeing the same problematic showing up, so no wonder that Rust ecosystem is now facing the same issues.
A rendering library I use added an 'async' framework, but it turns out using it is not going to be mandatory. Much relieved.
Also works better than callbacks for single threaded/event loop scenarios like JS or some GUI framework (with the caveat that you need to make sure you are resuming on the event loop if environment is multithreaded).
Writing in Rust takes more engineering effort and is less agile. You can code yourself into a corner which requires considerable rewriting, or unsafe code, to escape.
What I'm writing in Rust is a metaverse client. These are not common yet. They have both the real-time problems of a game client and the unexpected content-driven loads of a web browser. The goal is to keep the frame rate constant as a stream of update transactions comes in, perhaps hundreds per second. The transactions modify the scene graph and change what's on screen. So refresh is in one high-priority thread, while updating is in other low priority threads. Overload bursts happen routinely and have to be dealt with, updating near and large things first. In MMOs, such overloads are usually detected and prevented during game design, but a metaverse viewer does not have that luxury. Existing clients tend to choke and stall on overload, but with enough CPUs working on the problem, that can be overcome.
It's a fun problem. It doesn't have the shape of most common applications.
But typical web stuff: Go or even Java would seem to be a much better choice than Rust, for the same reasons I would not write a typical web service in C or C++.
Depends on whether you are talking about a single machine or a distributed program.
it pollutes every function with async annotation.
it allows you to quickly write the code imperatively by just annotating a function and using the async function inside it.
it results in code that is massively polluted with async when at most places you never had to use it at all (if you just restructured the code).
having async be as verbose as possible would enforce people to not use it as much and to structure their code to have async bits nicely separated from non-async bits.
Whatever the reason is for using async, I believe that the mismatch happens not because Async is optimized for I/O bound programs but because it's insanely easy to use (in Rust, Javascript).
That's 'front end and back-end' ... probably the bulk of software right there.
A bunch of threads running 'compute bound tasks' is probably a special case.
Everyone understands that the code is horrible and you are regarded as a hero for diving in but honestly most of it is just straightforward procedural code and you can be productive in minutes.
Now those projects that went all crazy on OO? Yeah, I would rather burn it all to the ground.
Though I would say, all in all the PSRs did also lots of good for PHP. It makes navigating modern projects quite a bit easier.
The comment could have just as easily been:
I've worked on legacy PHP code. Most of it is just straightforward OOP code.
Now those projects that went all crazy on procedural code? Yeah, I would rather burn it all to the ground.
I know, because that's my experience. The issue is not particularly OOP vs procedural, but rather how crazy it is.
However in the PHP world, I would question those wanting to write in a procedural style, as all the mature libraries and testing tools are built with OOP in mind.
Obviously you wouldn't write modern software like this but maintaining those old systems is a hell lot easier then those where people went nuts on OO and inheritance was used more lightly. Bad abstraction are worse that lack of abstractions, basically.
Either work on a modern project that has been build in recent years or if you have to maintain things, better go with something really, really old. Those in-between projects that don't seem to be totally hopeless but are still rotten, yeah that is there most pain lies.
Sure it depends depends on the specific project, everything can be abused but that is a totally different discussion.
I have found creating test seams in legacy OO code easier than starting from scratch with an untested 5000 line procedural file.
In that case, having some abstractions was better than having none.
But that's just my opinion.
But yeah good point with the testing. I should have specified that I am talking about agency work, not actually caring about the software you keep alive.
It gave people a recognisable syntax and structure, to run things on PHP's shared-nothing architecture, which is arguably its real strength.
Similarly it was a great move for Symfony to take inspiration from Spring. All the older PHP projects like Drupal have slowly been ported over to using it.
I have never met anyone who seriously wants to go back to PHP 4.
Function autoloading, without it you need to come up with a reliable way to require dependencies. This usually differs from project to project in a non-reliable way and will be messy if you don’t think this thru. It will also affect things like testing.
Only global state or (closed) function state. Namespaces are fake in PHP, it only adds the namespace name to the class name or function name, you can’t store anything inside a namespace like in C++. Thus everything is global, which is horrible and bug prone.
You can use static inside a function to keep state as an alternative, but that is hard to manipulate from the outside when needed. It can be done with a separate function that keeps track on other functions state. When I tested modern pure procedural that is what I did.
No typedef for function signatures. If you want to create a contract enforced by the compiler you use interfaces in OOP, in procedural you want to define function signatures instead, but there is no reliable way to do that in PHP without classes (ignoring any external tooling that uses phpdoc)
Testing. Have you loaded a function into the runtime you can’t unload it again, thus mocking a function you already tested becomes impossible. You can solve this by running every test unit in a separate PHP process, but that is expensive.
These are the biggest and none of them will probably never be fixed, perhaps function typedefs has the biggest chance but I doubt it.
If you are writing a large project (+500k lines) all of these are very important. That is why OOP PHP rules because it can handle all of this.
The problem with OOP is that many of the legacy OOP projects inherited the inheritance based OOP from Java, however today we know that composition based OOP is better (both in Java and PHP).
use xml;
foreach (xml::select(<STDIN>, "item[color=orange]")) {
$_->setattr("color", "red")
}
print(xml::result)
But just install XML::Parser you could say! Well, look here: [123] and tell how to do the above from it quickly. You can't. You still want regexp-like selector thing and Perl semantics, not just a deep hash of hashes of arrays of hashes which loses Perl's appeal instantly.Better languages have better tools to work with out of box, and these tools match the language spirit. There is no XML Parser for Perl on CPAN, they all are for some other language that Perl isn't.
[1] https://metacpan.org/pod/XML::Parser
[2] https://stackoverflow.com/questions/487213/whats-the-best-xm...
I would also count that as "shephearding" or "pushing into a direction" or however you want to call it.
I'm not necessarily agreeing with the whole post, but I believe it even matters how easy it is to integrate libraries in general. Some programming languages make that easier than others and also require it more than others (by having a smaller stdlib).
And that definitely changes the way people think and act within the whole ecosystem.
use XML::Twig;
my $twig = XML::Twig->new(
twig_handlers => {
'items/item[@color="orange"]' => sub {
$_->set_att(color=>"red")
}
}
)->parse(*STDIN);
$twig->print;But for me it's still hard to continue the search after those two, when you have s///, and stackoverflow is few years in the future. I believe that most Perl programmers felt similar.
Well, that's what meant with "shepherding" though. It's not like a language forces you to use a particular feature or only offers that way of doing things.
It's about a language encouraging such a use, including by making it very easy (and, in Perl, first class) - and it's also the culture (and existing codebases) of the language going that way (which, for Perl, is also the case).
Bullshit. Any reason for writing such flamebait? On /. you would have been voted into oblivion for that.
use XML::LibXML;
my $dom = XML::LibXML->load_xml(IO => STDIN);
$_->setAttribute(color => 'red')
for $dom->findnodes('//item[@color="orange"]');
print $dom->toString;Look at the doc again, where is your snippet there? Down 2 levels of links probably. You didn't write this right the first time you opened it in your browser. Where is ->findnodes() reference? I skimmed ::DOM, ::Document, ::Element to no avail. Even now my patience is over, while e.g. jQuery made it obvious "$(sel).attr(…)" even though I never really used it nor read its docs.
When I said bullshit, I meant to express that you are talking nonsense regarding the quoted part. I proved that it is nonsense by producing the desired code. You don't get to redefine the word bullshit how it suits you after the fact.
You previously wrote that "you [ed: that is, the other HN readers] can't" "tell how to do the above from [the documentation] quickly". But now it turns out that you meant to express that "I [that is, wruza] can't" use the documentation in the specified way. That's a completely different idea.
> Perl, google, CPAN documentation […] gave no clear reason to not s/// and go deeper.
This is the same abdication of personal responsibility as a programmer as shown in the article. I won't stand for it. The premise of "shepherding" as described is plain wrong: a programming language or its documentation can't make one do anything. It is not a living being with will and intentionality, it is just a thing. Anthropomorphising a thing is not a rational way of looking at the world, but belongs into the realm of magical thinking because this idea allows one to say "a thing made me do something" when the reality is that someone chose to react to the thing in the way he did, and the responsibility for doing so falls on him alone.
The true meaning of "shepherding" involves another human being. I do not rely just on Web search engines and documentation (i.e. technical means) as I have a healthy amount of scepticism about my own ability, so I can take a moment to talk to another programmer about my idea of XML parsing, and if he sees regex, he will point out the potential flaws and set me right. Code review is a thing, problem averted by social means. Given the results, I suppose you did not talk a programmer. I suppose you also did not bother to stop for a moment and think "TIMTOWTDI, am I doing this in the best possible way?". If that is so, then you can see there is room for growth and improvement. This is the difference between banging with one's car against the crash barriers on the mountain to keep it on the road and taking the time to learn to drive properly.
The reason why the concept of regex for XML has not been eradicated yet even though we could do so by expending a bit of technical and social effort is that we have come to agree that Perl tries to be for everyone. I will not harshly criticise this practice or not say anything at all when beginners or non-professional programmers do it, but you cannot be afforded this protection, the expectations of conduct are higher.
> [I] had no time to read all the manuals while clients crowded at the door
The correct way to handle this situation would have been to take time management into your own hand (again, responsibility as an adult in a work setting) and say something along the lines: "Pardon, I have work to do and need to concentrate. I cannot be productive when I am perpetually distracted by people standing in my door. I need to ask you to go away for now. Please send me an email and we can appoint a meeting time for discussing what's on your mind. If you have something urgent, I cannot deal with it right now, notify my substitute colleague/my manager/my boss (as appropriate)."
And there you have 5 minutes of free time with a clear head and can get an overview of the documentation.
> look at the doc again, where is your snippet there? Down 2 levels of links probably.
That's false. Each method is down one link, i.e. directly reachable from the main documentation page.
• http://p3rl.org/XML::LibXML::Parser#load_xml
• http://p3rl.org/XML::LibXML::Element#setAttribute
• http://p3rl.org/XML::LibXML::Node#findnodes
• http://p3rl.org/XML::LibXML::Document#toString
> Where is ->findnodes() reference? I skimmed ::DOM, ::Document, ::Element to no avail. Even now my patience is over
If my junior programmers came to me with these words I would chew them out for not really trying. Randomly clicking links is not a structured and goal-oriented way to get at the desired information of which you know that it exists. Use search instead; off the top of my head:
https://metacpan.org/search?q=findnodes#:~:text=XML-LibXML-2...
$ rg -l '^=.*findnodes' lib
lib/XML/LibXML/Node.pod
lib/XML/LibXML/XPathContext.pod
These solutions are not particularly ergonomic, there are better ones, but these are guaranteed to be available.> You didn't write this right the first time you opened it in your browser.
You would be well advised to stop with your inflammatory assertions because you'll get to painfully regret such behaviour one day. You can always write "I assume" or "AFAICT" and not rile people up.
The truth is, I did write this right the first time. I examined working example code and used the online documentation to modify the code to my needs. The tutorial that is now prominently linked "above the fold" in the main documentation page did not exist back then, so the example code must have been something else, but I remember it being something similar in spirit. A whole lot of people do the same what I just described as evidenced by the strongly memetic nature of Stack Overflow.
It reinforces the idea that the best programming languages should be dumbed down to avoid people ever abusing features.
I fully support the right to carry and bear footguns and that programmers need to be trained in the responsible use of them.
So things like unicode identifiers (allowing the use of π as a variable name), custom operators and operator overloading (so `matrix * vector` rather than `matrix.VecMult(vector)`) can all be used in certain domains to produce perfectly readable domain-specific code. They can also be abused, but that isn't an argument against their use.
The argument by the author seems to be around a case where in perl bashing on XML with regexps is easier than XML parsing, which sounds like proper XML parsing needs to be made simpler, easier, more performant, more standardized or whatever it is about proper XML parsing that causes perl authors to not use it. Somehow there's too much friction there which needs to be addressed.
The way this argument usually winds up though is someone argues that regexp parsing needs to be made worse to punish people for using it in order to shepherd them into the "better" way.
And before I get a ton of "ackchyally you can do X" guys, yes you technically can but there's friction and you are implicitly not encouraged to do so.
I like languages that push you to think about X or Y so you can't miss it. Case in point: strong static types in many languages, or lifetimes/ownership in Rust.
Yet most modern languages steer away from checked exceptions, that you cannot miss. And I agree with them, catching UrlMalformedException from a hardcoded static url is unnecessary.
But most importantly, checked exceptions just don't play well with FP, that's why they are getting pushed off the ship.
checked exceptions would be fine id they were not so clunky.
This is why, unless forced or pressured to on a professional level, prefer dabbling in those languages where there is no classes or its truly an option, and not the main (forced) paradigm. Prefer less stress, hidden spaghetti code (pretending it's not), bullying, or shaming. Just way more enjoyable to write in languages where the paradigm that you choose is because it makes sense to solve the problem, not because "that's just what we do around here".
The example that I've run into most is trying to write functional immutable code in Java. I have just never gotten it to work well - of course there are libraries that have weird workarounds (that aren't compatible with standard data types or libraries), or tons of boilerplate you could write, but ultimately Java just fights you the whole way. It's not really worth going down that path imo, and better to switch to something like kotlin or scala if you can.
In my current project we use Error Prone, Checkstyle, Immutables, JSR305, NullAway and a couple of other things. It makes the language tolerable, but it's a massive waste that each setup needs to introduce and tune those, and often inexperience or lack of knowledge will mean they're never introduced, if only because after a while they become painful to introduce.
Better languages exists, some are even JVM, which just do this stuff properly.
What surprises me is that even among those lucky to work on a powerful platform such as the JVM or .NET it's more typical to stick to a single language. In my mind it'd be more natural to write Java/Kotlin/C# when you want imperative abstractions and Scala/F# when you are in mood for FP. The same platform/libraries, the same IDE/toolchain, only the syntax/abstractions differ.
But we know that Scala and F# adoption is nowhere near what some of us would hope for. We also know that the ecosystem fragments immediately and Scala projects don't share [m]any libraries with Java ones for example. So mixing the two languages in one system is mostly about wrapping some legacy code.
The "worse is better" essay touched upon some deep truth.
> C++ shepherds you into providing dependencies as header-only libraries.
There’re many C++ libraries provided as DLLs too, like QT. Just because the standard library is mostly header-only, doesn’t mean that’s always a good idea for other libraries.
> Java does not shepherd you into using classes and objects, it pretty much mandates them.
Maybe, but modern C#, despite similar to Java in many regards, doesn’t promote any particular coding style. Classes and objects are working fine, but so is FP or procedural. And there’re decent libraries, both Microsoft’s and third party, implementing reactive and actor approaches.
So that even multi-year hundred-people dozen-companies project still at least runs.
Of course, I've seen with my own eyes that even that could be broken with enough courage (seen a company ignoring all NPEs in main()), but on average Java projects stay more readable, than, say, Python.
Can we say that is due to shepherding or rather that Java lays it's types bare for you any time you create or pass a variable? You can do this in python with type annotations, but since they aren't required a lot of dev's just don't. This leads to the code being harder to read in my opinion.
Java didn't even have generics when it was released. Any sort of higher-order functions and support for anonymous function weren't introduced until Java 8 in 2014.
Language debuted 1995, generics in 2004 (almost a decade!), and lambda/FP facilities in another 20 years.
In Java, a public class has to have the same name as the file as well.
You had a very constrained design space historically and some non-negotiable enforced conventions.
It's similar to Go code, where most of it more-or-less looks the same and you can pick up most codebases and run with them if you're familiar with the language.
Similarly, the OP describes Perl, which is designed using a postmodern philosophy: however you think about your problem, you can solve it that way! In practice as OP describes many people reach for regex.
>...Java software is designed as big C-style procedures which call into APIs using object methods.
Do you mind going into more detail regarding those two styles in Java? I wouldn't exactly say that Java shepherd's its programmers towards the message passing style at all.
In practice, I've found most codebases don't care about any of that so every variable is private, they all have public getters/setters (so it's NOMINALLY encapsulated) and most methods are public with a few private methods strewn about.
>A good Java API wouldn't have many Setters/Getters that do nothing but set/get references to internal objects, but rather ones that parse and make sense of it's internal state.
I completely agree. Getter/Setter obsession often leads to breaking the Law of Demeter in most Java code bases I've seen.
My go-to language for personal projects/small tools at work is Java. I think if one looks at Java codebases as strict encapsulation at the package level it becomes a lot more bearable.
I fell into the rabbit hole of HTML+.js+Applets and just couldn't do anything else. I made my first HTTP server that I ran on the university account without anyone noticing! :) I found Karl Hornells javaonthebrain.com (little did I know he was sitting in the building next to me):
You don't need to code Java in a stupid way, here is how you do it like C without cache misses: http://www.javaonthebrain.com/java/iceblox/iceblox.java
"Java does not shepherd you into using classes and objects, it pretty much mandates them." - Well it mandates ONE class, you do you!
In other words, you've missed the whole point of the article. Just because you can do it, doesn't mean you'll end up doing it.
Combine that with peak computer hardware performance/efficiency and you have a time bomb that will leave nobody out of harm!
Java can do atomic parallel things on multiple cores you are hard pressed to build a VM with GC to get working in C.
WASM tried only VM and Go tried only GC, both failed compared to Java on the server.
Servers that crash are not an option.
So? It's not like the electricity bill is a major concern for 99% of companies with servers. It's usually less than what they pay to stock the fridge with water bottles...
Right now not a single company is making any profit, the margin calls are filling with debt at a rate that nobody can comprehend.
Just a question of time, it's inevitable, what goes up must come down.
I'm pretty sure this is not factually correct.
Also, not related to server electricity costs.
Oh, I wish. For example, in C you can easily return multiple values through pointers. In Java, you have no such luxury without allocating so it's even less flexible if you can't allocate.
I'd say that Java has similar semantics to C. One can easily pass object references to a function that sets their data. That looks and behaves almost exactly like C. The fact that the references are allocated on the heap is an implementation detail.
Let me be clear, a pointer to a struct on the stack in C is obviously not the same as a reference to an object allocated on the heap in Java. The point is that from the point of view of what the language induces you to do, Java and C are similar. Anyone that has used older libraries in Java can attest to this; they look and feel remarkably like C.
It does have index() and rindex(), but doesn't have a begins_with(), ends_with(), and other string functions that Python, Ruby, PHP, java, etc, have. You can roll your own, but there are arguably things missing.
The thing is that all of these functions are completely superfluous, because a Perlist will use regexp which are much more compact and allow writing in a nice, functional way (map and grep FTW). In fact, in most languages, manipulation of strings is so cumbersome that I go back to Perl almost every time, because a regexp is almost always the most compact, fast and readable solution.
Also frankly I never heard of anyone trying to parse XML or HTML with regexps this century for anything but a quick throwaway one-liner, so the example is actually quite poor.
"Note that there is nothing about Perl that forces you to [use regexes]...It provides all the tools needed"
I was saying that Perl does sort of force you down the regex path, because things like starts_with() aren't there. You're basically confirming that.
Also, it varies by search string size, version and machine type, but at least for me, index() is significantly faster in many situations...2 to 3x faster.
if ( (myvar.startsWith('hello') and myvar.endsWith('world')) or ((myvar.startsWith('Hello') and myvar.endsWith('World'))) {...}
I much prefer if ( $myvar =~ /^[H|h]ello.*[W|w]orld$/ ) { }
Perl doesn't have these functions because it has better, more powerful tools. It's akin to saying that Python lacks line numbers as GW-BASIC did...Benchmark index("abc","ab")==0 vs "abc" =~ /^ab/
Verbose, maybe, but that's not as fun as some of the catchy words there are to insult languages that aren't.
You'd end up with fast greenfield code to get your prototype working, but then could tighten it up to run faster, and clear out bad type assumptions as the discipline level goes up.
You can also do the opposite of what you suggest: start by writing the interface file (which has the functions and types that you will expose), and then write the implementation the way you want. This allows you to cleanly separates the API from the implementation, and also gives you fast compilation times.
There may be a slight difference compared to what you suggest, as everything is statically typed, but you can start a program with permissive types and gradually move on to stricted typed. For example, replace a string that can be anything with an enum (called variant types in OCaml).
If you don't need the native compilation/want the C# ecosystem, F# is a close cousin. Scala is a different beast, but shares some origins. It relies more on objects.
Does OCaml still have the infuriating "Provided A but elsewhere wanted B" error with no indication what this elsewhere is?
I was running into it surprisingly often when I last tried OCaml.
I am not a fan of this because the type attributes also act as documentation for that the variables, parameters, etc are supposed to be and that clearly typed out text is much easier and faster to read than some IDE's tooltip at a mouse hover or whatever (assuming you even have that in the context you see the code - even if your primary IDE supports this, you may still need to read the code outside of the IDE and sometimes outside of the surrounding code like, e.g. reading a diff).
And yes, for that reason C++'s "auto" is one of my least favorite additions to the language. Even when i have to use the language, i avoid using "auto".
(if anything sometimes feel that readability benefits when having to be explicit not only about types but also the parameter names in function calls ala Smalltalk... e.g. in Pascal i sometimes wonder what that "True" or "1" really means in a call. In recent versions of Free Pascal that have added scoped enum support - like class enums in C++ - i try to use those instead of booleans as they document the API even when it is used but this is only a small case)
https://dlang.org/spec/attribute.html
Most likely you are aware of this already, but even Delphi has type inference via inline variables.
Personally I enjoy using type inference everywhere on my own code.
At work, I take the rule of only using it when the type is clear, e.g.
auto mySomething = new Something();Ah yeah, this is why i added that EDIT but you replied before i did :-P. I agree about that part, i remember D being a bit attribute trigger happy :-P but i guess the alternative would be to break backwards compatibility which is IMO worse. Though perhaps something similar to Free Pascal's "modeswitch" directive would help (FP devs use it to introduce new syntax without breaking backwards compatibility).
> Delphi has type inference via inline variables
I know it is there but i don't really like it myself for the reasons i mentioned above (also i prefer sticking all variables together even in C++, too much code is usually a sign i need to break the functions into subfunctions and Pascal allowing nested functions is a feature i use very often). It is also one of the very very few case where Free Pascal developers have explicitly decided to not implement something because they feel it doesn't fit the language :-P.
And any repetitive words can be handled by IDEs, editors, and macros.
An IDE or editor may be able to write code for you but it can't read or understand it for you, and that's what takes most of the time on nontrivial projects. Macros undermine your tooling and mean the reader has to learn your dialect before understanding your code - they work best when you have a few common ones that are used across every codebase and supported in your tools, but at that point you're better off having them included in the language itself.
From my experience the design planning and just thinking about programs takes so much more time than the typing in that I sometimes wonder if I do something wrong.
What is the advantage of a less verbose language if let’s say the language capabilities are similar?
Similarly i rarely update the var section manually, i just press F9 to compile, the IDE moves the cursor right at the missing variable (the compiler is fast enough to make this almost instant), i press Ctrl+Shift+C and the IDE adds the variable. Same for the interface and implementation sections (think header and source files in C but at the same source code): i write the declaration for the classes, procedures functions, etc, press Ctrl+Shift+C and the IDE creates the bodies for their definitions automatically. Using Ctrl+Shift+Up/Down arrow i can move quickly between the declaration (in interface section) and definition (in implementation section) for each entry, allowing for quick filling in the code.
There are other shortcut keys, etc in Lazarus and i never really feel like having to type boilerplate code myself - but i still get the benefit of the more readable Pascal code (i have decades of active C and Pascal experience and i always find Pascal to be the more readable of the two, especially when looking at other people's code).
The people writing those lame scripts have the same logic the author does. "Must be hard, probably? So I won't even try to learn, I'll just use this regex"
A Shepherd's job is to keep the flock safe. If the blind are leading the blind, that is a lack of shepherding. It's not shepherding users to use regex. It's letting the sheep roam free and they're falling off the cliff.
Perl's problem has always been that it is too easy to use. 97% of people who write Perl aren't even programmers. It doesn't shepherd you, it lets you write programs even when you don't know what you're doing.
We should require drivers licenses in order to program. Programmers think they are capable even when they're not, and their false confidence and laziness leads to shitty code that causes problems. Same for people who try to drive without passing a driver's test. It just causes harm.
We should set legal engineering requirements for code just like for other trades. Otherwise some idiot is going to assume XML parsers are super annoying without even looking at them and try to use regex.
Your code calls a library. A framework calls your code, which is what React does.
It is a view framework, not an app framework though.
Debatably, the Go language has put itself somewhere in the middle, as a more modern acceptable compromise and blend with its own twists.
The V language is continuing on the path of where Go went, where readability, maintainability, and simplicity is more evident. Doing so, where it feels like a more natural feature. At the end of the day, many want to be more focused on the problems to be solved, not tripping over the eccentricities and traps of the language.
Clearly, perfection will probably never be achieved. But, I do like where various newer languages are going, as it shows progress. Usability, being easy to learn and teach, as well as being more practical about common programming issues.
A few years ago it was heatly discussed on HN... Most people seem to agree V is a textbook example of vaporware[1].
Vaporware is about something that doesn't exist. V clearly does exist and has a number of developers and contributors working on it. I understand there was some controversy from 2019, and mostly prior to it being open sourced on GitHub. Then some lingering animosity that stayed around in 2019 and a bit of 2020. Well, that's a few years ago, and its 2022 now. better to look at what's going on in the present.
Of any controversy these days, seems more about competing languages and their advocates, not liking another "new kid on the block" or wanting to protect their interests. But, such is the usual between programming languages. I'm talking more from the perspective of how various languages are designed for greater convenience, simplicity, and usability.
> V functions are pure by default, meaning that their return values are a function of their arguments only, and their evaluation has no side effects (besides I/O).
> This is achieved by a lack of global variables and all function arguments being immutable by default, even when references are passed.
But there is nothing preventing you to create side effects. A function making a side effect isn't pure. For example, Wikipedia [1] says that the return value depending only on input variables is necessary, but you also need no side effects.
The claim about Option and Result types [2] is also a bit weird. It feels closer to Zig's error handling than to regular Option and Result types. An important part of these types is that they can be replaced by user-made types without losing much, while in V they seem to rely a lot on compiler support.
In the comparaison with Go [3], there is a "No GC". On the main page, "Most objects (~90-100%) are freed by V's autofree engine: the compiler inserts necessary free calls automatically during compilation. Remaining small percentage of objects is freed via GC.". "There's also an option to use GC for everything via v -gc boehm.".
This is just the beginning. I don't have any trust in the people behind V and will not rely on them for building software.
[1]: https://en.wikipedia.org/wiki/Pure_function
[2]: https://github.com/vlang/v/blob/master/doc/docs.md#optionres...
V is in alpha, and has been making steady progress and changes over the last few years. To get an idea about this, refer to https://github.com/vlang/v/discussions/7610 or search their feature request history. The developers and contributors had and are clearly having a healthy exchange on what features they believe are best for the language.
It's at 0.2.4, and have stated they won't lock in most things until 0.3. Which at their rate of progress (based on their history), looks like that will be this year. Then they are shooting for 1.0 (which is quite fast).
Some volatility is expected type behavior for such a relatively young language. Various things are options or "up in the air" versus built-in or might not make the cut for 0.3. Which is understandable.
Various people and "angry" supporters of other languages, are acting like everything about V was supposed to be carved in stone and on lock from day 1. Actually, as if the developers were suppose to supply a finished product upon its release on GitHub. That's outrageously unrealistic and bias hostility that appears to show threatened interests.
I'm sure they will be updating their website to reflect the state of their language, as necessary. Their users, contributors, and developers understand where they are at.
> Some volatility is expected type behavior for such a relatively young language. Various things are options or "up in the air" versus built-in or might not make the cut for 0.3. Which is understandable.
It is understandable, what I'm asking them is to be clear and honest about that.
> Various people and "angry" supporters of other languages, are acting like everything about V was supposed to be carved in stone and on lock from day 1. Actually, as if the developers were suppose to supply a finished product upon its release on GitHub. That's outrageously unrealistic and bias hostility that appears to show threatened interests.
I think you're being a bit too paranoid here. The vast majority of new languages fail without anyone trying to make them fail. Especially these days in the "C/C++ replacement" niche. There's at least Ada, Rust, D, Zig, Go, Nim, Odin, Myrddin, ATS, Swift. There are probably way more than I forgot.
> I'm sure they will be updating their website to reflect the state of their language, as necessary.
What makes you so sure? They haven't been doing it in the last 3 years. I think you're the one showing pro-V biais here in the face of objective evidence.
> Their users, contributors, and developers understand where they are at.
But they still show something else to everyone. This is my issue, a lack of clarity, honesty ; thus creating a lack of trust for me.
The "trust" argument appears disingenuous. There is a difference between a bias preference or supporting another language, versus not trusting.
Anybody who took the time to actually use the language and study them on GitHub, would see they are on a tremendous development pace where they have been doing weekly releases for years (https://github.com/vlang/v/releases). That's called consistency. Usually the hallmark of reliability, which can instill trust.
On GitHub (https://github.com/vlang), you can see the activity of not only V, but many of the related projects such as vc, vinix, gitly, vls, vab, etc...
> The people behind the language have made wild claims that are objectively false. > It is understandable, what I'm asking them is to be clear and honest about that.
Again, this is quite a distorted take, which many might say is clouded by bias. V's GitHub (https://github.com/vlang) very accurately shows the state and activity of the programming language. There are no "wild claims" on V's GitHub, other than clear and precise data, activity, and releases.
In regards to V's web page (https://vlang.io/), they describe the language relatively accurately, in addition to promotion. No different to what other languages do. For example, Nim describes itself as "efficient, expressive, elegant". Really? Is that a "wild claim" too or simple self promotion?
For greater clarity, it's quite simple to click the link to the documentation on V's web page (https://github.com/vlang/v/blob/master/doc/docs.md), which goes over the features and capabilities of the language more precisely. There is no "wild claims" in their documentation, other than the usual need for some updates here and there, for a language in alpha.
If anything, the "wild claim" is to keep saying or pretending V is vaporware, as if it doesn't exist, after more than 84 releases (they just had a new one), YouTube videos, ebooks, documentation, etc...
> That is my issue with it. They have been called out for it multiple times. The first time I discovered V was in 2019...
V was debatably being called out by advocates and supporters of other competing programming languages before they could even release an alpha on GitHub (mid 2019).
Then the developers were getting dragged, as if the just released alpha was supposed to be a finished product. And if you read into the various back and forth, there appeared to be elements of jealousy and odd focus over them having Patreon support or it being equivalent to that of their preferred language. That's more indicative of "cat fighting" between languages and disguised pettiness.
> I think you're being a bit too paranoid here. The vast majority of new languages fail without anyone trying to make them fail. Especially these days in the "C/C++ replacement" niche. There's at least Ada, Rust, D, Zig, Go, Nim, Odin, Myrddin, ATS, Swift. There are probably way more than I forgot.
Not being paranoid, so much as calling it as I see it. There is a lot of bias and competitiveness between languages. So, when assessing the viability of a language, its best to see through some of what is being said.
True, many languages and projects have failed. This then demonstrates that V's present weekly release schedule makes them even more credible. They are demonstrating that they can independently stand with Rust, Go, Zig, and Nim as a useful language.
No they don't, see my various points that you have ignored about false claims. You're again talking a lot about bias, as if I was defending another language "against" V. I am not. I'm judging V on what they show, and the first thing you see is vlang.io, which contains many false claims, and has contained them for years now. You're right that consistency is important. The actual developement behind the language seems to be going well, which is great. All I'm asking is for them to stop lying and making false claims on their page, because this completly breaks my trust in them.
> For example, Nim describes itself as "efficient, expressive, elegant". Really? Is that a "wild claim" too or simple self promotion?
For the record, I think Nim marketing (on the page or by people using it) is also exagerated, and that's one of the thing that makes me avoid it. Their claims are more vague, which makes them harder to refute, but for example:
> Modern type system with local type inference, tuples, generics and sum types.
That's ML. 1973. I wouldn't call that "modern". But that's more of a nitpick. The website of Nim is in general more reasonable about their claims. It doesn't say "here are the coreutils, reimplemented in Nim" while pointing ot an empty repo.
For V, on the comparaison page with Go: "- No GC", on the main page of V: "Most objects (~90-100%) are freed by V's autofree engine: the compiler inserts necessary free calls automatically during compilation. Remaining small percentage of objects is freed via GC.". Either no GC is false, or the frontpage gives false informations about memory management. In both case that's not inspiring confidence.
> If anything, the "wild claim" is to keep saying or pretending V is vaporware, as if it doesn't exist, after more than 84 releases (they just had a new one), YouTube videos, ebooks, documentation, etc...
V, as in the language that's on github.com/vlang, exists and is very real. V, as in the language described on vlang.io, doesn't. They had two full years to fix this, and they didn't. So this means that at best, showing correct information is not high-priority for them. This goes against my values. Since you're accusing pretty much everyone else of showing bias, I can get a bit paranoid too. Maybe they keep a page like that because it'll make people talk about the language. Maybe it will even convince a few people to try. I can't accept that attitude coming from people working on a compiler, and thus won't use V as long as this issue exists.
> True, many languages and projects have failed. This then demonstrates that V's present weekly release schedule makes them even more credible. They are demonstrating that they can independently stand with Rust, Go, Zig, and Nim as a useful language.
We are using two different definition of "failed" here. I could work on a C replacement for 50 years, and say that it "stand with Rust, Go, Zig and Nim as a useful language". Which would be technically true. But people here often talk about popularity. In that sense, V stands with Zig and Nim as "known by a few people but not much more", while Rust and Go now have a place in the industry.
I also wish you would spend less time accusing me of being biased against V and spend more time explaining to me why the main page of V still has wrong information in the favor of V 2 years later. It's easy to dismiss any external criticism as competition trying to put you down. Some people are probably doing this. But there is still valid criticism from non-users, like what I'm offering here. You're free to take it or leave it.
Maybe vlang.io is first, depending on the search. Even so, likely a search will also bring up V's GitHub page, usually right underneath it.
There are no outright false claims or lies on the vlang.io main page, in my opinion. Debatably, there are some disputable or questionable claims, which mostly center around comparing V to other programming languages. This appears to be where the hostility towards V comes from, and where advocates of similar competing languages feel they are compared unfavorable, so are possibly out for "revenge" or upset.
Those upset at various claims by the comparison of V to their favorite language, might be better served to study V in more detail. Simply saying they are "lying", without ever having used V or knowing anything about its development details, results in erroneous counter claims or comes off very odd to those who have used V. I made a point of providing links, so that those making such counter claims, could either download or examine information about V.
Comparisons and competitiveness is inevitable, but clearly, there is nowhere for the outrageous opinion that V is vaporware, after 84 releases and weekly updates on GitHub (https://github.com/vlang/v/releases).
> For V, on the comparaison page with Go: "- No GC", on the main page of V: "Most objects (~90-100%) are freed by V's autofree engine: the compiler inserts necessary free calls automatically during compilation. Remaining small percentage of objects is freed via GC.". Either no GC is false, or the frontpage gives false informations about memory management. In both case that's not inspiring confidence.
It's quite telling that is the focus of saying V is "lying" or is "vaporware", which is not on the front page, but a link to comparing it with other languages (in this case Go).
If a person bothered to take time to use V, or study the discussion and issues on it's GitHub, they would have likely realized that the intent of the V developers/contributors appears to be to use the autofree engine. The existing capability to use GC (-gc boehm) is both optional and experimental, because autofree isn't finished yet.
GC is not a feature of V, as its considered too slow, and the experimental -gc boehm appears to be provided for temporary purposes. This can be assumed through reading of their discussions and issues by developers and contributors to users on their GitHub.
On V's web page, they make it very clear that the autofree engine won't be ready until 0.3 (they are at 0.2.4). And they will allow for the disabling of autofree, -noautofree. It's not anything forced, but an option. This would appear to include any use of GC (as part of autofree) for additional clean up.
So, the necessity of having to change statements on their web page is clearly a matter of opinion, but it's definitely not "lying" or looks like any intention to deceive. Furthermore, as they are in alpha, if they find that the experimental GC (-gc boehm) is working better than they expected then don't see anything wrong whatsoever with them deciding to keep it as another option. Which if they did, they would probably update their web page. That is the nature of alpha. You experiment, play with things, then make decisions based on the result.
> ...language that's on github.com/vlang, exists and is very real. V, as in the language described on vlang.io, doesn't...
First, those arguing against V (and perhaps some hiding their true reasons), usually don't make such a distinction. Usually its straight up claiming the V is vaporware, which clearly doesn't make any sense, with a quick look on GitHub.
The new position that you appear to be presenting is that the language as described on vlang.io is "fake". I just don't see that. The "real" V language is clearly described by the documentation (https://github.com/vlang/v/blob/master/doc/docs.md), which is linked to from vlang.io. Thus it doesn't look like any attempt at deception, as all can read their clear documentation (and download it).
> ...But people here often talk about popularity. In that sense, V stands with Zig and Nim as "known by a few people but not much more", while Rust and Go now have a place in the industry.
I can agree with you on that part. A very good argument can be made that V is closer to Zig and Nim, in terms of its present niche. But... Rust and Go were thrust upon us from major players like Google and Mozilla. It took both of these languages more than 10 years to achieve their present position. In regards to Rust, its much further down the list.
Who is to say where V (coming out in 2019) could be in another 10 years? Another counter is that both Go and Rust appear to be purposely placed in more specific niches versus more generalized programming and application development. The goal of the V developers, so far, appears to be for as much widespread use as possible.
> ...But there is still valid criticism from non-users, like what I'm offering here. You're free to take it or leave it.
Fair point. Just like non-users have their right to express their opinion, users and those that like V, also have that same right too.
You've ignored my earlier point about pure functions that are allowed to have side effects.
> A very good argument can be made that V is closer to Zig and Nim, in terms of its present niche.
Zig maybe, Nim I doubt it. Nim is a managed langauge trying to appear as if it was not, like Swift.
Now for the rest I don't see the point of arguing, as you spend half of your energy trying to frame my comments as some kind of attacks on V. They are not. My comments are raising points that, to some people, are obvious red flags. If you don't see those as red flags and think the current situation is fine, no problems. I'll go my way, you'll go yours, and that's it. Energy is better spent on what the actual users want, not what external people think. Now, if you want to actively fight the reputation of vaporware that V has, it might be a good idea to listen to my feedback and feedback like that. Remove/replace the claim about "pure functions by default", remove the coreutils or mark them as x% complete or wip, like the C translation.
I'll stop replying here, as it's honestly exhausting to be constantly accused of something I'm not doing, especially when there is no way to prove you the opposite. Please keep the paranoia away next time.
I was more hoping that you would read what they actually wrote. If one bothers to actually click the link on vlang.io which refers to "pure functions" it would go to (https://github.com/vlang/v/blob/master/doc/docs.md#pure-func...) their documentation where they elaborate on it further.
There you will find, "V is not a purely functional language however".
You are arguing (or condemning) from the perspective of what is a pure function, where there are various opinions about such. V's documentation gives their interpretation and explanation on what is a "pure function".
Thus they are not lying, attempting to deceive, or hiding anything. Click the link, get their explanation about pure functions. Arguing about the "purity" of pure functions or the possibility of side effects happening, is somewhat similar to arguing about what is or is not "real" OOP. There are different interpretations of such. It can become a more philosophical debate than about practical programming.
> ...to be constantly accused of something I'm not doing...
Reading your other comments on this thread (in other locations), it does appear that you are a Go language supporter. Nothing wrong with that and you are definitely entitled to your opinion. Not saying you are doing anything malicious, but rather people are allowed to also have a different opinion to what you have stated, or like the V language as you appear to with Go.
:= is used for assignment, as is = ??? Yikes
Immutable strings - How do you pull the first character out from a line?
The horror, the horror!
And to keep the discourse focused on the interesting things, let us exclude "into Microsofts hands / into Azure cloud".
Then the whole WinDev vs DevDiv politics seem to always influence what sheppards happen to take care of the herd, and the way decisions are taken.
Meanwhile C# seems to be turning into the C++/CLI replacement while sucking the life out of F# and VB.NET.
While I enjoy many of the latest .NET improvements, I occasionally wonder where they are going now.
McCluhan applied his thinking to radio and television, but that idea is applicable to computer software and language platforms.
"if all you have is a hammer, everything looks like a nail"
When I looked it up, I was surprised this is of fairly recent origin by Abraham Maslow in 1966
Overly simplified IMHO, this was probably true for K&R C, but in more recent C versions (e.g. in the last two decades or so), passing structs around by value is fine and often preferable.
(in general I agree with the article though, the shepherding side effect of programming languages is probably more important than the syntax and feature set)
Few languages pass structs/objects by value nowadays, at least not as a default action, because it is rarely what you want. Usually, a reference is what you want. In fact, if C++ wasn't designed as a superset of C, passing by reference probably would have been the default, like in most other languages.
C shepherds you into manipulating data via pointers simply because pointers are what you are using for the all important task of referencing objects.
On the other hand, C sometimes nudges you to pass by pointer where semantically you want a copy. Well, Matrix4x4 is the only example I can think of right now actually, and here I usually just bite the bullet and pass a copy, which might be a little wasteful but whatever (copying 64 bytes is not the end of the world, and the code might actually be inlined). And here, C++'s const-references can be an improvement to the situation.
Lots of them blatantly encourage there users to "copy&paste" instead of using functions and structures.
And if they try to encourage good behavior, the sales people start to bugger the developers to "make it simpler" and the result is again a 9000 lines long papyrus of copy & paste.
However just like in text based languages, there are always those that write functions bodies that take several screens.
Around the turn of the century, Java seemed to require a lot of "discipline" because the people who were embracing OO at the same time they were embracing Java did all kinds of stupid things. They created deep object hierarchies with unnecessary layers and strained relationships, and their systems were slow because simple operations like formatting a date required ten different object allocations and acquiring a mutex lock or two. The GoF patterns book, a good and well-intentioned book, was co-opted as a kind of extremist OO programming handbook, so that if you got stuck trying to apply OO to a problem, you could always find a way to move forward by adding even more OO. Languages like Java enabled this (some said encouraged it) and a lot of people thought it was impossible for programmers to rein in the tendency for Java codebases to blow up with OO excess. Meanwhile, a lot of fanboys thought that OO shouldn't be reined in, that the way out was through, and they doubled down on their principles.
In other words, OO was the hot new thing, and early adopters using OO languages like Java had a tendency to spiral into an OO frenzy that created ridiculously convoluted codebases and slow, complex systems.
But this wasn't inherent to Java. It was a combination of Java's ability to write that kind of code, along with a naive zeal for exploring the kind of solutions you could build using inheritance-based OO. Even the fanboys warned that you could, if you weren't sufficiently dedicated to OO principles, write procedural code in Java, putting your logic in static methods and your data in behavior-free, non-encapsulated record-style classes.
Twenty years later, we no longer think of Java as a language that infects people with an insatiable desire to create inheritance spaghetti, which they can only resist through discipline. It selects for people who are infected with that desire, sure, but an average programmer coming to Java will be leery of building complex inheritance hierarchies and using inheritance to solve problems it isn't well-suited for. It isn't discipline any more; it's just something people know to do, for their own sake. Building deep inheritance hierarchies and FactoryFactoryFactoryFactories is foolishness, not the one natural way to use Java.
Right now, I think something similar is happening in functional programming. I wouldn't be surprised if every fundamentally new programming model suffers from the same growing pains, until the lessons are learned, blind optimism becomes educated appreciation, the zealots are forced to find something else to be fanatical about, and we no longer say it takes "discipline" to avoid the pathologies but merely an awareness of what has been learned from past mistakes.
items.filter(item => item.isSelected)
is not “functional trickery”.A funny trick question as it pretty much does both.
Also I'll take "functional trickery" over needing a page called Slice Tricks so you can find out how to delete an item any day of the week.
let show_items = items.filter(…)
But yeah, things like this you have to know in advance anyway.• `fn filter(self, P²) -> A` consumes self and returns a new thing of type A;
• `fn filter(&mut self, P)` mutates in-place;
• `fn filter(&self, P) -> B<'_>` doesn’t mutate self, but produces a new thing of type B containing references to the contents of self.
As a concrete example, Rust has `Vec::retain(&mut self, P)` to filter a list/array in place, and `Iterator::filter(self, P) -> Filter<Self, P>` to filter an iterator, consuming that iterator object and wrapping it in another.
This is a sterling example of the point of the original article: Rust pushes you to thinking about ownership, and it’s really great and I miss it when working in other languages, where I have to stop and think whether I’m allowed to mutate this object or whether I should make a copy, and the cost of getting it wrong is bad performance in one direction and bugs that can be a nightmare to diagnose in the other direction.
—⁂—
¹ You need linear types to make it just about impossible to get wrong by accident. Rust has a weaker substitute that you can annotate functions with, #[must_use], which says “warn the user if they neglect to use the return value, because they’re almost certainly doing the wrong thing”.
² I’ve omitted the filter predicate P from the signatures to reduce distraction; in each case it’d probably be something like `<P: FnMut(&Self::Item) -> bool>`: a function that is passed a reference to an item and returns true or false.
Because if it is not functional trickery, I don’t really know what is (and no, recursion is not the answer) and it is sure clearer than the 3 nested loop or whatever one might come up with.
But I also appreciate Go as a language. I love the wide standard library, I love how asynchronous IO is handled, I love the tooling and the fast compilation, I love the cross-compilation. And more than that, I recognize them as good solutions to problems. I wish other languages would adopt that. I wish making a web server/API in OCaml was as easy as in Go. I wish Rust had some M:N threading model when doing stuff that's a bit less performance-sensitive. I wish Stack/Cabal/Nix were as easy as go build. When I had to make a small program to cut text files for a friend, I did it in Go, and it was a great experience.
We're engineers, let's try to be a bit less tribalist and a bit more pragmatic, and we will all end up with better tools.
I'm not the GP, but I think this is off the mark. There's a big difference in purposefully choosing boring technology and purposefully taking pride in not knowing things.
"This is why I enjoy Go, it shepherds you to write clear and uniform code that cannot be mistaken. Go makes well-known tradeoffs that have already been discussed to death, and I find that I agree with these tradeoffs. In my experience, collaborating on Go code is easier as the absence of some means of abstractions prevents people from creating their "language inside the language", and thus allow for a team to share more easily their mental model about how the code works."
You could even add a caveat in the form of "Go isn't perfect, no language is, and has made what could be considered objective flaws. But on the other hand, the handling of things like generics shows that the maintainers are not closed to idea. They tend to err on the side of taking more time to make things rights, even if that time isn't always needed, and that's a tradeoff I agree with.".