In a somewhat similar vein, I made a concatenative array language inspired by kdb+/q: https://cryptm.org/xs/
Unlike other concatenative languages, the order of operation is right to left, and it supports infix operators. This gives the language a very conventional array language feel, while also giving you the power and flexibility of the stack.
As far as I'm aware, there's really nothing like it. Would love if someone gave it a try!
Here's an example, the factorial function:
xs> fac:(prod 1+til);
xs> fac 4
0: 24
You can also define new infix operators:
xs> add:{+.};
xs> 3 add 2
0: 5
Here's an example of generating a sequence of values, where n_t=n_{t-1}/2:
xs> (x:;x%2) fixes 12
0: [12 6 3 1 0]
Note how when we reach the fixpoint of zero, the recursion stops. These kinds of implicitly recursive combinators are very powerful. Also, equality on floats is not strict, so we can also do this with floats, without having to worry about small differences in precision. This is useful when working with equation optimizers and root finding methods.
I do have some examples in the examples section. There's a link to the OCaml implementation on the page as well. A repl.it would be awesome, I'll definitely do that.
I hadn't heard of golfscript, but those would make some really great examples. I do have a couple Advent of Code 2019 solutions though.
It is often said that K can beat the speed of C for some tasks.
The name of this site is "nsl" for No Stinking Loops.
Here is an array programming challenge for the K programmers reading this thread.
There are two large files A and B.
File A
AAAbjhOJxxxWXrCHt
AAAsofxxxLqKbYOAwLMHG
AAACgxxxOea
AAAwfHjDtoJc
AAAiCfYnbVXWhuvtEx
AAAkzzQwvf
AAAdjLalKFapoYAcGE
AAAthGazmnfK
etc.
File B
AAAwfHjDtoJc
AAAbjhOJxxxWXrCHt
AAAiCfYnbVXWhuvtEx
AAACgxxxOea
AAAkzzQwvf
etc.
In J (which might be slower than K) with excessive comments for a one-liner:
echo@> (2{ARGV) -.&([: <;._1 LF, 1!:1) (3{ARGV)
NB. 2nd arg 3rd arg
NB. g&f execute f for each, then g on both
NB. 1!:1 read file
NB. LF, prepend newline
NB. [: <;._1 split based on first char
NB. -. remove right elements from left array
NB. echo@> echo each line
exit 0
On two ~1.6MB files with ~15k lines (both the same except 3) I had lying around:
$ time j9 -c ./pseudo_grep.ijs test_b test_a
…
real 0m0.064s
user 0m0.032s
sys 0m0.017s
$ time grep -vf test_b test_a
…
real 0m5.815s
user 0m5.234s
sys 0m0.576s
Note that most of the script is for loading each file into an array of lines. Most work is done by -. on the two arrays, which is exactly what you asked for, e.g. 0 1 2 3 4 -. 2 4 is 0 1 3. https://code.jsoftware.com/wiki/Vocabulary/minusdot#dyadic
That grep is not doing the same thing as the code, nor necessarily what the exercise requires. By default, grep tests patterns, so it's turning all those entries into individual regular expressions. You want to use fgrep, or the -F flag to make it treat all the source matches as fixes strings.
In my simple test, that resulting in grep running in 44% of the prior amount of time it required (still more than python though).
I think theres probably a sweet spot in how large the files are compared to the method used, because eventually disk access may dominate the running time. Putting files on a ram disk (/dev/shm on some distros) would help.
I tested with files just over 2 MB on a small Digital Ocean VM. Depending on disk speed, based on running time I suspect you ran on files at least an order of magnitude larger. What time did python run in for those? Seeing memory usage from time might be illuminating for these tasks too. Using 4x the disk size in memory is fine for a couple MB file, but less so for a couple GB file (in which case creating a bloom filter or trie might be better, but I really have no idea if Pythons set functions do that already).
Loops are 100% the worst offenders of software complexity. Loops and tail recursion are the most powerful constructs we have, and are too powerful for the vast majority of problems. When using the principle of least power you find that a loop or tail recursive function is exceptionally rare.
They might be a great source of algorithmic complexity, but I would argue that by far the most part of software complexity is architectural, not algorithmic.
When you can reduce the amount of code you have to write by 100x, architecture no longer becomes important. When all programs are so short, they become very cheap. And when they become cheap, they are easy to replace and rewrite. At this point, architecture becomes irrelevant because you've removed all abstractions in the first place.
If the components of your company's system never are longer than a page or two of code, rewrites become trivial, making planning for the future unnecessary. This is the real promise and value of array languages.
Or put more simply, the size of a code base is the dominant factor in its complexity. Less code is less complex code, is better code.
Also, loops imply mutation, which is the second biggest indicator of complexity. Mutation kills code bases and kills projects. When you decide to never use a loop and to never mutate, you're on the right path. Now the next step is to try and remove as many lines of code as humanly possible.
A good way to measure how you're doing is by comparing the size of your gzipped (or zipped or whatever) codebase to its uncompressed form. If they are close in size, you know you are on the right track. Or in other words, you want the size of your code base to be close to its Kolmogorov complexity.
Less code to write, less code to read, less code to maintain, executes faster[1]. What's not to like?
What about someone who thinks "if" is a big source of software complexity? https://www.youtube.com/watch?v=z43bmaMwagI - talk is "If considered harmful: How to eradicate 95% of all your bugs in one simple step - Jules May".
[1] in interpreted languages, anything you can push down for the runtime to do instead of writing that same code in the interpreted language, tends to execute faster. e.g. my_string.count('c') in Python compared to a `for` loop over the characters written in Python.
`A 0: ("some";"lines";"are";"here"); / write to file A
`B 0: ("more";"lines";"follow";"here"); / write to file B
_/ 0:' `A`B / read files A, B and find lines of B that are not in A
more
follow
I've no experience with this language or it's genre of languages, but I doubt there is a good answer for you.
You're working in a very different space (business oriented, needs networking protocols, probably no major number crunching, your collection of languages do fine for your job etc.)
Some languages are just explorations of the language space, disjoint from your language-using space therefore irrelevant to you directly, and may be (currently) only of academic interest. I'd learn this if I had time, to understand different paradigms, and for fun. Sometimes this is useful. Sometimes not.
I don't know how F is implemented but in a lot of stack languages printing to the terminal isn't really a side effect. Every function takes the state of the entire world as it's one and only parameter and returns a new world.
Exactly. If the program won't read it (the printout) back, or unless it is designed specifically to sense the heat, then, strictly speaking, it's not a side effect (in the particular sense in which a side effect may reflect on the program execution).
The N programming language (1990?) is spookily reminiscent of PyTorch!
> Expecting a wide use of neural network algorithms in the near future, our objective is to get a complete software development environment for programming and testing new applications. We intend to produce a high level language for neural network specification, as a part of such an environment.
> The language we propose is characterized by a high degree of modularity, based on parameterizable data structures, with functionalities in the form of update methods attached to them. Composition rules of structures and methods enable to build, step by step, more complex structures from smaller ones previously defined. Objects are viewed as autonomous modules which are linked through plugs for communications. We particularly cared for the parallelization of methods running concurrently on different objects of the network. The syntax is largely related to those of the C and C++ languages.
M omits the language also known as MUMPS, and also there was a second .Net based language informally called M#, apparently developed by Microsoft Research as part of Midori project, and called "C# for Systems Programming" in public communications from the developers.
I would be shocked if there were any single letters remaining unused for programming languages. We reached <letter>FS saturation for filesystems many years ago, after all.
Unlike other concatenative languages, the order of operation is right to left, and it supports infix operators. This gives the language a very conventional array language feel, while also giving you the power and flexibility of the stack.
As far as I'm aware, there's really nothing like it. Would love if someone gave it a try!
Here's an example, the factorial function:
You can also define new infix operators: Here's an example of generating a sequence of values, where n_t=n_{t-1}/2: Note how when we reach the fixpoint of zero, the recursion stops. These kinds of implicitly recursive combinators are very powerful. Also, equality on floats is not strict, so we can also do this with floats, without having to worry about small differences in precision. This is useful when working with equation optimizers and root finding methods.