効果のコア:ZIIOプレリュードインスパイア&モジュール構造
100275 ワード
第1の記事では、我々は、2009年に使用されるHKTSのユニークな符号化の背後にある原則を説明しました
我々は、探索することから始めます
最後に、モジュール構造と利用可能なA - LA - Carteについて説明します.
単純な新しいプロジェクトから始めましょう(Scala/Haskellから来た場合に限ってみてください.
まず最初に、古典的なタイプ階層を改訂する理論と理由の少しで始めましょう.
静的に型付けされた関数型プログラミングは、今日我々が効率的にHaskellとその設計原理にルーツを持っていることを知っている何年もの間、我々はコミュニティとして、一つずつ借り入れの原則を行使し、別の言語にその道を見つけることを行った.
つの言語から別の言語への機能を移植するプロセスは簡単ではありません、そして、それは複数のステップを必要とします.
Haskellのタイプシステムはカテゴリー理論に触発されます、しかし、数学的にそれはHM家族の言語で意味をなす理論の特定の部分集合に集中する「近似」だけです.Haskellで作られた同じ仮定が我々の中で保持されていないかもしれないので(例えば、すべての機能がカレーディングされているように)、他の理論に対して特に無視するべきではありません.
ZIOのプレリュードは、抽象化の2番目のステップとScalaに機能プログラミングの概念の適応、それはScalaとレバレッジのために設計されているすべての機能は、言語で使用可能です.
我々にとって幸運なことは、言語としてScalaの特徴はタイプ・システム・レベルでのタイプスクリプトの特徴に非常に似ています、そして、いくつかのケースでは、typescript型システムはさらに柔軟です(すなわち、サポート交差点と組合タイプ).
さらに、ジーニョ・プレリュードは、以前は二次として認識された数学からのより広い範囲の構造を見ます.
見てみましょう
ではどうやって見ましょうか
エー
これらは
上記の定義から
ZIOプレリュードは異なった命名を使用して、非常に直交したデザイン(すなわち、最小限の型クラス(簡単に構成可能な))を活用します.
の等価物を見てみましょう
使用する名前は
既知のデータ型のいくつかの例を見てみましょう.
親愛なる愛を見ましょう
わずかに冗長であることは別として、
どのようによく見ることができます
読みました
それ自体は法律を説明する
のいくつかの例を見てみましょう
まず、どのような種類で動作するコードをどのように書くかを紹介する一般的な操作を紹介します
この汎用を使いましょう
古き良き友を見ましょう
何も簡単に、我々は読んで
これは理論的には古典的なバリアントと同じです
また、私たちが理論によって行くならばncatlab.org :
あなたが関係している用語を知っているならば、あなたは最後にこの定義が古典に比べて理論に非常に近いと認識するでしょう
いくつかのDSLを見てみましょう
親愛なる旧友を見てみましょう
何も例外的に古典的なバージョンとは別にの名前から
その使い方を見てみましょう.
親愛なる老人
何も特別な
The
@effect-ts/core
, 詳細を見る時間です.我々は、探索することから始めます
Type-Classes
利用できると我々は徐々にいくつかの使用例を作成します.最後に、モジュール構造と利用可能なA - LA - Carteについて説明します.
プロジェクト設定
単純な新しいプロジェクトから始めましょう(Scala/Haskellから来た場合に限ってみてください.
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.
ZIOプレリュードタイプクラス入門
まず最初に、古典的なタイプ階層を改訂する理論と理由の少しで始めましょう.
静的に型付けされた関数型プログラミングは、今日我々が効率的にHaskellとその設計原理にルーツを持っていることを知っている何年もの間、我々はコミュニティとして、一つずつ借り入れの原則を行使し、別の言語にその道を見つけることを行った.
つの言語から別の言語への機能を移植するプロセスは簡単ではありません、そして、それは複数のステップを必要とします.
Haskellのタイプシステムはカテゴリー理論に触発されます、しかし、数学的にそれはHM家族の言語で意味をなす理論の特定の部分集合に集中する「近似」だけです.Haskellで作られた同じ仮定が我々の中で保持されていないかもしれないので(例えば、すべての機能がカレーディングされているように)、他の理論に対して特に無視するべきではありません.
ZIOのプレリュードは、抽象化の2番目のステップとScalaに機能プログラミングの概念の適応、それはScalaとレバレッジのために設計されているすべての機能は、言語で使用可能です.
我々にとって幸運なことは、言語としてScalaの特徴はタイプ・システム・レベルでのタイプスクリプトの特徴に非常に似ています、そして、いくつかのケースでは、typescript型システムはさらに柔軟です(すなわち、サポート交差点と組合タイプ).
さらに、ジーニョ・プレリュードは、以前は二次として認識された数学からのより広い範囲の構造を見ます.
見てみましょう
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
このtypeclassはバイアスを示します、カテゴリー理論において、ファンターが共変的であるか反抗的でありえるとき、我々はここで我々を結びつけますFunctor
特定のケースの名前ではどうやって見ましょうか
Functor
定義はエー
Functor
の間Categories
のマッピングobjects
and morphisms
これは、分類構造を保持し、少なくとも2種類のFunctors
, その方向を保つものmorphisms
そして、方向を逆にするもの.これらは
Covariant Functor
& Contravariant Functor
.上記の定義から
fp-ts
私たちはhaskell
偏見、すべてが向いているCovariant
Functors
.ZIOプレリュードは異なった命名を使用して、非常に直交したデザイン(すなわち、最小限の型クラス(簡単に構成可能な))を活用します.
共変
の等価物を見てみましょう
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
と同じCovariant Functor
.既知のデータ型のいくつかの例を見てみましょう.
export const Covariant = P.instance<P.Covariant<[EitherURI], V>>({
map: E.map
})
どこE
はEither
モジュールV = Prelude.V<"E", "+">
パラメータの共分散を示すE
にEither
エラーチャネルE
私たちが後で表示されるようにユニオンのタイプとミックスします.モンド
親愛なる愛を見ましょう
Monad
: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 わずかに冗長であることは別として、
@effect-ts/core
動的にインスタンスレベルで指定された分散アノテーションに基づいてミックスすることができます10種類のタイプのパラメータをサポートします.どのようによく見ることができます
Monad
異なった、より具体的な、法定タイプクラスの向こう側に直交して分離されます.読みました
Monad
はCovariant
ファンクションidentity
とAssociative
操作を平らにする.それ自体は法律を説明する
Monad
尊重しなければなりません.のいくつかの例を見てみましょう
Monad
様々なdata-types
そして、分散の仕組みを見てみましょう.まず、どのような種類で動作するコードをどのように書くかを紹介する一般的な操作を紹介します
chain
インスタンスを指定した関数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 この汎用を使いましょう
chainF
いくつかの異なるインスタンスの関数: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))
)
)
);
パラメータを見ることができますR
, E
は以下のように指定されたインスタンスの分散によって異なる.// for Effect
export type V = P.V<"R", "-"> & P.V<"E", "+">
// for Either
export type V = P.V<"E", "+">
応募
古き良き友を見ましょう
Applicative
, 最初に注意すべきことはApplicative
から完全に独立しているMonad
本当に好きではないHaskell
陸!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
はCovariant
アイデンティティーとAAssociative
操作Both
.これは理論的には古典的なバリアントと同じです
ap
しかし、法律上の観点や使いやすさの観点からはるかに明確です.また、私たちが理論によって行くならば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.
あなたが関係している用語を知っているならば、あなたは最後にこの定義が古典に比べて理論に非常に近いと認識するでしょう
ap
.いくつかのDSLを見てみましょう
Applicative
関数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));
我々は、読者のための運動としてそれを引き出すために残すMonad
& Applicative
の宣言fp-ts
ヒントとしては、次の関数を使用できますPrelude/DSL
).横断できる
親愛なる旧友を見てみましょう
Traversable
: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.ts 何も例外的に古典的なバージョンとは別にの名前から
foreachF
関数名traverse
).その使い方を見てみましょう.
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),
]);
アイデンティティ
親愛なる老人
Monoid
: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
}
前に、我々はそれを読むことができる法律を知らない前にMonoid
をcombine
による連想操作identity
要素.折り畳み可能な
何も特別な
Foldable
: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
}
を使ってみましょうFoldable
インスタンス.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"],
]);
モジュール構造
The
@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
: NewTypeの定義と共通の型@effect-ts/core/Utils
: パターンマッチングと交差のためのユーティリティの小セット@effect-ts/core/XPure
: データ型XPure
, 逆の状態入力、共変状態出力、反対のリーダー、共変エラー、出力をサポートする効率的な同期データ型.…の目的XPure
特定の機能を満たすことができる複数のデータ型を構築するための基礎となる.また、非常に軽量であり、特に別のデータ型間で使用する場合、効率的にすることができます.XPure
また、Classic/Sync
原始的に存在するデータ型Effect
. @effect-ts/core/Modules
: 内部使用Reference
この問題について(効果のコア:ZIIOプレリュードインスパイア&モジュール構造), 我々は、より多くの情報をここで見つけました https://dev.to/matechs/effect-ts-core-zio-prelude-inspired-typeclasses-module-structure-50g6テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol