Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I might actually be more excited about Val as a C++ successor than I am about Herb Sutter's excellent CppFront... Admittedly, all I know about it is from the two presentations below. But, from what I can tell...

Statically compiled. Statically typed. Interops with C++. Memory safe. Typesafe. Data-race-free.

The elevator pitch I give when describing it goes like this: Imagine you were starting a new C++ project and didn't really care about performance (spoiler: perf comes back in the end). So, you decide to not bother using pointers or references anywhere. You just pass by value, return by value everywhere, all day long. If you ignored the obvious perf problems of passing around maps of vectors of objects by value, wouldn't it be nice? you don't have to worry about side effects or data races or anything. And yet, the data is not immutable. You can go ahead and mutate it all you want worry-free because the data is all completely local to your function.

Well, turns out the Val folks have figured out that by eliminating pointers and references from the language, the can get the compiler to automatically pass-by-const-reference and return-value-optimization under the hood such that it preserves both the performance and the semantics that you want at the same time.

Val: A Safe Language to Interoperate with C++ - Dimitri Racordon - CppCon 2022 https://www.youtube.com/watch?v=ws-Z8xKbP4w

https://cppcast.com/val-and-mutable-value-semantics/



Listen to the last two episodes of ADSP Podcast with Sean Parent, regarding Val.

Adobe Research labs is seriously sponsoring its development.

https://adspthepodcast.com/


> Interops with C++

Doubt. I simply don't believe any other language out there is capable of interoperating with C++. Even C++ compilers have broken binary compatibility, different versions of the same compiler even.


Clasp might be such a language, it seems.

https://github.com/clasp-developers/clasp


Sounds like we're trusting a lot to the compiler. Will this lead to me making a small change which breaks an optimization in some distant code and ruins performance?


Maybe we should tell the compiler somehow that we don't want to pass a value, but a reference. For example we could use a * or a & symbol.


Wait for the blog post on how beautiful Val++ is.


Val<<>> when they inevitably introduce templates and stringstreams


In 2028, when we revive the One True Programming Paradigm (for Enterprise Software)™, we'll have bestowed upon us the glorious Objective-Val


In 2028, we will still be writing code in C and Fortran. And there will be all these new languages X++ that would be promising to replace either or both ;) ;) ;)


X++ already exists, better pick another name.

EDIT: https://learn.microsoft.com/en-us/dynamicsax-2012/developer/...


Swift++, or Swifttt.


Taylor Swift’s subject-oriented programming language – Swifties.

It would have: • garbage collection (Shake It Off) • error correction (Bad Blood) • closures (Closure) • optionals (Would’ve, Could’ve, Should’ve) • automatic reference counting (Right Where You Left Me) • REPL (I Knew You Were Trouble) • variable mutability (Everything Has Changed and Evermore) • guard condition (Eyes Open) • strict typing (You Belong With Me)

After compiling run the binary by issuing the command: Run {filename}

To debug: Tell Me Why {filename}


I was almost murdered a few years ago when I told a classroom full of senior girls at a suburban high school that Taylor Swift was overrated (I just wanted to see what would happen if I poked the bear).

That is all I know about Taylor Swift. But it seems like you are onto something here. :D


You were lucky to make it out alive.


bahahaha this is such a nichey yet epic comment


Petition to change all languages with “+” to repeat the last character for each plus. Going down the list of languages on Wikipedia[0]

A+ becomes AA C++ = CCC Clik++ never mind this was a bad call. JJJ RRR Visual JJJ XXX (nice) xBaseee ZZZ

[0] https://en.wikipedia.org/wiki/List_of_programming_languages


X+++


With AbstractSingletonProxyFactoryBean and family present as core language features.


Might have a future in mobile.


sounds good, but ill wait for c-interop so my moneys on valjective-c


Better write it like `Val< <> >` otherwise the parser will get confused.


I believe rust is sort of similar in that apart from the references, it's very heavy on the move semantics. And IIRC, sometimes it's doing extra memcpys. But I don't know if Rust is explicitly targeting const reference passing.

That said, if you're all in on it I imagine that the front-end could pretty aggressively target that, much like how rust uses no alias.


By default, non-references in Rust are passed by move, although you can derive `Clone` on types composed of all `Clone` types and then explicitly call the `.clone()` method to copy things. The only types that will be copied by default are the ones that implement `Copy` in addition to `Clone`, which in the standard library is on primitives like integers and characters but overall is used fairly sparingly.

References need to be specified as mutable if mutation is needed (i.e. `&mut T` instead of just `&T`). Using immutable references is also encouraged from the borrow checking rules; having a second reference (either mutable or immutable) alive at the same time as a mutable one is a compiler error, but using multiple immutable references at the same time is fine. (Pointers are the same way, but not really used much outside of bridging with unsafe Rust, since they can be null and therefore can't be dereferenced outside of `unsafe` blocks).


I think OP was referring to the fact that implementation of moves can sometime (more often than desired I would add) do memcpys. e.g. if you have a struct of ~100 bytes and you move it, it will probably emit a memcpy to write it in the receiving function's stack. AFAIK this happens because addresses are kinda considered observable, and not doing these copies could thus be observable and potentially break some weird code. I don't think Val would have the exact same problem because it doesn't have the concept of addresses/pointers.


> AFAIK this happens because addresses are kinda considered observable, and not doing these copies could thus be observable and potentially break some weird code. I don't think Val would have the exact same problem because it doesn't have the concept of addresses/pointers.

I honestly don't understand what you've said here at all. I'm not familiar with what "observable" means in this context, and googling "observable aliases programming languages" didn't help clarify it (two results were articles about C/C++ aliasing, one of which used the term "observable" once but didn't seem to define it at all, and the other which was the Wikipedia article on "Aliasing (computing)" that didn't contain the term at all).

Even if I knew what "observable" meant, I think you might be missing a negation (or maybe including one you didn't mean to) somewhere; if addresses are "kinda considered observable", it's not obvious why it would be an issue to omit copies and "be observable". Maybe understanding what's meant by "observable" would shed some light on what you mean here, but naively, I don't understand how code that expects things to "kind of" hold a property would break if that property were somehow more strongly held.


> addresses are kinda considered observable, and not doing these copies could thus be observable and potentially break some weird code

This is certainly the case in c++: distinct objects have distinct addresses, so to remove a copy the compiler has to prove that the aliasing is not observable which is hard.

But does rust have the same guarantee?


> You just pass by value, return by value everywhere, all day long.

I cannot efficiently mutate or even store references in structs anymore. That seems to be a show-stopper if you want to have any non-trivial or non-standard data structures in the program. Granted, they are hard to get right, so maybe it's a feature.


I just learned about Val today, but Rust has similar constraints. To write to a particular variable requires having ownership or a mutable reference, which only one part of the program can have at once. Since you can't have two mutable references in different parts of the program or in different threads, you need some sort of atomic encapsulating data structure to mediate access. Something like a mutex.

Newbies to Rust have a lot of trouble learning how to get the borrow-checker to accept their code, but what these restrictions are doing is syntactically ensuring there are no deadlocks or data races. It is presumably a similar story with Val.

If Val actually handles this differently, I'd love to know.


Rust's borrow checker can prevent data races, but it can't prevent deadlocks.

The Pony language has something analogous to Rust's ownership system (there it's called reference capabilities [0]), but in addition to that Pony prevents deadlocks too [1]. It does so by not providing locks, but only providing actors with message passing (channels). Now, Pony is a much higher level language and unsuitable for some of the low level stuff that Rust does.

If you restrict your Rust program to not use locks, but just send messages through channels between threads [2], you won't have deadlocks either. But that's quite a handicap for a low level systems language, so Rust in general can't commit to that.

[0] https://tutorial.ponylang.io/reference-capabilities/referenc...

[1] https://www.ponylang.io/discover/#what-makes-pony-different

[2] If you would rather prefer to write async code, there are async channels too, but to be deadlock-free you need to always guarantee that your futures are polled https://rust-lang.github.io/wg-async/vision/submitted_storie... which is maybe a strange requirement that's alien to most languages


You can trivially deadlock just with queues and messaging.


> It does so by not providing locks

> If you restrict your Rust program […] you won't have deadlocks either.

So Rust is directly superior, because it gives you a choice? I get it, there is something nice about having an enforced paradigm, so you're not forced to work with foreign 'bad' code, but I don't understand your argument:

> But that's quite a handicap for a low level systems language, so Rust in general can't commit to that.

Rust is fast, so it can't afford being slow. Pony solved this problem by being a slow, high-level language, so now it can block fast solutions and get away with it, because it's slow anyway, and therefore it's presented as a superior alternative to Rust - which can do the exact same thing, and have at worst the same speed as Pony, but we expect more from Rust (because it's better) therefore it's worse? Just doesn't make sense to me, sorry :D

Again, I see the advantage in the aspect of a language being simpler, and enforcing a paradigm on the ecosystem so developers have less head scratching to do when cooperating.

What's nice about Rust is that it enforces a safe paradigm at first, but over time one learns Rc, RefCell, unsafe "rustonomicon" to be able to optimize. In *the* Rust book, channels are taught first, immediately at the beginning of the chapter about threads.


Rust deadlocks just fine.


Rust can prevent races, but generally speaking it cannot catch deadlocks.


It seems you would have to use the `unsafe` escape hatch for data structures such as linked lists, and externally verify that the implementation is correct. Which is better than the status quo of C++ where you are always in needed of externally verifying safety.

As for the safety mechanisms discouraging non-standard data structures, the language designers probably would consider that a feature, rather than a bug.

I am curious whether the use of a broken unsafe construct can break the safety guarantees of safe constructs, or if the unsafety is contained no matter what.



> You just pass by value, return by value everywhere, all day long

This is actually how I've been writing PHP programs for years, passing arrays around like I just don't care.[3]

I think the PHP interpreter is smart enough to actually only copy-on-write, but I admit I've never profiled these programs. But anecdotally, I've done some pretty complex stuff[1] this way and it never caused noticeable performance problems.

[1] Complex as in 'wow I sure am making the computer process a lot of data, parse some DSL character-by-character, dynamically generate SQL and massage the results into some arbitrary structure for EACH PAGE LOAD, geez'. Not as in 'print hello world, but using Laravel', which somehow manages to be about 30x as computationally expensive[2].

[2] quip about how 'web artisans' spend the time between page loads brewing coffee using their CPU as a heating source while thinking deep thoughts. But I digress.

[3] Back in the PHP 4 days, objects, too, acted as if stored in variables rather than just pointed-to by them. Not that anyone expected language design excellence from Rasmus et al, but strikes me as a missed opportunity when they changed object variables to having reference semantics to have 'value, variable reference, or pointer-to-value' be a property of variables orthogonal to the type of value they store/reference. e.g.

  $a =  $b; // Copy $b's value into $a
  $a = &$b; // $a and $b are now the same variable
  $a = *$b; // $a points to the same object as $b, but unlike $a = &$b, the variables themselves are not linked


c++ interop always seems like some sort of magic power, given how few languages achieve it.


To be fair, I don't think even C++ is very good at C++ interop.


What language has C++ interop?


Haskell:

It is implemented as a library, without needing compiler support [*].

You can write C++ in TemplateHaskell (Haskell's strongly typed AST-generating macro system), which allows you to use Haskell variables in scope inside your C++ functions.

The "inline-c" library allows this for C and C++.

Example of its use in the Haskell OpenCV bindings: https://hackage.haskell.org/package/opencv-0.0.2.1/docs/src/...

The way it works: It invokes the C++ compiler for you to generate a wrapper function that closes over the Haskell variables used in your code snippet, and on the Haskell side generates a corresponding type-safe function call for you.

This approach allows you to use all C++ features that a C++ compiler supports. But it also carries the drawback of ... invoking a C++ compiler, which gives you the slow build speed of C++.

[*] Some compiler support was added to be able to specify compiler flags that are specific to the C++ compiler but not the C compiler.


Love the non-standard formatting :). It took me a while to see where haskell ended and C++ started. Seems very powerful.


D does. Not source compatible, but mangling and ABI for linking. And D can generate headers for `extern(C++)` declarations defined in D


Swift just added C++ interop in version 5.9. https://forums.swift.org/t/c-interoperability-in-swift-5-9/6...


Nim does as well. You can use the C++ backend and wrap a decent bit of C++. The imports aren’t automatic so you need to define the FFI, but you can call templates, implement virtual methods, call constructors, etc.

NimForUE takes advantage of this: https://youtu.be/Cdr4-cOsAWA


i've come across carbon (google's c++-next language), clasp (common lisp with an llvm backend), felix (compiler generates c++), and maybe a couple of others i'm forgetting.

also some languages have packages that deal with c++ interop to varying extents - see this discussion: https://www.reddit.com/r/ProgrammingLanguages/comments/huhy8...


Besides all other answers, .NET ecosystem, and IBM i TIMI, by having a C++ compiler as well.


> Statically compiled. Statically typed. Interops with C++. Memory safe. Typesafe. Data-race-free.

So it is for C++ what rust is for C?


Val has a similar but different strategy to the big problem as Rust. In Rust the reference semantics are lifetime checked (hence all the talk about a "borrow checker") but in Val there are no reference semantics.

In Rust there are some things you can't (safely) do because the checker can't see why they're ok. Val guesses that, with appropriate language features in place, you can extend that to everything and still have a useful language but now you don't need to teach this complicated and difficult feature.

Because Val is young it's not yet obvious whether this is basically always better, or whether it's too limited.


> Because Val is young it's not yet obvious whether this is basically always better, or whether it's too limited.

This is sound wisdom. It's not always obvious at the beginning what the limitations are going to be, and what the opportunities are going to be. If it's a promising direction, pursue it, even though it won't always work out.


Since Val seems to be liable to infer more about the actual data flow, I wonder how does it reflect on compile times.


The a typing feature[1] in the language Eiffel was based on the assumption of the author that the typing holes it apparently introduced could be statically detected. It turned out he was wrong after the language was released IIRC. I guess you have to be pretty certain of something before basing a language around it.

On top of that being a generally shite language doomed Eiffel.

[1] IIRC again, you could cancel features in subtypes, so "all birds can fly" but "penguins are birds but can't fly". That would break the Liskov Substitution Principle, which Bertrand Meyer thought he could deal with but apparently couldn't.


May many languages be as successful as Eiffel is.

Eiffel Software is still in business, whereas many others hardly reach any audience.


Very true. So many languages people haven't heard of. It's like if not in the top 10 or 20, some think they don't matter.

In regards to Eiffel, always felt that hindrance to wider adoption was more about the licensing. Still, they are in business and in use.


I know it is used in education in some parts of Europe. But now I'm wondering, is it used in production? Or rather, does it have a niche or an industry where it is more widely used? I'd be curious to know what software is written in Eiffel! (Mainly to get an idea of what Eiffel source code looks like, beyond toy examples)


As someone generally not in the loop on these kinds of languages, what is the “big problem”?


If another function / sub-routine / thread / whatever alters this Thing while I'm looking at it, lots of nasty surprises await. For example maybe the Thing is a container of Objects and I was looping over it, taking out one object at a time and sometimes calling unrelated_function() but for some reason unrelated_function destroys the Thing occasionally and then... Boom, use-after-free.

It turns out that although we've often thought of this as a variety of different hard problems, including "data races" and "use after free", they're actually all one big problem, that of mutating something while somebody else used it. Solving this effectively solves all of those hard problems, at least in a subset of your language where you are able to address it.

In a language like C or C++ it's easy to make one or more references to a Thing, and then give away the references, or the Thing, or both, and then the programmer loses track (or maybe never knew they were related) and these nasty surprises are their reward.

In Rust, their borrowing / lending metaphor prevents the surprise. If you lend a mutable reference to the Thing, the borrower can't destroy it, that's not what "lending" means, and you can't even use it until they've stopped borrowing it. If you lend immutable references, nobody can destroy it, or mutate it at all, until all those references are given back. But this does make the language more complicated because of the new metaphor.

In Val, you can't have any references, so unrelated_function couldn't destroy Thing, you've got Thing, so unrelated_function doesn't have Thing and can't destroy it. No surprises.


> Solving this effectively solves all of those hard problems, at least in a subset of your language where you are able to address it.

No, "solving" this by preventing it in the first place just makes programming way too hard.

In practice, using something while other people are using it is perfectly fine in most cases. E.g. most Python programs do that, and most don't have bugs most of the time.

The "use-after-free" problem can far more easily be solved by removing `free` (e.g. by using Garbage Collection).

Most concurrency bugs remain even if you solve all data races (e.g. as Python does, with GIL) simply because the semantics are wrong (e.g. having to update 2 counters which cannot happen simultaneously), or iterating through a list while modifying it (no data races here if done from a single thread!).


Python still has data races. Modifying variables is not atomic.


And of course the trick is to avoid the nasty surprises and do do it in such a way that is still is easy to program. If you can only do it by adding additional hoops for a programmer to jump through (like having to annotate all your function parameters with lifetimes), it's not ideal. Val's subscripts are a nice idea; they make writing a function that returns a reference quite easy. On the other hand, it doesn't look as flexible as a regular function, so maybe it has the same amount of hoops, just different ones.


I thought it was that Go is to C what Rust is to C++? This seems like another animal altogether.


Rust is very much aimed at the same space of Go where Go can be used to replace C.

I'd argue Rust is more aimed at the C space than the C++ space because of its accent on systems programming.


> ...interops with C++...

Does it? With what qualifications? Requires C++ that uses clang modules or something?


What are you quoting?

EDIT: I assume you meant to copy "ìnterops with C++". The answer is that it basically doesn't right now, but they intend for it to in the future somehow.

https://accu.org/journals/overload/30/172/teodorescu/


Sorry, yeah. Autocorrect. I fixed it.

And if that's a goal, fine, but if there isn't interop yet, we should be hesitant to describe Val as having C++ interop. Especially since C++ interop probably requires some compromises, such as requiring modular C++ in some form.


In a lot of cases won't the C++ compiler just inline methods and remove the copies anyway...?


Inlining functions and copy elision are two different things. If you inline a function without copying it's args, then it'll change the value of the original argument: Copy semantics lost!


Right. I wonder if copy elision can be ignored if functionality is verified to be the same? I'm just a hobbiest c++ dev so sorry for ignorance :)


does this mean your object tree is acyclic since you can't have references ?


Val will have statement level unsafe annotations (not even full unsafe blocks), according to the podcast linked in a nearby comment. Linked lists will be tricky or impossible to do safely, like in Rust.


I’ll have to see about this, but I don’t really like that.

Yes, you can replace a block of unsafe calls with a bunch of individually unsafe-annotated lines. But a good model of unsafe to follow is that whatever unsafety you expose within a block should be brought back within safety requirements when you exit the block. Put differently, an unsafe block should act as unit-safe and not leak unsafety.

If all you get is statement-level unsafety, you have no way to indicate at what point you’ve re-upheld safety guarantees.


cross platform build tools similar to cargo or go build are important to me.

battery included stdlib that also has network and gui is super attractive too




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: