2

작업에는 몇 단계가 있습니다. 각 단계의 입력이 직접 마지막 단계에서만 나온다면 쉽습니다. 그러나 더 자주, 일부 단계는 직접 마지막 단계에만 의존하지 않습니다.folktale2에서 함수형 프로그래밍 javascript를 사용하여 이전 작업의 결과에 정상적으로 액세스하는 방법?

여러 가지 방법으로 해결할 수 있지만 모두 못생긴 중첩 코드로 끝납니다. 더 나은 방법을 찾는 데 도움이 될 수 있기를 바랍니다. (-> 작업 연결())

  • 찾기 계정 (연결 -> 작업

    1. GET 데이터베이스 연결 :

      내가 보여주기 위해 다음의 로그인 절차와 같은 예를 작성하는 과정은 다음과 같은 3 단계가 계정)
    2. 토큰 (연결을 생성 -> 계정 아이디 -> 작업 토큰)

    #의 STEP3 단계 # 2에뿐만 아니라 중요하지만, 더불어 # 1 단계.

    아래 folktale2

    import {task, of} from 'folktale/concurrency/task' 
    import {converge} from 'ramda' 
    
    const getDbConnection =() => 
        task(({resolve}) => resolve({id: `connection${Math.floor(Math.random()* 100)}`}) 
    ) 
    
    const findOneAccount = connection => 
        task(({resolve}) => resolve({name:"ron", id: `account-${connection.id}`})) 
    
    const createToken = connection => accountId => 
        task(({resolve}) => resolve({accountId, id: `token-${connection.id}-${accountId}`})) 
    
    const liftA2 = f => (x, y) => x.map(f).ap(y) 
    
    test('attempt#1 pass the output one by one till the step needs: too many passing around', async() => { 
        const result = await getDbConnection() 
         .chain(conn => findOneAccount(conn).map(account => [conn, account.id])) // pass the connection to next step 
         .chain(([conn, userId]) => createToken(conn)(userId)) 
         .map(x=>x.id) 
         .run() 
         .promise() 
    
        console.log(result) // token-connection90-account-connection90 
    }) 
    
    test('attempt#2 use ramda converge and liftA2: nested ugly', async() => { 
        const result = await getDbConnection() 
         .chain(converge(
          liftA2(createToken), 
          [ 
           of, 
           conn => findOneAccount(conn).map(x=>x.id) 
          ] 
         )) 
         .chain(x=>x) 
         .map(x=>x.id) 
         .run() 
         .promise() 
    
        console.log(result) // token-connection59-account-connection59 
    }) 
    
    test('attempt#3 extract shared steps: wrong', async() => { 
        const connection = getDbConnection() 
    
        const accountId = connection 
        .chain(conn => findOneAccount(conn)) 
        .map(result => result.id) 
    
        const result = await of(createToken) 
        .ap(connection) 
        .ap(accountId) 
        .chain(x=>x) 
        .map(x=>x.id) 
        .run() 
        .promise() 
    
        console.log(result) // token-connection53-account-connection34, wrong: get connection twice 
    }) 
    
    • 시도 # 1이 옳다 사용하여 농담 단위 테스트하는,하지만 단계가 필요할 때까지 그 경우, 초기 단계의 출력을 통과해야 많은 단계를 거치며 매우 성가시다.

    • 시도 # 2도 마찬가지지만 중첩 된 코드로 끝납니다.

    • 시도 # 3을 (를) 좋아하지만, 변수를 사용하여 값을 보유하지만 불행히도 작동하지 않습니다.

    업데이트-1 내가 통과하는 상태로 모든 출력을 넣을 수있는 또 다른 방법을 생각,하지만 그 수도 매우 유사한 시도 # 1

    test.only('attempt#4 put all outputs into a state which will pass through', async() => { 
        const result = await getDbConnection() 
        .map(x=>({connection: x})) 
        .map(({connection}) => ({ 
         connection, 
         account: findOneAccount(connection) 
        })) 
        .chain(({account, connection})=> 
         account.map(x=>x.id) 
         .chain(createToken(connection)) 
        ) 
        .map(x=>x.id) 
        .run() 
        .promise() 
    
    
        console.log(result) //  token-connection75-account-connection75 
    }) 
    

    갱신-2 @ Scott의 do 접근 방식을 사용하여 다음 접근 방식에 매우 만족합니다. 그것은 짧고 깨끗합니다. 이것은 당신의 두 번째 시도와 유사하다

    const withConnection = connection => 
        findOneAccount(connection) 
         .map(x => x.id) 
         .chain(createToken(connection)) 
    
    getDbConnection().chain(withConnection) 
    

    하지만 후속 chain(identity)의 필요성을 제거 chain보다는 ap/lift를 사용한다, 다음과 같이

    test.only('attempt#5 use do co', async() => { 
        const mdo = require('fantasy-do') 
    
        const app = mdo(function *() { 
         const connection = yield getDbConnection() 
         const account = yield findOneAccount(connection) 
    
         return createToken(connection)(account.id).map(x=>x.id) 
        }) 
    
        const result = await app.run().promise() 
    
        console.log(result) 
    }) 
    
  • +1

    이전 질문의 결과에 액세스하는 방법이 맞습니까? 명시 적으로 상태 객체를 체인을 통해 전달하는 것으로 이렇게하면 그렇게 나쁘지 않습니다. 게다가 당신은'task'와'readerT' 모나드 변환기 (또는 당신이 돌연변이가 필요하다면'stateT')와 결합하여 그 객체로부터 추상화 할 수 있습니다. 그러나이 추상화가 노력할 가치가 있는지, Javacript의 프로토 타입 시스템으로 적절한 'readerT'를 구현할 수 있는지에 대해서는 확신 할 수 없습니다. – ftor

    +0

    @ftor, 나는'readT' 모나드를 이해하기 위해 몇 가지 haskell을 배워야한다고 생각한다. 아마도 중앙 집중화 된 상태를 좋아할 것이다. 나는'readerT'와'stateT'를 살펴볼 것입니다. 이 정보에 대해 감사드립니다. – Ron

    +0

    관련하여, [이전의 약속 결과를 .then() 체인에 어떻게 액세스합니까?] (https://stackoverflow.com/q/28250680/1048572) - 'Task's가 열심히 아니지만, 모나드 인터페이스와'parallel' 조합은 약속과 정확히 같습니다. – Bergi

    답변

    2

    귀하의 예를 작성할 수 있습니다. 당신이 원한다면 이것은 또한 converge을 사용하도록 업데이트 될 수 있습니다. 그러나 나는 그 과정에서 많은 양의 가독성을 잃어버린다고 느낍니다.

    const withConnection = R.converge(R.chain, [ 
        createToken, 
        R.compose(R.map(R.prop('id')), findOneAccount) 
    ]) 
    
    getDbConnection().chain(withConnection) 
    

    발전기를 사용하여 세 번째 시도와 비슷하게 업데이트 할 수도 있습니다. Do 함수의 다음 정의는 "do 구문"의 일부 형식을 제공하는 기존 라이브러리 중 하나로 대체 될 수 있습니다.

    // sequentially calls each iteration of the generator with `chain` 
    const Do = genFunc => { 
        const generator = genFunc() 
        const cont = arg => { 
        const {done, value} = generator.next(arg) 
        return done ? value : value.chain(cont) 
        } 
        return cont() 
    } 
    
    Do(function*() { 
        const connection = yield getDbConnection() 
        const account = yield findOneAccount(connection) 
        return createToken(connection)(account.id) 
    }) 
    
    +0

    입력 해 주셔서 감사합니다. 첫 번째 코드 세트는 깨끗하지만 중첩 된 것처럼 보이지만 두 번째 코드 세트는 읽기가 쉽지 않습니다. 마지막은 좋은 소리인데, 약속의 세계에서'공동 '을 좋아하는 사람은 없지만 흥미 롭습니다. – Ron

    +1

    @Ron 중첩 된 연속을 자신의'withConnection' 함수로 끌어 들이기 위해 예제를 편집했지만, 단지 내부적으로 중첩을 줄입니다. 나는 또한 당신을 위해'Do'의 간단한 구현을 포함 시켰습니다. 이미 https://github.com/jwoudenberg/fantasy-do와 https://github.com처럼이 기능을 이미 제공하고있는 기존 라이브러리가 있지만/pelotom/burrido –

    +0

    감사합니다. 업데이트 된 코드 세트가 중첩되어 있다고 느낄 지 모르지만 두 라이브러리로'do' 접근법을 사용하고 싶습니다. 짧고 깨끗합니다. 이미 전달 된 단위 테스트를 원래 게시물로 업데이트했습니다. – Ron