Dennis Fricke

Monads in Typescript

Development | Best Practice

Beim Zugriff auf Objekte und Variablen gibt es immer wieder kleine Pitfalls. Manchmal ist das Objekt leer, wir lesen aber eine Property daraus und bekommen dann natürlich cannot read property 'x' of undefined. Einige dieser Pitfalls sind:

  1. Der Wert könnte oder könnte nicht existieren
  2. Der Wert könnte existieren, aber es gibt mehr als einen
  3. Um den Wert zu bekommen sind I/O Operationen notwendig
  4. Der Wert könnte eventuell in der Zukunft existieren
  5. Der Wert könnte einen Fehler produzieren
  6. Der Wert hängt von einem State ausserhalb ab

Ein Monad verhält sich wie ein Container der dieses Verhalten abstrahiert und uns die Möglichkeit gibt uns mehr auf das zu fokussieren was wir wirklich tun wollen: Mit den Werten arbeiten.

Schauen wir nochmal auf die Liste und gucken welcher Monad uns wann hilft?

  1. Der Wert könnte oder könnte nicht existieren. Dieses Verhalten wird vom Maybe Monad behandelt
  2. Der Wert könnte existieren, aber es gibt mehr als einen. Dieses Verhalten wird vom List Monad behandelt (Ja, List ist ein Monad!)
  3. Um den Wert zu bekommen sind I/O Operationen notwendig. Dieses Verhalten wird vom IO Monad behandelt
  4. Der Wert könnte eventuell in der Zukunft existieren. Dieses Verhalten vom Promise/Future Monad behandelt
  5. Der Wert könnte einen Fehler produzieren. Dieses Verhalten wird vom Error/Result/Either Monad behandelt
  6. Der Wert hängt von einem State ausserhalb ab. Dieses Verhalten wird vom State Monad behandelt

Die npm Library TsMonad implementiert den Maybe und Either Monad und gibt uns die Möglichkeit u.a. den Zugriff auf Properties und Methoden in Objekten elegant umzusetzen ohne vorher nervige, verschachtelte If Statements zu bauen, um die Existenz zu prüfen.

Maybe

ohne Monad

const vehicle = {
  type: 'Tesla Model S',
  color: 'red',
  engine: {
    horsepower: 386
  }
}

getVehicleHorsepower(vehicle) {
  if(vehicle && vehicle.engine && vehicle.engine.horsepower) {
    return vehicle.engine.horsepower;
  }

  return 0;
}

mit Monad

const vehicle = {
  type: 'Tesla Model 3',
  color: 'black',
  engine: {
    horsepower: 340
  }
}

getVehicleHorsepower(vehicle) {
  return Maybe.just(vehicle.engine.horsepower).caseOf({
    just: () => vehicle.engine.horsepower,
    nothing: () => 0
  })
}

Either

ohne Monad

const vehicle = {
  type: 'Tesla Model X',
  color: 'grey',
  engine: {
    horsepower: 420
  }
}

getVehicleHorsepower(vehicle) {
  if(vehicle && vehicle.engine && vehicle.engine.horsepower) {
    return vehicle.engine.horsepower;
  } else {
    console.log('Error: No horsepower found!');
    return 0;
  }
}

mit Monad

const vehicle = {
  type: 'Tesla Model 3',
  color: 'black',
  engine: {
    horsepower: 330
  },
  getHorsepower: () => {
    this.engine.horsepower ?
      Either.left<string, number>('Error: No horsepower found!')
      Either.right<string, number>(this.engine.horsepower)
  }
}

getVehicleHorsepower(vehicle) {
  return vehicle.getHorsepower()
    .caseOf({
      right: number => vehicle.engine.horsepower)
      left: errorMessage => {
        console.log(errorMessage);
        return 0;
      }
    });
}

3 Dec 2018 #Typescript #Monads #tsmonad #Either #Maybe