const head = (input: A[]) => input[0]
and it will happily infer a return type of A. Then you'll fall flat on your face the first time you encounter an empty array: head([]) // -> undefined
To make it correct you need to explicitly define a return type of (input: A[]): A | undefined, just as the Maybe. It's obviously impossible for TS to guarantee that an array will not be empty at runtime, but I wish this specific case triggered some help from the type checker. type NonEmptyArray<T> = { head: T, rest: T[] };
function makeNonEmptyArray<T>(array: T[]): NonEmptyArray<T> | null {
if (array.length == 0) return null;
return { head: array[0], rest: array.slice(1, array.length) };
}
function head<T>(array: T[]): T | null;
function head<T>(array: NonEmptyArray<T>): T;
function head(array: any): any {
if (Array.isArray(array)) {
if (array.length === 0) return null;
return array[0];
}
if (typeof array === "object") {
return array.head;
}
}
http://www.typescriptlang.org/play/#code/C4TwDgpgBAcg9gOwKIF...It's impossible for Haskell to guarantee an array will not be empty at runtime as well; that's why we can write a new type + a smart constructor to track runtime properties.
type NonEmpty<T> = [T, ...T[]];
function head<T>(input: NonEmpty<T>): T {
return input[0];
}
https://www.typescriptlang.org/play/?ssl=5&ssc=2&pln=1&pc=1#...Maybe you could use "Rest elements in tuple types" and do an overloaded signature like this:
function head<T>(...args: [T, ...any[]]): T;
function head<T>(...args: []): undefined
function head<T>(...args: [T, ...any[]] | []): T | undefined
{
return args[0];
}
(not tested)You'd have to spread the arguments or use call/bind/apply, though.
config.load_from_file(....
And you can't know if it check if the file exist first or not. From pure habit you add checks... (what? looking at the code of the library? thats crazy!).It's an anti-pattern to throw errors for expected behavior.
[0] https://docs.quantifiedcode.com/python-anti-patterns/readabi...
On the other hand, non-atomically asking for permission first in any concurrent (or re-entrant!) context is not just an antipattern but simply wrong and always a possible race condition. It is impossible to write a routine that tries to open a file and is statically guaranteed to succeed.
For me, the funny thing about this article is that I kind of had the core insight just yesterday while hacking together a cli framework, but like the author, I couldn't quite explain what I learned.