export type Option<T> =
  | {
      value: T;
      isSome: true;
    }
  | {
      value: undefined;
      isSome: false;
    };

export function Some<T>(value: T): Option<T> {
  return { value, isSome: true };
}

export function None<T>(): Option<T> {
  return { value: undefined, isSome: false };
}

export function map<T, U>(option: Option<T>, fn: (value: T) => U): Option<U> {
  return option.isSome ? Some(fn(option.value)) : None();
}

export function flatMap<T, U>(
  option: Option<T>,
  fn: (value: T) => Option<U>,
): Option<U> {
  return option.isSome ? fn(option.value) : None();
}

export function getOrElse<T>(option: Option<T>, defaultValue: T): T {
  return option.isSome ? option.value : defaultValue;
}

export function fold<T, U>(
  option: Option<T>,
  onNone: () => U,
  onSome: (value: T) => U,
): U {
  return option.isSome ? onSome(option.value) : onNone();
}