Elixir, on the other hand, has many nice features for scripting, such as better string handling, process pipe-lining, and an excellent set of first-class generic data structures (beyond just map and list). It also happens to have Erlang's fantastic concurrency model.
Have you tried Crystal? Both are on my list of programming languages to look into next, together with Elm, Reason and Zig.
It would be, but IME Crystal, or statically typed programming languages that don't have a type system at least as powerful as Haskells in general, don't get anywhere remotely close to that, and the overhead added to get what they do give is of dubious net benefit outside of large projects.
And even with Haskell, while you can express a much richer set of constraints in types, there's also more space for bugs in the type level programming.
Why would I want to spend my time "type level programming"?
Maybe I don't understand what's meant by a type. Is it on the level of string/double/int, or is it expected to cover more (string with a well formatted ISO 8601 datetime, etc)? What is accomplished that couldn't be done in vanilla Ruby with an object representing a more complex datatype with its own getters/setters to cover validation?
Elixir had a long startup time for one-off scripts in my experience, but Elixir’s syntax is still one of my favorites for scripting.
Compared with
time crystal eval <<<"puts 1"
time ruby <<<"puts 1"Rather try "echo 'puts 1' > puts.cr && crystal build puts.cr && time ./puts"
BTW, you can dramatically decrease the cold startup times of ruby if you call it with the options '--disable-gems --disable-rubyopt' if that's an option to you. For many scripts it certainly is.
But even on a 8 years old machine with spinning rust, after the ruby stuff is cached in the OS filesystem cache, my startup time are smaller than 10ms.
Python as a language can support parallelism via threads, and CPython as an implementation can via multiprocessing, but those are both very low level abstractions; Raku has higher level abstractions which allow much more succinct expression of parallel operations.
sub foo ( Int \n where 1..100 ) {
# ___ start is a prefix operator that creates a Promise
# V
start { sleep n.rand; (1..n).pick }
}
my $result = foo(10);
say $result.status; # Planned
say await $result; # 6 (after a delay)
# this is nearly identical to foo above
sub bar ( Int \n where 1..100 ) {
Promise.in( n.rand ).then({ (1..n).pick })
}
(Note that `start` does not need a block unless you are grouping more than one statement as I have above.)There are combinators like `Promise.allof` and `Promise.anyof`.
You usually don't need to use `Promise.allof`, because you can use `await` on a list.
my @promises = Promise.in(1.rand) xx 10;
my @results = await @promises;
(Note that the left side of `xx` is _thunky_ so there are ten different Promises with ten different random wait times.)---
You should see the `react`/`supply` and `whenever` feature.
lliamander replied that Elixir does too and it's worth a look
To that, 7thaccount replied that Perl6 and Elixir fill different niches.
So far, it seems Perl6 fills a niche that requires scripting and first-class concurrency. My question then is: what is this niche that requires very solid concurrency but also scripting. In other words, what does Perl6 have in terms of concurrency that Python does not (given they are both scripting languages)?
I don't know of many instances where scripting and concurrency would be needed in the same application. But if you wanted to use single language both for scripting tasks and applications that require concurrency, then Raku or Elixir would work.
One instance I can actually think of, that would be specific to Erlang/Elixir, is if you have a long running service that you sometimes run batch jobs against (like importing data from a CSV). An Elixir script can hook directly into an Elixir/Erlang service and interact with it using message passing. It's a heck of a lot simpler than RMI, and avoids the need to expose new HTTP endpoints specifically for such tasks.
Some differences:
* You can actually write your whole application in Elixir, whereas I could not see doing that with PowerShell
* In Erlang/Elixir, instead of objects you have processes. Think of your application as a microservice architecture on steroids, using the Erlang RPC protocol as a means for inter-process communication.
Because each process just sends and receives messages, your script can just send messages to any of the processes that make up your application, as if they were your own service. All you have to do is connect to the remote application using the Erlang Distribution Protocol (to connect different runtimes over the network).
To which the answer is “not only does Python not have them, but with the GIL CPython couldn't do much with them even if the language had them.”
edit: that is, as far as I can tell, after a quick Google, it's not too different from Python's Thread object.
(In that when I translate Go into Raku, I usually replace `go` with `start`.)
my Channel \input .= new;
my Channel \output .= new;
start for input -> \message { output.send(message) }
start for output -> \message { say message }
input.send(5);
But what he was really talking about is something more like the following. sub foo () {
sleep 10;
42
}
# ___
# V V
my $delayed-result = start foo();
say 'Hello World'; # Hello World
say $delayed-result.status; # Planned
say await $delayed-result; # 42I agree the GIL is a problem but it's only an issue for CPU-bound problems. Is there really an important amount of CPU-bound work that is written in a scripting language? If it's CPU-bound, wouldn't you want to use something lower level?
-----
Python's concurrency model is good for waiting on network or disk I/O because of its GIL (Global Interpreter Lock): https://realpython.com/python-gil/#the-impact-on-multi-threa...
If your program is CPU bound the GIL will slow you down. I'm sure since the python ecosystem is quite vast there are ways around the GIL... but then you have to worry about every library you use not supporting "real" multi-threading, or (b)locking for no reason and shitting all over your efforts.
When I have a bit of Raku code that is too slow I complain (send a bug report) and then someone else figures out why the JIT isn't doing a better job and fixes it.
Then bingo-bango it ends up even faster than using NativeCall to call into the C code.
Of course there may be a delay before someone gets around to figuring it out; so in the meantime, NativeCall is actually very easy to use.
---
I would like to note that someone wrote Perl6/Raku code and the equivalent C/C++ code. They reported that the Perl6/Raku code was faster. (It was before Raku was brought up as an alias/name.)
I'm fairly sure that the reason the C/C++ code was slower is that it copied strings around rather than using pointers and indexes into other strings like MoarVM does.
I actually love doing CPU bound work in Groovy which is usually described as a scripting language. But it gets converted straight to java byte code which is JIT'd and ends up as machine code. It only takes a few static typed hints here and there and it runs in the same league as C++ implementations. And it gets Java's excellent concurrency support for free.
I'm personally with you. I also don't tend to think object boxing is really the performance bottleneck for most applications, and if/when it is, likely the other requirements should've already ruled out using one (a scripting language).
It's like writing Nifs for Elixir, yeah sure you _can_, they have their purpose, but you could also just write another application to do that one thing and like you said, use IPC.
So in summary, we agree with each other, here's to:
the right tool for the job!
Hear, hear!