The crazy thing with reading this and the comments, is that it seems like we all have been daydreaming about completely different versions of a "high level rust" and what that would look like. For me I'd just want a dynamic run time + simpler types (like "number" or a single string type), but it looks like other people have a completely different list.
Some of the additions here, like a gradual type system, I would really not want in a language. I love gradual type system for stuff like Python, Typescript and Elixir, but those are cases where there's already so much untyped code written. I would way prefer the guarantees of a fully static typed codebase from day one when that's an option.
I loved this, both as a teaching aid, and as an eye-opener that programming languages are just an accumulation of choices with different trade-offs that can all go different ways and result in something that works, perhaps a bit better or perhaps worse, or perhaps just a bit more toward or away from one's own personal taste.
This is sort of the lisp idea of "create the language that is natural to write the application in, then write the application". Or Ruby's take on that idea, with more syntax than lisp but flexible and "human" enough to be DSL-ish.
But somewhat to my sadness, as I've progressed in my career, I've realized that the flip side of this is that, if you're building something big it will require lots of people and all those people will have different experiences and personal preferences, so just picking one standard thing and one standard set of defaults and sticking with that is the way to go. It reduces cognitive overhead and debate and widens the pool of people who can contribute to your effort.
But for personal projects, I still love this idea of thought experimentation around the different ways languages and programming environments could work!
A poor-fitting language is terrible for abstract thinking, on the other hand an internally-consistent and domain appropriate language can unlock new ways of looking at problems.
I'd highly recommend Martin Fowler's work on DSLs to see how you can apply these techiques to large projects.
This is one of his papers in Pl-Detective and Mystery for anyone interested: https://www.researchgate.net/publication/220094473_PL-Detect...
[1] https://blog.brownplt.org/2018/07/05/mystery-languages.html
Languages, with first class values, pattern matching, rich types, type inference and even fancy RTS, often can be embedded in Haskell.
For one example, it is very much possible to embed into Haskell a Rust-like language, even with borrow checking (which is type-checking time environment handling, much like linear logic). See [1], [2] and [3].
[1] http://blog.sigfpe.com/2009/02/beyond-monads.html
[2] https://www.cs.tufts.edu/comp/150FP/archive/oleg-kiselyov/overlooked-objects.pdf
[3] http://functorial.com/Embedding-a-Full-Linear-Lambda-Calculus-in-Haskell/linearlam.pdf
Work in [3] can be expressed using results from [1] and [2], I cited [3] as an example of what proper type system can do.These results were available even before the work on Rust began. But, instead of embedding Rust-DSL into Haskell, authors of Rust preferred to implement Rust in OCaml.
They do the same again.
why? and how much does it matter, if the goal is to have a compiler/interpreter? (as I assume is the case with Dada, and was with Rust)
It seems very hard to pick a good 'number' (JS's is actually a double-precision 64-bit IEEE 754 float, which almost never feels right).
print("...").await
I'm coming from Python, and I can't help but ask: If my goal as a programmer is to simply print to the console, why should I care about the await? This already starts with a non zero complexity and some cognitive load, like the `public static void main` from Java.
Because that isn't ever anyone's actual goal? Optimizing a language design for "Hello World" doesn't seem like a particularly useful decision.
if I want a short/simple program it would be cool to put a stanza on the top of the file to auto-await all futures.
'print()' should be async because it does IO. In the real world most likely you'd see the output once you yield.
What if I want to do synchronous IO?
.NET core will introduce something similar
At least let people change the default. For example
await {
// all the code here
// runs synchronously
async {
// except this part where
// async methods will return early
print("but not me!").await()
}
}
However the remark I make to people advocating for static typed Ruby holds for this language too: there are already languages like that (in this case await by default,) we can use them and let Dada do its own thing.(These kinds of questions are just unavoidable though; everyone will have these little pet things that they subjectively prefer or dislike.)
https://rust-lang.github.io/async-book/01_getting_started/04...
“Zero complexity print to the screen”
Is, quite possibly, the dumbest argument people make in favour of one language over another.
For experienced people, a cursory glance at the definitions should be enough. For new programmers, ignoring that part “for now” is perfectly fine. So to is “most programming languages, even low level ones, have a runtime that you need to provide an entry point to your program. In Java, that is public static void main. We will go over the individual aspect of this later. ”. This really is not that difficult, even for beginners.
Personally, I find more “cognitive load” in there not being an explicit entry point. I find learning things difficult when you’re just telling me extremely high level *isms.
If they're already making it gradually typed and not low-level, I don't understand why they don't throw away the C ABI-ness of it and make it more like Ruby with fibers/coroutines that don't need async/await.
I'd like parametric polymorphism and dynamic dispatch and more reflection as well if we're going to be making a non-low-level rust that doesn't have to be as fast as humanly possible.
(And honestly I'd probably like to keep it statically typed with those escape hatches given first-class citizen status instead of the bolted on hacks they often wind up being)
[Ed: also rather than go back to object oriented, I'd rather see really easy composition, delegation and dependency injection without boilerplate code and with strongly typed interfaces]
if __name__ == "__main__":
main()In JavaScript calling the function would start the task, and awaiting the result would wait for it. This lets you do several things concurrently.
How would you do this in Dada:
const doThings = async () => {
const [one, two, three] = await Promise.all([
doThingOne(),
doThingTwo(),
doThingThree(),
]);
};
And if you wanted to return a thunk to delay starting the work, you would just do that yourself.If I'm declaring an async function, why do I need to await inside it?
like, if the return of an async function is a promise (called a thunk), why can't I do
async async_foo() { return other_async_foo(); } and it will just pass the promise?
Then you await on the final async promise. Makes sense?
I'm aware that there are a few languages that come close to this (crystal iirc), but in the end it's adoption and the ecosystem that keeps me from using them.
[1] https://doc.rust-lang.org/book/ch15-04-rc.html
[2] https://doc.rust-lang.org/book/ch15-05-interior-mutability.h...
You’re telling people to just ignore the paved road of Rust, which is bad advice.
(To be clear, using RC for everything is fine for prototype-level or purely exploratory code, but if you care about performance you'll absolutely want to have good support for non-refcounted objects, as in Rust.)
Edit: Googled it. Found an answer:
> The only distinction between Arc and Rc is that the former is very slightly more expensive, but the latter is not thread-safe.
Still, the language is great. Plus, it has Java interop, JVM performance, and Jetbrains tooling.
You could still have your IDE showing you type hints as documentation, but have inferred types to be more fine grained than humans have patience for. Track units, container emptiness, numeric ranges, side effects and idempotency, tainted values for security, maybe even estimated complexity.
Then you can tap into this type system to reject bad programs ("can't get max element of potentially empty array") and add optimizations (can use brute force algorithm because n is known to be small).
Such a language could cover more of the script-systems spectrum.
Despite type systems being powerful enough to figure out what types should be via unification, I don't think asking programmers to write the types of module declarations is too much. This is one area where forcing work on the programmer is really useful to ensure that they are tracking boundary interface changes correctly.
The whole point of rusts type system is to try to ensure safe memory usage.
Opinions are opinions, but if I’m letting my runtime handle memory for me, I’d want a lighter weight, more expressive type system.
It isn't though. The whole trait system is unnecessary for this goal, yet it exists. ADTs are unnecessary to this goal, yet they exist. And many of us like those aspects of the type system even more than those that exist to ensure safe memory usage.
You might have to lose a few parens though!
For me it seems like the perfect match.
https://pcwalton.github.io/_posts/2013-06-02-removing-garbag...
That has changed through the years: https://graydon2.dreamwidth.org/307291.html
Well, since you can't really use without high adoption even if something comes up with all features you want, you still won't be able to use it for decades or longer.
There is Rune, but like you mentioned the issue is adoption, etc.
Whenever I've had to write kotlin for Android in the past I did quite enjoy it. It seems like the entire ecosystem is very enterprise-y when it comes to web though. Forced adherence to object orientedness and patterns like 100 files, 5 folders deep with 10 lines of code each keep cropping up in most kotlin projects I've seen.
Imperative code with functional constructs seems like the most workable approach to me, which rust, go, and other languages like kotlin, crystal etc. all offer.
Gradual typing is interesting, but I wonder if necessary. Static typing doesn't have to feel like a burden and could make it hard to reason about performance. I think more type inference would be better than gradually typed (like OCaml/ML).
The fundamental concurrency system is green threads (similar to Go), which makes for a fantastic programming model where you spend your time writing linear blocking code, while actually having full parallelism. This is achieved both with the VM and the abstractions built on top like GenServers.
The Task module is a convenience that allows you to do "await" type work when that makes sense - because (as you describe) sometimes it does.
Open question: Are there any languages that can be used in a (decent [1]) REPL, that are strongly typed, but do not have Hindley–Milner-based type inference?
We have multiple concrete proofs that you can have a REPL with Hindley-Milner inference, but I'm curious if this is perhaps a concession to the difficulty of a strongly-typed REPL without a deeply inferable type system. But it's just an idle musing I'm throwing out to see the response to.
[1]: That is, for example, multiple people have put a Go REPL together, but anyone who has used a "real" REPL from the likes of Lisp, Haskell, Erlang, O'Caml, Python, etc., will not find it a "decent" REPL, as Go just can't have one for various reasons.
Those are two very different feelings though!
Code in general is hard for me to mentally read. I know it sounds nitpicky, but to me all keywords should be obviously pronounceable, so something like "func" instead of "fn" would be mandatory. Also, using the permission keywords where I'd expect the type to be also seems a bit strange, as I'd imagine that keyword to prefix the variable -- that's just how I think though.
It does seem like less decorator magic and symbol-based syntax would make it easier for beginners to grasp.
I may sound like a curmudgeon, but I'd prefer only one type of language innovation at a time.
Cutting my teeth on Schemes and MLs and now working in Python, I have the complete opposite experience. It's jarring to have to specify return. What else would I want to do at the end of an expression? It seems tautological. The real reason it's there in Python is early return, which is even more dangerous and jarring.
When I looked at rust code before, it all seemed a bit weird. I couldn’t immediately understand it, but I’ve since come to realize this was because the dozen or so languages I can read well don’t really resemble rust, so my pattern matching was a bit off.
The more I learn about the syntax and core concepts, the more I’m learning that my brain absolutely loves it. Once I started to understand matches, lifetime syntax and the core borrowing mechanics, things clicked and I’m more excited about writing code than I’ve been since I taught myself GW-BASIC 25 years ago.
Just sharing this anecdote because I find it interesting how differently people experience languages. I also have an ongoing friendly debate with a friend who absolutely hates Python, while I rather enjoy it. I’ve tried to understand why he hates it, and he’s tried to understand why I like it. And it all seems to come down to hard-to-define things that just rub us in different ways.
I hope the benefits of rust find their way into more types of languages in the future.
For instance, I'm fine to write C++, Javascript or Python (with types at least). Ruby or Rust for some reason do rub me the wrong way, no matter how much I try to tough it out.
Have you tried Ada?
> so something like "func" instead of "fn" would be mandatory.
What about no keywords, like:
x => ...func bodyI have tried Pascal in that sphere, which was on the too verbose side.
Arrow notations like in JS/Typescript are fine to parse for me. Some clear symbols are actually easier to read than an unpronounceable alphanumeric.
I hear you. Internally, I always pronounced "var" as rhymes with "care", but then a colleague pronounced it "var" as rhymes with "car". I think the same guy pronounced "char" like char-broiled, whereas I had thought of it like "care". And he would say "jay-SON" for json, which I pronounced like Jason.
How would you feel about a notation that is not meant to be pronounced at all?
+Employee {
}
where + indicates a class definition.
:Rename() where : indicates a class method.
~DoStuff() where ~ indicates a static function
Keywords only? How about function names like strspn or sbrk? And how do you feel about assembly language, using mnemonics like fsqrt or pcmpeqd?
BTW, thinking about it, I notice, that I need all these lexemes to be pronounceable too, and I have my ways to pronounce sbrk or pcmpeqd. Probably if I do it aloud no one will understand me, but it doesn't matter because these pronunciations are for internal use only.
I do consider the lightning start-up speed of a program to be one of the killer features of Rust. Rust with garbage collection throws away one of it's biggest advantages compared to every other language around.
The slow startup you associate with GC language implementations like ones for Java and JavaScript mostly comes from JIT warmup.
As easy as JavaScript to write, as fast as Rust when the extra effort to write it justifies it.
But perhaps it's a viable "training wheels" approach for getting used to borrow-checker friendly patterns? And I guess a scripting interpreter option that is fully rust-aware in terms of lifetimes could be truly golden for certain use cases, even if it turns out to be completely hostile to users not fully in tune with the underlying Rust. Sometimes "no recompile" is very important.
I wonder if the genesis story of the project might be hidden in "Dada has a required runtime": perhaps it started with the what-if of "how nice could we make Rust if we abandoned our strict "no runtime!" stance and went for making it runtime-heavy like e.g. Scala"? Then the runtime pulls in more and more responsibility until it's easier to consume a raw AST and from there it's not all that far to making types optional.
Rusts “difficulty” stems from its single ownership model, and this model is “different” not “easier”.
Here is the original quote:
I speak only of myself since I do not wish to convince, I have no right to drag others into my river, I oblige no one to follow me and everybody practices his art in his own way, if be knows the joy that rises like arrows to the astral layers, or that other joy that goes down into the mines of corpse-flowers and fertile spasms.
changed to :
I speak only of myself since I do not wish to convince, I have no right to drag others into my river, I oblige no one to follow me and everybody practices their art their own way.
dada-lang about: https://dada-lang.org/docs/about/ Tzara, Dada Manifesto 1918: https://writing.upenn.edu/library/Tzara_Dada-Manifesto_1918....
[...]
# This function is declared as `async` because it
# awaits the result of print.
async fn print_point(p) {
# [...]
print("The point is: {p}").await
}
[...]
From the first page of the tutorial:> Dada, like JavaScript, is based exclusively on async-await. This means that operations that perform I/O, like print, don't execute immediately. Instead, they return a thunk, which is basically "code waiting to run" (but not running yet). The thunk doesn't execute until you await it by using the .await operation.
So, what it boils down to is that async/await are like lazily computed values (they work a bit like the lazy/force keywords in Ocaml for instance, though async seems to be reserved for function declarations). If that is the case, that method "print_point" is forcing the call to print to get that thunk evaluated. Yet, the method itself is marked async, which means that it would be lazily evaluated? Would it be the same to define it as:
fn print_point(p) {
print("The point is: {p}")
}
If not, what is the meaning of the above? Or with various combinations of async/await in the signature & body? Are they ill-typed?I wish they'd provide a more thorough explanation of what await/async means here.
Or maybe it is a dadaist[0] comment?
What this means, concretely, in Rust, is `.await` will return the thunk to the caller, and the caller should resume the async function when the result is ready. Of course the caller can await again and push the responsibility further back.
The most important thing here, is that `.await` yields the control of execution. Why does this matter? Because IO can block. If control wasn't given up, IO will block the whole program; if it is, then something else will have a chance to run while you wait.
In other words, print produces a thunk, and print_point also produces a thunk, and when await is used on the later, it is executed asynchronously, which will execute the print also asynchronously. So we end up with 3 different execution context: the main one, a one for each "await"?
What is the point of this, as opposed to executing the thunk asynchronously right away? Also, how does one get the result?
That would be Swift?
Interesting experiment. But it does seem like there are increasing numbers of languages trying to crowd into the same spaces.
I don't think it will be, it sounds like a concept of similar complexity and it won't make it an "easy language".
People are scared of Typescript, so a typed language with an extra ownership concept will sound exactly like rust in terms of difficulty.
Not that I get the reputation of Rust being hard, even as a complete novice I was able to fight a bit with the compiler and get things working.
The gradually typed approach is nice but it just sounds like smarter type inference would get you 99% there while keeping the performance (instead of using runtime checks).
Not having unsafe code is both interesting and limiting. I keep all my code safe for my own mental sanity but sometimes having bindings to some big library in c/c++ is convenient (eg Qt or OpenCV).
It's dynamically typed and uses lifetimes instead of a garbage collector.
JavaScript (new) is +++2, and ++3 (to me). Java is +++1 & --2, -3.
Personally I like OO ("has a") but think Class-ification,("is a") is a big mistake. Take a truck and a car. Start replacing the pieces of the car with pieces from the truck. When is a car not a car? Arbitrary. When does the car have a tail gate, a flat bed?
That is not a joke. Classes and Types are a way to think (Sapir Whorf) that makes you do strange things.
The interesting thing about Dada is the "borrow", "share" etc and seems very good. But then instead of wrapping it in a class can't we just use an Object?
If you are making a new programming language, please do us a favor and put your Hello World syntax example right on the landing page.
Rust is great but being an early adopter has made its usability imperfect in places. Combining substructural typing with gradual typing and OOP is interesting here. Others in this thread have also mentioned wanting a higher-level Rust, like Go. I'd like to see a purely functional Rust. Haskell has experimental support for linear typing[1], but I suspect a language built with it from the ground up would be very different.
[0]: https://verdagon.dev/blog/higher-raii-7drl
[1]: https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/line...
I'm coming from Python, and for situations when people grasp for C/++ kind of performance and control, I think people are aware of the need for high performance memory safe languages that are easier to use than Rust but with many of Rust's benefits being at least possible. So I am quite excited by thinking from Dada and the people who are behind Rust and I'm also intrigued by SerenityOS's Jakt language project. I hope the insecure "C code problem" has a smooth migration path that let's C/++ devs, Typescript devs, and others make progress quickly in a powerful way. What other sort of alternative languages are there, among Dada's aspirations? Jakt? Vale (I understand a lead dev is poorly, so it's slowed a bit lately)? D? Go? Obviously AI will have a big impact. What language is going to have a big impact in this space?
Otherwise, the idea of creating something close to rust but without the complexity sounds interesting. I just hope they don't stick to that name.
I wonder what that does...
> OK, from here on out I'm going to pretend that Dada really exists in its full glory.
This is a brilliant trick I only recently discovered in another context: write the docs first, to validate the user experience of a novel system.
All too often, the engineering has started at "customers want to be able to do $x", and that's the last time the customer was part of the consideration. The solutions are great, but often miss out on what it'd be like to actually use it, as a customer. Lots of foot guns, and expectations of knowledge that a customer couldn't possibly have unless they had as much understanding of what happens under the hood as the engineers did, etc.
Feel free to experiment on the syntax, but the concept is amazing, especially if you're planning on being dynamic-ish.
Are classes cool again?
Perhaps his most famous piece is a photo of Hitler captioned "millions stand behind me," showing a donor passing him stacks of cash.
https://graydon2.dreamwidth.org/307291.html
https://www.reddit.com/r/rust/comments/7qels2/i_wonder_why_g...
> I speak only of myself since I do not wish to convince, I have no right to drag others into my river, I oblige no one to follow me and everybody practices their art their own way.
> Tristan Tzara, "Dada Manifesto 1918”
I've probably written 100s of tiny little utility programs that are a couple of lines at most, and wouldn't need types for any of those, it would just add extra verbosity for no gain.
In garbage-collected languages, please give me gradual / optional annotations that permit deterministic fast freeing of temps, in code that opts in.
Basically to relieve GC pressure, at some modest cost of programmer productivity.
This unfortunately makes no sense for small bump-allocated objects in languages with relocating GC, say typical java objects. But it would make a lot of sense even in the JVM for safe eager deterministic release of my 50mb giant buffers.
Another gradual lifetime example is https://cuda.juliagpu.org/stable/usage/memory/ -- GPU allocations are managed and garbage collected, but you can optionally `unsafe_free!` the most important ones, in order to reduce GC pressure (at significant safety cost, though!).
> Updated to use modern pronouns.
I would say that that their updated quote is a more accurate translation of the original than the English translation they initially used.
As long as the meaning of the quote isn't changed I couldn't care less and it seems very important to some people.
What I personally dislike though is the whole "Ask me my pronouns" thing... like "No, I don't care about your gender or sex, as long as I am not interested in a romantic relationship with you - just tell me how to call you and I'll do it, but more effort? No!"
To elaborate a bit more: I find the topic exhausting not because I hate freedom of choosing your own gender or anything like that, but because I personally do not care about your gender at all.
I don't care about your religion, your skin color, your culture, your sex, your gender... I care about individual people but I don't reduce them to a certain aspect of their existence.
Now I find the whole "Ask me my pronouns" exhausting and also rude because it puts pressure on me to ask you about a topic I am not interested in. Like: I get it, there is social pressure, I understand that you're not happy with certain social "norms" and developments. I totally get that and I guess we are on the same side for many of them, but I still do not care about your gender until I care about your gender. (And also, I don't live in your country probably, so your local politics may be of interest, but I still don't like being forced to talk about them before I can ask a genuine question on e.g. a technology topic ;))
Just write his/her/theirs... and I will respect your choice. I will not think less of you, nor will I put you on a pedestal for something I do not care about.
Non-native speaker too, I find it easier to adjust in English compared to my native language (French), probably because the language is less engrained in me. I embraced the English neutral pleural - it's even convenient - but I found myself a bit more annoyed with the so called French *écriture inclusive", such as "les étudiant.e.s sont fatigué.e.s". Not really pretty IMHO. We could find something better..
Its been done before. See royal plural
In my native language it is quite old-school. Really polite form.
I predict this project will have its priorities backwards. There's a group of people who want to govern a programming language project, and inject their ideology into that structure, and maybe there's another group of avid language designers in there too. I think there are more of the first.
What makes you so reliant on significant white space that any language without is a automatic dismissal?