Fp ts P5 유틸리티: 적용, 시퀀스 및 반복


소개하다.
본 시리즈의 5부에 오신 것을 환영하며 실용적인 방식으로 fpts를 배웁니다.
지금까지 당신은 of, map, chain, flatten, ap에 소개되었지만 우리는 아직 ap 또는 apply에 대해 토론한 적이 없습니다.ap 연산자는 이른바 응용 프로그램의 큰 부분이다.응용 프로그램은 서열과 반복의 기초를 구성한다.
이 글에서, 나는 ap의 기본 원리, 그것의 용례, 그리고 우리가 실제로 그것을 필요로 하지 않는 이유를 설명할 것이다. 왜냐하면 우리는 서열과 반복이 있기 때문이다.

신청하다.
신비로운 map 연산자는 무엇입니까? 애플이라고도 부릅니까?
여러모로 write의 반대편과 같다.값 파이프를 함수로 만드는 것이 아니라 함수 파이프를 값으로 만드는 것이다.
이 점을 보여주기 위해 currying에 대해 알아보겠습니다.Currying은 매개 변수가 여러 개인 함수를 고급 함수로 변환하여 매개 변수를 반복적으로 사용합니다.
예를 들어 우리는 3개의 매개 변수를 받아들이는 true 함수를 가지고 있다.
declare function write(key: string, value: string, flush: boolean): unknown
다음과 같이 카레 함수로 변환할 수 있습니다.
const writeC = (key: string) => (value: string) => (flush: boolean) =>
  write(key, value, flush)
우리는 이렇게 간단하게 함수를 호출할 수 있다.
writeC('key')('value')(true)
만약 우리가 파이프 문법으로 같은 일을 하고 싶다면, 우리는 이런 방법을 시도해 볼 수 있다.
// ❌ Wrong
pipe(true, 'value', 'key', writeC)
그러나 불행하게도 파이프는 왼쪽에서 오른쪽으로 평가되기 때문에 효과가 없다.컴파일러는 value이 파이프를 통해 value key으로 전송할 수 없다고 불평할 것이다.이 점을 실현하기 위해서 우리는 더 많은 파이프를 사용하여 조작 순서를 집행해야 한다(수학에서처럼)!
// ✅ Correct
pipe(true, pipe('value', pipe('key', writeC)))
이제 컴파일러는 우리가 오른쪽에서 먼저 값을 구하도록 강요했기 때문에 알았다.그러나 이런 문법은 이상적이지 않다. 왜냐하면 주문을 위해 추가 파이프를 추가하는 것은 매우 짜증나기 때문이다.
솔루션은 ap입니다.
import { ap } from 'fp-ts/lib/Identity'

pipe(writeC, ap('key'), ap('value'), ap(true))
내가 ap은 단지 하나의 함수를 하나의 값으로 변환한다고 말한 것을 기억하십니까?이것이 바로 네가 여기서 본 것이다.writeC은 파이프를 통해 key에 연결되어 함수 (value: string) => (flush: boolean) => write(key, value, flush)을 형성한다.이 함수는 파이프를 통해 value에 연결되고 후자는 함수 (flush: boolean) => write(key, value, flush)을 구성한다.마지막으로 마지막 함수는 파이프를 통해 true으로 전송되며, 우리의 3개 파라미터를 호출하여 함수를 기록합니다: write(key, value, flush).
본질적으로 ap은 정확한 조작 순서를 유지하는 동시에 함수 값의 전환을 더욱 쉽게 실현할 수 있다.ap의 또 다른 용례는 함수와 값이 잘 협동하지 못할 때 그 중 하나가 Option 또는 Either에 갇혀 있기 때문이다.ap은 이 상황에서 매우 유용하다. 왜냐하면 값이나 함수를 특정 분류로 끌어올릴 수 있기 때문이다.
시범을 보이기 위해서 예를 하나 봅시다.
import * as O from 'fp-ts/lib/Option'
import { Option } from 'fp-ts/lib/Option'

declare const a: Option<number>
declare const b: Option<string>
declare function foo(a: number, b: string): boolean
보시다시피 우리는 변수 fooa을 사용하여 b을 호출하고자 합니다. 그러나 문제는 ab은 옵션 클래스에 있고 foo은 옵션 클래스에 있습니다.
간단한 값을 사용합니다.foo을 실행하는 간단한 방법은 chainmap을 사용하는 것이다.
// Option<boolean>
O.option.chain(a, (a1) => O.option.map(b, (b1) => foo(a1, b1)))
그러나 이것은 매우 무섭다. 왜냐하면:
  • 우리는 외부 변수에 음영을 주고 싶지 않기 때문에 서투르게 숫자 접미사로 변수를 명명해야 한다.
  • 더 많은 매개변수가 있으면 배율이 조정되지 않습니다.
  • 그것은 추하고 곤혹스럽다.
  • 다시 한번 해보자.
    우선, 우리는 foo을 하나의 통용 함수인 fooC으로 바꾸어야 한다.
    const fooC = (a: number) => (b: string) => foo(a, b)
    
    그리고 이것은 우리가 이전에 한 것과 같지만 fooC을 사용하여 Optionof 종류로 끌어올려야 한다. 왜냐하면 Optionap 버전은 두 가지 옵션에서 실행되어야 하기 때문이다.
    // Option<boolean>
    pipe(O.of(fooC), O.ap(a), O.ap(b))
    
    이 예를 한층 더 확장합시다.만약 우리가 또 다른 함수 bar을 가지고 있다면, 그것은 브리 값 (반환값 foo) 을 받아들이고, object을 되돌려준다.물론 우리는 foo을 호출한 다음에 bar을 호출하고 반환치는 foo이다.
    우리는 이미 fooOption<boolean>으로 계산했기 때문에 이것은 단지 ap으로 간단하게 향상시키는 과정일 뿐이다
    declare function bar(a: boolean): object
    
    const fooOption = pipe(O.of(fooC), O.ap(a), O.ap(b))
    
    // Option<object>
    pipe(O.of(bar), O.ap(fooOption))
    
    멋있다. ap은 분명히 강하다.그런데 ap에 무슨 문제가 있습니까?
    우선, fp를 사용하기 위해서는 기존의 모든 함수를 사용해야 하기 때문에 심심하다.
    그 다음에 파이프 내 함수의 입력 값을 왼쪽에서 오른쪽에서 오른쪽에서 왼쪽으로 전도하면 조작의 자연 순서 flow을 파괴할 수 있다.
    현실 세계에서 ap은 서열을 이용할 수 있기 때문에 거의 쓸모가 없다.1

    순서
    그럼 서열은 뭘까요?
    수학에서 우리는 하나의 서열을 하나의 숫자 서열로 본다.이와 유사하게 우리는 이를 일련의 옵션, 일련의 Eithers 등에 응용할 수 있다...
    서열에서 가장 흔히 볼 수 있는 용례는say 옵션의 그룹을 그룹으로 바꾸는 옵션입니다.
    // How?
    Array<Option<A>> => Option<A[]>
    
    이를 위해서는 Applicative의 실례를 제공해야 합니다.응용 프로그램에는 3가지 방법이 있는데 그것이 바로 of, map, ap이다.이 응용 프로그램은 컬렉션의 객체 유형을 정의합니다.Options의 목록에 대해 O.option을 제공합니다.
    import * as A from 'fp-ts/lib/Array'
    import * as O from 'fp-ts/lib/Option'
    
    const arr = [1, 2, 3].map(O.of)
    A.array.sequence(O.option)(arr) // Option<number[]>
    
    이제 우리는 문제로 돌아간다. 우리는 sequence를 어떻게 사용합니까? 그러면 우리는curried 함수를 작성하고 ap을 사용할 필요가 없습니다.sequenceT 을 입력합니다.

    SequenceTsequenceT은 일반적인 sequence과 같지만 rest parameter(vararg)을 전달했습니다.반환 값은 응용 프로그램이 제공하는 모듈을 형식 매개 변수로 합니다.
    예를 들면 다음과 같습니다.
    //  Option<[number, string]>
    sequenceT(O.option)(O.of(123), O.of('asdf'))
    
    이제 이게 어떻게 된 일인지 알겠네.우리는 파이프를 통해 그것을 최초의 foobar 함수로 가져올 수 있다.
    declare function foo(a: number, b: string): boolean
    declare function bar(a: boolean): object
    
    // Option<object>
    pipe(
      sequenceT(O.option)(O.of(123), O.of('asdf')),
      O.map((args) => foo(...args)),
      O.map(bar),
    )
    
    ... 확장 문법을 사용하여 원조를 매개 변수 형식으로 변환해야 합니다.

    순서
    때때로, 우리의 함수는 여러 개의 매개 변수가 아니라 하나의 대상 매개 변수만 받아들인다.이 문제를 해결하기 위해 우리는 sequenceS을 이용할 수 있다.
    import * as E from 'fp-ts/lib/Either'
    
    type RegisterInput = {
      email: string
      password: string
    }
    
    declare function validateEmail(email: string): E.Either<Error, string>
    declare function validatePassword(password: string): E.Either<Error, string>
    declare function register(input: RegisterInput): unknown
    
    declare const input: RegisterInput
    
    pipe(
      input,
      ({ email, password }) =>
        sequenceS(E.either)({
          email: validateEmail(email),
          password: validatePassword(password),
        }),
      E.map(register),
    )
    

    통과
    때때로 입력이 잘 맞지 않을 때가 있습니다. sequence을 적용하기 전에 추가 계산을 실행해야 합니다.두루 돌아다니는 것이 답이다.그것은 같은 일의 서열을 실행하지만, 우리는 중간 값을 바꾸게 한다.
    하나의 좋은 예는 파일 부분을 검색하는 네트워크 요청이다.너는 모든 부품을 원하든지, 아니면 하나도 원하지 않는다.
    import * as TE from 'fp-ts/lib/TaskEither'
    import { TaskEither } from 'fp-ts/lib/TaskEither'
    import * as A from 'fp-ts/lib/Array'
    
    declare const getPartIds: () => TaskEither<Error, string[]>
    declare const getPart: (partId: string) => TaskEither<Error, Blob>
    
    // ✅ TE.TaskEither<Error, Blob[]>
    pipe(getPartIds(), TE.chain(A.traverse(TE.taskEither)(getPart)))
    

    결론
    이 글에서 우리는 애플과 그 용례, 그리고 그것을 어떻게 서열과 반복을 통해 우리의 생활에 응용하는지 이해했다.
    읽어주셔서 감사합니다. 만약에 이 내용을 좋아하신다면 저에게 후속을 주십시오. 문제가 있으면 저에게 DM을 주세요!
    시퀀스와 스트리밍은 내부에서 ap을 사용합니다. 

    좋은 웹페이지 즐겨찾기