fp-ts:ReaderTaskEither를 사용하여 typescript에서 함수 프로그래밍

32529 단어 functionaltypescript
본고는 fp-ts 라이브러리에서 함수 프로그래밍을 사용하여 typescript에서 비동기 요청을 하는 방법을 보여 준다.
만약 우리가 HTTP 요청을 보내고 싶다면이로써 우리는 적어도 다음과 같은 몇 가지를 안다.
  • HTTP 클라이언트가 전화를 걸어야 합니다.
  • Promise 우리는 하나를 처리해야 한다describe.
  • 우리는 오류를 예측해야 한다.
  • 이번에는 이 세 가지를 다시 한 번 말씀드리겠습니다.
  • 의존 관계.
  • 비동기 호출.
  • 실수할 수 있는 일.
  • 함수식 프로그래밍에서, 우리는 일부 데이터 형식을 사용하여 이러한 유형의 복잡성을 처리한다.구체적인 사례는 다음과 같습니다.

  • Reader - 의존항 주입에 사용됩니다.

  • Task - 비동기적인 사물에 쓰인다.

  • Either - 실수할 수 있는 일에 대해.
  • 우리는 스스로 이 리스트들을 창작할 수 있지만 fpts는 우리에게 기존의 유용한 작품을 제공했다. 예를 들어ReaderTaskEither.이것은 임무를 포함하는 리더입니다. 이 임무는 임무를 포함합니다.우리가 그들의 작문에 대해 무엇을 할 수 있는지 검사해 봅시다.
    주의: Reader<A, B> 블록을 포함하는 코드 세그먼트는 Jest 또는 Jasmine로 실행할 수 있습니다.

    1. 독자AB에서 HttpClient까지의 함수일 뿐이다.그러나 이런 유형의 별명을 가지는 것은 매우 유용하다. 이것은 우리가 의존 주입을 하고 싶다는 것을 의미한다.
    import * as R from 'fp-ts/lib/Reader'
    
    describe('Reader', () => {
      it('should just be a function', () => {
        const myReader: R.Reader<number, string> = (a: number) => String(a)
        expect(myReader(15)).toBe('15')
      })
    }
    
    의존 주입을 알면, 클래스 실례를 구조 함수에 주입해서 의존 트리를 만들고, 맨 윗부분 함수에서 파라미터를 보내는 데 익숙해질 수 있습니다. 이 함수는 트리를 두루 돌아다닐 것입니다.이것은 의존항을 먼저 주입한 다음에 함수 파라미터를 전달하는 것을 의미한다.함수식 프로그래밍에서 상황은 정반대이다.외부 의존 함수를 만들기 위해 우선 매개 변수를 주입합니다.오직 이렇게 해야만 우리는 이 의존항을 보내서 파이프를 실행할 수 있다.이 작업이 실제로 어떻게 작동하는지 살펴보겠습니다.
    유형Responseasks<A, B>(f)는 가설입니다.
    // imperative example
    class MyController {
      private HttpClient client
      constructor(HttpClient client) {
        this.client = client
      }
    
      async fetch(path: string): Response {
        return await this.client.get(path)
      }
    }
    
    // ...
    
    const controller = new MyController(new HttpClient()) // inject dependencies
    const res: Response = await controller.fetch('events') // pass parameters and execute
    
    // functional example
    import * as R from 'fp-ts/Reader'
    
    // ...
    
    const fetch = (url: string) =>
          R.asks((client: HttpClient) => client.get(url))
    const fetchEvents = fetch('events') // pass parameters
    const res: Response = await fetchEvents(new HttpClient() // inject depdencies and execute
    
    A는 카드 리더기를 되돌려줍니다. 이 카드 리더기는 f를 매개 변수로 B에 제공하고 Task는 반드시 되돌려야 합니다Promise.
    Reader monad에 대한 자세한 내용은 이 문서에서 확인할 수 있습니다.

    A note on pipelines

    That last example was straightforward, but operations like that involve more logic before and after our fetch function. In functional programming we use pipelines to compose functions for more complicated operations. With fp-ts we can use pipe or flow for that. We can rewrite fetchEvents as follows:

    // with pipe
    const fetchEvents = pipe('events', fetch)
    
    // with flow
    const fetchEvents = flow(fetch)('events')
    

    With pipe and flow we can easily put more functions in:

    • Put them in front of fetch for things we want to do to the > events parameter before calling fetch.
    • Put them after fetch for things we want to do to the result of calling fetch.


    2. 임무
    APromise는 ATask를 되돌려 주는 함수다.이렇게 하면 우리는 우리가 호출할 때까지 집행을 연기할 수 있다. Either
    import * as T from 'fp-ts/lib/Task'
    
    describe('Task', () => {
      it('of should work', async () => {
        // Arrange
        const mytask = T.of('abc')
        // Act
        const result = await mytask()
        // Assert
        expect(result).toEqual('abc')
      })
    
      it('should work with promise', async () => {
        // Arrange
        const mytask: T.Task<string> = () => Promise.resolve('def')
        // Act
        const result = await mytask()
        // Assert
        expect(result).toEqual('def')
      })
    })
    

    3. 또는left는 실패를 예측하는 방법이다.그것은 두 가지 예가 있다.rightleft-right는 고장, Either는 모든 것이 정상임을 나타낸다.
    import * as E from 'fp-ts/lib/Either'
    
    describe('Either', () => {
      it('of should create a right', () => {
        // Act
        const myEither = E.of('abc')
        // Assert
        expect(myEither).toEqual(E.right('abc'))
      })
    })
    
    fromNullable 일부 조수 구조 함수, 예를 들어 null 매개 변수가 undefined 또는 TaskEither라면left를 되돌려줍니다.
    import * as E from 'fp-ts/lib/Either'
    
    describe('Either.fromNullable', () => {
      const errorMessage = 'input was null or undefined'
      const toEither = E.fromNullable(errorMessage)
    
      it('should return right with string', async () => {
        // Arrange
        const expected = 'abc'
        // Act
        const myEither = toEither(expected)
        // Assert
        expect(myEither).toEqual(E.right(expected))
      })
    
      it('should return left with undefined', async () => {
        // Act
        const myEither = toEither(undefined)
        // Assert
        expect(myEither).toEqual(E.left(errorMessage))
      })
    })
    

    4.Task+other=taskotherEitherTask 내부tryCatch로 실패할 수 있는 비동기식 조작임을 의미한다.이것은 우리에게 유용한 함수left를 제공했다. 두 가지 인자를 받아들인다. 첫 번째는 약속한 함수를 되돌려주는 것이고, 두 번째는 거절당한 결과를 String에 비추는 함수이다. 이 예에서 우리는 Reader 구조 함수를 사용할 것이다.
    import * as TE from 'fp-ts/lib/TaskEither'
    import * as E from 'fp-ts/lib/Either'
    
    describe('TaskEither', () => {
      it('should work with tryCatch with resolved promise', async () => {
        // Arrange
        const expected = 135345
        // Act
        const mytask = TE.tryCatch(() => Promise.resolve(expected), String)
        const result = await mytask()
        // Assert
        expect(result).toEqual(E.right(expected))
      })
    
      it('should work with tryCatch with rejected promise', async () => {
        // Arrange
        const expected = 'Dummy error'
        // Act
        const mytask = TE.tryCatch(() => Promise.reject(expected), String)
        const result = await mytask()
        // Assert
        expect(result).toEqual(E.left(expected))
      })
    })
    
    Click hereTask,other,taskother에 관한 광범위한 문장을 읽습니다.

    5. 카드 리더기 + 작업 + 임의 선택 = 카드 리더기 작업
    이제 이 부분을 함께 놓고 기능적인 데이터 구조를 만들어서 HTTP 요청을 할 수 있습니다.천진난만한 방법부터 시작하자.
    import * as TE from 'fp-ts/lib/TaskEither'
    
    const client = new HttpClient()
    const myTryCatch = TE.tryCatch(() => client.get('events'), String)
    
    우리는 currying 을 사용하여 매개 변수화된 클라이언트와 URL을 재구성합니다.
    const myTryCatch = (url: String) => (client: Client) => TE.tryCatch(() => client.get(url), String)
    
    이 함수의 유형을 설명합니다.
    type MyTryCatch = (url: string) => (client: HttpClient) => TE.TaskEither<string, Response>
    const myTryCatch: MyTryCatch = (url) => (client) => TE.tryCatch(() => client.get(url), String)
    
    현재 우리는 Reader를 사용하여 형식을 다시 쓸 것이다.
    type MyTryCatch = (url: string) => Reader<HttpClient, TE.TaskEither<string, Response>>
    
    네가 짐작할 수 있는 바와 같이 aTaskEither가 aReaderTaskEither로 돌아오면 aHttpClient:
    import * as RTE from 'fp-ts/lib/ReaderTaskEither'
    
    type MyTryCatch = (url: string) => RTE.ReaderTaskEither<HttpClient, string, Response>
    
    이것은 하나의 유형으로 당신이 알아야 할 봉인에 대한 모든 논리를 알려줍니다.
  • 의존string
  • 오류가 Response로 포맷됨
  • 이 성공적으로 실행되면 Reader

  • 움직이는 거 봤어요.asks의 장점은 우리가 ReaderTaskEither를 사용하여 파이프의 어느 위치에서든 의존 관계를 얻을 수 있다는 점이다. 이 점은 본고에서 이미 논의한 바와 같다.이것이 바로 그것의 작업 원리이다.
    import * as RTE from 'fp-ts/lib/ReaderTaskEither'
    import * as TE from 'fp-ts/lib/TaskEither'
    import * as R from 'fp-ts/lib/Reader'
    
    // ...
    
    pipe(
      // ...functions leading up to a ReaderTaskEither containing a userId
      RTE.chain((userId) =>
        R.asks((client: HttpClient) => TE.tryCatch(() => client.get(`user/${userId}`), String))
      )
      // ...functions handling the response
    )
    
    일반적으로 이 함수를 추출하여 파이프를 더 읽을 수 있도록 합니다.
    pipe(
      // ...
      RTE.chain(fetchUser)
      // ...
    )
    
    // ...
    
    const fetchUser = (userId: string) => (client: HttpClient) => TE.tryCatch(() => client.get(`user/${userId}`), String))
    

    If chain is unfamiliar to you, it takes a function that maps whatever the monad is holding and returns a monad of something else. In this example it is assumed we start with a ReaderTaskEither of a string userId and we chain it to a ReaderTaskEither of a Response. For more information about monads and chain in fp-ts checkout



    결론
    typescript로 함수 프로그래밍을 하는 것은 필수적이지 않기 때문에 하나의 학과이다.비동기적인 동작을 강요하는 것은 없지만, 보상은 함수입니다. 어떤 파이프에서든 사용할 수 있고, 서명을 통해 그 기능을 성실하게 설명할 수 있습니다.그것 또한 매우 좋은 테스트 가능성을 가지고 있다. 사용Reader을 통해 우리는 우리가 관심을 가지는 주어진 테스트보다 더 많은 의존성을 필요로 하거나 필요로 하지 않을 수 있는 유형을 실례화할 필요가 없다.
    자세한 내용은 official documentation를 참조하십시오.

    좋은 웹페이지 즐겨찾기