We define the Either type and see how it works. Then try it out to enforce a null check and branch our code.
I still don't understand how the inspect method works. Anyone please help? Thanks
Hi!
Node will implicitly call inspect() on x when you console.log(x). Browsers do not support this so I often use .toString() and log with console.log(String(x))
For those checking their work on browser you can use instead of node console, as per suggested above.
toString: () => `Right(${x})`
...
console.log(String(result))
hi. you r a genius
I've been trying to write a typescript version of Right:
export class Right<T> {
public constructor(private x: T) { }
public chain<T1>(f: (x: T) => Either<T1>): Either<T1> {
return f(this.x);
}
public map<T1>(f: (x: T) => T1): Either<T1> {
return new Right(f(this.x));
}
public fold<T1,T2>(f: (x: T) => T1, g: (x: T) => T2): T1|T2 {
return g(this.x);
}
public inspect(): string {
return `Right(${this.x})`;
}
}
But I'm having problems with getting fold correct.
const res = new Right(3)
.map(x => x * 3)
.fold(x => x, x1 => x1);
The compiler give the following error:
error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '(<T1, T2>(f: (x: number) => T1, g: (x: number) => T2) => T1 | T2) | ((f: (x: number) => number, g...' has no compatible call signatures.
Any ideas what I'm doing wrong?
I've got it going now, here is the full implementation:
export type Either<T> = Right<T> | Left<T>;
export class Right<T> {
public constructor(private x: T) { }
public chain<T1>(f: (x: T) => Right<T1>): Right<T1> {
return f(this.x);
}
public map<T1>(f: (x: T) => T1): Right<T1> {
return new Right(f(this.x));
}
public fold<T1, T2>(f: (x: T) => T1, g: (x: T) => T2): T1 | T2 {
return g(this.x);
}
public inspect(): string {
return `Right(${this.x})`;
}
}
export class Left<T> {
public constructor(private x: T) { }
public chain(f: (x: T) => Left<T>): Left<T> {
return new Left(this.x);
}
public map(f: (x: T) => T): Left<T> {
return new Left(this.x);
}
public fold<T1, T2>(f: (x: T) => T1, g: (x: T) => T2): T1 | T2 {
return f(this.x);
}
public inspect(): string {
return `Left(${this.x})`;
}
}
function findColor(name: string): Either<string> {
const colors: IColorIndexable = { red: '#ff4444', blue: '#3b5998', yellow: '#fff68f' };
const found: string = <string>colors[name];
return found
? new Right(found)
: new Left(null);
}
interface IColorIndexable {
[key: string]: string
}
console.log(findColor('green').fold<string, string>(x => 'error', x1 => x1.toUpperCase()))
console.log(findColor('yellow').fold<string, string>(x => 'error', x1 => x1.toUpperCase()))
Small gotcha
const fromNullable = x =>
x != null ? Right(x) : Left(null)
note: !=
is used not !==
.
I don't understand why you named function "fromNullable" and used
x != null
Actually findColor need verification for undefined, not null.
I don't understand why you named function "fromNullable" and used
x != null
Actually findColor need verification for undefined, not null.
Hey @Oleg, the reason that statement works is because of type coercion. So, undefined
is treated like null
in this case. If you run undefined != null
in a REPL, you will still get back false
for that reason. If you add another =
to the end, then we get some finer grained differentiation, and undefined
is not treated the same as null
.
In the description transcript there is a code error. Right sould be different.
const Right = x =>
({
map: f => Right(f(x)),
fold: (f, g) => g(x),
inspect: () => 'Right(${x})'
})
In the description transcript there is a code error. Right sould be different.
const Right = x =>
({
map: f => Right(f(x)),
fold: (f, g) => g(x),
inspect: () => 'Right(${x})'
})
You are correct! I got this issue fixed in the transcripts.