6 ms·
It would be nice to know why the submitter thought this was interesting. Otherwise, I can only see it as just yet another dead language.
by Volt 8y ago
It would be nice to know why the submitter thought this was interesting. Otherwise, I can only see it as just yet another dead language.
- evincarofautumn 8y agoIt’s interesting to me because it’s both concatenative—so the basic building block of programs is composition—and array-oriented—so you can implicitly lift operations over arrays like in APL. These are both “weird” families of languages, but people who take the time to learn them tend to speak highly of them, for good reasons that are hard to explain. :) For example, here’s a program to toss a die 100 times and print the arithmetic mean of the results: : throws(*) dup 6 swap reshape ? int 1 + '+ reduce swap / ; 100 throws . “: name … ;” introduces a definition (like in Forth) with the given name, and “.” prints a value. This executes like so: # push number of tosses 100 # stack: 100 # copy it dup # stack: 100 100 # push number of sides of each die 6 # stack: 100 100 6 # swap the number of sides and number of throws swap # stack: 100 6 100 # reshape the scalar 6 to dimension 100 # i.e., generate 100 copies of 6 reshape # stack: 100 [ 6 6 6 … ] # generate a random number in # range of each element [0,6) ? # stack: 100 [ 0.347891 4.126314 2.314372 … ] # truncate each element # to an integer [0,5] int # stack: 100 [ 0 4 2 … ] # add 1 to each element to place it # within the range [1,6] 1 + # stack: 100 [ 1 5 3 … ] # sum the array by reducing # with the addition function '+ reduce # stack: 100 351 # retrieve the number of throws swap # stack: 351 100 # calculate the mean / # stack: 3.51 Note that whenever we’re applying a scalar function (“?”, “int”, and “1 +”) but its argument is an array, the operation is implicitly lifted over each element of the array. Lang5 is cool because it basically combines the terse expressiveness of APL with the compositional higher-order functional style of concatenative languages like Joy and Factor. Since everything is based on composition, you don’t need to use any local variables by default—the mantra is “name code, not data”—and you can factor out any subexpression (“extract method”) and give it a name just by cutting and pasting, like: : sum '+ reduce ; : randint ? int 1 + ; : throws(*) dup 6 swap reshape randint sum swap / ; (And even if you do use local variables, this is still an advantage in terms of simplicity of reasoning about programs.) Concatenative programming languages and array languages are basically two different approaches to “function-level programming”, a style of functional programming based on combinators instead of lambda calculus, in which all terms denote functions. A literal value like “100” is a function that accepts a stack and returns a “new” stack with the value 100 on top. In a way, they’re the “most functional” languages—yet they also have a straightforward imperative interpretation that makes them map nicely to real-world hardware. You can think of a concatenative program as a series of pure functions taking the current program state (a stack) and returning a new state, or as a series of imperative procedures mutating a stack in-place; because the stack is “linear”, consumed on each call, these two views are equivalent, so you can think about programs as either pure mathematical rewriting rules or step-by-step procedures. They also have a bunch of nice theoretical properties, especially when you add static types, that make it easy to provide good tooling and achieve good performance.
- jonahx 8y agoSimilarly in J: f=. [: (+/ % #) 1 + ?@$&6 f 100 3.58
- nathancahill 8y agoIf you want to see this family of langauges taken to the exteme, check out Code Golf: https://codegolf.stackexchange.com/ https://codegolf.stackexchange.com/
- exikyut 8y agoHuh. What a weird form of functional programming. (Cue proper realization of what "array language" means) If only this could be smoothly transferred over to mainstream languages...
- evincarofautumn 8y agoI’ve been working on a concatenative language, Kitten, which I hope eventually bridges the gap to more mainstream programmers with a seamless blend of functional and imperative semantics, useful language features that work best in a concatenative setting, and straightforward reasoning about correctness and performance. Kitten is small, but not nearly as minimalistic as other concatenative languages—while it’s meant to be a “systems” language in the realm of C++ or Rust—with unboxed data types by default and no GC required—there are various concessions for usability like a traditional tokenizer, local variables, infix operators, an expressive static type system similar to Haskell, and a compositional effect/coeffect system. Moreover, thanks to static types, thinking of the program in terms of a “data stack” is somewhat discouraged—instead of stack shuffling operations, it encourages the judicious use of local variables and dataflow combinators (e.g., patterns like “apply a function to two values and get both results”). The stack isn’t even really an implementation detail: no stack is actually present in memory at runtime in the latest iteration of the compiler, since data lives in registers or on the call stack, just like in C. Anyway, in the meantime, you can toy around with an existing concatenative language like Factor, which feels very Lisp-like and has a Smalltalk-like object system and nice interactive environment; or you can get many of the benefits of concatenative programming by preferring a compositional/dataflow style (not necessarily stack-oriented) in languages where it’s reasonably easy, such as Haskell and Clojure.
- stepvhen 8y agoany array talk on HN is fine with me. its an opaque topic and more posts means more illumination. whats particularly interesting is this is implemented in perl, and from a quick look pretty well formatted perl. which, for somebody who might be interested in array languages, would make it a much more palatable starting point for understanding how they are built as opposed to the actual arcane wizardry that is the J source code. whats more, with this laguage operating with a stack and not infix, its parsing can be more easily understood, and in turn, reimplemented. J and APL, on the other hand, are context sensitive, and require more detailed understanding to implement.