typed
Types are important. They keep you away from inconsistent mess, guard from certain errors, allow to model and document things, and write performant code. The better type system of your language is, the better your code should be.
JavaScript type system... JS type system... well it's formally there 😐. But informally it feels like you're at a playground and nobody's watching, so you can easily find yourself swinging on a merry-go-round and spinning on a swing (I honestly don't mind that kind of fun 🤓).
There are lots of crazy cool examples related to JS built in data-types and its type coercion. If somehow you've missed a hilarious WTFJS - enjoy. Here are some of my favourites:
> true + true
2
> Math.pow(10, false)
1
> "hello " + + "world"
'hello NaN'
> 9_999_999_999_999_999
10000000000000000
And can a poor monkey dodge a snake and have enough mangoes and maybe a pie in a cruel JS world?
let mangoes = [Math.PI]
mangoes[3] = "🥭";
console.log(`${mangoes.length}`);
mangoes[-1] = "🐍";
mangoes[Infinity] = "🥭";
mangoes.forEach((mango, index) => {
console.log(`🐒 eating ${mango} at ${index}`);
});
🤔
There have been quite a few data-types added to the language over time, like
BigInt and
Symbol for example. To me, addition of
Typed arrays was like entering
The Twilight Zone -
science fiction, suspense, horror and super cool at once 😐.
Promise and
Generator (JS functions are good parts for sure) additions were warmly welcomed
as well. How about mango-infinity for that monkey?
function* mangoInfinityGenerator() {
for(let i = 0; i < Infinity; i++) {
yield "🥭";
}
}
let mangoInfinity = mangoInfinityGenerator();
let nextMango;
while(nextMango = mangoInfinity.next().value) {
console.log(`🐒 eating ${nextMango}`)
}
And I can't but agree that If it walks like a duck and it quacks like a duck, then it must be a duck is a no fuss behavior oriented way to code. And since why not, how about some Duckenstein:
let ifItWalksLikeADuck = it => typeof it.waddle === "function"
let itQuacksLikeADuck = it => typeof it.quack === "function"
let itMustBeADuck = it => ifItWalksLikeADuck(it) && itQuacksLikeADuck(it);
let duckenstein = it => {
it.__proto__.quack = () => console.log("quack");
it.__proto__.waddle = () => console.log("waddle");
return it;
}
[Infinity, false, "not a duck", /.*/, {}, Symbol("@"), null, undefined].forEach(it => {
try {
it = duckenstein(it);
if (itMustBeADuck(it)) {
console.log(`${it.constructor.name}, ${it.toString()} - It's Aduck!`)
}
} catch (err) {
console.log(`${it} - I am alone and miserable. Only someone as ugly as I am could love me.`)
}
})
➜ ✗ java Var.java
Var.java
package typed;
public class Var {
public static void main(String[] args) {
var var = new Object() { int x; }; //var isn't a keyword
var.x = 10;
var hi = new Object() { public String toString() { return "¡Hola!"; }};
/*
by all means use a properly named
local record class if you need stuff like this
*/
System.out.println(hi);
System.out.println(hi.getClass());
System.out.println(var.getClass());
}
}
¡Hola!
class typed.Var$2
class typed.Var$1
Obviously Lambdas happened earlier (Java 8 vs. Java 10) and were a much-much-...-much
bigger than var (why did I mention it first then? 🤔). And they brought in a notion of a target type
slash functional interface, and also default methods in interfaces. And then there were.. and also... Well, somebody has to put
together a good article about Java type-system history and evolution for sure (📝).
Fast forward and there you have your
Pattern Matching for instanceof,
Records,
Sealed Classes
and already a third preview of Pattern Matching for switch.
➜ ✗ java java -version
openjdk version "19-ea" 2022-09-20
OpenJDK Runtime Environment (build 19-ea+31-2203)
OpenJDK 64-Bit Server VM (build 19-ea+31-2203, mixed mode, sharing)
➜ ✗ java --enable-preview --source 19 HeadsOrTails.java
HeadsOrTails.java
package typed;
import typed.HeadsOrTails.Toss.Tails;
import static java.lang.System.out;
import static typed.HeadsOrTails.Toss.*;
public class HeadsOrTails {
sealed interface Toss {
record Heads() implements Toss {};
record Tails() implements Toss {};
}
public static void main(String[] args) {
var toss = Math.random() >= 0.5 ? new Tails() : new Heads();
out.println(
switch (toss) {
case Heads() -> "heads";
case Tails() -> "tails";
}
);
}
}
And with current and ongoing type system changes Java is up for yet another programming paradigm - DOP. (Doh!)
julia> double(x) = 2x
julia> double(1)
2
julia> double(x::Rational) = 2x
julia> double(1//1)
2//1
primitive type Bool <: Integer 8 end
primitive type «name» «bits» end
primitive type «name» <: «supertype» «bits» end
Julia doesn't allow fields to be declared in abstract types, which (to be honest surprisingly to me) is a really useful restriction that resolves Circle–ellipse problem by not allowing it in the first place. Moreover all concrete types (struct) are final. So there is no inheritance per se (sorry interface inheritance), instead you get behavior subtyping: a function defined for a supertype naturally handles its subtypes. So a kind of (Any) duck typing is possible.
abstract type Bird end
struct Duck <: Bird
sound::String
Duck() = new("quack")
end
struct Silence
sound::String
Silence() = new("sound of silence")
end
sound(it::Bird) = "bird: $(it.sound)"
sound(it) = "any: $(it.sound)"
With first class support for functions and type unions it's possible to be very specific about what behaviors a method (a definition of one possible behavior for a function) is willing to accept.
up() = println("move on up")
left() = println("everybody's looking left")
right() = println("what the hell is happening right")
down() = println("dont let me down")
Direction = Union{typeof(left), typeof(right), typeof(down), typeof(up)}
move(direction::Direction, steps::Int) = foreach(step -> direction(), 1:steps)
julia> move(up, 3)
move on up
move on up
move on up
julia> move(sin, 3)
ERROR: MethodError: no method matching move(::typeof(sin), ::Int64)
Closest candidates are:
move(::Union{typeof(down), typeof(left), typeof(right), typeof(up)}, ::Int64)
And unreasonable effectiveness of multiple method dispatch is also rooted in Julia's type system, and once again, Types manual is awesome.