英文:
How to compose nested Option and Reader?
问题
我有一个查找环境中数值的函数:
type Env = unknown
declare const getFromEnv: (key: string) => Reader<Env, Option<number>>
同时,我有一个键数组,并且我想在环境中查找最后一个键的值,但这将返回 Option<Reader<Env, Option<number>>> 类型:
const getLastFromEnv = (keys: string[]): Reader<Env, Option<number>> =>
pipe(keys, array.last, option.map(getFromEnv), ...? ) // Option<Reader<Env, Option<number>>
如何使其返回 Reader<Env, Option<number>>?谢谢!
英文:
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!
答案1
得分: 2
以下是代码部分的翻译:
第一个代码示例:
你可以使用Option库中的fold来显式地编写这个转换,但它确实有点迂回:
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(
// 处理数组中没有最后一个元素的情况
// 明确返回一个返回 none 的 Reader
(): R.Reader<Env, O.Option<number>> => () => O.none,
// 如果有一个字符串,那么你拥有的函数
// 正是你需要的。
getFromEnv,
)
)
}
第二个代码示例:
另一个选择是使用ReaderEither,它将Reader逻辑和Either逻辑捆绑到一个单一的类型类中。使用ReaderEither,你可以更直接地将未找到数组中最后一个值的失败折叠到输出类型中:
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;
// 与单一的 `None` 值不同,`Either` 允许在错误情况下存储任意值。在这里,我选择了 `null`,因为我需要在 `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,
// 这是一个辅助转换,避免了显式编写它
// 使用 `fold`。如果最后一个值是 `None`,这将用 `null` 替换它。
RE.fromOption(constant(null)),
// 链接将用你的助手返回的旧 ReaderEither 替换
// 但仅当输入的 ReaderEither 是 `Right` 时。
RE.chain(getFromEnv)
);
}
英文:
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)
);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论