Artikel sebelumnya dalam seri:
Pada artikel sebelumnya, saya menjelaskan bagaimana Anda dapat meniru polimorfisme genus tingkat tinggi di TypeScript. Sekarang mari kita lihat apa yang diberikan ini kepada programmer fungsional, dan kita akan mulai dengan pola kelas tipe.
Konsep kelas tipe berasal dari Haskell dan pertama kali diusulkan oleh Philip Wadler dan Stephen Blott pada tahun 1988 untuk mengimplementasikan polimorfisme ad hoc. Kelas tipe mendefinisikan satu set fungsi dan konstanta yang diketik yang harus ada untuk setiap tipe yang dimiliki kelas tertentu. Kedengarannya rumit pada awalnya, tetapi sebenarnya desainnya cukup sederhana dan elegan.
Apa itu kelas tipe
, , . - TypeScript JavaScript , ( this
). , , GHC Core Language, .
Pertimbangkan, sebagai contoh, salah satu kelas tipe paling sederhana - Show
, - yang mendefinisikan operasi pengecoran. Ini didefinisikan dalam modul fp-ts/lib/Show
:
interface Show<A> {
readonly show: (a: A) => string;
}
Definisi ini berbunyi seperti ini: tipe A
milik kelas Show
jika A
fungsi didefinisikan untukshow : (a: A) => string
.
Kelas tipe diimplementasikan sebagai berikut:
const showString: Show<string> = {
show: s => JSON.stringify(s)
};
const showNumber: Show<number> = {
show: n => n.toString()
};
// , «» name age:
const showUser: Show<User> = {
show: user => `User "${user.name}", ${user.age} years old`
};
. , Show
β , , β Show
:
// any , .. T
// Show β infer.
// T ,
// Show:
const getShowTuple = <T extends Array<Show<any>>>(
...shows: T
): Show<{ [K in keyof T]: T[K] extends Show<infer A> ? A : never }> => ({
show: t => `[${t.map((a, i) => shows[i].show(a)).join(', ')}]`
});
(principle of least knowledge, principle of least power) β , . TypeScript , .
β , . , , . Mappable, Functor β . β , , map
, ; β map
; - β map
. :
import { Kind } from 'fp-ts/lib/HKT';
import { Functor } from 'fp-ts/lib/Functor';
import { Show } from 'fp-ts/lib/Show';
const stringify = <F extends URIS, A>(F: Functor<F>, A: Show<A>) =>
(structure: Kind<F, A>): Kind<F, string> => F.map(structure, A.show);
, Β« Β» , ? β , .
, . , , , β :
interface Comment {
readonly author: string;
readonly text: string;
readonly createdAt: Date;
}
const comments: Comment[] = ...;
const renderComments = (comments: Comment[]): Component => <List>{comments.map(renderOneComment)}</List>;
const renderOneComment = (comment: Comment): Component => <ListItem>{comment.text} by {comment.author} at {comment.createdAt}</ListItem>
, , , , comments
.
, :
interface ToComponent<A> {
readonly render: (element: A) => Component;
}
const commentToComponent: ToComponent<Comment> = {
render: comment => <>{comment.text} by {comment.author} at {comment.createdAt}</>
};
const arrayToComponent = <A>(TCA: ToComponent<A>): ToComponent<Comment[]> => ({
render: as => <List>{as.map(a => <ListItem>{TCA.render(a)}</ListItem>)}</List>
});
const treeToComponent = <A>(TCA: ToComponent<A>): ToComponent<Tree<Comment>> => ({
render: treeA => <div class="node">
{TCA.render(treeA.value)}
<div class="inset-relative-to-parent">
{treeA.children.map(treeToComponent(TCA).render)}
</div>
</div>
});
const renderComments =
<F extends URIS>(TCF: ToComponent<Kind<F, Comment>>) =>
(comments: Kind<F, Comment>) => TCF.render(comments);
...
// - :
const commentArray: Comment[] = getFlatComments();
renderComments(arrayToComponent(commentToComponent))(commentArray);
// ... , :
const commentTree: Tree<Comment> = getCommentHierarchy();
renderComments(treeToComponent(commentToComponent))(commentTree);
, TypeScript :
- , , , .
- , , «» . , /instance β .
- UPPER_SNAKE_CASE, camelCase . , , β $tyled_like_php, .
fp-ts
, , «» .
Functor (fp-ts/lib/Functor)
map : <A, B>(f: (a: A) => B) => (fa: F<A>) => F<B>
, :
- -
F
,A => B
F<A>
,F<B>
. -
A => B
F
,F<A> => F<B>
.
, , , , -. , β - .
:
- :
map(id) β‘ id
- :
map(compose(f, g)) β‘ compose(map(f), map(g))
, β , , , , , Functor map
.
Monad (fp-ts/lib/Monad)
, , , railway, . , . , !
Β«1-2-3Β»: 1 , 2 3 :
- β , Array, List, Tree, Option, Reader .. β , , .
- , β
chain
join
,of
:
- :
of : <A>(value: A) => F<A> chain : <A, B>(f: (a: A) => F<B>) => (fa: F<A>) => F<B>
- :
of : <A>(value: A) => F<A> join : <A>(ffa: F<F<A>>) => F<A>
- :
- , :
- :
chain(f)(of(a)) β‘ f(a)
- :
chain(of)(m) β‘ m
- :
chain(g)(chain(f)(m)) β‘ chain(x => chain(g)(f(x)))(m)
- :
of
pure
, chain
>>=
( Β«bindΒ»):
- :
pure a >>= f β‘ f a
- :
m >>= pure β‘ m
- :
(m >>= f) >>= g β‘ m >>= (\x -> f x >>= g)
, , , . : type Reader<R, A> = (env: R) => A
.
, , , . , β , , . , (property-based testing).
. chain
: «» F
A
, β , A
, B
. A
F<A>
, F
. β Promise<A>
, A
«» . , , .
- β do-, for comprehension, β TS . - , Do fp-ts-contrib. .
Monoid (fp-ts/lib/Monoid)
:
- , /unit:
empty : A
- :
combine : (left: A, right: A) => A
3 :
- :
combine(empty, x) β‘ x
- :
combine(x, empty) β‘ x
- :
combine(combine(x, y), z) β‘ combine(x, combine(y, z))
? β , , . , Β«Monoids, monoids, monoidsΒ». Scala, β .
β , Foldable/Traversable , - ; Applicative ( , ) ; Task/TaskEither/Future , . . , .