fp-ts 시작하기: IO

19833 단어 functionaltypescript
fp-ts에서 동기식 유효 계산은 기본적으로 썽크인 IO 유형, 즉 다음 서명이 있는 함수로 표시됩니다. () => A
interface IO<A> {
  (): A
}

IO는 절대 실패하지 않는 계산을 나타냅니다.

이러한 계산의 예는 다음과 같습니다.
  • 읽기/쓰기 localStorage
  • 현재 시간 가져오기
  • console에 쓰기
  • 난수 얻기

  • 예(localStorage에 읽기/쓰기)

    import { fromNullable, Option } from 'fp-ts/Option'
    
    const getItem = (key: string): IO<Option<string>> => () =>
      fromNullable(localStorage.getItem(key))
    
    const setItem = (key: string, value: string): IO<void> => () =>
      localStorage.setItem(key, value)
    


    예(현재 시간 가져오기)

    const now: IO<number> = () => new Date().getTime()
    


    예(console에 작성)

    const log = (s: unknown): IO<void> => () => console.log(s)
    


    예(난수 가져오기)

    const random: IO<number> = () => Math.random()
    

    IO 유형은 인스턴스를 허용하므로 map ...

    import { io } from 'fp-ts/IO'
    
    /** get a random boolean */
    const randomBool: IO<boolean> = io.map(random, n => n < 0.5)
    


    ...또는 chain 계산

    /** write to the `console` a random boolean */
    const program: IO<void> = io.chain(randomBool, log)
    
    program()
    

    program() 를 호출할 때까지 아무 일도 일어나지 않습니다.
    program는 효과적인 계산을 나타내는 값이므로 부작용을 실행하려면 "IO 작업을 실행"해야 합니다.
    IO 작업은 값일 뿐이므로 이를 처리하는 것과 같은 유용한 추상화를 사용할 수 있습니다.

    예(던전 앤 드래곤)

    import { log } from 'fp-ts/Console'
    import { getMonoid, IO, io } from 'fp-ts/IO'
    import { fold, Monoid, monoidSum } from 'fp-ts/Monoid'
    import { randomInt } from 'fp-ts/Random'
    
    type Die = IO<number>
    
    const monoidDie: Monoid<Die> = getMonoid(monoidSum)
    
    /** returns the sum of the roll of the dice */
    const roll: (dice: Array<Die>) => IO<number> = fold(monoidDie)
    
    const D4: Die = randomInt(1, 4)
    const D10: Die = randomInt(1, 10)
    const D20: Die = randomInt(1, 20)
    
    const dice = [D4, D10, D20]
    
    io.chain(roll(dice), result => log(`Result is: ${result}`))()
    /*
    Result is: 11
    */
    


    ..또는 유용한 정의

    /** Log any value to the console for debugging purposes */
    const withLogging = <A>(action: IO<A>): IO<A> =>
      io.chain(action, a => io.map(log(`Value is: ${a}`), () => a))
    
    io.chain(roll(dice.map(withLogging)), result => log(`Result is: ${result}`))()
    /*
    Value is: 4
    Value is: 2
    Value is: 13
    Result is: 19
    */
    


    오류 처리



    실패할 수 있는 효과적인 동기식 계산을 나타내려면 어떻게 해야 합니까?

    두 가지 효과가 필요합니다.


    유형 생성자
    효과(해석)

    IO<A>동기식 유효 계산
    Either<E, A>실패할 수 있는 계산


    해결책은 Either 안에 IO 를 넣으면 IOEither 유형이 됩니다.

    interface IOEither<E, A> extends IO<Either<E, A>> {}
    

    IOEither<E, A> 유형의 값을 "실행"하면 Left 유형의 오류로 계산이 실패했음을 의미하고 E 그렇지 않으면 Right를 얻습니다. 유형의 값A .

    예(파일 읽기)
    fs.readFileSync가 던질 수 있으므로 tryCatch 도우미를 사용하겠습니다.

    tryCatch: <E, A>(f: () => A) => IOEither<E, A>
    


    여기서 f 는 오류( tryCatch 에 의해 자동으로 포착됨)를 발생시키거나 A 유형의 값을 반환하는 썽크입니다.

    import { toError } from 'fp-ts/Either'
    import { IOEither, tryCatch } from 'fp-ts/IOEither'
    import * as fs from 'fs'
    
    const readFileSync = (path: string): IOEither<Error, string> =>
      tryCatch(() => fs.readFileSync(path, 'utf8'), toError)
    
    readFileSync('foo')() // => left(Error: ENOENT: no such file or directory, open 'foo')
    readFileSync(__filename)() // => right(...)
    


    리프팅


    fp-ts/IOEither 모듈은 IOEither 유형의 값을 생성할 수 있는 다른 도우미를 제공하며, 이를 집합적으로 리프팅 함수라고 합니다.

    다음은 요약입니다.


    시작 값
    리프팅 기능

    IO<E>leftIO: <E, A>(ml: IO<E>) => IOEither<E, A>Eleft: <E, A>(e: E) => IOEither<E, A>Either<E, A>fromEither: <E, A>(ma: Either<E, A>) => IOEither<E, A>Aright: <E, A>(a: A) => IOEither<E, A>IO<A>rightIO: <E, A>(ma: IO<A>) => IOEither<E, A>

    예(임의 파일 로드)

    세 파일( 1.txt , 2.txt , 3.txt ) 중 하나의 내용을 무작위로 로드한다고 가정해 보겠습니다.
    randomInt: (low: number, high: number) => IO<number> 함수는 닫힌 간격에 균일하게 분포된 임의의 정수를 반환합니다[low, high].

    import { randomInt } from 'fp-ts/Random'
    


    위에서 정의한 randomInt 함수와 readFileSync를 연결할 수 있습니다.

    import { ioEither } from 'fp-ts/IOEither'
    
    const randomFile = ioEither.chain(
      randomInt(1, 3), // static error
      n => readFileSync(`${__dirname}/${n}.txt`)
    )
    


    그러나 이것은 유형 검사가 아닙니다!

    유형이 정렬되지 않음: randomIntIO 컨텍스트에서 실행되고 readFileSyncIOEither 컨텍스트에서 실행됩니다.

    그러나 randomInt를 사용하여 IOEitherrightIO 컨텍스트로 들어올릴 수 있습니다(위의 요약 참조).

    import { ioEither, rightIO } from 'fp-ts/IOEither'
    
    const randomFile = ioEither.chain(rightIO(randomInt(1, 3)), n =>
      readFileSync(`${__dirname}/${n}.txt`)
    )
    

    좋은 웹페이지 즐겨찾기