Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I would love to have a scripting language has typed features and you can replace bash with.

What comes close is:

    #! /usr/bin/env elixir

    Mix.install([:jason])

    defmodule JsonPrettyPrinter do
      def get_stdin_data do
        :stdio
        |> IO.read(:all)
        |> Jason.decode()
        |> case do
          {:ok, json_data} -> json_data
          _ -> raise "Invalid JSON payload was provided"
       end
     end
    end

    JsonPrettyPrinter.get_stdin_data()
    |> JsonPrettyPrinter.pretty_print_json()
    |> IO.puts()




Have you seen nushell? It lets me one-liner so many things that would have previously taken breaking out a "real" language to do

Contrived example:

  ls | where type == 'file' | sort-by size | take 4  | each {|f| {n: $f.name, s: ($f.size | format filesize MB) }} | to json
outputs

  {
    "n": "clippy.toml",
    "s": "0.000001 MB"
  },
  {
    "n": "README.md",
    "s": "0.000009 MB"
  },
  {
    "n": "rustfmt.toml",
    "s": "0.000052 MB"
  },
  {
    "n": "typos.toml",
    "s": "0.00009 MB"
  }

With this:

    E = Struct.new(:name, :size, :type)
    def ls = Dir.children('.').map{ s=File::Stat.new(_1); E.new(_1, s.size, s.file? ? 'file' : 'dir') }
This becomes valid Ruby:

    ls.find_all{_1.type == 'file'}.sort_by(&:size).take(4).map{ {n: _1.name, s: _1.size } }.each { puts JSON.pretty_generate(_1) }
(drops your size formatting, so not strictly equivalent)

Which isn't meant to "compete" - nushell looks nice -, but to show that the lower-threshold option for those of us who don't want to switch shells is to throw together a few helpers in a language... (you can get much closer to your example with another helper or two and a few more "evil" abuses of Ruby's darker corners, but I'm not sure it'd be worth it; I might a wrapper for the above in my bin/ though)


I've tried nushell and other shell replacements and it just feels like I'm learning a new programming language for no good reason

To be fair the example above is easier to remember than:

  ls -l --sort=size | head -n 5 | tail -n 4 | awk '{print $5 " = " $9}' | numfmt --to iec | jq --raw-input --null-input 'inputs | gsub("\r$"; "") | split(" = "; "") | select(length == 2) | {"s": (.[0]), "n": .[1]}'

This example shows nicely how ugly text processing is: you have to use head and tail simply to trim out the first line of ls (the total).

I think it doesn't even work correctly. ls lists files and directories and then picks the first 4 (it should only select files).

And this also uses awk and jq, which are not just simple "one purpose" tools, but pretty much complete programming languages. jq is not even part of most standard installations, it has to be installed first.


I'd replace the first part with (which isn't any shorter, but in general if I want a list of files for a pipeline, find is usually more flexible than ls for anything but the most trivial):

    find -maxdepth 1 -type f -printf '%s %f\n' | sort -n | head -n 5
For the latter part, I'd tend to think that if you're going to use awk and jq, you might as well use Ruby.

   ruby -rjson -nae ' puts(JSON.pretty_generate({n: $F[1], s: "%.5f MB" % ($F[0].to_i / 10e6) }))'
("-nae" effectively takes an expression on the command line (-e), wraps it in "while gets; ... end" (-a), and adds the equivalent to "$F = $_.split" before the first line of your expression (-n))

It's still ugly, so no competition for nushell still.

I'd be inclined to drop a little wrapper in my bin with a few lines of helpers (see my other comment) and do all Ruy if I wanted to get closer without having to change shells...


Ruby is a pretty natural fit for shell scripting.

https://lucasoshiro.github.io/posts-en/2024-06-17-ruby-shell...


It's close, but there are some things that could be better to make it easier to access e.g. file size and type. I think maybe a 50-100 line set of helpers and a one line wrapper (to spawn Ruby with -r<helper> -e <command line>) would get you mostly to where nushell is.

> And this also uses awk and jq, which are not just simple "one purpose" tools, but pretty much complete programming languages

In a way that exactly illustrates the GGP's point: why learn a new language (nushell's) when you can learn awk or jq, which are arguably more generally- and widely-applicable than nushell. Or if awk and jq are too esoteric, you could even pipe the output of `find` into the python or ruby interpreters (one of which you may already know, and are much more generally applicable than nushell, awk, or jq), with a short in-line script on the command line.


> awk or jq, which are arguably more generally- and widely-applicable than nushell

That is backwards. I know I said "complete programming languages", but to be fair, awk only shines when it comes to "records processing", jq only shines for JSON processing. nushell is more like a general scripting language — much more flexible.


Yes, the point was to show that nushell is pretty awesome. I totally punted on the file only part.

I'm aware ;)

Yeah... https://www.sophiajt.com/case-for-nushell/ makes a really good case for Nushell as an alternative to Bash.

Unfortunately, I don't think Nushell brings much benefit for folks who already know Bash enough to change directories and launch executables and who already know Python enough to use more complicated data structures/control flow/IDE features

I'm still rooting for Nushell as I think its a really cool idea.


For me the blocker was having to switch to bash/powershell when moving to a different machine (ie: servers, work machine, etc..). I would end up needing to redo same things to be compatible with the existing tools; eventually I just gave up and got used to readily available shells instead.

Ok if it's not for you. But there is of course a very good reason — work with objects in the pipeline instead of "dumb text". Also PowerShell and nushell are quite nice to learn, whereas Bash is absolutely horrible.

I wonder if this is a case of "worse is better", or just the long-term entrenchment of text. Because nushell hasn't been adopted all that much compared to bash or even zsh (or a "real" scripting language like python or ruby). I don't know much about PowerShell adoption (haven't used Windows in over 20 years), but I'd assume since it's a first-party system that's default installed(?), it's done better adoption-wise.

I agree that bash sucks, but I really have no motivation to learn something like nushell. I can get by with bash for simpler things, and when I get frustrated with bash, I switch to python, which is default-available everywhere I personally need it to be.

Back to text, though... I'm honestly not sure objects are strictly better than dumb text. Objects means higher cognitive overhead; text is... well, text. You can see it right there in front of you, count lines and characters, see its delimiters and structure, and come up with code to manipulate it. And, again, if I need objects, I have python.


I get the point of "either Bash or straight to a real programming language". That's what I do too, for automation. I like how PowerShell makes one-off tasks easier which would otherwise be the typical pipe of cat, grep, sed, awk etc.

About objects vs. text: I'm convinced that objects are vastly superior. There was a comment about this here with good arguments: https://news.ycombinator.com/item?id=45907248


Well, the reason is you can stop using Bash. If you never write Bash scripts already then you probably don't need it (and also congratulations on doing things right), but most people at least have lazy colleagues that write shell scripts. One day I'd like them to be not awful.

In PowerShell:

  gci -file | sort-object size | select name, size -first 4 | % { $_.size /= 1MB; $_ } | ConvertTo-Json

ConvertTo-Json? What kind of naming convention is that, especially with all the other commands being lowercase?

The naming convention is `verb-noun`. It's convenient for discoverability and consistency. The short commands are aliases.

The last command is properly cased, because I pressed tab (it auto-completes and fixes the case). The other commands I typed without tab completion. You can write however you want, PS is not case sensitive.


i dont think you need to select name and size, you can just remove it and use `select -first 4`, but cool, I never knew about `/=` syntax

I was trying to replicate the nushell example, which only has name and size in the output.

For me the best benefit of nushell is not the easier syntax, but the static type checks. It catches most typos before running the script, which is a godsend when the script is slow and/or has destructive operations.

Was just about to suggest nushell. I love programming in nushell, the out of the box features are excellent.

this is pretty cool!

OCaml is a scripting language in this sense. No need to compile an executable ahead of time, just have a `#!/usr/bin/env ocaml` shebang (or more realistically: `#!/usr/bin/env -S ocaml -I +unix unix.cma`) at the top of a source file.

Though, I don't think it has the capability for single-file scripts to declare 3rd-party dependencies to be automatically installed.


It also has poor support for Windows.

The best option I've found for this use case (ad-hoc scripting with third party dependencies) is Deno.

I'm hoping Rust will get there in the end too.


There's a trick shared here days ago to add a kind of shebang to Go that may interest you: https://lorentz.app/blog-item.html?id=go-shebang

Discussion: https://news.ycombinator.com/item?id=46431028


Two interesting options for everyday scripting are Python and Powershell.

A nice part of PowerShell: you can `Add-Type` and embed C#/F# when you want.

I am unhappy with python. It degrades fast. It deprecates libraries every minor release and that tends to break the applications I use. Recent examples are distutils and opsaudio.

For everyday scripting -- the types of things where I'd be writing in bash but get frustrated with it and switch to python -- I nearly always only need what's in the stdlib.

Sure, for "applications", the ecosystem can be frustrating at times, but I don't think that's what we're talking about here.


Thats why venv exists. Much better solution than lock files.

Doesn't help much because even the standard library bitrots after enough Python releases. I have things I write today but can't run on a NAS that has older Python. No issues like that with Powershell for example.

I dunno what issues you are running into, but generally, code from old Python should work fine under new releases if you are developing, you just have to set up your venv right up front and install the specific version of libraries that don't have modern Python code.

I still work on projects that were written under 3.6.

If you care enough, you can also use something like asdf to install an older Python alongside the system one.


you won't believe how powerful Python is with libraries. ChatGPT and Claude made a brand new browser, that isn't based on Chromium or Firefox, and yet still follows many aspects of layout correctly. I read the article we're discussing ("stop designing languages") on this browser and I'm currently using it to post this reply.

Knowing that LLM’s have been extensively trained on public code, I wonder how much of it is based on Chromium or Firefox.

That's a good question! You can read through the entire source code for the latest version:

https://taonexus.com/publicfiles/jan2026/171toy-browser.py.t...

it doesn't look like it would be easily derived from Chromium or Firefox, because this code is Python and those don't use Python this way.

By the way is there any feature you'd like to see added to the toy browser? The goal is that one day it's a replacement for Chrome, Firefox, etc. It's being built by ChatGPT and Claude at the moment. Let me know if there are any feature ideas you have that would be cool to add.


A python-based browser? What are you using for the GUI toolkit?

>A python-based browser? What are you using for the GUI toolkit?

Great questions. 1. Yes, for the moment. Like the title of this article suggests - we're using a library! :)

It's great to iterate in Python, which has a large ecosystem of libraries. Believe it or not, there is a chance that in the future it would be able to translate the language into a different one (for example, C++) while using C++ bindings for the same gui libraries. This would speed up its actions by 40x. However, not all of the libraries used have C++ bindings so it could be harder than it looks.

2. Here's the current version of the source code:

https://taonexus.com/publicfiles/jan2026/171toy-browser.py.t...

you can have a quick read through. Originally it was using tkinter for the GUI toolkit. I believe it is still using tkinter, but the AI might be leaning on some other library. As you read it, is it using anything but tkinter for the GUI toolkit?

These libraries are doing a lot of heavy lifting, but I think it is still ending up drawing in tkinter (not handing off rendering to any other library.)


Python is actually kind of awkward for this use case since you can't import from other files easily unless you are in a proper Python package, which isn't usually the case for everyday scripting.

Lol what.

Python lets you dynamically import from anywhere. The syntax is a bit funky, but thats what llms are for.


No it doesn't. You can't do something like `import ../../foo/bar`. You can mess around with PYTHONPATH and importlib to work around that but that's a horrible hack that also breaks all tooling. Not a good idea.

With Deno you can just import by relative file path and it just works like you'd expect and the tools support it. I wish more languages worked like that.


> You can't do something like `import ../../foo/bar`.

https://docs.python.org/3/reference/import.html#relativeimpo...

You'd use:

  import ...foo.bar

Common misconception. That doesn't import by relative file path; it imports by relative package path. It's actually quite different. Scripts you run directly aren't in a package so you can't use it at all. You can go above the package root. Namespace packages mean it can jump to other directories.

Everyone wants that to just mean "import relative to this file" but it doesn't.


>You can mess around with PYTHONPATH and importlib to work around that but that's a horrible hack that also breaks all tooling

....no.

import keyword uses importlib under the hood. It just does a lot of things for you like setting up namespace. But importlib has all the functionality to add the code in any python file cleanly.

My custom agent that I use basically has the functionality to wrap every piece of code it writes as a tool and stores it into python files. During tool calls, it pretty much dynamically imports that code as part of a module within the project. Works perfectly fine.


> Works perfectly fine.

Unless you want to use type checkers, linters, IDEs, etc.


Have you considered PowerShell? It's open-source, and typed, and definitely usable from the command line with lots of resources for.

https://github.com/PowerShell/PowerShell


And dotnet run can now run single files of C# code, with the ability to import other projects and packages if needed.

  #!/usr/bin/dotnet run
https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-...

https://andrewlock.net/exploring-dotnet-10-preview-features-...


If you just wait a few months, then that program will be written in a typed language. The type checker for Elixir is coming along nicely and every release brings more checks.

Suggest you take a look at https://raku.org for a strongly (but gradual) typed scripting language.

that's just perl with a new coat of paint

Perl 5 does not have types, as far as I know.

I’m taking the GP seriously instead of dismissing it. Raku looks like more fun than nushell tbh.



    print 42 + 99;          # 141
    print &print.file ;     # ...src/core.c/io_operators.rakumod
    print &infix:<+>.file;  # ...src/core.c/Numeric.rakumod
    print ?CORE::<&print>;  # True 
I barely understood these four example lines.

It has. They are just poorly supported and disliked.

Lead paint with asbestos sprinkles and radium racing stripes! ;)

it is so much slower to start than perl, that you don't want to run it for quick one-off tasks

  ~ > time perl -e ''
  perl -e ''  0.01s user 0.01s system 10% cpu 0.179 total
  ~ > time raku -e ''
  raku -e ''  0.09s user 0.04s system 53% cpu 0.232 total
  ~ >

well waiting for Raku (formerly perl6) to be built was like watching paint dry

They should have named it Godot. ;) But now that name is taken.


Babashka (https://babashka.org/) is an interesting tool for scripting. It's Clojure, so dynamic typing, but it's data orientation makes it a great fit for something like your example.

I wanted to say Haskell with shh[^1] and stack's or nix's shebangs[^2][^3], but interpreted haskell is not particularly fast.

Also I think a Python script is reasonable if you use a type-checker with full type annotations, although they are not a silver bullet. For most scripts I use fish, which is my preferred interactive shell too.

[1]: https://hackage.haskell.org/package/shh

[2]: https://docs.haskellstack.org/en/v3.9.1/topics/scripts/

[3]: https://wiki.nixos.org/wiki/Nix-shell_shebang. On a side note, if you were to use nix's shebang for haskell scripts with dependencies, you should be using https://github.com/tomberek/- instead of impure inputs, because it allows for cached evaluation. I personally cloned the repo to my personal gitlab account, since it's small and should never change


It probably is perl or Python.

@dev_l1x_be: The answer isn't a new typed scripting language. It's recognizing what the interpreter already is.

LLMs are eval(). Skills are programs. YAML is the motherboard.

@unkulunkulu nails it -- "library as the final language", languages all the way down. Exactly. Skills ARE languages. They teach the interpreter what to understand. When the interpreter understands intent, the distinction dissolves.

@conartist6: "DSL is fuzzy... languages and libraries don't have to be opposing" -- yes. Traditional DSL: parse -> AST -> evaluate. LLM "DSL": read intent -> understand -> act. All one step. You can code-switch mid-sentence and it doesn't care.

The problem with opinionated frameworks like ROR and their BDFLs like DHH is that one opinion is the WRONG number!

The key insight nobody's mentioned: SPEED OF LIGHT vs CARRIER PIGEON.

Carrier pigeon: call LLM, get response, parse it, call LLM again, repeat. Slow. Noisy. Every round-trip destroys precision through tokenization.

Speed of light: ONE call. I ran 33 turns of Stoner Fluxx -- 10 characters, many opinions, game state, hands, rules, dialogue, jokes -- in a single LLM invocation. The LLM simulates internally at the speed of thought. No serialization overhead. No context-destroying round trips.

@jakkos, @PaulHoule: nushell and Python are fine. But you're still writing syntax for a parser. What if you wrote intent for an understander?

Bash is a tragedy -- quoting footguns, jq gymnastics, write-only syntax. Our pattern: write intent in YAML, let the LLM "uplift" to clean Python when you need real code.

Postel's Law as type system: liberal in what you accept. Semantic understanding catches nonsense because it knows what you MEANT, not just what you TYPED.

Proof and philosophy: https://github.com/SimHacker/moollm/blob/main/designs/stanza...


Holy slop!

[dead]


> LLMs are eval(). Skills are programs. YAML is the motherboard.

This for some reason irritated me so much I wrote the comment.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: