The Bosque Programming Language
microsoft.com
microsoft.com
The Bosque language is currently in a very early state with lots of work to do and not ready for practical use. However, I am very excited by the potential in the concepts and wanted to make the project a collaborative endeavor, including both other academics and developer community, from the start. At this point the goal is to explore interesting options in the language design space – whether the Bosque language becomes mainstream or just provides useful ideas for other languages to adopt. So, please take a look, expect plenty of rough edges, and we would love comments, suggestions, and PR’s on the GitHub repo.
A lot of programming languages that are coming out these days talk about simplicity, lowering “cognitive load”, increasing expressiveness, being nimble/lightweight/easy/whatever, and—this one stated by you—reducing “accidental complexity”. When I looked at your grammar and some examples, I saw atomic building blocks that don’t lead to any less complexity than what I’d get if I strung together similar building blocks in Lisp or Python or ML.
To me, “accidental complexity” doesn’t seem like a very well defined concept. I might say such a term in a meeting room arguing to upper management that we need to pay off tech debt. Or I might blog using such a term to talk vaguely philosophically about software engineering. But I don’t think I’d find myself using it in a formal context to argue the merits (or lack thereof) of a technology. I admit that this may be due to my ignorance of a term of art.
The introduction to your publication is even more bold: “Regularized programming” (and hence Bosque) will (supposedly) unleash a revolution on programming the same way structured programming did the paradigms before it, ushering a “golden age” of programming and the like.
Focusing on the term “accidental complexity”, can you elaborate what that means? What is an example of accidental complexity and how does it naturally come about with the current crop of languages. And how—in the large—does this programming language avoid it (or help the programmer avoid it)?
Edit: I see in your publication, upon the mention of “accidental”, you cite “Frederick P. Brooks, Jr. 1987. No Silver Bullet Essence and Accidents of Software Engineering. Computer 20 (1987), 10–19.” But in reading this [0], Brooks says:
> The complexity of software is an essential property, not an accidental one.
He only speaks of certain difficulties as being “accidental”, and past ways in which we’ve improved the impedance of such difficulties.
[0] http://www.cs.nott.ac.uk/~pszcah/G51ISS/Documents/NoSilverBu...
Section 5 of the paper explores some scenarios that, once this “accidental complexity” has been removed, become much more feasible. These types of experiences are currently aspirational, and much work remains, but the hope is to demonstrate the practicality and value of these concepts.
I would do it if the right PhD student came along!
I talked about this with colleagues a few years back, but we had very distinctly different feelings about what we were doing when writing C vs C#: C often feels more like writing text that will be translated to code while C# already feels like you are manipulating code. It makes no sense when I write this out but that's phemenology for ya.
We (PL researchers) think about this a lot, but so far no one has come up with any good answers on how to empirically measure PL usability for an entire PL (vs chipping off small features to evaluate). At best, we can run qualitative studies with little in the way of rigor.
Quit trying to lure unsuspecting CS grads into your van!
That sounds bizarro. Accidental complexity is pretty obvious to show in an example of a function or an application architecture or class.
It might be harder to define in abstract (except as e.g. "complexity not imposed inevitably by the functionality/problem domain"), but it's very easily observable in specific code examples.
I do not understand the concept op typed strings. Would that not be simple a subtype of strings (implementing additional restrictions on the values of the strings, a true substype)?
Also, when I see something like: 'args.all(fn(x) => x % 2 == 1)' I do experience it as 'simple, obvious, and easy to reason about for both humans and machines' because it only makes sense if you already have a lot of knowledge about languages like TypeScript. I think that something like: 'All x in args: x % 2 == 1' is easier to read. BTW, why does 'fn(x)' not have a type? Are types optional in your langauge?
I would not give examples of mechanisms that have not been implemented and seem to go against your principle ideas, such as references. What happens, I pass a part of a value to a 'ref' argument of a function?
There are some other language out there that are only based on immutable values. If you are fond of immutable values, why not implement them in an existing language and see how far you get. This has the benefit that people do not have to learn a new language and keeps you from reimplementing a lot of stuff that others already have implemented.
Perhaps you don't see the benefit of an identity function either ("why not just use the variable")?
Encapsulating things in a function instead of a statement is key to certain patterns (and functional style).
That ‘sign’ example could be somewhat improved by having it return an optional int instead (returning null on null inputs), or by having it take a double and return an optional int (returning null for not-a-numbers), but I think it isn’t that hard to come up with a more realistic short example.
I am curious about the following:
> Since the semantics and design of the language ensure fully determinized execution of any code there is actually no real need to perform logging within a block of code. So, logging is not available in the compute language.
While this may be true for execution, this is not true of data on which the program operates. For example, when processing large amounts of data, how should one keep track of statistics, performance information, data irregularities (which don't cause errors but may be useful to look into further), and other such events which might otherwise be logged?
Similarly, suppose the program performs some operation which depends on the system time which fails at runtime for certain system times. If run in deployed mode, it would restart and run the same thing in debug mode, but the environment would be different. How would such an operation be able to be traced back to the particular conditions which caused the error?
With this model the host can just log/record the environment interactions. The details of this host and integration are an open issue, both research and engineering, but I am very excited by the AMBROSIA architecture (https://github.com/Microsoft/AMBROSIA) by one of my colleagues as a possible design.
From your doc:
>var z = 5;
z = y; //error z is not updatable
"var" is shorthand for 'variable' as you're no doubt aware, so use "const", or use something else altogether to indicate a var.
I stopped reading after coming across your unintuitive "var!" syntax. Let an apple be an apple and an orange an orange.
https://github.com/marcusz/floyd https://www.reddit.com/r/ProgrammingLanguages/comments/arwjl...
Some examples of better than existing languages are included as case studies in section 5 of the technical report:
-Automatically finding (ideally) any runtime error and producing a test case for it.
-Verifying that a SemVer update is safe or flagging where it will change the behavior of your code.
-Supporting compilation to high speed SIMD code or other accelerator architectures.
This is all still an aspirational goal and a lot of work remains though.
Isn't that similar to what Elm does?
"Basque is a language spoken in the Basque Country, a region that straddles the westernmost Pyrenees in adjacent parts of northern Spain and southwestern France. Linguistically, Basque is unrelated to the other languages of Europe and is a language isolate to any other known living language."
I didn't know about the forest meaning at all. (Maybe a subconscious influence, if you've ever heard any reference to Basque.)
The correct usage would be to talk about an error "in" a sentence, not "on" a sentence.
Muphry's law get you every time!
this just gets better and better
Sems like it got you too!
(Yes that was intentional
That said it wouldn't surprise me at all if I've misspelled something here : )
"The Bosque programming language is designed for writing code that _is_ simple, obvious, and easy to reason about for both humans and machines."
1. overall quite nice looking and easy to understand
2. too verbose in places, e.g. this constant structure:
const winPositionOptions: List[List[[Int, Int]]] = List[List[[Int, Int]]]@{
List[[Int, Int]]@{ @[ 0, 0 ], @[ 0, 1 ], @[ 0, 2 ] },
List[[Int, Int]]@{ @[ 0, 1 ], @[ 1, 1 ], @[ 2, 1 ] },
List[[Int, Int]]@{ @[ 0, 2 ], @[ 1, 2 ], @[ 2, 2 ] },
List[[Int, Int]]@{ @[ 0, 0 ], @[ 1, 0 ], @[ 2, 0 ] },
List[[Int, Int]]@{ @[ 1, 0 ], @[ 1, 1 ], @[ 1, 2 ] },
List[[Int, Int]]@{ @[ 2, 0 ], @[ 2, 1 ], @[ 2, 2 ] },
List[[Int, Int]]@{ @[ 0, 0 ], @[ 1, 1 ], @[ 2, 2 ] },
List[[Int, Int]]@{ @[ 0, 2 ], @[ 1, 1 ], @[ 2, 0 ] }
};
Has types defined in 10 places, 9 of which are redundant.Also you are using @{...} for lists and @[...] for tuples; a more usual convention (in e.g. Python or Haskell) would be [...] and (...) respectively.
3. String types aren't intuitive (at least to me). E.g. in this code:
const playerX: String[PlayerMark] = 'x'#PlayerMark;
It appears that a String[PlayerMark] is a subset of String that can have the values 'x' or 'o'.I would have preferred something like:
type PlayerMark = 'x' | 'o';
type PlayerMarkOrBlank = 'x' | 'o' | ' '; point<~(y=value)
instead of other languages' point.y = value
?I believe that syntax should be designed to make things simple for developers, not for the designers of languages or for compilers.
"point.y = value" is typically a statement for a single update of a mutable record.
I'm not sure how you'd extend the latter to support the features of the former. The meaning is completely different. Different things should look different.
iex(1)> point = %{x: 1, y: 2, z: 3}
%{x: 1, y: 2, z: 3}
Of course this doesn't work iex(2)> point.x = 4
** (CompileError) iex:2: cannot invoke remote function point.x/0 inside match
This is how we do it, verbosely iex(3)> Map.put(point, :x, 4)
%{x: 4, y: 2, z: 3}
Or with a syntax shortcut iex(4)> %{point | x: 4}
%{x: 4, y: 2, z: 3}
A better syntax shortcut would be the usual iex(nope)> point.x = 4
%{x: 4, y: 2, z: 3}
which everybody would understand no matter the language of origin. I imagine that it would require quite an overhaul of the internals of the compiler, but the compiler should bend to us, not us to the compiler. This is my point about obscure language syntaxes. 2.3.0 :001 > s = "abc"
=> "abc"
2.3.0 :002 > s.gsub("b", "B")
=> "aBc"
2.3.0 :003 > s
=> "abc"
2.3.0 :004 > s.gsub!("b", "B")
=> "aBc"
2.3.0 :005 > s
=> "aBc"
I guess that if an immutable language shows some mutable syntax we can expect it to be a short form. Actually Elixir has at least a mutable short form: iex(1)> s = "abc"
"abc"
iex(2)> String.replace(s, "b", "B")
"aBc"
iex(3)> s
"abc"
The next line would be an error in Erlang and other languages because we can't mutate an already assigned variable there, but Elixir allows rebinding iex(4)> s = String.replace(s, "b", "B")
"aBc"
iex(5)> s
"aBc"Your proposed syntax also doesn't have any obvious way to do a bulk update, and I'm not sure what syntax you're proposing for that.
I maintain that it's more important for things to look like how they act, rather than look familiar -- especially if the familiar appearance does something functionally different -- https://stackoverflow.com/a/522168
point.copy(y=value) let point2 = { point1 with y = value }
https://docs.microsoft.com/en-us/dotnet/fsharp/language-refe...EDIT This is inherited from OCaml - https://v1.realworldocaml.org/v1/en/html/records.html#functi...
--Niklaus Wirth (Good Ideas,Through the Looking Glass)
>Thus, x = y does not mean the same thing as y = x.
Unless it does, or should.
The specfic argument you're arguing here is on that is just a simple syntax. You don't like = for assignment, but I do. You like = for comparison, but I like ==... To be fair === is dumb.
You're arguing that point<~(y=value) makes sense, and I'm still not seeing it.
Pascal: Is X equal to Y?
C: Change X, making it equal to Y.
Mathematics: X is equal to Y.
Those are all valuable in programming. Normally programmers get the mathematical meaning via a feature such as __assume or __builtin_expect or assert. It can be used for optimization. When the constraint is violated, various bad things can happen. The code can run slower, halt with an error message, or behave in an undefined way.
Every new generation of programmers starts off expecting the mathematical meaning. I don't know of any programming language which uses it. Ideally that would be fixed, but the change might be difficult for the current generation of programmers.
Note that the Pascal meaning can be fairly compatible with the other meanings. The solution is simply to be aware of context. On a line by itself, X=Y makes no sense as an equality test. Within the controlling part of an "if" or loop, assignment need not be supported. That does the job. Alternately, the meaning could change by having a keyword in front for assignment ("let" is popular) and/or for the assertion. So we could distinguish all three meanings with syntax like "if X=Y", "let X=Y", and "assume X=Y".
Most lines of code are written as true statements. If you understand how a computer actually works at the register level you'd see that
x=5; has a result. And it's not 5. It's TRUE. I'm not asking if x equals 5, I'm saying HEY YOU, X IS EQUAL TO 5. I'm telling the computer this is now the truth. 5 is moved into the x RAM location and the result is true, because the CPU is doing what I told it.
You're just wrapped up in your semantic view. There is nothing wrong with = for assignment and == for evaluation. Someone would complain if it was the other way around, so just accept this is the standard - or make your own language that "does things right". But.... Just because it isn't the way you like it, doesn't make it wrong.
eq is often the wrong one to use: two instances of the same number can fail to be eq.
Obviously someone please correct me if I'm wrong.
As I mentioned in the discussion two days ago, PL researchers throw around these terms but never actually measure them. You can learn a lot from a small user study.
For example, have participants do a few comprehension tasks with Bosque code snippets and also with C++ code snippets. Measure task completion time, task success, and self-reported cognitive load [1]. Then do a semi-structured interview to get some insights into the participants' opinions.
[1] Cognitive Load Measurement as a Means to Advance Cognitive Load Theory https://www.tandfonline.com/doi/abs/10.1207/S15326985EP3801_...
High level languages exist to remove the complexity of opcodes of the bare metal running underneath. And that's because assembly code (unless you're well versed in it) is hard to reason about.
I think this is the one that should be highlighted most. Otherwise, from an engineering perspective, many of the other language features have already been implemented and widely used in other languages
My humble opinion aligns with yours, it seems intuitively "better" to have no undefined behavior.
I only wish that undefined behaviour in C++ were harder to accidentally use, or at least, if the compiler warned me "hey, I'm gonna do X here because that undefined behaviour here allows me to do so, this may not be portable to other compilers and may not be what you wanted, you've been warned!"
That said, there is UBSan, which is a runtime checker for undefined behavior that can help you to identify if your code is depending on some undefined result.
If you're going around thinking computing resources are cheap, it doesn't make much sense to avoid those correctness checks, and undefined behavior is probably not good from that perspective.
Whether or not you agree with Evan Buswell, it is a solid argument, both for its thesis, and for the utility of Code Studies in general.
We needed to think more about all of these usecases and how things worked so we decided to start with just strings as the most useful. It is great to see you had the same idea though and would definitely like to revisit this in the future.
For your Int[Seconds] example, I haven't used them at all but could it work something like the F# measures concept? That seems sort-of similar but more general from scanning the docs.
https://writing.kemitchell.com/2019/03/09/Deprecation-Notice...
Either Apache 2.0 or Blue Oak Model would be better choices.
Could you expand on this? I can't see this assumption anywhere in the MIT license, and the link you provide doesn't clarify on this point.
I also find the Blue Oak license much more difficult to understand than the MIT license, FWIW.
> Copyright (c) <year> <copyright holders>
Where <copyright holders> is often filled as the company of the primary developer, e.g. "Facebook". What I often see (and use) to avoid this, is filling it in with "The <project> developers", e.g. "The React developers".
> Copyright the Project developers
conveys zero information. Of course contributors to a software project hold copyright in it. Who else should?
But who are those contributors? The Blue Oak Model embraces the practical reality: When we want to know contributed to an open software project, we look to development files and revision-control data, not the license.
“MIT and BSD terms include a space for a single copyright notice, because they were written for releases from academic institutions that own all the copyrights in their employees’ work. What about other contributors to the project? They hold copyright in their contributions.”
Apache 2.0 DOES address this issue in a complicated way, through the supplementary use of Contributor License Agreements. Other non-Apache projects like Fossil also require you to physically sign a CLA before contributing code to the project.
Blue Oak handles this in a more straightforward way, by explicitly saying that, as a condition of the license, every contributor in turn allows any use of their code that would otherwise infringe their copyright or patent claims. Who is a contributor? Anyone who contributes code. No CLA needed.
Expressing that expectation in writing can help make a legal case that submitting a contribution for inclusion in the public project implies a license under the same terms. But that's a distant second best to creating a written, public record of each contributor's affirmative intent to license their work under Blue Oak Terms.
That evidence doesn't have to look like a "formal" CLA. It could be as simple as a public comment on the PR:
> I license my contributions under Blue Oak Model 1.0.0.
It could be as simple as having contributors add their names to a THANKS or AUTHORS or CREDITS file in the repo, under a note that everyone licenses BlueOak-1.0.0, with Git commit data showing each contributor added their own name.
In general, having a long list of copyright holders is bad, because it makes it almost impossible in practice to verify that all of the copyright holders actually consent to release their code under the relevant license.
When there is more than one copyright holder, you absolutely need language to address that in the license. MIT license has no such language. Apache 2.0 addresses it by requiring CLAs, per-file copyright and attribution notices, etc. Blue Oak addresses it by using terms that start with "Each contributor".
The bottom line is that any kind of open source license has copyright as its foundation. If you don't have a proper record of the copyright holders, then the license doesn't mean much. You need to know exactly who has the copyright to the code and exactly who is agreeing to release the code under a given license.
If the MIT license assumes everyone works at the same place then that seems horrible for GitHub repositories, right?
No, GitHub exists to somehow benefit the current owners, Microsoft, however they decide is the best way.
> No one benefits from the choice of MIT as a GitHub default.
Actually, there is a good argument to be made that Microsoft actually benefits from MIT being the default, as nickpsecurity pointed out in a sibling comment.
I mean, my auto shop exists to benefit me however I decide is best, and there is a good argument to be made that I benefit from sabotaging my customers' cars. So what!
Oh, that explains the downvotes. No, that’s not what I meant. What I was trying to do was to socratically tease out the strangeness of calling something “horrible for GitHub repositories”, as if GitHub repositories had an inherent purpose.
Whether they'll actually do it or not is an unknown. I'd prefer we default on licenses that nullify the patent risk.
I suppose if the learnings of Bosque find their way into TypeScript that might be useful but I don't see a practical use for Bosque as it is. Either that or I've completely missed the point.
"We believe that, just as structured programming did years ago, this regularized programming model will lead to massively improved developer productivity, increased software quality, and enable a second golden age of developments in compilers and developer tooling."
https://www.microsoft.com/en-us/research/publication/regular...
var v: List[Int ?] = List@{1, 2, none, 4};
OK! I think it needs some work. AM I supposed to intuitively know what ? and @ mean?
O.o
Why? What even was the point in making them 'typed' then?
Also, what exactly are the relevant language-level details which BOSQUE contributes? It seems more like a large library than anything else.
To prevent you from passing a String as a String[Zipcode].
method makeAutoMove(mark: String[PlayerMark], rnd: Int): Game
requires !this->hasWinner();
method makeExplicitMove(x: Int, y: Int, mark: String[PlayerMark]): Game
requires !this.board->isCellOccupied(x, y);
If I'm reading it right though, it looks more like it simple doesn't execute the function at all instead of throwing etc. So yet another possible route to deal with an invalid contract.That said, some of the refinements, especially the (TypeScript-inspired?) record typing and manipulation facilities, look quite pleasant.
> I don't see how you can stop the map().. or filter() operators.
You use an early-terminating operator like find or take. (The general solution is probably Clojure-style transducers, which compose in a way which enables, among other things, plugging termination conditions into an operator which otherwise isn't early terminating.)
> Bosque is designed to encourage limited uses of recursion [and using map/filter etc instead]. This is done by introducing the rec keyword which is used at both declaration sites to indicate a function/method is recursive and again at the call site so as to affirm that the caller is aware of the recursive nature of the call
i don't think i've seen a way marking functions that might not terminate at the call site. (though termination doesn't seem to be a specific focus here)
I'm still not sure what is the raison d'etre here.
Why would I use this instead of, say, Rust to WASM?
Along with literally every other language. I really wish the splash pages for new languages would cut the boilerplate and just open with a list of the specific traits that make this one different from its peers.
Edit: it appears that this is relatively easy to find after visiting the repository. Still, that opening paragraph is meaningless, and many languages don't even make the worthwhile bits easy to find for those who actively seek them.
The fact that there are idioms that they could use doesn't mean those idioms are natural to the way people think, in particular, to the way they think when trying to solve problems.
I avoided a fully functional style in my language[1] (even it has a functional kernel) because I've watched people write code while working with them or as interview candidates, and observed them (and myself, tbh) having trouble solving problems with "big" elements.
Thinking about the whole array makes it "big" mentally. I'm not a psychologist or a linguist, so I can't tell you what that means, just that I've observed a pattern. When I was doing interviews, we had the infamous stars question, which goes like this: "Imagine you're trying to find the (maximum distance) of all the stars in the sky. How do you compute this?"
Candidates reliably get hung up on "all the stars in the sky" and don't realize they can break the problem into the current maximum star plus all the rest of them. (That some people get it and some just don't is why I don't use the question.)
With the humble for loop, you're naturally dealing with a single thing at a time. That's why I think it's unwise to throw it out and only use functional constructs.
And that said, often we do want to address the whole structure. When you look at problem solving, it's about reexpressing a question in such a way that the answer falls from it naturally. To do that, we need a multitude of idioms that can expose different aspects of a problem and be composed reliably.
> We believe that further work, such as identifying recursive idioms or other fundamental algorithmic patterns, can further reduce the need for unstructured recursion – eventually limiting it to an infrequently used part of the language.
I do agree with this. If you're going to have find, map, filter, etc. then it makes sense to have a generalized tree-walking algorithm. That's how I'd probably like to express it.
But, again, when I'm solving the problem initially, I need to be able to deal with small things to keep it all in my brain at once. That's how we teach it and often how we will want to express it to ourselves when we're in the fog of solving an unknown.
I absolutely would love something that could describe boundaries, enforce invariants and make guarantees that a traversal won't hang, but I wouldn't want that to be the only way to express such traversals. Development is an evolutionary process; we often start with something that might suck but is viable, and then iterate and improve it.
Edit: Funnily the issue ( I & l )is now visibly noticeable with HN's font choice
Might be a bit over-critical, but when I read that it's main selling point is making it more human readable, this seems a bit troll