1. 3
    Enforce a null check with composable code branching using Either
    5m 58s

Enforce a null check with composable code branching using Either

InstructorBrian Lonsdorf

Share this video with your friends

Send Tweet

We define the Either type and see how it works. Then try it out to enforce a null check and branch our code.

Vamshi
~ 8 years ago

I still don't understand how the inspect method works. Anyone please help? Thanks

Brian Lonsdorfinstructor
~ 8 years ago

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))

Chapin
~ 8 years ago

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))
Dillon
~ 7 years ago

hi. you r a genius

Michael
~ 7 years ago

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?

Michael
~ 7 years ago

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()))

Iain Maitland
~ 7 years ago

Small gotcha

const fromNullable = x =>
  x != null ? Right(x) : Left(null)

note: != is used not !==.

Oleg
~ 7 years ago

I don't understand why you named function "fromNullable" and used

x != null

Actually findColor need verification for undefined, not null.

Guy
~ 6 years ago

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.

mathias gheno
~ 2 years ago

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})'
  })
Lucas Minter
~ 2 years ago

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.