2

I have a function that looks up a value from the environment:

type Env = unknown

declare const getFromEnv: (key: string) => Reader<Env, Option<number>>

At the same time I have an array of keys, and I want to look up the value of the last key in the environment, but this returns Option<Reader<Env, Option<number>> type:

const getLastFromEnv = (keys: string[]): Reader<Env, Option<number>> =>
  pipe(keys, array.last, option.map(getFromEnv), ...? ) // Option<Reader<Env, Option<number>>

How can I make it return Reader<Env, Option<number>>? Thanks!

Mxcpanel
  • 95
  • 7

1 Answers1

2

You can write this transformation explicitly with fold from the Option library, but it does feel a bit roundabout:

import * as R from 'fp-ts/lib/Reader';
import * as O from 'fp-ts/lib/Option';
import * as A from 'fp-ts/lib/Array';
import { pipe } from 'fp-ts/lib/function';

type Env = unknown;
declare function getFromEnv(key: string): R.Reader<Env, O.Option<number>>;

function lookUpLastKey(keys: string[]): R.Reader<Env, O.Option<number>> {
  return pipe(
    keys,
    A.last,
    O.fold(
      // Handle the case that there was no last element in the array
      // By explicitly returning a Reader that returns none
      (): R.Reader<Env, O.Option<number>> => () => O.none,
      // If there was a string, then the function you have is
      // exactly what you need.
      getFromEnv, 
    )
  )
}

Another option would be to instead use a ReaderEither which bundles together Reader logic and Either logic into a single type class. With ReaderEither you can more directly fold the failure to find the last value in an array into the output type:

import * as RE from "fp-ts/lib/ReaderEither";
import * as A from "fp-ts/lib/Array";
import { constant, pipe } from "fp-ts/lib/function";

type Env = unknown;
// Instead of a single `None` value, `Either` allows for an arbitrary
// value to be stored in the error case. Here I've just chosen `null`
// because I need to put something in there on a `Left`.
declare function getFromEnv(key: string): RE.ReaderEither<Env, null, number>;

function lookUpLastKey(keys: string[]): RE.ReaderEither<Env, null, number> {
  return pipe(
    keys,
    A.last,
    // This is a helper transformation that avoids explicitly writing it
    // yourself with fold. If the last value was `None` this will replace
    // It with a Left value holding `null`.
    RE.fromOption(constant(null)),
    // Chaining will replace the old ReaderEither with the one returned
    // by your helper but only if the input `ReaderEither` is a `Right`.
    RE.chain(getFromEnv)
  );
}
Souperman
  • 5,057
  • 1
  • 14
  • 39