I've been designing a language since 2009 – friends and I implemented a beta
bolinlang.com
bolinlang.com
Personally I would have simplified its syntax a little bit more, if possible.
For instance, the following example code
main() {
sum := 0
for v in [5, 7, 11] index i //index is optional
{
if v == 11 { break }
sum += v
if sum > 20 { break }
print("$i: $v")
}
_OnBreak { //<-- must be immediately after loop
print("Index $i broke out of loop")
}
_OnComplete { //<-- Will not execute on break
print("Array is OK")
}
}
would have wrote it as follows: main() {
sum := 0
for v in [5, 7, 11] index i //index is optional
{
break if v == 11
sum += v
break if sum > 20
print("$i: $v")
}
_OnBreak :: print("Index $i broke out of loop") //<-- must be immediately after loop
_OnComplete :: print("Array is OK") //<-- Will not execute on break
}
I honestly like simple, well-written code that is easy to follow.I don't know if it'll make things more clear to you but curlys aren't nessicary when you use return/break/continue. I didn't write it because I didn't want to scare people off with the lack of curlys or confuse people on the front page
The `::` part looks nice. Would you want the statement to be forced on the same line? Or not like how C allows statements after `else`? As mentioned the compiler forces curly braces so it'll have to be forced to be on the same line as the on statement and I don't know if you'll find that to be a dealbreaker
What's confusing about that? Perl has that, I use it all the time.
Perl is my main, and I never use that feature.
A for loop also doesn’t match what’s being executed. It’s s actually a do/while loop
For me, the problem is when my expression got long or if I call a function with multiple parameters. The more complex the line is the easier it is to not notice the `if`. Also I agree with what the other person said, it doesn't match the order if you read top down left to right
I like the _OnBreak _OnComplete _OnEmpty . I think they simplify some loops and make those found_something variables go away. However I don't like that they start with an underscore. It feels so old (C and Python, which was designed at the end of the 80s.)
Case conventions are a little strange, something is lower case (main, for), something is camel case, something are upper case (methods?)
= := .= could be a little confusing. Why not only one? The compiler should know what to do because of the mut, out keywords.
About style: { sometimes leads a line, sometimes doesn't.
Early on _[A-Z] meant reserved variable so that's why it's currently _On. Many keywords are a single word and common enough that it shouldn't bother anyone that it's reserved. However I'm not great coming up with a style guide. On empty is two words, I have no idea how it should be written and everyone seems to have their own style.
Do you have a suggestion for the _On keywords?
> Why not only one? The compiler should know what to do because of the mut, out keywords
You noticed the out keyword? It's not recommended we only use that to interface with C code. Returning multiple variables is much better IMO (I won't speak for everyone). Accumulator and dynamic arrays are common enough that I didn't want to force people to write mut each time. It's simply `:=`
Let's suppose that in the future you want to add an argument to the break keyword to signal why the loop returned early. I use a string (btw, does the language have symbols / atoms?)
break "found"
break "eof"
break "invalid input"
Then you could have on "found"
on "eof"
on "invalid input"
with only one keyword. And on "complete"
on "empty"
> Returning multiple variables is much better IMOI agree but declaring a name for what you return is nice because it explains the purpose of the returned values. They could go outside the ()s.
stat(file) out length, created, modified> I agree but declaring a name for what you return is nice because it explains the purpose of the returned values. They could go outside the ()s.
So you're not suggesting an 'out' keyword your saying names on return values? I like the idea but in practice I'm not sure if any IDE would show you the names? I think many do show comments if you place them the line before so that could be an alternative solution
One reason I don't like out params is because it conflicts with the design of the language. The language prefers you to assign variables and people will want to modify their variables (or arrays and struct members) with the out param and having something be declare OR assign causes problems because its too easy to make a typo and not find it for many many minutes. Writing the type then using out goes against the flow of the language which is why I prefer returning multiple values.
But I don't know all the use cases and everyones preference so there very well be a good reason you'd want an out parameter
If it’s C like then the ExpressionStatement, umm…statement covers this; it’s how “if (foo=1) bar();” works (or leads to bugs depending on your point of view).
Also curious on how the automatic memory management works, might be on the website somewhere but I didn’t look past the first page.
I wrote some specifics in this thread over a few comments but that and the type system are complex. It can be a talk and writing short post probably will satisfy no one
(I would have preferred if the _OnBreak and _OnComplete statements was inside the for loop as that removes any ambiguity that it is part of it and effects the for loop).
However, `if v == 11 {break}` is definitely more readable than `break if v == 11`. If you are using curly braces as part of your syntax, and for blocks of code, it's better to use it with consistence. That can be confusing with `break {if v == 11}` as it breaks the syntax grammar, when you consider what break does.
the break, food out break out of the if block? Or somewhere else? It’s confusing
Do you have a list of those features?
"If you look through the docs you'll find many reasons"
Respectfully, I browsed all the examples I could find on the site (Highlights, Examples, and Quick Start) and I'm not really sure what you expect to be recognized as novel ("Has features not found in any other language"). You may have a combination of features that is unique, but nothing individually stood out as something never seen before. Perhaps you just need to highlight these things better.
> value = fn1() error { return }
Allow for Error short circuits. This has slowing been adopted by other languages - An error can be handled/or not and continue cleanly without crashing anything (erlang style) or typed semantics (Java style). Go can return an error message, as part of the multi-return semantics, similarly allowing for error short circuits. Because of Bolin's multiple return semantics, you can implement this pattern as well, but it's nice to not bake it in.
The for-loop conditional exit handlers are novel enough.
Some unique syntax choices, like .= for assign and mutable/immutable assignment dynamically is great. Ponylang has typed containers that act similarly, but they are not dynamically declared.
I know I've seen lazy type signatures before, but I don't remember where. So I'd consider it novel enough to note: ie you don't need to specify int every param -> add(int a, b) and implicit void on main()
I can't speak to under-the-hood compiler optimizations, but there's a little bit here and there that's interesting.
P.S. Typo in: https://bolinlang.com/quickstart/basics //you don't need to specificy int every param
Is the `try` keyword in the highlight section what you're looking for? You can write `var = try fn()`
Thanks. I fixed the typo when I first saw this hours ago but it's been busy and I forgot to reply
I worded it poorly.
> Allow for Error short circuits
I was trying to say that I liked the feature as-is, because it Allows for Error short circuits. There are a lot of common patterns that modern languages avoid idiomatically, despite their widespread use in roundabout ways.
Is the problem with short circuits that you can ignore the error? It's not documented yet but `error return` requires the function to be an error type so the error can propagate. If thats not good enough do you have some insight on how to fix it? What's worse and was designed intentionally by me is `fn() error { return }` not requiring the function to have an error return (although this would be a void return type). I make the assumption if you have a block (instead of `error return`) you're handling the error. You can also propagate by doing something like this `nonErrVal = fn() error { print("write a better error log"); return error }`.
Never used it myself but it is probably handy, handy enough to be included in the language at least.
Ergonomically solving this problem is a major research area, so if you’ve found a solution to it’d be worth presenting that front and center.
Just as some basic questions you might want to answer:
1. If I allocate some memory and store a reference to it in a struct or object, how do you track ownership for that memory? Can I alias it? Store references to it in multiple structs? How do you know that it's safe to free that memory without reference counting?
2. Am I allowed to allocate memory in a function and return a reference to that memory? How do you ensure that memory lives long enough for those references to be valid?
3. Are multiple aliases allowed to overlap with each other? If so, how do you prevent (e.g.,) type confusion from one mutiple alias changing the data out from under the other?
You do realize you can download the compiler and confirm for yourself that it's all working right this very moment?
There's probably a few aliasing bugs since I add new features. types and haven't tried to break my code or find difficult to run into bugs
What would be very compelling are some code examples and either high level descriptions of what the compiler is doing or some analysis of the type system and IRs the compiler uses to verify correctness.
Particularly things like ownership and lifetimes. These can be pretty nuanced in degenerate cases and it would be interesting to see how you solved them.
Like take Rust. It has ARC to be sure, but the core semantics behind its "compile time GC" are pretty well defined and easy to follow so people have faith that they work (they're also provably correct). If you have some motivating examples and descriptions of how the language solves them it would be very compelling to see!
Essentially this isn't an attack on the project, it's just curiosity about the approach and implementation. It's easy to poke at code examples and see that you're right, what is more valuable to folks that are approaching the same problems is how you got there.
All these choices you have to make had me wonder: we've made formatting entirely part of a language but it seems a lot of it could be moved into the editor. Does it really matter if:
if(foo==bar){{/*do something*/}}
if foo==bar (/*do something*/)
<if foo==bar>/*do something*/</if>
foo&bar && /*do something*/
{if:foo==bar, do:/*do something*/, oncomplete:a(), break:b()}
It could just be whatever the user chose in their editor (with an option to publish their custom solution) The code in the actual file can be any of the existing languages which would put some limitations on the formatting choices but if that gets annoying it could be addressed at many levels.Does this sound fun or should we pretend no one ever mentioned it?
I think it would be hard to avoid a lisp-like {{{ hell though
For.Loop(10, (i, @break) =>
{
Console.WriteLine(i);
@break();
})
.OnComplete((i) => Console.WriteLine("Done"))
.OnBreak((i) => Console.WriteLine("Break"));I felt having `.` on a loop would be weird because it isn't working on an object
A few people said they want single statements on conditionals. If `=>` (or `::` as someone else suggested) forced the statement to be on the same line would you be fine? I can imagine people wanting to use it on it's own line like people use else without braces which leads to bugs
Something like TypeScript, for example, really caught my attention because of how well it integrated into the existing JS ecosystem, adding sanity while still not sacrificing interoperability.
I wouldn't know what people want to know about. In real life with friends and coworkers don't really want to hear about details or why I designed something
I forgot to answer this. It isn't but that'd be a good milestone.
Kudos to you for turning your own programming language dreams into a reality!
---
If arrays are passed by reference, how does the bounds-checking code deal with aliasing? For instance (apologies if this syntax is wrong):
munge_arrays(int n, int[] a, int[] b) {
if (n < 0) || (n >= b.size)
return
a.Pop()
return b[n]
}
// good callsite
first_array := [1,2,3]
second_array := [4,5,6]
munge_arrays(1, first_array, second_array)
// callsite
target := [1,2,3,4]
result = munge_arrays(target.size-1, target, target)
The "Array Bounds" section on the "Highlights" page suggests that the compiler will try to spot indexing errors (like accessing target[target.size]), but thanks to aliasing b can change size "at a distance" if the right parameters are passed to munge_arrays.---
What are the scoping rules for enum and bitflag names? The page in the Quick Start is unclear - sometimes they are qualified with the type name (Fruit.Apple) but not always (Banana, Dir farther down). Is this like Haskell records where every name has to be globally-unique?
---
Similar question for "extend" - the last couple lines of the "Struct and Class" page hint at a feature, but it's unclear what the scope of extension is. Is that based on the type of the first argument of the function being extended (Test in this case)?
---
One more scoping question: what names are in scope in an _OnBreak clause? For instance, in the nested part of the "LoopsWithExits" example, can that inner _OnBreak see v2?
---
How does constructing an instance of a class with arguments work (if at all)? The examples of constructor() functions are all arity zero, is the intent that an instance be constructed "blank" and then subsequently mutated?
---
One feature notably absent from the examples is passing functions as parameters. For instance, is it possible to write a "mapIntArray" function that takes an "int[]" and a function (that takes an int and returns an int) then returns the result of applying the function to each element of the array?
---
Has features not found in any other language
IMO you should either list them, or change the marketing language. I'd recommend the latter - people don't pick languages because they have unique features, they pick them because they NEED what the unique features help them accomplish. What's some code that Bolin makes easier/clearer/safer/whatever to write?---
(e) Licensee shall not publish or disclose to any third party the results
of any benchmark tests or other evaluation run on the Software without the
prior written consent of DeLellis.
Is this 1994? Did I accidentally click on a link to Oracle.com? If I downloaded the software and did some experiments to answer my own questions above, would I have to ask for PERMISSION to post the results?You want "the community" to contribute feedback and ideas for free, but you want to retain absolute control over both the software and the conversation. That's not a good trade.
The compiler should complain about aliasing but I should check when I get home. The problem here is array's have an invalidation function (it may resize and the pointer may change). It's not allowed to have aliases.
If the compiler knows what type is expected (for the enum/bitflag) it can figure out the value. 2. No, its highly dependent on knowing the type.
Yes, first argument.
_OnBreak can see the index if you have it but I can't remember if it lets you see v2. It should but I never actually tested it
> they pick them because they NEED what the unique features help them accomplish
If you read it off a list would you recognize what it does? People in the last 24hrs told me they don't understand why I would want onbreak and didnt notice you can chain breaks so you don't need a goto
> That's not a good trade.
It's tough. One goal is how to make the developers lives easier. I heard about people quitting their project because of comments over github
More to the point, I think your site could do a much better job on describing what's actually unique about this.
For example, I'm not convinced that the `_OnBreak`/`_OnComplete`/`_OnEmpty` machinery is useful enough to be part of a language. A bigger example where these features come in handy would be helpful to understand them.
Just skimming the highlights and examples page, it looks very similar to C++. So I think it could use some side-by-side examples to show how it makes ordinary programming easier/more readable/etc.
The Quick Start guide is a bit obtuse and feels incomplete. I'm having trouble understanding some features, and I feel the standard library is underdocumented---for example, I couldn't find what `.WSNL()` does, and it's used for your MIME type parsing example.
I apologize if I'm sounding a bit harsh; I'm trying to provide some constructive criticism here. I appreciate how hard it is to get something out the door and I wish you the best of luck.
At the point that it was clear we're going to have a usable compiler I asked my entire team what license we should release this under. Absolutely everyone had a problem with each and every open source license. The more we talk the more apparent that none of us wanted to wake up one day to find out a trillion dollar (possibly cloud related) company took our code and want to compete with us. We don't want embrace, extend, and extinguish to happen to this project if we could help it. So we settled on a EULA (which very likely will change at 1.0) that says you can learn for free which lets face it, is the only thing people would want to do in its present state
The language field is very crowded. I'm all about new languages, I wish each and every one success and yours is no exception.
If it's going to succeed, you'll have to beat people over the head with it. The current situation won't reach escape velocity. Good luck.
But I guess it depends on what your goals are. If you want this language to be used by as many people as possible, you want to remove any obstacles to that, and a sign-up wall is a big one. You also have to consider the competition. I can go to my package manager and download compilers and runtimes for probably over 200 languages right now without having to submit any information about myself - I did it for Zig the other day. If your language and compiler are so mind-blowing that I should be glad to subject myself to the friction I have to go through to use yours instead, you really need to sell that to me with more than, as mentioned in another subthread, a promise of ambiguous features not found in other languages.
So free your code. Sell it to me as much as possible on the web site with all of its amazing capabilities and features and libraries. And if some company rips it off and passes it off as their own thing, make hay from it - write up a blog post about how the nasty big evil company stole the work of a plucky li'l FOSS team, but you're just going to keep plugging away and improving your code regardless. Then post it to HN. You'll get updoots through the roof and plenty of sympathetic new eyes on the project.
Also: forking /= stealing
[1] https://ziglang.org/news/statement-regarding-zen-programming... (HN discussion: https://news.ycombinator.com/item?id=24481142 )
I think many people just fundamentally don't understand sharing or friendly coexistence. The drive to dominate and control is sooo maladaptive for society but very widespread.
Especially in a pre-production state, it's unlikely that anyone will want to use this commercially anyway. It'll eliminate a big barrier to any adoption you're hoping to get.
Deciding to learn something in the first place is a question of how I want to use my limited time. I don't think I want to learn something with a presently-bad EULA that might change arbitrarily in the future.
It's totally fine that your team wants to keep this project close to you, it is your work and copyright privileges after all. You don't have to be open source if you don't want to. [1] Don't let anyone tell you what to do with your own work.
Just don't be surprised when you find that nobody's interested in it.
[1] I do think it's a bit hypocritical to require people to be "a part of the community" to download the thing when the license doesn't resemble a community or copyleft license. This is no different than downloading from the Oracle Download Center.
That's a fantastic idea. I'll focus to improve this by the next release. Do you want short side by sides or real code examples? I'm worried that short ones may look contrive which is probably why you don't think it's useful? But I'm not certain people would look at anything that isn't short
Correct. I wasn't sure if I'll rewrite the reader class so I only used it in the example program section. The standard library is pretty nonexistent. We didn't want to make the language for only ourselves so we thought we should launch sooner rather than later
> I think your site could do a much better job on describing what's actually unique about this.
In another comment I mentioned that listing features tends to be ignored. Do you have any suggestions on how to counteract it? Maybe your side by side suggestion is ideal https://news.ycombinator.com/item?id=32460252
I can see where you're coming from, it might be different from person to person. I personally would've been happy to read through a short feature list highlighting the most interesting ideas that the language introduces. For example, you mention automatic memory management without reference counting and garbage collection, which sounded interesting but wasn't specific enough for me to figure out what's unique. In another comment I saw that you expanded on it and mentioned it tracks lifetimes across scopes, which then sounds super interesting. But I didn't find where to learn more about it.
> Do you want short side by sides or real code examples?
I think short examples can do the trick, if they are not toy examples but can work as part of a bigger program, if that makes sense. Coming to think of it, ideally it would be best to have an actual program written in the language. That's what I usually do to feel out a new programming language---seek out a codebase written in it and read through. So if you have something like that, maybe you could showcase features using small snippets from the program itself, maybe side-by-side with equivalents in a well-known language.