3 ms·
Kinda disingenuous, you don't reskin one language in another to make an argument about syntax -- you develop a clear syntax for a given semantics. That's what r
by mjburgess 2y ago
Kinda disingenuous, you don't reskin one language in another to make an argument about syntax -- you develop a clear syntax for a given semantics. That's what rust did not do -- it copied c++/java-ish, and that style did not support the weight.
When type signatures are so complex it makes vastly more sense to separate them out,
Consider,
read :: AsRef(Path) -> IO.Result(Vec(U8))
pub fn read(path):
inner :: &Path -> IO.Result(Vec(U8))
fn inner(path):
bytes := Vec.new()
return? file := File.open(path)
return? file.read_to_end(&! bytes)
return OK(bytes)
inner(path.as_ref())
- tcfhgj 2y agoTo me this example is not more clear than normal Rust
- timeon 2y agoNow there are too many colons.
- bvrmn 2y agoOriginal code contains more. (::). Personally I don't understand why there are `::` and `.` to access deeper namespace levels. It's a static language, compiler should know how to resolve attributes and `.` should be enough for all cases.
- hmry 2y agoIt's because then you could have name collisions between variables and modules. E.g. it's ambigious whether io.Error is in a module called io, or a field of a variable called io. Which means you can no longer use fully qualified names regardless of context.
- bvrmn 2y ago> Which means you can no longer use fully qualified names regardless of context. How about don't shadow module names with variables if you want to use it? It works amazingly well for a bunch of other languages.
- demurgos 2y agoPeople may disagree on specifics, but you're definitely right that being able to separate the function signature from its definition would be very helpful in complex cases.
- scotty79 2y agoWhy? You can easily find parameter names in the signature if you just put them on separate lines. For me there's very little reason of putting them all together on separate line after the signature. And then when you look for a type of a parameter you know the name of, it gets difficult.
- degurechaff 2y agoRust target user is C/C++ developer. not using brace is out of options.
- blksv 2y agoI strongly support your point, but the example is still sand-in-the-eyes for me. I hold that one symbol should not alter the semantics of a program and there should never ever be sequences of one-symbol syntactic elements. In an Ada-like language, it would be something like generic type Path_Type implements As_Path_Ref; type Reader implements IO.File_Reader; function Read(Path: Path_Type) return Reader.Result_Vector_Type|Reader.Error_Type is function Inner_Read(P: Path) return Read'Result_Type is begin File: mutable auto := try IO.Open_File(P); Bytes: mutable auto := Reader.Result_Vector_Type.Create(); try Reader.Read_To_End(File, in out Bytes); return Bytes; end; begin return Inner_Read(Path.As_Ref()); end Read;
- mst 2y agoHuh. My brain says logically I should find that better, but the -lack- of punctuation is making it really tricky to skim read the way I do most languages. I'm not arguing the example you found sand-in-the-eyes is necessarily good but my mental skim reading algorithm copes with it much better.
- blksv 2y agoIt is indeed harder to skim, and I find myself much more relying on syntax highlighting and file outline when working in Ada than in C++. Not due to the lack of punctuation, though, which is in place but serves the guiding role only (it is MLs that tend to abolish all the unnecessary punctuation), but because of the overall dense style. But while it is harder to _skim_, it is easier to _read_, as you don't have to concentrate on and decipher the syntax, risking to miss some of the crucial elements (oh, how do I hate missing ampersands in C++!).
- kelnos 2y agoRust does allow you to split out generic specifications into a separate "declaration block" so things don't get too busy. Like you could write the original Rust code as: pub fn read<P>(path: P) -> io::Result<Vec<u8>> where P: AsRef<Path>, { // ... } Personally I don't find your example with the type signature completely separate to be easier to read. Having to look in more than one place doesn't really make sense to me. Funny, though, Rust's 'where' syntax sorta vaguely superficially reminds me of K&R pre-ANSI C: unsigned char *read(path) const char *path; { /* ... */ }
- _answ 2y agoI have a feeling that most of the clarity you find in your example comes from better use of whitespace. Consider: pub fn read<P>(path: P) -> io::Result<Vec<u8>> where P: AsRef<Path>, { fn inner(path: &Path) -> io::Result<Vec<u8>> { let mut bytes = Vec::new(); let mut file = File::open(path)?; file.read_to_end(&mut bytes)?; Ok(bytes) } inner(path.as_ref()) } Plus, your example does not have the same semantics as the Rust code. You omitted generics entirely, so it would be ambiguous if you want monomorphization or dynamic dispatch. Your `bytes` and `file` variables aren't declared mutable. The `try` operator is suddenly a statement, which precludes things like `foo()?.bar()?.baz()?` (somewhat normal with `Option`/`Error`). And you weirdly turned a perfectly clear `&mut` into a cryptic `&!`. Please don't assume that the syntax of Rust has been given no thought.