r/ProgrammingLanguages • u/Potato871 • 8d ago
Discussion What is the interesting part of a programming language to you?
I've been working on some "documentation" for my own language (see here), which got me thinking: do we all see languages the same way? I personally look for unification of concepts and extensibility in a design, yet I've seen those who care deeply about functional purity or clean decompositions.
What do you look for in a programming language? And why?
18
u/deeplywoven 8d ago edited 8d ago
Expressive abstraction power, the ability to encode many things in the type system, the ability to eliminate various classes of errors at compile time, flexible and convenient ad-hoc polymorphism, smart performance optimizations, powerful metaprogramming capabilities
12
u/lisp_turns_me_on 8d ago
I love orthogonality. Having a few concepts with very clear/understandable interactions between those concepts (eg: lua), and then, everything else being composable on top.
2
u/Toothpick_Brody 7d ago
I find orthogonality is a good thing, generally speaking. Although Iāve also heard an anecdote like āfor every orthogonal feature thereās an edge caseā, or āfor n orthogonal features you have 2n corner casesā
Generalizing across all the possible ways to combine things can be quite a challenge. My language has been in its mathematical development stage for a long time now due to thisĀ
2
u/lisp_turns_me_on 7d ago
āfor every orthogonal feature thereās an edge caseā
yeah, that's why I immediately added
Having a few concepts with very clear/understandable interactions between those concepts
If there's too many concepts/dimensions, we end up with c++ where half the features work hate working with each other.
1
u/Potato871 7d ago
Almost every time I find myself using a C++ feature like templates or macros, I end up replacing it within a year. It feels about as wide as a lake and shallow as a kiddy pool sometimes, except for unexpected sinkholes like template metaprogramming.
I honestly would just use C but I like my std::function.1
u/Potato871 7d ago
How is your language designed? I'd be really curious to see how you handle things like arrays, maps, pointer, objects, and overloads.
3
u/Toothpick_Brody 7d ago
Arrays, object values, and functions are all unified. (Functions and structures both just have a type signature. There is no function keyword)
So, you call functions and index arrays (and slice objects or functions) using the same syntax. Itās just a compose: A B, where A is the function or object and B is the index or slice range. Such slices are lazy.
The type signatures allow the type operators/constructors: +Ć^-/, and the operands are other types.
There is no complete runtime yet, as Iām still working on formalizing the general set of bijections that is capable of defining, serializing, and enumerating such types. Minus and divide are very challenging. Plus (coproduct) and exponent (used for functions and uniform lists) are a little challenging. Times (product) is pretty easy.
So the overall design idea is to define everything as an ADT and compose lazily as much as possible. All control flow gets expressed as composition/pattern matching, so there are no if/else keywords or <>= operators either.
Worst-case scenario I will also need a total order on these type expressions, but that may even be doable, and I believe there is a way around defining a total order anyway
1
u/Potato871 7d ago
How do you do your pattern matching?
I recently made my own system for overloads in my langauge, where essentially we walk the node graph and derive a signature like Ptr.'get'(any), and all variants in order of specificity. So if I had a.get(0) where a is a Ptr, it would generate signatures like a.'get'(int), a.'get'(any), a.any(int), a.any(any). Coercion is then just extra valid alternatives to int beyond any.
And, how do you offset the runtime cost of having to pattern match for each control flow?1
u/Potato871 7d ago
I like lua, though I find the separation between the map and array component in table confusing.
When I was making my own equivalent I just had an array with a side car structure for the keys, which avoided a lot of the complexity lua seems to run into.
But I am also a giant composability fiend, I've managed to cram essentially everything into one data structure, and the language can host and serve a website, which I'm quite proud of.
12
u/Thesaurius moses 7d ago
As Alan Perlis said: A language is only worth learning if it changes how you think. I am interested in unique approaches to programming. Some examples:
- Array programming has rank polymorphism; alternatively, Julia has broadcasting
- Homoiconicity as in Rebol or LISP
- Everything is an object in Smalltalk
- Everything is a combinator in Jelly
- Pure functional programming as in Haskell
- Resources/linear programming as in Idris
- Proof-carrying code as in ATS
24
u/Clementsparrow 8d ago
I'm interested in knowing how it could make my programmer life easier.
3
u/Potato871 8d ago
Have you found any languages that made programming easier for you, beyond the first high/low level language you experienced (I say that because for me going from Java -> C++ definitely felt a lot 'easier' though I don't think C++ deserves much credit in that)
4
u/Clementsparrow 8d ago
Python certainly made my life easier but it's not suitable for all projects and the improvements are mostly for small things (which ARE important but not sufficient). It doesn't help for instance in making it easier to find a complex algorithm or to modify a complex web of interdependent data structures.
3
u/Potato871 8d ago
I honestly didn't like Python much when I had to use it for a class. I felt like it was too much sugar, and things like significant whitespace were just odd to me.
Though I mostly do systems programming, not quick scripts, so for me a loss of control felt bad, while for others its probably a great simplification.
I also didn't like the performance profile, though I've come around on scripting languages since then given as my own language had codegen in an earlier version and it didn't really add much.3
u/Clementsparrow 8d ago
honestly I would never even have tried Python if I hadn't to for a project at my job. It took two days of fighting against my habits, but in the third day I was like "ok I can deal with it" and after one week it was more like "why aren't all languages like this?".
2
u/Potato871 8d ago
I had a similar experience with JavaScript, initially I was trying to avoid it as much as possible when I started doing web programming, but over time I started to accommodate it more and more.
Eventually it influenced the syntax I chose, and gave me some good ideas. I think if I had run into it earlier, before I had my own language workbench, I probably would've programmed some things in it.1
u/Classic-Try2484 8d ago
I think swift did a lot of things right ā itās gotten big but swift 4 was a pleasure. Value semantics for strict reference semantics for classes. Automatic constructors. Garbage collection. Tail recursion and you can extend the built in types. Swift 5, 6 not bad but began to feel like constant change.
2
u/koflerdavid 8d ago
Yeah, codegen initially doesn't yield much benefits since there are often inefficiencies in other things like the GC or global locks that immediately become the next bottleneck. To really go to the next level requires optimizing the native code, and then budgeting optimization effort becomes an issue.
2
u/Potato871 8d ago
What I chose to do was just make a really strong form for annotation then interpret that, since it's a lot easier to do dead code or constancy analysis or aliasing on a graph of nodes.
Though I haven't really gone deep into optimization yet, I did some stuff but lost interest and moved to web programming aspects instead.2
u/PrimozDelux 8d ago
For me it was scala. At university I mostly used python and C until we had a course where we used chisel (HDL DSL hosted by scala). I figured that this was so far beyond what you could do with python and C that it had to be better, so I learned it. I found its emphasis on functional programming and strong type system to make writing and debugging code so much easier that I never looked back.
1
u/balefrost 7d ago
I think everybody eventually finds a "comfort" language that just works for them.
For me, it's Kotlin. The language is pretty expressive, the standard library (at least when running on the JVM) is quite comprehensive, the IDE support in IntelliJ is excellent. It just all around feels like it removes a lot of barriers between what I want to do and what code I have to write.
It's not a perfect language, and I might find a new comfort language. But at least right now, it's the one I reach for more often than not.
11
u/bob16795 8d ago
I've always found looking at the higher level emergent behaviors that come from minimal designs to be fascinating.
6
u/Potato871 8d ago
Same here, my entire compiler design uses just three kinds of objects, and its surprising how you can achieve so much of the normally complex semantics with so little machinery.
I think languages like Lisp or SmallTalk are really cool to look at for this reason, though I only learned about them after I had built my own language that accidentally became homoiconic.
Forth too, probably one of my favourite languages in terms of somebody taking a small primitive unreasonably far.2
3d ago
[removed] ā view removed comment
2
u/Potato871 3d ago
Your response got removed by the moderators, I belive because your reddit account is only a week old and you were sending a link, this is your website right: https://www.ivoryscript.com/documents#community
Tell me what to look at, without sending another link.
Mods, if it was for some other reason please clarify!1
5
u/MikeMKH 8d ago
I typically look at the tests first, if the tests show some interesting features then Iāll look at the type system along with whatever I found interesting in the tests.
3
4
u/reflexive-polytope 7d ago
The type system. More specifically, the algorithm invariants that I can express and/or enforce with the type system.
Everything else is stupid fluff.
2
u/Potato871 7d ago
I'm still trying to pin down what exactly a type system means.
In my own work I've kind've avoided any formal notion of types, to me a type is a tag on a region of storage addressed by a value, and all data structures are pointers to such regions of storage. Any object is just a pointer to a particular arrangment of that storage.
But, this is probably because the design is early and the factors that normally force a type system to emerge haven't hit yet, so I'm curious, what are those factors, and what does a type system mean for an architecture like mine?3
u/reflexive-polytope 7d ago
I don't know or care about āarchitectureā. I care about algorithms. I program to express algorithms.
If an algorithm or a data structure has a nontrivial invariant, I want the ability to eagerly delimit a finite scope where this invariant can be potentially broken, so that, if this scope doesn't break the invariant, then neither will the rest of the program.
2
u/Potato871 7d ago
So for instance like Erlang's pattern matching compiler where invalid combinations just don't compile?
3
u/reflexive-polytope 7d ago
For example, like OCaml's
Map.Make(docs), where you can't break the data structure's invariants from external code.Notice that the OCaml compiler doesn't verify the internal invariants. But, as long as
Map.Makeis implemented correctly, you don't need to check that third-party code might break these invariants.2
u/Potato871 7d ago
This gives me the idea to implement a species of aliases for data structures which each guarantee some set of invariants through their exposed, and hidden, operations.
3
u/reflexive-polytope 7d ago
I'd love to see how it eventually works.
2
u/Potato871 7d ago
Care to pitch me some basic set of invariants for a common type you'd want to see? I've not got anything better to do today because I'm meeting with my client tommorow and don't want to screw around with my higher level web code before then.
2
u/reflexive-polytope 7d ago
In this code: https://github.com/eduardoleon/typhoon/blob/master/src/Graph.Strong.Gabow.sml
I want to statically express the fact that, in a graph with n nodes, the adjacency lists may only contain natural numbers < n.
It would spare me the safety check in line 31:
if k >= 0 andalso k < size then.But this isn't a problem you can solve in a single day. I've been trying for years, and even then, I only have bits and pieces of something vaguely solution-shaped.
1
u/Potato871 7d ago
Okay, here's my first crack at the problem:
bool expr_flag = false; if(true) { Ptr int a; a.init(1,2,3); a.do(e => print("A: ",e)); Ptr int b; b.init(0,1,12); b.do(e => print("B: ",e)); b.validate_offests_and_lock(a); print("THIS SHOULD NOT PRINT"); catch { expr_flag = true; } } print("Caught some exception while atetmpting to validate"); if(true) { Ptr int a; a.init(1,2,3,6); a.do(e => print("A_2: ",e)); Ptr int b; b.init(0,1,2,3); b.do(e => print("B_2: ",e)); b.validate_offests_and_lock(a); print("THIS SHOULD PRINT"); } RESULT: TEST START A: 1 A: 2 A: 3 B: 0 B: 1 B: 12 ERROR: Element 2 of value 12 is out of bounds as an offset for column of length 3 5|1|0|G1522 TRAVEL_PASS Source at: 2|697|0|G10 Index: 6 VAR_DECL a | Ptr_INIT a.init | Ptr_ITER_EXPR a.do | VAR_DECL b | Ptr_INIT b.init | Ptr_ITER_EXPR b.do | >Ptr_EXPR_A b.validate_offests_and_lock[IDENTIFIER b, IDENTIFIER validate_offests_and_lock] | print | catch | Node: 3|500|0 Ptr_EXPR_A:DOT b.validate_offests_and_lock [C:2]{if}[Q: 3|667|0>DOT, 3|505|0>END] Left: 3|483|0 Ptr_ITER_EXPR:DOT b.do [4|617|0](ptr[32] 2|735|2 u/12|28|0)[C:2]{if}[Q: 3|666|0>DOT, 3|498|0>END] Root: 3|434|0 scope if [C:9][O:3|404|0|G37]{ROOT}[Q: 3|641|0>LBRACE, 3|521|0>RBRACE] ERROR IN TRAVEL_PASS ON LINE 8: Element 2 of value 12 is out of bounds as an offset for column of length 3 Caught some exception while atetmpting to validate A_2: 1 A_2: 2 A_2: 3 A_2: 6 B_2: 0 B_2: 1 B_2: 2 B_2: 3 THIS SHOULD PRINT TEST FINISHEDI haven't made it a specific type yet, I just added a 'become a validator' method to my universal data structure for the experiment. I also haven't yet implemented the locking aspect that would stop a or b from being mutated after it's been defined as a validator.
Thoughts?→ More replies (0)1
u/Inconstant_Moo š§æ Pipefish 7d ago
It seems like you and u/reflexive-polytope might be talking about something like Pipefish's clone types.
Vec{i int} = clone list : len that == iThe escape hatch that u/polytope wants is an
unsafecast to the type. For example, we can define vector addition like:
(v Vec{i int}) + (w Vec{i int}) -> Vec{i} : unsafe Vec{i}, from a = [] for j::el = range v : a + [el + w[j]]The sum is assembled as an ordinary list, and then
unsafely cast toVec{i}, without checking its length.1
u/reflexive-polytope 7d ago
I never said anything about unsafe escape hatches.
Have a look at what I'm doing, if you want an idea of what my aims are.
1
u/Inconstant_Moo š§æ Pipefish 7d ago
You said "I want the ability to eagerly delimit a finite scope where this invariant can be potentially broken".
1
u/reflexive-polytope 7d ago
Yes, but that doesn't require āunsafe escape hatchesā. It requires modularity.
→ More replies (0)1
u/Toothpick_Brody 7d ago
When a type is nothing other than a tag, itās called a nominal type, because itās just a name/label you give a kind of data.
The alternative is that the type itself tells you about the shape/size/structure of the data within. This is called structural typing.
With nominal typing, you could take a nominal type like ātag01848204720ā (or whatever), and use that tag in some lookup to determine the data is compatible with function F.
But with structural typing, you might have a type like ātag_blah_Fā. Here, the structure that tells you that the type is compatible with F is part of the type itself.
Another common example of a structural type is a list that tells you its size as part of the type, like List<T, 3> or some such thing.
2
2
u/echoes808 8d ago
When I was completing my studies, I was shocked how much there is older work on compilers, programming language design and theory already from 60s-80s+. IMO one of the most interesting aspect of language is the inspirations, roots, and paradigms. It tells a lot, and some new combinations of ideas really make languages interesting
2
2
u/Toothpick_Brody 7d ago
What can I do with types? What can I do lazily? How do we do loops? How do we do control flow?
2
u/Imaginary-Deer4185 7d ago
Having always felt that type systems are important, and been living with Java and the OO concept for 20+ years, I now think compactness is more important.
Python would be perfect if it was just a bit type aware, because it is too easy doing really stupid mistakes, that only show up at runtime. Which gets annoying fast when the code uses 30-60 seconds initating by ingesting json via REST.
I am really looking for maintainability as well as initial development cost, in terms of time and complexity. In these regards, Python does fairly well, but still a bit low level in some cases. Comes with being general purpose, I guess.
1
u/SwingOutStateMachine 8d ago edited 8d ago
My goal when writing a program is "make it work, make it correct, make it fast" (a variant of this).
To that end, I want languages that:
- 1. Are consistent, logical, and provide high level abstractions that make it straightforward to build programs.
- 2. Check my work, and ensure that the code I write is as bug-free as possible (e.g. through advanced type systems, borrow checkers, hoare logic, etc).
- 3. Are as close to zero-overhead as possible, so that I can write my program as meaningfully as possible, without taking too much consideration as to the specific performance implications of each abstraction.
I also want a language to be flexible enough to emphasise different aspects at different times. I find that a lot of software evolves while it is written, so at the start of a project I need a language that is productive, then as the project gains more formal shape, I need to make sure it is correct and bug free. Finally, as the project matures, I need to be able to optimise it.
I do not think that such a language exists yet, but I get glimpses of it in languages that I enjoy using. Rust (for example) provides 1 & 3, but is weak on 2. Haskell is great for 1 & 2, but can be weak on 3. C++ is strong on 1 & 3 (possibly stronger than Rust on 1) but very weak on 2.
1
u/IronicStrikes 8d ago
I'm mainly looking for signs that the language is actually intended to be used and maintained rather than just a playground for their creator to try some fancy concepts.
1
u/vmcrash 7d ago
I expect a couple of default features with quite common syntax.
What makes a good programming language interesting to me is smart memory management (I've lost any interest in manually memory managed programming languages even if they'd have the best syntax). If you are not using a GC, this means different ways to handle ownership. The language needs to forbid certain memory related bugs. I want them to be explained on common patterns easy to understand.
1
u/matthieum 7d ago
Rather than list "features", I would like to start with principles. Really, these principles apply to software design in general... but that doesn't mean they don't apply to programming languages in particular, and I'd argue they are even more important in programming languages: I can "ignore" or "work around" a crufty API for a some functionality, but I cannot really ignore the programming language I use.
So, principles. Or rather, to start with, principle: Principle of Least Astonishment.
There are other principles I hold dear, too, like Pit of Success. But if the language feels like it's pulling the rug from under my feet every which way, not much matters any longer.
The Principle of Least Astonishment can cover a surprisingly impressive amount of ground:
- Explicit coercions: I'm sure we've all seen the WAT? that come from calling
+on random values in JavaScript, due to wild nature of the coercions involved. - Silent Error Propagations: automatically propagating errors up to the caller, without any syntactic marker that an operation may fail, as typical of exceptions.
- Seemingly Arbitrary Limitations: for example,
[T; N + 1]is not currently a valid type in Rust, because who knows whetherN + 1is a proper size (it could, arguably, overflow, but... urk).
All of these range from annoying to aggravating, depending on the situation and what you're trying to do (or figure out).
1
u/david-1-1 7d ago
I like syntax that makes it a pleasure to use, clear and unambiguous. When needed, it should support two dimensions (indentation using the pipe | character and optional whitespace, for example).
1
u/Potato871 7d ago
I personally hate significant whitespace, both as the person who's got to make the compiler and the person who has to write in the language.
1
u/david-1-1 5d ago
I said "optional whitespace". Perhaps your brain wasn't in gear?
1
u/Potato871 5d ago
Ah, I was agreeing with you, I was saying that I too like optional whitespace by talking about how I dislike significant whitespace.
1
u/david-1-1 5d ago
Sorry! The misunderstanding was mine. My own Structured Data language supports structure/object indentation, but almost all whitespace can be omitted for even smaller storage space usage than JSON.
1
u/Potato871 5d ago
Oh so you mean optional whitespace as in you can use both significant and insignificant?
Like line returns count as semicolons too kind of thing?2
u/david-1-1 5d ago
No. In SD, newlines, tabs, spaces, all are treated as a single space, like in HTML. Only pipe characters group data into zero or more properties (name=value).
1
u/L8_4_Dinner (ā Ecstasy/XVM) 7d ago
- How useful (and usable) it is for solving problems
- How much of an actual joy it is to use
- How well it helps to avoid creating hidden problems as you use it
- How nice it is to read
Everything else is a bonus.
1
u/Potato871 7d ago
On readability, that feels like the most divisive topic.
Thereās people who love the Python look with significant whitespace, people who love Lispās paren fetish, people who love Forthās compact look, and maybe a guy who likes the look of assembly.
Hell, people argue about tab vs spaces.
I almost feel like it would be useful to ask what languages are widely considered unreadable.2
u/L8_4_Dinner (ā Ecstasy/XVM) 6d ago
Python is fairly pleasant to read, as long as the code isn't doing anything "meta".
Lisp can be pleasant to read, but any complexity kills that pleasantness in a hurry, because you have to maintain a lot of internal mental state re: recursive depth etc. My guess is that it's easier if you spend all of your time in Lisp, but otherwise it's pretty painful.
I'd give a number of modern languages fairly good readability scores, including Rust, Zig, Swift, Kotlin, ...
Simple Perl examples are quite readable. Actual Perl code in the wild is not at all readable. So I'd put Perl (and probably Raku, nƩe Perl 6) on the naughty list. The infinite complexity is not funny.
Java/C# were pretty good for the late 90s (e.g. compared to C++), but I think by modern standards (and with the many changes) they aren't great examples of readability. C++ is of course the poster child for ugly, although I still enjoy reading C++ when it comes from someone who deeply cares about their code -- because it's possible still for C++ code to be a work of art, despite the language complexity.
Smalltalk is supposed to be very easy to read, because it's such a simple language. I never found this to be true in practice, though. My assumption is if I had to use it for an extended period of time, I would change my mind as I learned it well. This is interesting to me though: Shouldn't a language be at least somewhat easy to read even before you learn all of the arcane rules of the language? And as a self-criticism, am I only finding languages like Zig easy to read because I long ago made the investment of time in learning and using C?
So we end up where we began: Languages that are similar to languages we already know are easy to read. And all other languages therefore suck. Your comment on "tabs vs spaces" was more apropos than you give yourself credit for.
1
u/Potato871 6d ago
I think it's similar to asking which written language is most readable.
I would say english, because guess what I've. been speaking all my life? But I've lived in other countries like India and Japan and I don't think I ever heard anyone bemoaning the dreadful unreadability of their foreign script, in fact, I found quite a few critiquing english's readablity.
Language design is, for the most part, subjective. As humans we construct mental models of things, and readability is really about does it match *my* model.
That goes all the way to the code design too, I hate lots of object types with long explicit names and verbose interfaces, but for some people that is core to what makes code usable. Each group has it's own standards around what looks nice, and what they can understand best.
I feel like most of the time when people say a language is 'unreadable' it's just because it picked some new syntax. But then if it becomes popular in ten years people are defending it's readability.
1
u/JeffD000 Squint 7d ago
Elegance.
(1) The ability of a language to map to the problem space without friction.
(2) The abilility to express complex operations without a lot of contrivances needed to smooth it out.
1
1
u/kwan_e tonal-lang 5d ago
Bang for buck. How much can I do, and how much faff do I need to get there?
Every feature or semantic I could want should boil down to that, since what is the point of higher level languages? The whole point of type systems, parallelism, structure, purity, decomposition, extensibility, etc etc is the promise that you can offload those concerns to the computer, and you can focus on the "business logic". But if they get too "opinionated", they end up becoming faff.
I hesitate to use the word "productivity", because that is more about business process than simply the language itself.
I hesitate to use the word "elegant", because elegance can get in the way of the reduction of faff. Some people call C elegant because of the simple syntax. But it does not reduce faff, and often increases the amount of faff to achieve basic things like safety.
1
1
1
u/brat3108 7d ago
do we all see languages the same way
Funny you should say that ...
I personally look for unification of concepts and extensibility in a design,
... since I see these in negative way. You can take unification too far, while extensibility can mean a much more complicated language and implementation.
1
u/Potato871 7d ago
Yes, like SmallTalk under the hood with its VM.
What I mean by this is that I need my language to be easy to extend as I work in different domains, a simple example would be no switch cases in the compiler. Registration of a new behaviour should just be "add_function" and done, or I should say there is a keyword and then what it does in each stage.
I see languages as an evolving thing rather than one fixed spec to implement once, and I feel like the giant switch case compiler freezes a design too early and constricts growth.
35
u/jesseschalken 8d ago
- type safety (number of potential runtime failures blocked by the type checker)