/r/ProgrammingLanguages
This subreddit is dedicated to the theory, design and implementation of programming languages.
This subreddit is dedicated to the theory, design and implementation of programming languages.
Be nice to each other. Flame wars and rants are not welcomed. Please also put some effort into your post, this isn't Quora.
This subreddit is not the right place to ask questions such as "What language should I use for X", "what language should I learn", "what's your favourite language" and similar questions. Such questions should be posted in /r/AskProgramming or /r/LearnProgramming. It's also not the place for questions one can trivially answer by spending a few minutes using a search engine, such as questions like "What is a monad?".
/r/ProgrammingLanguages
I am hoping to convert these 10 Haskell files of Kind lang into TypeScript using Claude AI (once I get another batch of requests after hitting the limit...). But in the meantime...
Are there any (ideally somewhat robust) dependently typed programming languages with the typechecker / compiler written in TypeScript?
Victor Taelin (author of Kind), also wrote ITT-Flavored Calculus of Constructions Type Checker, which is in TypeScript, but it's not anywhere near as robust as Kind, unfortunately (dang, so close!).
Anything else out there to learn from? If not TypeScript, any other imperative language would be fine (TypeScript > JavaScript > Ruby > Python > C).
Across various subs I see lots of posts asking if X language is good to start with. Java, JS, C#, Python, C, etc.
Usually speaking the answer is “Yes, if you can make projects that will interest you with it”. I rarely see a “no, don’t start with this”, and the closest is usually “Eh, you might be better off with something else but it won’t matter too much.”
So what’s a language you feel is actively a BAD decision for a new programmer to start with as their very first language?
I’m talking about commonly used languages too, of course. Not like… idk, Algol which is rarely used in this day & age
Hi everyone!
I'm quite happy to announce that after almost two years since the first release, my Goal array language, with a focus on scripting, has just had its first stable release! I posted here an update about it last year, but this year has been quite productive, so a lot of things happened since then.
First, several features were introduced to facilitate table processing, including column-select and row-filter dedicated functionality, as well as what I call “field expressions”, originally inspired by an “expression” feature as described in a tutorial for a non-released K version. Those “field expressions” allow to write expressions that refer to table columns as you would refer to variable names, a bit like in SQL or some dataframe packages like dplyr, but without introducing a separate DSL, only a couple of syntactic sugar rules. The FAQ has a detailed entry about them that also links to a new tutorial and translations from dplyr and Lil queries at the end. While you won't get as much features as with a full dataframe package like dplyr or data.table, Goal allows to solve typical dataframe problems in a lighter language with fast startup-time and without introducing any special DSLs on top of the main language. I'm quite satisfied with how that part ended up :-)
More recently, I introduced read-only file system values to Goal, based on Go's fs
package. They're nice in at least two ways: they simplify writing portable (read-only) operations on file systems (including virtual ones like zip files), and they make it possible to simplify deployment by easily embedding a set of Goal files as a file system value in a Go program and then using import
as if they were on the operating system's file system.
There were many other changes, among them: tail-call self-recursion optimization, improved pipe support (with configurable redirections), glob matching, better NaN support, improved error handling (error values have now a standard way of providing a message), to name a few. And many cleanups and fixes, of course, as well as fleshing out the documentation. You can see the full changelog for the details.
The core language will probably not see any significant new features anymore, as it's now stable and most new stuff will be provided as optional extensions: Goal is designed to be extended by creating derived interpreters (which can be done in just a few lines of code, as explained in a new tutorial about providing zip file-system support) or by embedding it into a larger Go program. The work-in-progress ari third-party project by semperos already provides, for example, an sql mode (interacting with duckdb), an http client, and more. I'm hoping to see more interesting extensions by users!
Thanks for reading. I hope you enjoy!
How much progress have you made since last time? What new ideas have you stumbled upon, what old ideas have you abandoned? What new projects have you started? What are you working on?
Once again, feel free to share anything you've been working on, old or new, simple or complex, tiny or huge, whether you want to share and discuss it, or simply brag about it - or just about anything you feel like sharing!
The monthly thread is the place for you to engage /r/ProgrammingLanguages on things that you might not have wanted to put up a post for - progress, ideas, maybe even a slick new chair you built in your garage. Share your projects and thoughts on other redditors' ideas, and most importantly, have a great and productive month!
Nim has a feature where a variable representing the return value of a procedure is automatically declared with the name result
:
proc sumTillNegative(x: varargs[int]): int =
for i in x:
if i < 0:
return
result = result + i
I think a tiny tweak to this idea would make it a little bit nicer: allow the return variable to be user-declared with the return
keyword:
proc sumTillNegative(x: varargs[int]): int =
return var sum = 0
for i in x:
if i < 0:
return
sum = sum + i
Is this already done in some other language/why would it be a bad idea?
I've never thought too deeply about how call stacks and stack-based memory allocation work (beyond the basics of the return pointer, tail recursion and the fact that there's no need to explicitly deallocate memory) but today I was wondering if the stack was used to manage scopes within a function, and in particular how destructors can be called on stack-based items (i.e. how do you know the boundaries between items, and some minimal type info that's needed)?
I found https://en.wikipedia.org/wiki/Stack-based_memory_allocation and saw its mention of the need for a frame pointer once you have variable length frames, but can folks here recommend a more in-depth treatment of the subject?
TIA!
So I'm working on a lua like language, while taking a break from my main project. I thought of an interesting optimization for the problem of tables being a massive bottle neck in JIT compiled lua-like languages.
Tables take a while to access fields. But my language has static types, so what if I treat tables that we know will have certain values as "structs" during compilation. Example:
let Person = {
name = "",
age = 0
}
Type of person is now { "name" String, "age" Number }
, but because Person can be cast to a normal table and is still a table we can't treat it like a struct.
Person.dob = "October 26, 1979"
What if we bring the known fields out of the table, and just reference them in the table. So the memory layout will now look something like this:
# The Cover object header
| Size | # Includes the string & number
| Type ID | # Different from a normal table
# the fields
| Table { Any Any } | # the table
| String | # name
| Number | # age
And in that table, we put references to name
and age
that would unwrap automatically, if you are requesting it through a table, but the compiler can also optimize the calls to just accessing the right indexes in a tuple. And even if we cast a different table to a type(Person)
it won't cause problems because the type IDs will be different and we can make sure that those fields exists there.
EDIT: Some qualifications:
By static types, i mean that this technique requires static types that are possible in the language. This is still lua-like language, by static i mean closer to c# or typescript.
I want to support using the file system to define modules / objects along with subdirectories structures.
So, the file foo.ext
would create the object foo
and bar/foo.ext
would create the object bar.foo
All good there.
Now things get slightly convoluted if I support source paths as a list of places where the source can be:
Say I have:
src/
bar/
foo.ext
etc/
bar/
baz.ext
I can invoke the building tool like:
tool sourcepath: "src, etc"
And it will create bar.foo
and bar.baz
it finds the objects in the directories src/ and etc/
Now my question: What do you think my tool should do if the source path contains a subdirectory:
tool sourcepath: "src, src/bar"
Strictly speaking it would create the objects bar.foo
(from parent src/
and path bar/foo.ext
) and foo
(from parent src/bar
and path foo.ext
)
Should that be a warning? An error? Should that be two different objects with different fully qualified names but same content?
Evaluate right-to-left or left-to-right?
I love APL's lack of precedence, and I love C and C++'s power. I write mostly C++ but have done extensive work in K and Q (APL descendants).
I have been toying with a language idea for about a decade now
that is an unopinionated mix of C, C++, Rust, APL, and Java.
One of the things I really liked about K was how there is no
precedence. Everything is evaluated from right to left (but
parsed from left to right). (eg, 2*3+4
is 14, not 10).
Is something like that possible for a C-like language? I don't mind making the syntax a little different, but there are certain constructs that seem to require a left-to-right evaluation, such as items in a struct or namespace (eg namespace.struct.field).
However, function application to allowing chaining without the
parens (composition) would need to be rigt-to-left (f g 10
).
But maybe that isn't a very common case and you just require
parens.
Also, assignment would seem weird if you placed it on the right for left-to-right evaluation,and right-to-left allows chaining assignments which I always liked in K.
// in K, assignment is : and divide is % and floor is _
up: r * _ (x + mask) % r: mask + 1
with such common use of const by default and auto type inferance,
this is the same as auto const r = ...
where r can even be
constained to that statement.
But all that requires right-to-left evaluation.
Can you have a right-to-left or left-to-right language that is otherwise similar to C and C++? Would a "mostly" RtL or LtR syntax be confusing (eg, LtR except assignment, all symbols are RtT but all keywords are LtR, etc?)
// in some weird C+K like mix, floor is fn not a keyword
let i64 up: r * floor x + mask / r:mask + 1;
R3 is a concatenative language inspires by ColorForth, taking simplification to the maximum, strongly typed, in fact there is only one type of data and therefore it is not necessary to specify it.
I'm interested in hearing feedback, preferably constructive (but not exclusive), about what people who like language design think about the design decisions.
Following recent discussions here I am wondering if anyone has successfully implemented a syntax they think makes their code beautiful but which shuns the usual tooling and/or approaches, i.e. not LL(k), LR(k), LALR(k) etc.?
I have been toying with the idea for a while but never took the leap because I fell for the academic koolaid.
EDIT: Some great examples here. Zimbu's asymmetric }
:
FUNC Main() int
RETURN exitVal
}
Pipes to demark indentation:
int main()
| printf("Hello World\n");
| return 0;
"Eyebrowless" JSON inspired by Jason where all :
, ,
and "
have been replaced with minimal whitespace for a 15% space saving and probably faster parsing to boot:
{id "0001" type donut name Cake
image {url "images/0001.jpg" width 200 height 200}
thumbnail {url "images/thumbnails/0001.jpg" width 32 height 32}}
I considered this:
for i ∈ [0..9) do
print i
C has some ambiguities. Is it a multiply or a pointer type:
T ** c;
Is it a++ + b
or a + ++b
:
a +++ b
I was hoping for beautiful weirdness but this is interesting!
I really like haskell types and maybe even the syntax but the syntax is weird to most people.
I was wondering if someone has attempted to do add haskell typing to Python (mypy isn't really it).
Or.. maybe make a python-like syntax that compiles to Haskell.
Would this be something useful?
It seems to be a decent rule of thumb that any language used to instruct a computer to do a task is Turing complete (ignoring finite memory restrictions).
Surprisingly, seemingly simple systems such as Powerpoint, Magic: the gathering, game of life, x86 mov, css, Minecraft and many more just happen to be Turing complete almost by accident.
I'd love to hear more about counterexamples. Systems/languages that are so useful that you'd assume they're Turing complete, which accidentally(?) turn out not to be.
The wiki page on Turing completeness gives a few examples, such as some early pixel shaders and some languages specifically designed to be Turing incomplete. Regular expressions also come to mind.
What surprised you?
TLDR: Made an interpreted language (based on Lox/Crafting Interpreters) with a focus on design by contract, and exploring the possibility of having code blocks of other languages such as Python/Java within a script written in my lang.
I worked my way through the amazing Crafting Interpreters book by Robert Nystrom while learning how compilers and interpreters work, and used the tree-walk version of Lox (the language you build in the book using Java) as a partial jumping off point for my own thing.
I've added some additional features, such as support for inline test blocks (which run/are evaled if you run the interpreter with the --test flag), and a built-in design by contract support (ie preconditions, postconditions for functions and assertions). Plus some other small things like user input, etc.
Something I wanted to explore was the possibility of having "blocks" of code in other languages such as Java or Python within a script written in my language, and whether there would be any usecase for this. You'd be able to pass in / out data across the language boundary based on some type mapping. The usecase in my head: my language is obviously very limited, and doing this would make a lot more possible. Plus, would be pretty neat thing to implement.
What would be a good, secure way of going about it? I thought of utilising the Compiler API in Java to dynamically construct classes based on the java block, or something like RestrictedPython.
Here's a an example of what I'm talking about:
// script in my language
fun factorial(num)
precondition: num >= 0
postcondition: result >= 1
{
// a java block that takes the num variable across the lang boundary, and "returns" the result across the boundary
java (num) {
// Java code block starts here
int result = 1;
for (int i = 1; i <= num; i++) {
result *= i;
}
return result; // The result will be accessible as `result` in my language
}
}
// A test case (written in my lang via its test support) to verify the factorial function
test "fact test" {
assertion: factorial(5) == 120, "error";
assertion: factorial(0) == 1, "should be 1";
}
print factorial(6);
Long-time Linux user but mostly avoid using Bash since I don't like it. I wrote a programming language once already, and that didn't go so well. The problem I'm having lately is that I can't kick this idea I have for a shell.
I've looked at and/or tried what feels like a dozen alternative shells, and none of them hit that sweet spot. I want a shell that gives me Bash's job control, redirection, and so on, but with control flow that I can remember.
The shell has two modes for parsing. Which one it picks depends on the first token after the initial identifier. It looks like this:
#!/usr/bin/env xyz
print('Hello world!') # ( = exec
echo 'Hello, world!' # String = command
var name = "BOB" # keyword var = exec
@name = @name.lower() # @ required for vars
print('Hi ' ++ @name) # not sure about this concat
echo 'Hi ' @name
I hate memorizing letters for test
, so spell it out:
if Path.is_dir("/bin") {
...
}
Core literal types: Integer, String, List, Hash. Not interested in adding floats. Might add actual booleans, or might just use Integer as boolean...
String.to_i_or(...)
to prevent oops string didn't convert, script is now a bomb.
Core types have a bunch of methods on them: Lists have a push, a size, strings can be split, etc. Strings have ".words" to get individual space-separated words. Possibly ".words_or('...')." Batteries very included.
Non-literal types like Json with special transformers:
var my_data = @["a": 1, "b": 2]
Json.write(@my_data) > some_file.txt
Explicit word expansion:
var abc = "a b c.txt"
ls @abc # ls "a b c.txt"
ls @abc.words() # ls "a" "b" "c.txt"
Environment variable set and get:
var my_dirlist = $['PATH'].dirs_to_list() # convert to List[String]
paths.push($['HOME'])
$['PATH'] = paths.to_dirs()
Builtin commands (ex, a history) have typed alternatives (always? usually?), with more common flags available by methods:
history -c # clear history
History.clear() # Same, but typed
Running a command with some temporary adjustment should be like this:
with <assignment> { commands }
By default, commands that fail terminate further execution. However, a maybe { ... }
block allows commands to fail.
These are the ideas that I have so far. Does this seem like a good foundation for building a shell upon? Are there parts that I have missed?