Remix.run Logo
onlyrealcuzzo 5 hours ago

I can't recommend Rust enough. It has such a bad reputation, but it isn't that hard. I truly think it's easier than many languages with much less-intimidating reputations.

That said, one of the places Rust loses people pretty early on is an example they have early in this intro:

```rust

let parts: Vec<&str> = "a,b,c".split(',').collect(); // Vec<&str>

```

I never understood why Rust didn't / couldn't make functions able to return different outputs depending on context. If you chain `.split()` to something else that can take an iterator, you want to pass an iterator. If you don't, ~99% of people would probably rather have a collected array. And if you want an `it`, you could just do `.it()` or this is when type inference could be overloaded and you could do:

```rust

let it: Split<'_, char> = "a,b,c".split(',');

```

I think Rust should've put more effort into making the thing newbs want to do the default, and easy ways to get the most efficient thing for experts.

```rust

let parts = "a,b,c".split(); // Gives an Array/Vec

let count = "a,b,c".split().count(); // Optimized stream, no array allocation

```

It could work like that, and I think almost everyone would be happy. But it doesn't.

Instead, they've created a language that I think could have been nearly as easy as a scripting language, but isn't.

It obviously isn't only collection iterators this applies to. There's dozens of very small places that add up and make what - I believe - is an otherwise relatively easy and sensible language feel too far out of reach for too many people.

`Option<T>`, `Result<T, E>`, `Future<T>` all impact linguistically how you can interact with a Type. I think the impacts of this don't make sense to people who've never encountered this before. `Arc<T>`, `Rc<T>`, `Mutex<T>`, and `RwLock<T>`, etc also have similar consequences.

Not only do people just not get it. But also, the type system quickly becomes "scary". To do pretty basic concurrency, you need to build a pretty "scary" looking type if you come from Python.

Which is why I'm a psychopath and attempting to create a language where it defaults to the things most people want, and it's very easy for experts to override.

mightyham 4 hours ago | parent | next [-]

Having the result type of a function change based on context sounds like a horrible idea because it would introduce tons of unnecessary ambiguity. If it's an issue that you have to chain one extra collect call, just write a helper function for splitting that returns a vec.

onlyrealcuzzo 4 hours ago | parent [-]

> Having the result type of a function change based on context sounds like a horrible idea because it would introduce tons of unnecessary ambiguity.

I think you're assuming the language won't warn you if you're doing something cost ineffective, and that there aren't modes to compilation which will make you make things explicit whenever its ambiguous.

For a person to get started, they should be able to compile in `easy` mode and do things that make sense to them, and the compiler should only bitch at you to be pedantic when you ask for that.

Especially because an LLM can probably do that pedantic part for you.

You: write code almost as high level as a scripting language, it works, turn on strict mode, most of the time you get auto-fix solutions/options from the compiler.

That's my opinion anyhow. I assume most Rust people won't like it. That's fine. You already have your language!

I'm not trying to make a slightly different Rust for Rust people...

I'm trying to make Rust more accessible to everyone else.

sgeisenh 3 hours ago | parent | prev | next [-]

This closely resembles implicit conversion from C++ and in many serious codebases it is considered poor practice because it leads to a lot of hidden control flow.

I think Rust strikes a nice balance but there’s enough magic that some people still get frustrated. Following traits can get tedious at times.

onlyrealcuzzo 3 hours ago | parent [-]

> This closely resembles implicit conversion from C++ and in many serious codebases it is considered poor practice because it leads to a lot of hidden control flow.

Yes, which is why there's progressive modes of compilation to not allow anything ambiguous if that's what you want (i.e. an enterprise codebase).

But, a junior can still try things out in weaker modes of compilation, and then once they've got something working, it is typically very easy to do the pedantry to remove ambiguous behavior.

pie_flavor 3 hours ago | parent | prev | next [-]

You may notice no language on the planet does this. That is because it is bad. Type guesstimation is a great way of ensuring random problems crop up in random places where they aren't expected and of making the typechecker much slower and more prone to unresolvability (see Swift's multi-minute compiles). All to save you from having to learn what an iterator is, in case you come from a language where lists are more common than iterators; the experience of being scared by a type, and then discovering that the type is not scary in chapter 13.2, is not actually worth making the simple type system instead staggeringly complicated.

onlyrealcuzzo 3 hours ago | parent [-]

> You may notice no language on the planet does this. That is because it is bad.

Perhaps I've explained this poorly, but C#, Java, Perl, & Haskell (and I'm sure others as well) do versions of this already...

You even seem to imply that Swift does, though I have almost no experience directly with Swift.

The vast majority of times it is NOT ambiguous. The compiler can flag it, IFF you want it to.

If you're coming from Python and you want to ease your learning experience, you probably don't want to hit several brick walls before you can do anything...

If you have an enterprise codebase, you probably don't want to allow anything to be compiled that could be ambiguous, so you can force that mode of compilation (and likely should)...

I don't know of any major language which have progressive modes of compilation like this. I think people will find it useful.

Maybe it'll be a disaster. Time will tell. The whole point is to intelligent design the modes such that you can't ever get a MASSIVE surprise "upgrading" from one mode to the next, any error that is too hard to resolve basically automatically / through selecting options needs to be dealt with at the easiest mode.

pie_flavor 2 hours ago | parent [-]

In none of the languages you mentioned do stdlib functions either return an iterator or a list depending on whether you feed it into an iterator operation; in none of the languages you mentioned can a fully-generic return as a method receiver be inferred by the name of the method. It has nothing to do with how well you explained it; it just doesn't exist. Every single thing you are complaining about exists in all of the languages you brought up, although Perl does a small portion of that in some cases depending on the variable sigil (ie still not guessed).

Your post is a restricted special case of "it would be great if any old sequence of characters compiled correctly and the compiler just read my mind". Wouldn't it just, but the rules of programming languages exist for more purposes than just annoying the user.

onlyrealcuzzo 2 hours ago | parent [-]

I think you're stuck completely on the iterator example or only named functions (which makes sense, since that's all you've seen), rather than several languages supporting various types of polymorphic returns and/or downstream target typing (mainly in lambdas or with sigils).

Certainly if you allow a language do something like:

```rust

fn foo() -> i32 { 42 }

fn foo() -> String { "42" }

```

That would be a nightmare.

But you can have something like:

```rust-ish

@eager_fallback(String[])

fn split(s: String, separator: Char) -> String::Iterator { ... }

```

Which, essentially, defaults to calling collect to an Array for you when bound to a variable, or when chained to a function that doesn't take an iterator but does take a String Array. And in a different mode of compilation, there are no fallbacks (you're back to Rust).

Sure, I don't know a language that supports polymorphic return exactly on `.split()`, but there are languages that support all of the different mechanisms to do this intelligently.

You're assuming the implementation is going to do a number of things that would cause either code bloat or force ambiguity (and potentially a number of other assumptions).

I think you're also assuming the type is not resolved until who knows what happens to it, rather than once it's bound.

None of those HAVE to be true.

To the best of my knowledge, for the most common cases, this can be solved, and when it can't, the compiler can flag it.

embedding-shape 4 hours ago | parent | prev [-]

> I never understood why Rust didn't / couldn't make functions able to return different outputs depending on context

Referential transparency probably being the first reason I could see. Having the behaviour of a callee sounds horrible and something we usually try to actively work against, you want to be able to look at the function in isolation and be able to understand how it works and what values it gives back, without jumping around and seeing where it's being called.

And yes, I'd agree with your last part, you do sound a bit like a psychopath ;) With that said, the world needs those too, so I hope your experiment is fun and brings you lots of learnings, enjoy! :)