如何在形态召唤师中注入形态类型?



让我们看一个例子:

import * as Summoner from '@morphic-ts/batteries/lib/summoner-ESBST';
export interface IAppEnv {}
const { summon, tagged } = summonFor<IAppEnv>({});
const ReplyEntry_ = summon((F) =>
F.interface(
{
subject: F.string(),
action: F.string(),
},
'ReplyEntry'
)
);

我想使用io-ts-types模块中的NumberFromString而不是F.string()

我的问题:

  1. 有办法这样做吗?
  2. 有一种方法来扩展F对象与自定义编解码器?

我知道我可以使用io-ts-types的一些编解码器,通过这样改进它们:

const NES = summon(F => F.refined(F.string(), NonEmptyString.is, 'NES'))

也许有一种方法可以这样做,使用F.newtype。我可以在https://github.com/sledorze/morphic-ts/blob/master/packages/morphic-io-ts-interpreters/test/io-ts-interpreter.spec.ts#L97

中看到这个例子
interface NT extends Newtype<{ readonly NT: unique symbol }, Date> {}
const NT = summon(F => F.newtype<NT>('NT')(F.date()))

但是它似乎不包含任何类型转换逻辑,我不知道把它放在哪里。

我不熟悉库,所以可能有一个更干净,更少黑客的方式来做到这一点,但在我看了@morphic-ts的代码后,我想到了这个:

import { summonFor } from '@morphic-ts/batteries/lib/summoner-ESBST';
import { EqType, EqURI } from '@morphic-ts/eq-interpreters';
import { IOTSType, IoTsURI } from '@morphic-ts/io-ts-interpreters';
import { ShowType, ShowURI } from '@morphic-ts/show-interpreters';
import * as Num from 'fp-ts/Number';
import { NumberFromString } from 'io-ts-types';
import type { AlgebraUnion } from '@morphic-ts/batteries/lib/program';
import type { M } from '@morphic-ts/batteries/lib/summoner-ESBST';
import type { HKT, Kind, URIS } from '@morphic-ts/common/lib/HKT';
import type { AnyEnv } from '@morphic-ts/common/lib/config';
import type { Eq } from 'fp-ts/Eq';
import type { Show } from 'fp-ts/Show';
import type { Type } from 'io-ts';
const customIoTs = <A, O>(
type: Type<A, O>,
eq: Eq<A>,
show: Show<A>
): (<Env extends AnyEnv>(F: AlgebraUnion<'HKT', Env>) => HKT<Env, O, A>) => {
const ioTsInterp: Kind<IoTsURI, AnyEnv, O, A> = () => new IOTSType(type);
const eqInterp: Kind<EqURI, AnyEnv, O, A> = () => new EqType(eq);
const showInterp: Kind<ShowURI, AnyEnv, O, A> = () => new ShowType(show);
return (<Env extends AnyEnv>(
F: AlgebraUnion<URIS, Env>
): Kind<URIS, Env, O, A> =>
F._F === IoTsURI
? ioTsInterp
: F._F === EqURI
? eqInterp
: F._F === ShowURI
? showInterp
: ((): never => {
throw new Error(`Unknown URI: ${F._F}`);
})()) as <Env extends AnyEnv>(
F: AlgebraUnion<'HKT', Env>
) => HKT<Env, O, A>;
};
interface IAppEnv {}
const { summon } = summonFor<IAppEnv>({});
const numberFromString = customIoTs(NumberFromString, Num.Eq, Num.Show);
// Usage
const Foo = summon(F => numberFromString(F));
Foo.type.decode('123'); // Right 123
Foo.type.decode('abc'); // Left [...]
Foo.eq.equals(Foo.build(1), Foo.build(1)); // true
Foo.show.show(Foo.build(1)); // '1'
const Bar = summon(F => F.interface({ num: numberFromString(F) }, 'Bar'));
Bar.strictType.decode({ num: '123' }); // Right { num: 123 }
Bar.strictType.decode({ num: 'abc' }); // Left [...]
Bar.eq.equals(Bar.build({ num: 1 }), Bar.build({ num: 1 })); // true
Bar.show.show(Bar.build({ num: 1 })); // '{ num: 1 }'

ESBST召唤器使用不同的"解释器"(summon(F => ...)中的F)多次调用您传递给summon(program)的函数:

const { create, type } = program(modelIoTsNonStrictInterpreter<NonNullable<R>>())(env)
return {
build: a => a,
eq: program(modelEqInterpreter<NonNullable<R>>())(env).eq,
show: program(modelShowInterpreter<NonNullable<R>>())(env).show,
strictType: program(modelIoTsStrictInterpreter<NonNullable<R>>())(env).type,
type,
create
}

@morphic-ts/batteries/src/summoner-ESBST.ts

(顺便说一下,上面的RsummonFor的通用参数(例如IAppEnv))

对于'基元'的modelIoTs(Non)StrictInterpreters在这里定义:

export const ioTsPrimitiveInterpreter = memo(
<Env extends AnyEnv>(): ModelAlgebraPrimitive<IoTsURI, Env> => ({
_F: IoTsURI,
date: config => env => new IOTSType(iotsApplyConfig(config)(DateFromISOString, env, {})),
boolean: config => env => new IOTSType(iotsApplyConfig(config)(t.boolean, env, {})),
string: config => env => new IOTSType(iotsApplyConfig(config)(t.string, env, {})),
number: config => env => new IOTSType(iotsApplyConfig(config)(t.number, env, {})),
// some more omitted for brevity

@morphic-ts/io-ts-interpreters/src/model/primitives.ts

这里重要的部分是new IOTSType。我不知道我们的NumberFromString需要任何配置,所以我在实现中跳过了iotsApplyConfig部分。

这与modelEqInterpretermodelShowInterpreter非常相似,除了分别使用EqTypeShowType:

export const eqPrimitiveInterpreter = memo(
<Env extends AnyEnv>(): ModelAlgebraPrimitive<EqURI, Env> => ({
_F: EqURI,
date: config => env =>
new EqType(
eqApplyConfig(config)(
eq.contramap(eqNumber, (date: Date) => date.getTime()),
env,
{}
)
),
boolean: config => env => new EqType(eqApplyConfig(config)(eqBoolean, env, {})),
string: config => env => new EqType(eqApplyConfig(config)(eqString, env, {})),
number: config => env => new EqType(eqApplyConfig(config)(eqNumber, env, {})),

@morphic-ts/eq-interpreters/src/model/primitives.ts

export const showPrimitiveInterpreter = memo(
<Env extends AnyEnv>(): ModelAlgebraPrimitive<ShowURI, Env> => ({
_F: ShowURI,
date: config => env => new ShowType(showApplyConfig(config)({ show: (date: Date) => date.toISOString() }, env, {})),
boolean: config => env => new ShowType(showApplyConfig(config)(showBoolean, env, {})),
string: config => env => new ShowType(showApplyConfig(config)(showString, env, {})),
number: config => env => new ShowType(showApplyConfig(config)(showNumber, env, {})),

@morphic-ts/show-interpreters/src/model/primitives.ts

最新更新