STRING A WHAT, MOTHERFUCKER
IT'S SHOWTIME
I NEED YOUR CLOTHES YOUR BOOTS AND YOUR MOTORCYCLE a
GET TO THE CHOPPER a
HERE IS MY INVITATION "ArnoldC is the best."
ENOUGH TALK
TALK TO THE HAND a
YOU HAVE BEEN TERMINATED
The actual reason why let ... in syntax tends to not use C-style "type var" like syntax is because it's derived from the syntax type theory uses, and type theorists know about parameterised types. Generics, in C++ parlance, excuse my Haskell:
let foo :: Map Int String = mempty
We have an empty map, and it maps integers to Strings. We call it foo. Compare:
Map Int String foo = mempty
If nothing else, that's just awkward to read and while it may be grammatically unambiguous (a token is a name if it sits directly in front of =
) parser error messages are going to suck. Map<Int,String>
is also awkward but alas that's what we're stuck with in Rust because they reasoned that it would be cruel to put folks coming from C++ on angle bracket withdrawal. Also Rust has ML ancestry don't get me started on their type syntax.
How do you do nested parameterized types without it becoming ambiguous though? That's IMO the biggest advantage of the bracket syntax. For example: Map<Tuple<Int, Int, Int> Int>
Map (Int, Int) Int
. Kind of a bad example because tuples have special-case infix syntax, the general case would be Map Int (Either Int Bool)
. Follows the same exact syntax as function application just that types (by enforced convention) start upper case. Modulo technical wibbles to ensure that type inference is possible you can consider type constructors to be functions from types to types.
...function application syntax is a story in itself in Haskell because foo a b c
gets desugared to (((foo a) b) c)
: There's only one-argument functions. If you want to have more arguments, accept an argument and return a function that accepts yet another argument. Then hide all that under syntactic sugar so that it looks innocent. And, of course, optimise it away when compiling. Thus you can write stuff like map (+5) xs
in Haskell while other languages need the equivalent of map (\x -> x + 5) xs
(imagine the \
is a lambda symbol).
Interesting. Thanks!
There is also the thing where the compiler might mistake your c++ style variable declaration for a function, e.g.
String myfunction():
String myvariable();
Good, now invent a keyword for variables you don't want to declare the type. And now that you have a mix of keywords and identifiers on the same place, you can never update your language again.
Also, make the function declarations not use a keyword too, so you get the full C-style madness of code that changes meaning depending on what libraries you import.
I don't understand how not using a keyword to define a function causes the meaning to change depending on imports. I've never run into an issue like that before. Can you give an example?
Some declarations terminate on the name, other declarations go one requiring more tokens. In C, the only thing that differentiates them is the type.
Parenthesis in particular are completely ambiguous. But asterisks and square brackets also create problems.
python:
a: str = 1
And then assign an int to a string just to mess with the interpreter.
only the linter gives a hoot - the interpreter will happily leave that footgun for later
You're encoding more information in the typescript one. You're saying it's a string that will get updated.
Yeah, it's explicitly distinct from const a: String
which says it won't change, and var a: String
, which means this is legacy code that needs fixing.
let a: &'static str
Rust is verbose, but C++ might still take the cake with its standard library templates. Especially when using fully-qualified type names...
auto a = ::std::make_shared<::std::basic_string<char, ::std::char_traits<char>, MyAllocator<char>>>();
A reference-counted shared pointer to a string of unspecified character encoding and using a non-default memory allocator.
Yeah, I mean Rust is only verbose if you want it to be. let foo = "bar";
is valid rust too, no need to declare the type and definitely no need to declare the lifetime.
For that matter, if you ever declare something as explicitly 'static
in code that isn't embedded or super optimized, you're probably doing it wrong.
fully qualified type names make any language awful.
Here's the same example in rust:
let a = std::rc::Rc::new(std::vec::Vec<u8, MyAllocator>::new());
I believe u8 also comes from a module, so it would be something like std::u8::u8
, but I'm not sure.
Because sometimes that let
can be replaced by other things like const
. Which can be managed statically by the machine and not by my (imperfect) ability to know if it's mutated or not
String a: new String()
=
?
Programmer Humor
Welcome to Programmer Humor!
This is a place where you can post jokes, memes, humor, etc. related to programming!
For sharing awful code theres also Programming Horror.
Rules
- Keep content in english
- No advertisements
- Posts must be related to programming or programmer topics