That misses my poorly written point. Do you use a separate, awkward tool to call functions you wrote in python? Or do you call the function in python itself?
The shell is a useless abstraction that was only necessary in unix because the alternative was c. Now that we have better languages, why not use them to write the OS bottom-up?
> Do you use a separate, awkward tool to call functions you wrote in python?
I use a separate tool to call a lot of functions written in C. That tool is called “Python”. Or “Ruby”.
Sometimes I use a separate tool to call functions written in Python (or JavaScript, or...) and that tool is called PostgreSQL.
Sometimes I use Ruby to call C to call Postgres to call JS.
> Now that we have better languages, why not use them to write the OS bottom-up?
What better languages for implementing an OS? Lisp? We had that before C. Rust? Either way—and I like both languages—I don't want to use either for a shell. Red? Maybe, but I don't think we're to the point of a Red OS yet, and while it's probably eventually usable for that purpose, I don't see it as necessarily ideal for OS implementation though it might be tolerable as a shell language.
And thus the tech industry piled abstraction upon abstraction, decade after decade, until finally all software collapsed into a singularity and destroyed the earth. Which, frankly, came as somewhat of a relief to all the people forced to use it.
The choice of python as your shell would surely be as arbitrary as bash?
There is a long history of alternative shells csh, tcsh, zsh, fish, etc. All with various level of abstraction and various amounts of "programming language type constructs" (for lack of a better term).
At the end of the day, long arguments have been had over which is the better shell. It's all just personal preference. Hence it can be set in /etc/passwd per user.
I have a different theory; perhaps line printers (which terminals try to emulate) are to blame.
Space on a line is limited and one directional, so thr tools naturally evolved towards strange single line incantations instead of full fledged programs.
Commodore Pet did it right by introducing a navigable terminal where you could move between lines and don't need to open an editor to write multiline programs.
This was already a solved problem in the 70s. IBM's 3270 series offered full buffered access to a character matrix display, and DEC's VT52 and later terminals added bi-directional scrolling.
Modern CLIs can usually emulate at least a few of the old scrolling character matrix displays, but only a few bash commands (e.g. top) use the extra features.
It's strange to me to imagine that there would be one language that was the best choice for OS implementation and day to day user interaction/automation. I'm curious what language do you think would be good for both use cases?
That’s great for developers who are competent in that paradigm but what about hobbyists or sysadmins who have little interest in software development?
I’m not saying POSIX shells are without fault but they weren’t just created because C is too low level; they were created because people used a terminal who weren’t always techies so Bell Labs created a language which anyone could easily write simple programs with. Granted that need has dissipated a little but your solution of having one language to rule them all creates more problems than it solves (really the only problem it solves is OCD for a few LISP enthusiasts).
The reason there are so many different programming languages isn’t just an element of NIH; some languages do genuinely handle some use cases better than some other languages. Plus there is always going to be an element of user preference. So your idea of a perfect system would be hell for a great many other people - myself included (and I do generally enjoy functional programming).
Yeah I never understood why many people, at least among programmers, seem to be strongly opposed towards shell and shell scripts.
At some point when I wasn't proficient enough in Bash yet, I was writing scripts for automation using Perl and Ruby. The 'logic part' is definitely much easier in these languages. But simple file/directory stuff is far more complex actually. A lot of this has to do with error handling and different expectations how that is supposed to work. In a default shell script, errors are handled very forgivingly which is very much how most people work when performing tasks manually - not every step is super important.
On the other hand Shell scripting documentation is crap. Most of it is from 80s/90s, full of irrelevant details for the practical person. A bit like 90s/00s JS documentation before MDN.
Haskell's lazy-by-default evaluation makes it more difficult to reason about memory usage, because it is all too easy to accidentally build up large expression trees at runtime [1]. This makes it a rather bad choice for a kernel or other low-level stuff.
Compare Rust, which tries to keep allocations (and the size/asymptotic complexity of allocations) explicit and visible.
[1] The optimizer catches some cases, but not all of them.
I think that if we use purish FP as our system language it doesn't make sense to just emulate imperative/mutable system.
But mm... why you would want to mutable state like current directory in first place? Or even mutable filesystem? Why aren't those just immutable parameters of your program?
> But mm... why you would want to mutable state like current directory in first place? Or even mutable filesystem? Why aren't those just immutable parameters of your program?
Because that's how just about any OS, application, library, filesystem, device driver, database, nearly everything related to computing at all, works. Reinventing the universe has never, ever lead to success. If you want even the slightest hope for non-negligible adoption of your operating system, you need to be able to interface with the rest of the world. And that's why you don't want the filesystem to be an "immuatble parameter of your program".
You can still build compatibility layer on top of immutable FS. Like being mutable from program(s) perspective while being able to control how these changes are capsulated from rest of the system. You could, for example, see what your
> any OS, application, library, filesystem, device driver, database, nearly everything related to computing at all
-is trying to actually do to your files and operation like reverting changes is trivial. Sure it's not performance optimal and effects what kind of software is ideal to work within such a system. It's just a different approach with different trade-offs.
Anyway if I one day want to build new OS it's for trying different interesting approaches and not yet another 'successful any-OS'.
Because one of the scenarios we were talking about is interactive use - i.e. command prompt. Current directory (and other implicit state like that) is convenient there, and removing it would make it that much harder to use.
If your workflow is like:
1. Manipulate current directory
2. Run some program
3. Manipulate current directory again
4. Run yet another program...
You can actually consider current directory as immutable already.
It became "badly mutable" for example if you manipulate the current directory and this change is observable from a program point of view which is already running. Would that be really useful?
To clarify I am not trying to say that absolutely everything should be immutable but it's fun thought experiment how far you can go.
I don't see how. The suggestion was to use a Haskell-like scripting language for interactive use on the command prompt. Note, we're not talking about apps that run from that prompt; we're talking about the shell itself. And all shells today have mutable global state in form of current directory (and pushd/popd stack, usually).
Shell is kind of REPL (read–eval–print loop). In OOP you use looping with mutation and in FP you use recursion. This may not sound very informative but still: "Just fold over inputs, what's the problem?".
Being immutable doesn't mean that everything is static but that things aren't changeable in place. So instead of having a global mutable state, you have this local state which you can alter by creating new instances and passing them around. In the end, the difference is that on what scope changes are observable and how that constraint effects on your design. In some sense: mutable = changes are uncontrollable, immutable = changes are controllable.