Uiua: A minimal stack-based, array-based language
uiua.org
uiua.org
Edit: I think I understand from the language tour. You're supposed to write the ascii names like `find` then running the program converts built-ins to glyphs.
That glyph panel is pretty neat because it also serves as a language reference and you can ctrl+click into the documentation pages.
# create an array of number 1-100
ns ← ∵(+1)⇡[100]
# make a copy of it in a string form "1"-"100"
nsStr ← ∵(□$"_") ns
# every i % 3 == 0 is replaced with "Fizz", everything else is ""
f ← ∵(□▽∶ "Fizz" =0◿3) ns
# every i % 5 == 0 is replaced with "Buzz", everything else is ""
b ← ∵(□▽∶ "Buzz" =0◿5) ns
# every i % 3 == 0 && i % 5 == 0 is replaced with "FizzBuzz", everything else is ""
fb ← ∵(□▽∶ "FizzBuzz" × =0◿5∶ =0◿3 .) ns
# combine them into a 2D array
gs ← [nsStr f b fb]
# run a columnwise map
# | pick the greatest string of each column
# | |
⍉ ≡ (⊡⊢⇌⌂.) ⍉gs
Inability to use maximum function with arrays of different sizes is a bummer, I had to workaround it with grade and pick. ⍉[∵(□$"_"+1)⇡100
[⍥.99□"Fizz"]
[⍥.99□"Buzz"]
[⍥.99□"FizzBuzz"]]
+∵(×2=0◿5+1)⇡100 ∵(=0◿3+1)⇡100
≡⊡
⍉
There are probably a few more simplifications experienced array programmers can apply. FB=:((0 i.~15 3 5|]){::'FizzBuzz';'Fizz';'Buzz';":)"0
FB i.100
I unfortunately don't know enough J or Uiua to be able to translate that Perhaps someone else can step in!This is the case with most of the languages in the APL family, but especially APL [1] and BQN [2].
[1] https://en.wikipedia.org/wiki/APL_(programming_language)
APL (the first, invented in the 1960s): https://en.wikipedia.org/wiki/APL_(programming_language)
BQN (a modern APL, looks like an inspiration for Uiua though I don't know): https://mlochbaum.github.io/BQN/
Too many smaller esoteric languages to count.
I shall call all instances of ternary conditionals "wut" statements now.
How would I go about file i/o and parsing json? I have a data processing benchmark[1] that I'd like to use this for.
Why is Uiua right to left?
example fibanacci fn:
fib:(n:(neg 1)+;0@n do(2 enlist 1@x sum x~)[0 1]); ♭ deshape flattens an array into a 1D array.The difference between scanning algol-style and apl-style code is a little like the difference between scanning history books and maths books: on one hand, one must scan the latter much more slowly (symbol by symbol, not phrase by phrase), but on the other hand, there's much less to scan than in the former.
* Edit: as an example, compare the typical sub-10 character expression of Kadane's algorithm in an array language with the sub-10 line expressions one typically finds online.
They make the argument "the word 'average' has more symbols than its definition, so why not just use the definition inline as a tacit function?"
There's some elegant beauty in this that I'm sympathetic to. However, I think it's fundamentally flawed for one big reason, which in my opinion is the core of the unreadability of APL (and other array languages that rely on custom symbology):
Humans think in words, not letters. There is never semantic content in individual letters; a word is not defined by its letters. Sometimes a word's definition can be deduced from its syllables ("un-happi-ness") but the number of prefixes/suffixes is miniscule compared with the number of root words.
We naturally chunk concepts and create referential identifiers to the abstractions. The point of an alphabet is to make the identifiers generally pronounceable and distinguishable.
+/÷≢ is not pronounceable as a single word, even among APL experts ("add reduce divide tally" is not a word). We have a word for this concept in English, "average" (and synonyms "avg" and "mean"), in common usage among programmers and laymen alike. Using +/÷≢ to mean "average" would be like defining a function AddReduceDivideTally(x), which in any reasonable codebase would be an obviously bad function name.
The semantics of array languages, like stack languages, already lend themselves to extraordinary expressivity and terseness, even without a compressed symbology. What is wrong with this?
sum := add reduce
average := sum divide tally
I mean that is it, the essence of "average"! Anyone who knows both English and Computer Science can look at that and understand it more or less immediately. Compressing this into an esoteric alphabet does nothing for furthering understanding, it only creates a taller Tower of Babel to separate us from our goal of developing and sharing elegant definitions of computation concepts.I’m not entirely certain most programs will ever be pronounced.
> What is wrong with this? average := add reduce divide tally
Nothing, but but it seems to imply that this: average := +/÷≢
… nicely resolves this issue of wanting to use words — AKA sequences of pronounceable but semantically meaningless glyphs —- as human-friendly (at least for English-fluent humans) mnemonics for programs — AKA sequences of not-muscle-memory-pronounceable but semantically meaningful glyphs), yeah?
Mind you I haven’t looked at the language deeply enough to know you can actually define an alias like that.
My opinion on the overall question, which I've written about at [0], is that it's very widely acknowledged that both symbols and words are useful in programming. This is why languages from PHP to Coq to PL/I all have built-in symbols for arithmetic, and usually a few other things, and built-in and user-defined words. The APL family adds symbols for array operations, and often function manipulation. Perhaps not for everyone, but, well, it's kind of weird to see a proof that a language I use to understand algorithms more deeply couldn't possibly do this!
[0] https://mlochbaum.github.io/BQN/commentary/primitive.html
That's true. Just a nitpick: it's not about letters, it's about things that make up a word, and many words in natural languages are made up of smaller parts that have meaning.
But people do tend to forget them and just use the resulting word as a unit
Can't think of a good example in English but in Italian the word "alarm" is "allarme" which comes from "alle armi !" (to the weapons!). At some point people used that word to mean you should grab your weapons and then later they just meant it metaphorically. Most people today don't even make the connection between these two words despite it being right in the face.
What would be an equivalent example in English?
In some languages, e.g. Chinese, each symbol means more than a single letter would in a latin alphabet. These languages to me are just a bit more like that.
I agree with your overall point (which is, I think, DRY) but not this specific criticism.
So I easily grant you this, though I don't think it's much different in practice than using subroutines.
But even if I grant you that a high level of experience allows rapid scanning, there's still a barrier of translating to and from English (or whatever language you develop in) that seems to be higher due to the symbolic nature of the language. This is also true with certain procedural languages, but there's also been an enormous number of think pieces about how best to encode procedural programs so that they do naturally translate into natural language domains. I'd imagine that not being able to leverage those decades of discussion straightforwardly would be a loss.
Granted, this is also a problem with stuff like Haskell too, but the type system goes a long way to ameliorating this concern by classifying structures very rigidly.
If you're looking for something more understandable, rebol syntax is phenomenal. It's a concatenative prefixRL language like Uiua (forth is postfixLR), which you can use like a stack or array language by passing the stack/array as the far-right operand. http://blog.hostilefork.com/rebol-vs-lisp-macros/
Furthermore it handles types and has (declarative) scoping unlike say forth which is typeless (panmorphic) and global.
The idea with rebol, similar to Joy, is that operations-on-a-stack is analagous to passing-mutable-stack-to-function so you get tacit programming with both approaches. PrefixRL allows more of a lispy feel, especially when combined with blocks.
Emoji is not just for tap-typing/swipe-typing mobile users. (Also, I think it is really handy as developer to learn your local emoji IME: including emoji in test data is really handy for checking unicode safety in your applications and regularly using any IME at all while in your applications helps you test some accessibility issues that might affect users that must use an IME for there language such as CJK languages and Braille writers and more. English software developers get to overlook a lot of how languages around the world work and can easily break accessibility needs with bad assumptions and it is great that emoji are a grand field leveling tool to bring those experiences to us English speaking developers in a way we can easily "read"/"write".)
I'm well aware it's possible to type eg Chinese rapidly, and I use the Windows emoji entry keyboard quite often.
Lisp beats APL by a few years though, so there it really depends on whether you value lisp's seniority or APL's connection to math notation traditions more when judging how "classical" a language syntax is.
⌊×10[⍥^999]
yields a list of numbers, but ⌊×10[⍥^1000]
yields an audio clip. That's somewhat unexpected.The ^ above stands for the 3-dot cube dice glyph, which gets eaten by the HN backend, so it's ^ instead.
[1 2 3 ... 99999]
Which is unsurprising, but not very usable. Audio is useful!I think using simple words: find, reverse, etc. is probably a better option.
(Also, this language supports the "simple words" approach but autoformats to the glyphs for final "readability".)
Just write "random" instead, then the snippet is copy-pasteable to the web-based interpreter. That's how you would normally write it out anyway, and interpreter will turn that into the dice glyph when run.
Not affiliated, just recommending. The regular co-hosts appear to each be experienced with various array languages such as J, APL, etc. They don't get deeply technical, but it's a nice introduction, especially on explaining the appeal.
A recent episode had Rob Pike (UTF-8, Go, etc.) on to talk about his array based calculator, Ivy[2]
Here's a 2-layer perceptron, first in Python+NumPy:
import numpy as np
X = np.array([[0,0,1], [0,1,1], [1,0,1], [1,1,1]])
y = np.array([[0,1,1,0]]).T
np.random.seed(1)
w0 = 2*np.random.random((3,4)) - 1
w1 = 2*np.random.random((4,1)) - 1
for j in range(10000):
l1 = 1/(1+np.exp(-(X @ w0)))
l2 = 1/(1+np.exp(-(l1 @ w1)))
l2_error = y - l2
l2_delta = l2_error * l2 * (1 - l2)
l1_error = l2_delta @ w1.T
l1_delta = l1_error * l1 * (1 - l1)
w0 += X.T @ l1_delta
w1 += l1.T @ l2_delta
print(np.round(l2,3))
[[0.007]
[0.991]
[0.992]
[0.01 ]]
And again in J: input =: 4 3 $ 0 0 1 0 1 1 1 0 1 1 1 1
target =: 4 1 $ 0 1 1 0
dot =: +/ .*
sig =: {{ %>:^-y }}
train =: {{
'ignore_me w0 w1' =. y
l1 =. sig input dot w0
l2 =. sig l1 dot w1
l2_error =. target - l2
l2_delta =. l2_error * l2 * 1 - l2
l1_error =. l2_delta dot |: w1
l1_delta =. l1_error * l1 * 1 - l1
w0 =. w0 + (|:input) dot l1_delta
w1 =. w1 + (|:l1) dot l2_delta
l2;w0;w1 }}
5j3":0{::train^:10000 {{<:+:?.y$0}} each 1 ; 3 4 ; 4 1
0.009
0.990
0.990
0.011
Here's that assembler (for the Nand2Tetris assembly language) in K. It's intentionally golfed. L:({x^"\r "}'*'"/"\'0:0)^,""
T:(("R",'$!16)!!16),(($`SCREEN`KBD`SP`LCL`ARG`THIS`THAT)!16384,24576,!5),((-1_1_)'L@&i)!{x-!#x}@&i:"("=*'L
T,:n!16+!#n:?(({$[("@"=*x)&^`I$1_x;1_x;`]}'L)^!T)^`
C:(" "\"D&A D+A A-D D !D D-1 -D D-A D|A D+1 0 A !A A-1 -A A+1 -1 1")!0 2 7 12 13 14 15 19 21 31 42 48 49 50 51 55 58 63
D:$`" "`M`D`MD`A`AM`AD`AMD
J:$`" "`GT`EQ`GE`LT`NE`LE`MP
B:{,/$(x#2)\y}
P:{c:*d:|"="\*j:";"\x;"111",/(B.7,(64*|/"M"=c)+C@"A"/"M"\c;B.3,D??d 1;B.3,J??1_j 1)}
`0:({(P;{"0"^-16$,/$2\(`I$1_x)^T@1_x})[2/"@"=*x;x]}'L)@&~i[1] https://help.dyalog.com/latest/#Language/Control%20Structure...
https://www.dyalog.com/case-studies/index.htm https://github.com/interregna/arraylanguage-companies
If you're already experienced with array languages I'm not confident to recommend any particular episodes yet. You might find the Rob Pike/Ivy episode entertaining but maybe not informative. Rob is not an array language expert (he says this in the episode) but he made Ivy partly from nostalgia of APL in his early days. His recollection of that era I found good to listen to.
Full disclosure: I am on the ArrayCast
The thing that stands out the most to me is how the language itself functions as a work of art.
I do have to question the choice of right-to-left (array language) evaluation order. I've personally always preferred left-to-right (stack language) evaluation. I feel like right-to-left requires you to think to the end of a line before you start typing anything at all, as the first thing you type is the last thing that's evaluated. Left-to-right would also allow re-evaluating and visualizing the stack as you write each operator.
Many people who are used to lisp would probably prefer prefix notation. non-stack or array langs have a nice tradeoff that you can look at the code without having to keep a model in your head, notably many forth users annotate their code with stack diagrams to help. Personally I'm quite taken with rebol syntax, passing a stack/array as the rightmost operand. http://blog.hostilefork.com/rebol-vs-lisp-macros/
Maybe this will make people tend to shorter lines, counterbalancing the natural tendency towards incomprehensibility of array and stack languages
Edit: I'll be specific. Stranding required a special character in BQN '‿', but appears to be just '_' in Uiua.
I like the use of non-ASCII Unicode glyphs and symbols for some functions/commands.
It makes me wonder why (since we now have rich graphical user interfaces as opposed to monochrome ASCII command-line dumb terminals that were prevalent in the mid-early history of computers) more languages don't support those.
On a related note, I think it would be interesting to research computer languages which were developed in base languages that are not English, i.e., any computer languages originating out of India, Russia, China, Japan, etc. -- to name a few...
Maybe there's such a thing as an Esperanto programming language...
Getting back to symbols though, a future society might implement a purely symbol based programming language.
Were ancient Egyptian hieroglyphs -- a programming language for a computing architecture that has vanished in the depths of time?
One can only speculate!
We do know that in our current day there are programming languages like Mathematica, where Math symbols can be used in lieu of English(y) code, and there may be a trend to more symbols replacing ASCII named/spelled keywords and functions in our code in languages of the future...
The use of symbols to replace words might result in smaller tighter, more readable code -- but (I'm thinking from the perspective of a future society here) it also might sever the bridge to computing's past...
Which may be desirable to have, IF that future society would want to resurrect computers and compuation from a future apocalyptic event...
But perhaps I'm thinking too hard about all of this...
Interested parties may want to check out the following video:
"Why are these 32 symbols found in caves all over Europe | Genevieve von Petzinger":
https://www.youtube.com/watch?v=hJnEQCMA5Sg
Or, fast forward to this point to see the 32 symbols:
https://youtu.be/hJnEQCMA5Sg?t=403
Also, here is a great tutorial page for people who are new to stack-based languages:
https://skilldrick.github.io/easyforth/
Anyway, Uiua looks very nice!
Of course, as such things go, APL's appeal fell as the mainframe world settled on EBCDIC or ASCII (depending on if you were an IBM shop or not) as a "universal" character encoding and also as saving punch card space was less of a problem as other storage systems became more prominent.
Uiua seems a fun "emoji-native" take on "your grandmother's" APL. (BQN is generally cited as among the first "Unicode-native" in the APL family. Beyond the stack language changes as neither APL nor BQN are stack languages, Uiua swaps some niftier emoji-based choices for operators that feel fresher and less "ancient math hieroglyphics" and little bit more "texting your modern friend who in this case is a computer" hieroglyphics.)
It feels like there really is something special about how the combination of all the little things make it more than the sum of its parts. On a language level therews a stack + absence of operator overloading + simple array model to make it easier to reason about. On an interface level there is word-based input with autoformatting, and a zero-install web-based interface with reminder tooltips and clickable links.
The examples are a lot easier for me to read through than with other array languages I've tried. And I've seriously tried both Dyalog and BQN! I think they're brilliant, but something just refuses to click in my brain for them.
J & APL are elegant but not easy to get started with, and difficult to install. This looks like a newer, nicer, more open version.
Would be great to see some Advent of Code puzzles in Uiua..
> The main language that inspired Uiua is BQN. While I had heard about APL before, BQN was my first real exposure to the power of the array paradigm. I think the language is an astounding feat of engineering. Marshall is both a genius and a great communicator.
https://www.uiua.org/docs/design
Also, a week ago there were only two contributors to the project: 1000+ by kaikalii, and this single commit by Marshall:
Think I would like Uiua more if it required a space between terms. It wouldn’t be quite as tacit but it would be easier to read and more flexible — you could have multi-symbol operators.
Here's a blog post that tries to motivate them: https://evincarofautumn.blogspot.com/2012/02/why-concatenati...
Here's another interesting recent attempt: https://www.dawn-lang.org/posts/introducing-dawn-(part-1)/
I'm a bit surprised by
[1 2 ; .] 3
Producing [1 2 3]
Instead of 3
[1 2]That is a little surprising, but making stack operations special in that they don't need to pop their arguments might be a worse option.