fp-ts 시작하기: 적용

27217 단어 functionaltypescript
게시물에서 우리는 f: (a: A) => F<B>이 functor 인스턴스를 허용하는 경우 g: (b: B) => C을 함수 g으로 들어 올려 순수한 프로그램 lift(g): (fb: F<B>) => F<C>으로 효과적인 프로그램 F을 구성할 수 있음을 보았습니다.


프로그램 f
프로그램 g
구성


순수한
순수한g ∘ f
효과적인
순수(단항)lift(g) ∘ f

그러나 g은 단항이어야 합니다. 즉, 하나의 인수만 입력으로 받아들여야 합니다. g이 두 개의 인수를 허용하면 어떻게 될까요? functor 인스턴스만 사용하여 여전히 g을 들어올릴 수 있습니까? 자, 시도해보자!

카레



먼저 BC(튜플을 사용할 수 있음) 유형의 두 인수를 받아들이고 D 유형의 값을 반환하는 함수를 모델링해야 합니다.

g: (args: [B, C]) => D


커링이라는 기술을 사용하여 g을 다시 쓸 수 있습니다.

Currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument. For example, a function that takes two arguments, one from B and one from C, and produces outputs in D, by currying is translated into a function that takes a single argument from C and produces as outputs functions from B to C.



(출처: currying on wikipedia.org )

따라서 g을 다음과 같이 다시 쓸 수 있습니다.

g: (b: B) => (c: C) => D


우리가 원하는 것은 리프팅 작업입니다. 이전 liftA2과 구별하기 위해 lift이라고 부르겠습니다. 이 작업은 다음 서명이 있는 함수를 출력합니다.

liftA2(g): (fb: F<B>) => (fc: F<C>) => F<D>


우리 거기 어떻게가? g은 이제 단항이므로 functor 인스턴스와 이전 lift을 사용할 수 있습니다.

lift(g): (fb: F<B>) => F<(c: C) => D>


그러나 이제 우리는 막혔습니다. 함수 F<(c: C) => D> 에 값 (fc: F<C>) => F<D> 을 압축 해제할 수 있는 functor 인스턴스에 대한 합법적인 작업이 없습니다.

적용하다



따라서 이러한 압축 풀기 작업(이름: Apply )을 소유하는 새로운 추상화 ap을 소개하겠습니다.

interface Apply<F> extends Functor<F> {
  ap: <C, D>(fcd: HKT<F, (c: C) => D>, fc: HKT<F, C>) => HKT<F, D>
}

ap 함수는 기본적으로 인수가 재정렬된 unpack입니다.

unpack: <C, D>(fcd: HKT<F, (c: C) => D>) => ((fc: HKT<F, C>) => HKT<F, D>)
ap:     <C, D>(fcd: HKT<F, (c: C) => D>, fc: HKT<F, C>) => HKT<F, D>


따라서 apunpack에서 파생될 수 있으며 그 반대도 마찬가지입니다.

참고: HKT 유형은 제네릭 유형 생성자를 나타내는 fp-ts 방식입니다(Lightweight higher-kinded polymorphism 문서에서 제안된 기술). 따라서 HKT<F, X>을 볼 때 유형 생성자 F450을 생각할 수 있습니다.

적용



또한 X 유형의 값을 F<X> 유형의 값으로 올릴 수 있는 작업이 있으면 편리합니다. 이런 식으로 AF<A> 유형의 인수를 제공하거나 liftA2(g)F<B> 유형의 값을 들어 올려 F<C> 함수를 호출할 수 있습니다.
B을 기반으로 하고 이러한 작업을 소유하는 C 추상화를 소개하겠습니다(이름: Applicative).

interface Applicative<F> extends Apply<F> {
  of: <A>(a: A) => HKT<F, A>
}


몇 가지 일반적인 데이터 유형에 대한 Apply 인스턴스를 살펴보겠습니다.

예( of )

import { flatten } from 'fp-ts/Array'

const applicativeArray = {
  map: <A, B>(fa: Array<A>, f: (a: A) => B): Array<B> => fa.map(f),
  of: <A>(a: A): Array<A> => [a],
  ap: <A, B>(fab: Array<(a: A) => B>, fa: Array<A>): Array<B> =>
    flatten(fab.map(f => fa.map(f)))
}


예( Applicative )

import { Option, some, none, isNone } from 'fp-ts/Option'

const applicativeOption = {
  map: <A, B>(fa: Option<A>, f: (a: A) => B): Option<B> =>
    isNone(fa) ? none : some(f(fa.value)),
  of: <A>(a: A): Option<A> => some(a),
  ap: <A, B>(fab: Option<(a: A) => B>, fa: Option<A>): Option<B> =>
    isNone(fab) ? none : applicativeOption.map(fa, fab.value)
}


예( F = Array )

import { Task } from 'fp-ts/Task'

const applicativeTask = {
  map: <A, B>(fa: Task<A>, f: (a: A) => B): Task<B> => () => fa().then(f),
  of: <A>(a: A): Task<A> => () => Promise.resolve(a),
  ap: <A, B>(fab: Task<(a: A) => B>, fa: Task<A>): Task<B> => () =>
    Promise.all([fab(), fa()]).then(([f, a]) => f(a))
}


리프팅



따라서 F = Option에 대한 F = Task의 인스턴스가 주어지면 이제 Apply을 쓸 수 있습니까?

import { HKT } from 'fp-ts/HKT'
import { Apply } from 'fp-ts/Apply'

type Curried2<B, C, D> = (b: B) => (c: C) => D

function liftA2<F>(
  F: Apply<F>
): <B, C, D>(g: Curried2<B, C, D>) => Curried2<HKT<F, B>, HKT<F, C>, HKT<F, D>> {
  return g => fb => fc => F.ap(F.map(fb, g), fc)
}


멋진! 그러나 세 개의 인수가 있는 함수는 어떻습니까? 또 다른 추상화가 필요합니까?

좋은 소식은 대답은 아니오, F이면 충분하다는 것입니다.

type Curried3<B, C, D, E> = (b: B) => (c: C) => (d: D) => E

function liftA3<F>(
  F: Apply<F>
): <B, C, D, E>(
  g: Curried3<B, C, D, E>
) => Curried3<HKT<F, B>, HKT<F, C>, HKT<F, D>, HKT<F, E>> {
  return g => fb => fc => fd => F.ap(F.ap(F.map(fb, g), fc), fd)
}


실제로 liftA2 의 인스턴스가 주어지면 각 Apply 에 대해 Apply 함수를 작성할 수 있습니다.

메모. liftAnn, liftA1 연산입니다.

이제 "구성 테이블"을 업데이트할 수 있습니다.


프로그램 f
프로그램 g
구성


순수한
순수한lift
효과적인
순수한, Functor -aryg ∘ f


어디 n

일반적인 문제가 해결되었습니까?



아직 아님. 여전히 중요한 경우가 빠져 있습니다. 두 프로그램이 모두 효과적이라면 어떻게 될까요?

다시 한 번 더 필요한 것이 있습니다. 이 게시물에서 함수형 프로그래밍의 가장 중요한 추상화 중 하나인 모나드에 대해 이야기하겠습니다.

TLDR: 함수형 프로그래밍은 구성에 관한 것입니다.

좋은 웹페이지 즐겨찾기