효과 TS 핵심: ZIO Prelude 계발 유형 클래스 및 모듈 구조
100545 단어 typescriptfunctional
@effect-ts/core
에서 사용된 HKT의 독특한 인코딩 배후의 원리를 묘사하였으니 이제 세부 사항을 살펴볼 때가 되었다.우리는 사용 가능한
Type-Classes
탐색을 시작하여 점차적으로 용법 예시를 제시할 것이다.마지막으로, 우리는 모듈 구조와 사용할 수 있는 주문에 대해 토론할 것이다.
프로젝트 설정
간단한 새 프로젝트부터 시작하겠습니다 (Scala/Haskell에서 왔을 때만 곤란합니다. TS를 알고 있으면 무시합니다).
mkdir effect-ts-series;
cd effect-ts-series;
npm init -y;
yarn add typescript@next @effect-ts/core @types/node;
mkdir src;
다음과 같은 파일tsconfig.json
을 만듭니다.{
"compilerOptions": {
"strict": true,
"target": "ES5",
"outDir": "lib",
"lib": ["ESNext"]
},
"include": ["src/**/*.ts"]
}
다음 내용을 포함하는 파일을 만듭니다src/index.ts
.import * as T from "@effect-ts/core/Effect";
import { pipe } from "@effect-ts/core/Function";
pipe(
T.effectTotal(() => {
console.log("Hello world");
}),
T.runMain
);
다음과 같이 구축 스크립트를 package.json
에 추가합니다.{
"name": "effect-ts-series",
"version": "1.0.0",
"description": "Effect-TS Series",
"main": "lib/index.js",
"scripts": {
"build": "tsc",
"start": "node lib/index.js"
},
"keywords": [],
"author": "Michael Arnaldi",
"license": "MIT",
"dependencies": {
"@effect-ts/core": "^0.2.0",
"@types/node": "^14.11.2",
"typescript": "^4.1.0-dev.20201004"
}
}
우리는 이 항목을 다음과 같이 컴파일할 수 있어야 한다.yarn build
다음 작업을 실행합니다.$ yarn start
yarn run v1.22.4
$ node lib/index.js
Hello world
Done in 0.46s.
치오 전주곡 유형 과정 안내
우선, 우리는 약간의 이론과 고전적인 유형의 차원 구조를 수정하는 원인부터 시작합시다.
우리가 오늘 알고 있는 정적 유형 함수 프로그래밍은haskell과 그 설계 원칙에 효과적으로 뿌리를 둔다.여러 해 동안 우리는 지역 사회로서 원칙을 하나하나 빌려 다른 언어에 융합시켰다.
기능을 한 언어에서 다른 언어로 이식하는 과정은 쉽지 않다. 여러 가지 절차가 필요하다. 첫 번째 단계는 유사한 인코딩을 찾는 것이고, 두 번째 단계는 기초 위에서 개선하는 것이다.
하스켈의 유형 체계는 범주 이론의 계발을 받았지만 수학적으로 말하자면 그것은 일종의'근사'일 뿐이고 HM 가족 언어에서 의미 있는 이론의 특정 서브집합에 전념한다.우리는 이 이론의 나머지 부분을 소홀히 해서는 안 된다. 특히 개념을 다른 언어로 확장할 때haskell에서 한 같은 가설이 우리 언어에 적용되지 않을 수 있기 때문이다. (예를 들어 처리 중인 모든 함수)
ZIO Prelude는 함수 프로그래밍 개념을 Scala로 추상화하고 개편하는 두 번째 단계로 볼 수 있으며, 이것은 Scala를 위해 설계된 것으로 이 언어의 모든 사용 가능한 기능을 이용한다.
다행히도 Scala의 언어로서의 특성은 TypeScript의 유형 시스템 단계에서의 특성과 매우 비슷하며, 어떤 경우 TypeScript 유형 시스템은 더욱 유연하다(즉 교차 및 병렬 유형 지원).
이 밖에 ZIO Prelude는 이전에 부차적인 수학 구조로 여겨졌던 더욱 광범위한 범위를 연구했다.
Functor
의 fp-ts
을 살펴보겠습니다. 우리는 사물의 소형화를 유지하기 위해 하나의 정의만 열거할 것입니다.export interface Functor<F> {
readonly URI: F
readonly map: <A, B>(fa: HKT<F, A>, f: (a: A) => B) => HKT<F, B>
}
이와 유사하게 다른 fp 언어에서 정의한다. 예를 들어 purescript
& haskell
이 유형류는 일종의 편견을 나타낸다. 실제로 범주론에서 편지는 협동하거나 역변할 수 있다. 여기서 우리는 Functor
명칭을 특정한 상황과 연결시킨다이제
Functor
가 어떻게 분류되어 정의되었는지 살펴보겠습니다.Functor
사이의 Categories
은objects
과 morphisms
의 반사로 범주 구조를 유지하고 적어도 두 가지 유형의 Functors
, 하나는 morphisms
의 방향을 유지하고 다른 반전 방향을 유지한다.이것들은
Covariant Functor
& Contravariant Functor
라고 불린다.fp-ts
의 상술한 정의에서 우리는 haskell
의 편차가 모든 것을 가리킨다Covariant
Functors
를 인식했다.ZIO Prelude는 서로 다른 이름을 사용하고 매우 정교한 디자인(즉 최소 유형 클래스, 조합하기 쉬운)을 이용하여 개념적으로는 같지만 유형 클래스의 실제 규칙에 더욱 가깝다.
협동적
Functor
중@effect-ts/core
의 등가물을 살펴보자.export interface Covariant<F extends HKT.URIS, C = HKT.Auto> extends HKT.Base<F, C> {
readonly map: <A, B>(
f: (a: A) => B
) => <N extends string, K, Q, W, X, I, S, R, E>(
fa: HKT.Kind<F, C, N, K, Q, W, X, I, S, R, E, A>
) => HKT.Kind<F, C, N, K, Q, W, X, I, S, R, E, B>
}
코드: core/src/Prelude/Covariant/index.ts사용된 이름은
Covariant
입니다.알려진 데이터 유형의 예를 살펴보겠습니다.
export const Covariant = P.instance<P.Covariant<[EitherURI], V>>({
map: E.map
})
그 중에서 Covariant Functor
모듈은 E
지시 파라미터Either
에 사용되는 협방차V = Prelude.V<"E", "+">
에서 오차 채널E
과 연합 유형이 혼합된 것을 뒤에서 볼 수 있습니다.명세서
사랑하는 애인을 봅시다
Either
:export type Monad<F extends URIS, C = Auto> = IdentityFlatten<F, C> & Covariant<F, C>
export type IdentityFlatten<F extends URIS, C = Auto> = AssociativeFlatten<F, C> &
Any<F, C>
export interface Any<F extends HKT.URIS, C = HKT.Auto> extends HKT.Base<F, C> {
readonly any: <
N extends string = HKT.Initial<C, "N">,
K = HKT.Initial<C, "K">,
Q = HKT.Initial<C, "Q">,
W = HKT.Initial<C, "W">,
X = HKT.Initial<C, "X">,
I = HKT.Initial<C, "I">,
S = HKT.Initial<C, "S">,
R = HKT.Initial<C, "R">,
E = HKT.Initial<C, "E">
>() => HKT.Kind<F, C, N, K, Q, W, X, I, S, R, E, any>
}
export interface AssociativeFlatten<F extends HKT.URIS, C = HKT.Auto>
extends HKT.Base<F, C> {
readonly flatten: <
N extends string,
K,
Q,
W,
X,
I,
S,
R,
E,
A,
N2 extends string,
K2,
Q2,
W2,
X2,
I2,
S2,
R2,
E2
>(
ffa: HKT.Kind<
F,
C,
N2,
K2,
Q2,
W2,
X2,
I2,
S2,
R2,
E2,
HKT.Kind<
F,
C,
HKT.Intro<C, "N", N2, N>,
HKT.Intro<C, "K", K2, K>,
HKT.Intro<C, "Q", Q2, Q>,
HKT.Intro<C, "W", W2, W>,
HKT.Intro<C, "X", X2, X>,
HKT.Intro<C, "I", I2, I>,
HKT.Intro<C, "S", S2, S>,
HKT.Intro<C, "R", R2, R>,
HKT.Intro<C, "E", E2, E>,
A
>
>
) => HKT.Kind<
F,
C,
HKT.Mix<C, "N", [N2, N]>,
HKT.Mix<C, "K", [K2, K]>,
HKT.Mix<C, "Q", [Q2, Q]>,
HKT.Mix<C, "W", [W2, W]>,
HKT.Mix<C, "X", [X2, X]>,
HKT.Mix<C, "I", [I2, I]>,
HKT.Mix<C, "S", [S2, S]>,
HKT.Mix<C, "R", [R2, R]>,
HKT.Mix<C, "E", [E2, E]>,
A
>
}
코드: core/src/Prelude/Monad/index.ts약간의 지루함을 제외하고는
E
최대 10개의 다른 유형의 매개 변수를 지원하며, 이 매개 변수들은 실례 단계에 따라 지정한 방차 주석에 따라 동적 혼합을 할 수 있다.우리는
Monad
가 어떻게 서로 다른, 더욱 구체적인, 합법적인 유형류 사이에서 분리되고 있는지 볼 수 있다.우리는
@effect-ts/core
와 Monad
평행 연산이 있는 Monad
편지를 읽었다.기본적으로 a
Covariant
가 반드시 준수해야 하는 법률을 묘사하였다.각종
identity
의Associative
의 몇 가지 실례를 살펴보고 방차가 어떻게 작동하는지 봅시다.우리는 우선 어떤 유형의 코드를 어떻게 작성하는지 보여주기 위해 범용 조작을 소개할 것이다. 범용
Monad
함수, 이 함수가 정한 Monad
실례를 살펴보고 일련의 조작을 실행할 것이다. 그 중에서 두 번째 조작은 첫 번째 조작의 결과에 달려 있다.export function chainF<F extends HKT.URIS, C = HKT.Auto>(
F: Monad<F, C>
): <N2 extends string, K2, Q2, W2, X2, I2, S2, R2, E2, A, B>(
f: (a: A) => HKT.Kind<F, C, N2, K2, Q2, W2, X2, I2, S2, R2, E2, B>
) => <N extends string, K, Q, W, X, I, S, R, E>(
fa: HKT.Kind<
F,
C,
HKT.Intro<C, "N", N2, N>,
HKT.Intro<C, "K", K2, K>,
HKT.Intro<C, "Q", Q2, Q>,
HKT.Intro<C, "W", W2, W>,
HKT.Intro<C, "X", X2, X>,
HKT.Intro<C, "I", I2, I>,
HKT.Intro<C, "S", S2, S>,
HKT.Intro<C, "R", R2, R>,
HKT.Intro<C, "E", E2, E>,
A
>
) => HKT.Kind<
F,
C,
HKT.Mix<C, "N", [N2, N]>,
HKT.Mix<C, "K", [K2, K]>,
HKT.Mix<C, "Q", [Q2, Q]>,
HKT.Mix<C, "W", [W2, W]>,
HKT.Mix<C, "X", [X2, X]>,
HKT.Mix<C, "I", [I2, I]>,
HKT.Mix<C, "S", [S2, S]>,
HKT.Mix<C, "R", [R2, R]>,
HKT.Mix<C, "E", [E2, E]>,
B
>
export function chainF<F>(F: Monad<HKT.UHKT<F>>) {
return <A, B>(f: (a: A) => HKT.HKT<F, B>) => flow(F.map(f), F.flatten)
}
코드: core/src/Prelude/DSL/dsl.ts우리는 몇 가지 다른 실례에서 이 통용
data-types
함수를 사용합시다.import * as IO from "@effect-ts/core/XPure/XIO";
import * as Either from "@effect-ts/core/Classic/Either";
import * as Effect from "@effect-ts/core/Effect";
import { pipe } from "@effect-ts/core/Function";
import { chainF } from "@effect-ts/core/Prelude/DSL";
const chainIO = chainF(IO.Monad);
const chainEither = chainF(Either.Monad);
const chainEffect = chainF(Effect.Monad);
// IO.XIO<number>
const io = pipe(
IO.succeed(0),
chainIO((n) => IO.succeed(n + 1))
);
const checkPositive = (n: number): Either.Either<string, number> =>
n > 0 ? Either.right(n) : Either.left("error");
// Either.Either<string, number>
const either = (n: number) =>
pipe(
n,
checkPositive,
chainEither((n) => Either.right(n + 1))
);
// Effect.Effect<{ s: string; } & { n: number; }, string | number, number>
const effect = pipe(
Effect.accessM((_: { n: number }) =>
Effect.ifM(Effect.succeed(_.n > 0))(() => Effect.succeed(_.n))(() =>
Effect.fail("error")
)
),
chainEffect((n) =>
Effect.accessM((_: { s: string }) =>
Effect.ifM(Effect.succeed(_.s.length > 1))(() =>
Effect.succeed(n + _.s.length)
)(() => Effect.fail(0))
)
)
);
보시다시피 매개변수 chain
, Monad
의 혼합은 다음과 같은 인스턴스로 지정된 방차에 따라 다릅니다.// for Effect
export type V = P.V<"R", "-"> & P.V<"E", "+">
// for Either
export type V = P.V<"E", "+">
실용적
이 오랜 친구
chainF
를 살펴보자. 우선 주의해야 할 것은, R
완전히 E
육지와 같지 않다는 것이다!export type Applicative<F extends URIS, C = Auto> = IdentityBoth<F, C> & Covariant<F, C>
export type IdentityBoth<F extends URIS, C = Auto> = AssociativeBoth<F, C> & Any<F, C>
export interface AssociativeBoth<F extends HKT.URIS, C = HKT.Auto>
extends HKT.Base<F, C> {
readonly both: <N2 extends string, K2, Q2, W2, X2, I2, S2, R2, E2, B>(
fb: HKT.Kind<F, C, N2, K2, Q2, W2, X2, I2, S2, R2, E2, B>
) => <N extends string, K, Q, W, X, I, S, R, E, A>(
fa: HKT.Kind<
F,
C,
HKT.Intro<C, "N", N2, N>,
HKT.Intro<C, "K", K2, K>,
HKT.Intro<C, "Q", Q2, Q>,
HKT.Intro<C, "W", W2, W>,
HKT.Intro<C, "X", X2, X>,
HKT.Intro<C, "I", I2, I>,
HKT.Intro<C, "S", S2, S>,
HKT.Intro<C, "R", R2, R>,
HKT.Intro<C, "E", E2, E>,
A
>
) => HKT.Kind<
F,
C,
HKT.Mix<C, "N", [N2, N]>,
HKT.Mix<C, "K", [K2, K]>,
HKT.Mix<C, "Q", [Q2, Q]>,
HKT.Mix<C, "W", [W2, W]>,
HKT.Mix<C, "X", [X2, X]>,
HKT.Mix<C, "I", [I2, I]>,
HKT.Mix<C, "S", [S2, S]>,
HKT.Mix<C, "R", [R2, R]>,
HKT.Mix<C, "E", [E2, E]>,
readonly [A, B]
>
}
코드: core/src/Prelude/Applicative/index.ts이보다 더 쉬운 것은 없다. 왜냐하면 우리는 하나
Applicative
가 하나Applicative
의 편지이고 하나의 항등식과 하나Monad
의 연산Haskell
을 가지고 있기 때문이다.이론적으로 말하자면 그것은
Applicative
의 고전적인 변체와 같지만 법칙의 각도와 가용성의 측면에서 보면 더욱 뚜렷하다.그 밖에 만약에 우리가 이론에 따라 읽을 수 있다면 ncatlab.org:
In computer science, applicative functors (also known as idioms) are the programming equivalent of lax monoidal functors with a tensorial strength in category theory.
만약 당신이 관련된 용어를 알고 있다면, 당신은 경전
Covariant
에 비해 이 정의가 최종적으로 이론에 가깝다는 것을 알게 될 것이다.Associative
편지에 사용할 수 있는 DSL을 살펴보겠습니다.import * as Either from "@effect-ts/core/Classic/Either";
import * as DSL from "@effect-ts/core/Prelude/DSL";
const struct = DSL.structF(Either.Applicative);
const tupled = DSL.tupledF(Either.Applicative);
// Either.Either<never, { a: number; b: number; c: number; }>
const resultStruct = struct({
a: Either.right(0),
b: Either.right(1),
c: Either.right(2),
});
// Either.Either<never, [number, number, number]>
const resultTupled = tupled(Either.right(0), Either.right(1), Either.right(2));
우리는 그것을 연습으로 삼아 독자들이 이 성명에서 Both
& ap
의 ap
성명을 내보낼 수 있도록 하고, 반대로 (제시: 당신은 Applicative
에서 사용할 수 있는 함수를 사용할 수 있습니다.)두루 훑어볼 수 있었어
이 사랑하는 옛 친구를 봅시다
Monad
:export interface Foreach<F extends HKT.URIS, C = HKT.Auto> {
<G extends HKT.URIS, GC = HKT.Auto>(G: IdentityBoth<G, GC> & Covariant<G, GC>): <
GN extends string,
GK,
GQ,
GW,
GX,
GI,
GS,
GR,
GE,
A,
B
>(
f: (a: A) => HKT.Kind<G, GC, GN, GK, GQ, GW, GX, GI, GS, GR, GE, B>
) => <FN extends string, FK, FQ, FW, FX, FI, FS, FR, FE>(
fa: HKT.Kind<F, C, FN, FK, FQ, FW, FX, FI, FS, FR, FE, A>
) => HKT.Kind<
G,
GC,
GN,
GK,
GQ,
GW,
GX,
GI,
GS,
GR,
GE,
HKT.Kind<F, C, FN, FK, FQ, FW, FX, FI, FS, FR, FE, B>
>
}
export interface Traversable<F extends HKT.URIS, C = HKT.Auto>
extends HKT.Base<F, C>,
Covariant<F, C> {
readonly foreachF: Foreach<F, C>
}
코드: core/src/Prelude/Traversable/index.tsApplicative
함수(최초칭 fp-ts
의 명칭을 제외하고는 고전 버전과 별다른 차이가 없다.사용법을 살펴보겠습니다.
import * as Either from "@effect-ts/core/Classic/Either";
import * as Array from "@effect-ts/core/Classic/Array";
import * as Record from "@effect-ts/core/Classic/Record";
import { pipe } from "@effect-ts/core/Function";
import { sequenceF } from "@effect-ts/core/Prelude";
const foreachArray = Array.Traversable.foreachF(Either.Applicative);
// Either.Either<string, Array.Array<number>>
const resultArray = pipe(
[0, 1, 2, 3],
foreachArray((n) => (n > 2 ? Either.left("error") : Either.right(n)))
);
const foreachRecord = Record.Traversable.foreachF(Either.Applicative);
// Either.Either<string, Readonly<Record<"a" | "b" | "c" | "d", number>>>
const resultRecord = pipe(
{
a: 0,
b: 0,
c: 0,
d: 0,
},
foreachRecord((n) => (n > 2 ? Either.left("error") : Either.right(n)))
);
const sequenceArray = sequenceF(Array.Traversable)(Either.Applicative);
// Either.Either<string, Array.Array<number>>
const sequenceArrayResult = sequenceArray([
Either.left("error"),
Either.right(0),
Either.right(1),
Either.right(2),
]);
신분
친애하는 노인
Prelude/DSL
:export interface Identity<A> extends Associative<A> {
readonly identity: A
}
export interface Associative<A> extends Closure<A> {
readonly Associative: "Associative"
}
export interface Closure<A> {
combine(r: A): (l: A) => A
}
앞에서 말한 바와 같이 법칙을 모르는 상황에서 우리는 Traversable
과 foreachF
원소의 traverse
관련 연산을 읽을 수 있다.접을 수 있었어
Monoid
특별한 점은 없습니다.export type Foldable<F extends URIS, C = Auto> = ReduceRight<F, C> &
Reduce<F, C> &
FoldMap<F, C>
export interface Reduce<F extends HKT.URIS, C = HKT.Auto> extends HKT.Base<F, C> {
readonly reduce: <A, B>(
b: B,
f: (b: B, a: A) => B
) => <N extends string, K, Q, W, X, I, S, R, E>(
fa: HKT.Kind<F, C, N, K, Q, W, X, I, S, R, E, A>
) => B
}
export interface ReduceRight<F extends HKT.URIS, C = HKT.Auto> extends HKT.Base<F, C> {
readonly reduceRight: <A, B>(
b: B,
f: (a: A, b: B) => B
) => <N extends string, K, Q, W, X, I, S, R, E>(
fa: HKT.Kind<F, C, N, K, Q, W, X, I, S, R, E, A>
) => B
}
export interface FoldMap<F extends HKT.URIS, C = HKT.Auto> extends HKT.Base<F, C> {
readonly foldMap: FoldMapFn<F, C>
}
export interface FoldMapFn<F extends HKT.URIS, C = HKT.Auto> {
<M>(I: Identity<M>): <A>(
f: (a: A) => M
) => <N extends string, K, Q, W, X, I, S, R, E>(
fa: HKT.Kind<F, C, N, K, Q, W, X, I, S, R, E, A>
) => M
}
몇 가지 Monoid
실례를 사용해 봅시다.import * as Array from "@effect-ts/core/Classic/Array";
import * as Record from "@effect-ts/core/Classic/Record";
import * as Identity from "@effect-ts/core/Classic/Identity";
const fromArray = Record.fromFoldable(Identity.string, Array.Foldable);
// Readonly<Record<string, string>>
const record = fromArray([
["a", "foo"],
["b", "bar"],
]);
모듈 구조
combine
패키지는 다음 디렉토리에 따라 구성됩니다.identity
: 어디서나 사용할 수 있는 경량급 모듈 및 일반 유형(브라우저, 노드)Foldable
: 효과 기반 모듈로 주로 노드를 대상으로 개발되었습니다. 이 모듈은 다양한 데이터 유형을 가진 고도로 병행되고 테스트 가능한 서비스를 구축하는 데 사용되는 완전한 세트입니다. Foldable
이것은 전단 개발에도 사용할 수 있지만 원가 효율을 고려해야 한다. 만약에 프로젝트가 충분하다면 비교적 작은 프로젝트에서 프로젝트의 할당을 바탕으로 하고 @effect-ts/core
같은 @effect-ts/core/Classic
특정한 사용 데이터 유형이 더욱 바람직하기 때문이다.@effect-ts/core/Effect
: 함수 기반 유틸리티(예: Fiber, FiberRef, Layer, Managed, Promise, Queue, Ref, RefM, Schedule, Scope, Semaphore, Stream, Supervisor
Classic
: 새로운 유형 정의 및 일반적인 새로운 유형Async
: 패턴 일치 및 교차에 사용되는 유틸리티 그룹@effect-ts/core/Function
: pipe
의 데이터 유형을 바탕으로 하는 효율적인 동기화 데이터 유형으로 역변 상태 입력, 협동 상태 출력, 역변 읽기기, 협동 오차, 출력을 지원한다.@effect-ts/core/Newtype
의 목적은 특정 기능을 충족시킬 수 있는 다양한 데이터 유형을 구축하는 기초이다.그것도 매우 경량급이다. 만약 서로 다른 데이터 유형을 뛰어넘어 사용한다면, 그것은 특히 효과적일 것이다.@effect-ts/core/Utils
또한 @effect-ts/core/XPure
데이터 형식을 지원하는 데 사용되며, 이 데이터 형식은 본 컴퓨터에서 XPure
라는 원어를 포함하고 있습니다.XPure
: 내부 사용법Reference
이 문제에 관하여(효과 TS 핵심: ZIO Prelude 계발 유형 클래스 및 모듈 구조), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/matechs/effect-ts-core-zio-prelude-inspired-typeclasses-module-structure-50g6텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)