Is it just me that doesnt like automatically returning the last statement in functions? It makes it hard to see where a function returns, and I dont see how you would do a guard clause at the start of a function without having the entire rest of the function in an else block
I remember really bumping up against this learning OCaml in college after having experienced oodles of imperative programming.
I understand the sort of philosophy and ergonomics of not having an early return, but it really does hurt certain kinds of code that otherwise would be more readable
I think it is much more obvious than being able to return from anywhere in a function. If the last expression is a match, I know every match body must return the same type. if the last is a (cond ...) I know ever cond branch must return a value. I vastly prefer that.
It does make me think that the usual types of guards might typically happen higher up (handled by the caller) or hidden with safe / monadic type operators that simply pass through rather than bailing out, so to speak.
In most functional languages however you can view the end of any statement/expression as a return/assign which makes it very easy and trivial to assign anything to variables, or split anything into function calls.
Interesting. there are some parts i like a lot here, but two things that I really dislike syntax wise. One is the lean towards a chainable syntax - this has proven to a big footgun for many devs in both java streams and typescript, making it very easy to go from O(n) to O(2n). The other part i really dislike is the first argument principle noted. If i myself define `string_and_reverse` and I can call it both through `string_and_reverse(42)` and `42.string_and_reverse()` i could definitely see this leading to some very funky looking chaining.
Perhaps it's just one point from me - not liking chaining :D
Fair! That'd depend on the operations right? For example, AFAIK typescript can't do much about multiple chained `map` calls, and i've seen quite a few `.filter(...).map(...).filter(Boolean).map(...)` :/
To be fair this likely should be handled by the interpreter/compiler for the compiled JS. V8 probably can merge this into one loop or another similar based on runtime types
Then either n is a constant and it's really O(1), or k isn't a constant and its naming was in violation of the International Conventions on Naming Things to Avoid Silly Arguments, section 3, paragraph IV.7 & ff.
I know there are people that are used to the indention based scope but that has a real problem when it comes to copy/pasting code. I think a alternative that still looks pretty clean is to do like Ruby and Julia and have the function/class imply begin and have a literal 'end'.
Fun fact, in Python, the indentation is checked per block. So, in the outer block, indentation can be 2 spaces, while in the inner block, the indentation is 3 spaces. The only prerequisite is that the indentation in the block is the same.
I don't understand this concern. How exactly are you copy/pasting code such that significant indentation causes "real problems"?
I remember the creators of Go explained [1] that they chose explicit block delimiters because of problems they saw when embedding snippets of Python in other languages. But this seems like a very niche kind of problem.
Why is `pure` a keyword that needs to be added, with impure being the default? This discourages programmers from marking functions as pure. I like how Nim does it, with `func` declaring a function (pure) and `proc` declaring a procedure (impure).
If your goal is - like Crystal - to be as pain free of a migration from Python to Blorp, this shouldn't really impact it, since the compiler can and should be able to auto-fix this.
I also really like the distinction between a function and procedure. The function is a pure mathematical function. The procedure is a series of instructions.
Roc defaults functions to being pure, and functions that can run side effects are inferred to have a different type by the compiler based on usage. By convention, their names should also end in `!` (e.g. `transform` for the name of a pure function and `transform!` if it does side effects), and the compiler warns you if you don't follow that convention.
Readable syntax with mandatory indentation is a very questionable idea. For me its easier to understand that something ends with a specific designation, not with a lack of it. Indentation should be solved by formatter and not the language.
And I don't quite understand the memory model, is it something similar to Rust?
That's more of a compiler limitation that became cultural for a while. Most languages (both natural and artificial) use delimited structures sparingly and rely more on other cues. It sometimes appears spontaneously (e.g. "∫ dx f(x)" is logically fine, but feels wrong) but in general it's rare.
The move away from indentation in programing came as a rebellion against the too-constraining fixed column languages, in the interval between punched cards and python, with a brief resurgence in the early blink tag and font potpourri web era. These days, it's perfectly reasonable.
In my experience there are many problems with significant whitespaces, things like copying pieces of code require much more work, when indentation actually changes the logic you can not ask your tool to do it automatically - because there is no single right way to do it. Tabs vs spaces can also be a problem.
I applaud the effort, but every time there's a new hobbyist programming language on HN, almost always it's something I've already seen in countless other hobbyist languages, just a slight variation of it based on the author's personal tastes. It doesn't tell me why I should adopt it over language X. What I'd like to see is exploration of novel practical ideas that would make certain types of projects much faster to write/read compared to most other languages.
For example, a typical web service I work on:
- uses JSON APIs
- it's fully stateless (uses external DBs/caches for persistence)
- has the concepts of value objects, entities, architectural layers (app, domain, infra), ports/adapters etc.
- only entities are proper rich objects, while most of the code is stateless services that operate on requests + entities + value objects
- stateless services are composed (via interfaces) into a dependency tree (stored in the dependency container)
Currently I'm playing around with an idea for a language that makes writing things like that fast and compact to read. Something like:
module my_service
layer app {
service Adder { // stateless service
uses base int // a value-based dependency, injected in the container below
method add(x int) int {
return base + x
}
}
service Doubler {
uses a Adder // delegates to another service
method double(x int) int {
return a.add(x) + a.add(x)
}
}
}
container { // dependency container construction with injections
A = Adder { base: 10 }
D = Doubler { a: A }
}
// automatically generates a web server that exposes a JSON API with method "double" and accepts the "n" argument
endpoint double(n int) int {
return D.double(n)
}
This is a synthetic example, but you get the idea (entitites and value objecst omitted here)
What do you think? Does it make sense? It basically moves something usually implemented by a framework into the language, but that's the entire point: a language optimized for writing compact, architecturally safe stateless services in a few lines of code. For example, since we know a request's memory is bound to that request (no global state), we can have very optimized memory management without a full GC => improved latency. Or for example, we can have compile-time checks for things like dependency direction validation (i.e. the domain layer cannot reference the infrastructure layer) to keep the architecture clean, etc.
I like these hobby languages just because they help expose and experiment with interesting higher level language constructs. Because of that, I don't really care if they try to sell me on the language or not.
As for your concept, I think this is super interesting. A language catered towards higher level abstractions that we use for web services these days is very appealing. The service and container constructs are particularly enticing.
I would recommend building a macro system and/or library for an existing language - that most closely aligns with your goals.
It seems like your goal is to make things more declarative / readable.
Creating a language is a pretty large undertaking, and unless you need to do it to achieve your goals, I wouldn't recommend it - unless you really just want to see what it's all about and make one.
"Blorp" is the notional noise of kimchi or sauerkraut fermenting as the carbon dioxide escapes the airlock. Vigorous fermentation can be described as "the kimchi is really blorping along today". It's almost onomatopoetic, but not quite.
We ferment wine or beer in a different vessel with different airlock, so it does not blorp. We don't have a word for that yet.
The crock we used that birthed this word is this one:
> "Blorp" is the notional noise of kimchi or sauerkraut fermenting as the carbon dioxide escapes the airlock. Vigorous fermentation can be described as "the kimchi is really blorping along today".
I find it difficult to believe that a noise assigned to the preparation of kimchi would be so flagrantly incompatible with the Korean language.
I'm a basically monolingual white guy from the Midwest USA, transplanted to Vermont. I discovered kimchi in a restaurant and learned to make it from the internet[0].
I'm sure people who come by their kimchi-making through their family or culture natively probably have words that work better for them that I would stumble over and mangle is truly epic fashion :-)
This is in the very first example you see on the site. If it's a mistake, that's not encouraging. If this is actually how the language works, that's even less encouraging -- the syntax highlighter doesn't even get it right!
voidUpdate | 6 hours ago
rtpg | 6 hours ago
I understand the sort of philosophy and ergonomics of not having an early return, but it really does hurt certain kinds of code that otherwise would be more readable
orthoxerox | 3 hours ago
I wonder who came up with this idea first. I find obvious early returns incredibly ergonomic.
voidUpdate | 3 hours ago
orthoxerox | 3 hours ago
voidUpdate | 3 hours ago
bjoli | 6 hours ago
zdragnar | 6 hours ago
https://github.com/kablorp/blorp/blob/main/benchmarks/blorp/...
Then again, there's really not too many examples of early return guards, but I did manage to find one where the body is stuffed in an `else`:
https://github.com/kablorp/blorp/blob/main/benchmarks/blorp/...
It does make me think that the usual types of guards might typically happen higher up (handled by the caller) or hidden with safe / monadic type operators that simply pass through rather than bailing out, so to speak.
troupo | 5 hours ago
In most functional languages however you can view the end of any statement/expression as a return/assign which makes it very easy and trivial to assign anything to variables, or split anything into function calls.
cupofjoakim | 6 hours ago
Perhaps it's just one point from me - not liking chaining :D
KolmogorovComp | 5 hours ago
Strictly speaking I assume everyone knows O(n) = O(2n) =O(kn) for k in R.
But I see your point. I assume any decent compiler would merge the loops though
cupofjoakim | 4 hours ago
c0balt | 4 hours ago
frwrfwrfeefwf | 3 hours ago
MarkusQ | 3 hours ago
xigoi | 3 hours ago
At least for me,
is much more readable thancupofjoakim | 3 hours ago
``` thing.doThis() thing.thenDoThat() thing.andFinallyThis()
// or
doThis(thing) thenDoThat(thing) andFinallyThis(thing) ```
xigoi | 2 hours ago
bobajeff | 4 hours ago
xigoi | 4 hours ago
Narishma | 3 hours ago
mohragk | 3 hours ago
This, to circumvent copy/paste issues.
nchammas | 2 hours ago
I remember the creators of Go explained [1] that they chose explicit block delimiters because of problems they saw when embedding snippets of Python in other languages. But this seems like a very niche kind of problem.
[1]: https://go.dev/talks/2012/splash.article#:~:text=we%20have%2...
ramon156 | 4 hours ago
onlyrealcuzzo | 3 hours ago
It's not as true to Ruby as Crystal is, because I aim to make it far safer. It's closer to Elixir, if anything.
But I love Ruby to death, and it is definitely the desire to make it as close to Ruby spiritually as possible.
lekevicius | 4 hours ago
xigoi | 4 hours ago
onlyrealcuzzo | 3 hours ago
We want languages that encourage good design.
If your goal is - like Crystal - to be as pain free of a migration from Python to Blorp, this shouldn't really impact it, since the compiler can and should be able to auto-fix this.
adamddev1 | 3 hours ago
rtfeldman | 2 hours ago
https://github.com/roc-lang/roc/blob/b2503210da6b58a4ce1254d...
miroljub | 2 hours ago
Marketing.
Instead of reading the code littered with "impure" keywords, you look at the beautiful code marked as "pure".
mapcars | 3 hours ago
And I don't quite understand the memory model, is it something similar to Rust?
MarkusQ | 3 hours ago
The move away from indentation in programing came as a rebellion against the too-constraining fixed column languages, in the interval between punched cards and python, with a brief resurgence in the early blink tag and font potpourri web era. These days, it's perfectly reasonable.
mapcars | an hour ago
3836293648 | an hour ago
kgeist | 3 hours ago
For example, a typical web service I work on:
Currently I'm playing around with an idea for a language that makes writing things like that fast and compact to read. Something like: This is a synthetic example, but you get the idea (entitites and value objecst omitted here)What do you think? Does it make sense? It basically moves something usually implemented by a framework into the language, but that's the entire point: a language optimized for writing compact, architecturally safe stateless services in a few lines of code. For example, since we know a request's memory is bound to that request (no global state), we can have very optimized memory management without a full GC => improved latency. Or for example, we can have compile-time checks for things like dependency direction validation (i.e. the domain layer cannot reference the infrastructure layer) to keep the architecture clean, etc.
jjice | 3 hours ago
As for your concept, I think this is super interesting. A language catered towards higher level abstractions that we use for web services these days is very appealing. The service and container constructs are particularly enticing.
onlyrealcuzzo | an hour ago
It seems like your goal is to make things more declarative / readable.
Creating a language is a pretty large undertaking, and unless you need to do it to achieve your goals, I wouldn't recommend it - unless you really just want to see what it's all about and make one.
mauvehaus | 3 hours ago
We ferment wine or beer in a different vessel with different airlock, so it does not blorp. We don't have a word for that yet.
The crock we used that birthed this word is this one:
https://www.lehmans.com/product/striped-european-style-ferme...
thaumasiotes | 3 hours ago
I find it difficult to believe that a noise assigned to the preparation of kimchi would be so flagrantly incompatible with the Korean language.
mauvehaus | 3 hours ago
I'm sure people who come by their kimchi-making through their family or culture natively probably have words that work better for them that I would stumble over and mangle is truly epic fashion :-)
[0] https://www.maangchi.com/recipe/tongbaechu-kimchi
flintenmuschi | 3 hours ago
anentropic | 2 hours ago
Looks somewhat Python-like but modernised (great!) - is it indentation sensitive?
semilin | 2 hours ago
This is in the very first example you see on the site. If it's a mistake, that's not encouraging. If this is actually how the language works, that's even less encouraging -- the syntax highlighter doesn't even get it right!
ModernMech | 2 hours ago