Programming Language Features I'd Like Most Languages to Have
Here are some things I am dissappointed to leave behind when I switch back and forth between languages that dont have certain features.
If Expressions
Found in: Rust; probably other functional languages
Irritatingly missing from: Everything
let x = 5 + if true {
1
} else {
7
}
println!("{x}"); // 6
What I have to do instead:
let x = 5;
if true {
x += 1;
} else {
x += 7;
}
System.Console.WriteLine($"{x}");
Ternary
Found In: Everywhere except Go
Ternaries are like If Expressions but less powerful
int i = if true ? 5 : 7;
What I have to do intead:
var i int
if true {
i = 5
} else {
i = 7
}
For-Else Blocks
Found in: Python
Irritatingly Missing From: Everything
def for_else(arr):
for i in range(len(arr)):
print(f"loop at {i}")
else:
print("Arr was empty")
for_else([]) # "Arr was empty"
What I have to do instead:
void for_else(arr: List<int>) {
List<int> arr = new List<int>();
if arr.length() == 0 {
System.Console.WriteLine("Arr was empty");
} else {
for(int i = 0; i < arr.length(); i++) {
System.Console.WriteLine($"Loop at {idx}");
}
}
}
Result Types
Found in: Rust; Haskell; any of the OCaml derived languages
Irritatingly Missing From: Everything
fn r(b: bool) -> Result<u32> {
if b {
Some(5)
} else {
None
}
}
let r_t = r(true).unwrap(); // 5
let r_f = r(false); // None
What I have to do instead:
int? r(bool b) {
if (b) {
return 5;
} else {
return null;
}
}
var r_t = r(true); // 5
var r_f = r(false); // null
Built-in Tuples
Found In: C#; Rust; Python; Most functional languages
Irritatingly Missing From: Older C#
var tup = (1,"a");
System.Console.WriteLine(tup.Item1); // "1"
System.Console.WriteLine(tup.Item2); // "a"
What I have to do instead:
var tup = new Tuple2<int, string>(1, "s");
or even worse, define my own custom tuple type:
struct Tup2<T1, T2> {
T1 Item1;
T2 Item2;
}
var tup = new Tup2<int, string>(1, "s");
Destructuing
Found In C#; Python; Rust; JavaScript; Most functional languages
Irritatingly Missing From: Older C#
(int, string) f() {
return (5, "s");
}
(int t_num, string t_str) = f();
System.Console.WriteLine($"t_num: {t_num}; t_str: {t_str}"); // "t_num: 5; t_str: s"
What I have to do instead:
Tuple2<int, string> f() {
return new Tuple2<int, string>(5, "s");
}
var tup = f();
int t_num = tup.Item1;
string t_str = tup.Item2;
Negative Array Indexes
Found In: Python; Rust (kind of)
Not Found In: Everywhere else
arr = ["a", "b", "c"]
print(arr[-1]) // "c"
What I have to do intead:
List<string> arr = new List<string>(){ "a", "b", "c" };
int len = arr.length;
int idx = len - 2;
System.Console.WriteLine($"{arr[idx]}"); // "c"
Trailing Commas for all list-like structures
Found In: Python; Go; Rust; C# (lists only)
Not Found In: C# (func parameters); JSON
C# has this for literal lists:
List<int> lst = new List<int>() {
1,
2,
3,
};
While Go also has it for function parameter lists:
func f(param_a int, param_b int,) {
}
What I have to do instead: Meticulously prune commas and re-order elements whenever the list structure changes:
List<int> lst = new List<int>() {
2,
1,
3
}
Numeral Separators
Found In: C#; Go; JavaScript;
Not Found In: Dart;
int i = 1_500_000;
System.Console.WriteLine($"{i}"); // 1500000
Byte Literals
Found In: C#; Go; JavaScript;
int i = 0xFF;
System.Console.WriteLine($"{i}"); // 255
Typed Range Literals
Found In: ADA
I’ve never used these because they dont exist in any language except ADA, but I really wish they were more common.
with Ada.Text_IO; use Ada.Text_IO;
procedure Learn is
subtype Alphabet is Character range 'A' .. 'Z';
begin
Put_Line ("Learning Ada from " & Alphabet'First & " to " & Alphabet'Last);
end Learn;
Default Function Parameters
Found In: C#; JavaScript; Python; Most languages tbh
void f(int i = 0, String a = "abc") {
print("{i} and {a}");
}
f(); // "0 and abc"
f(5); // "5 and abc"
f(10, "xyz"); // "10 and xyz"
What I have to do instead:
void f(int i, String a) {
...
}
f(0, "abc");
Function Overloading
Notably missing from: Dart
string f(int i) {
return "int";
}
string f(string s) {
return "string";
}
string f(SomeStruct s) {
retrun "Struct";
}
SomeStruct ss = new SomeStruct();
List<Object> l = new List<Object>() {
5,
"hello",
new SomeStruct(),
};
let x = l.select(x => f(x)).ToList(); // ["int", "string", "Struct"]
What I have to do instead:
String f_int(int i) {
return "int";
}
String f_str(String i) {
return "string";
}
String f_class(Object i) {
return "Object";
}
List<Object> l = [
5,
"hello",
SomeStruct(),
];
var l2 = l.map((x) {
switch (x.runtimeType) {
case int:
return f_int(x as int);
case String:
return f_str(x as String);
default:
return f_class(x);
}
});
Chained Evaluators
Found In: Nowhere?
bool f(int n) {
return 1 < x < 10;
}
f(5); // true
f(11) // false
f(0) // false
What I have to do instead:
bool f(int n) {
return (1 < x) && (x < 10);
}
Object Assigment Shorthand
Found In: Rust; Go
struct S {
id: i32
name: String
}
let name: String = "xyz";
let id: i32 = 0
/// But using shorthand, as long as the symbol name and the field name are the same,
/// the field name can be omitted in favor of the symbol
let s_short: S {
name,
id
}
What I have to do instead:
struct S {
id: i32
name: String
}
let name: String = "xyz";
let id: i32 = 0
let s_long: S {
name: name,
id: id,
}
Spread
Found In: JavaScript; Dart;
This one has many uses, including spreading arguments into function call parameter lists but what I wnat to focus on is the ability to create new lists and objects. Taken from the MDN docs:
const parts = ['shoulders', 'knees'];
const lyrics = ['head', ...parts, 'and', 'toes'];
What I have to do instead:
const parts = ['shoulders', 'knees'];
const head = ["head"];
const lyrics = head.concat(["shoulders", "knees"]);
lyrics.push("and");
lyrics.push("toes");
Null Coalesce
Found In: C#; JavaScript;
string may_be_null(bool b) {
if (b) {
return null;
} else {
return "some value";
}
}
var x = may_be_null(true) ?? "WasNull";
var y = may_be_null(false) ?? "WasNull";
System.Console.WriteLine(x); // "WasNull"
System.Console.WriteLine(y); // "some value"
Null Chain
Found In: C#; JavaScript;
string may_be_null(bool b) {
if (b) {
return null;
} else {
return "some value";
}
}
may_be_null(true)?.ToUpper()?.split("_"); // null
may_be_null(false)?.ToUpper()?.split("_"); // ["SOME", "VALUE"]
If Let
Found In: Rust; Kotlin
foo?.let { baz(it) }
Instead, I have to do:
if (foo != null) {
baz(foo);
} else {
null;
}