몬나드가 뭐라고 했어?(첫 부분)

최근 함수식 프로그래밍과 구도의 시끄러운 소리가 끊이지 않고 있다. FunctorMonad 같은 용어는 당신의 요점을 넘어섰을지도 모른다. "이 사람들이 도대체 무슨 말을 하는지 알고 싶다."이러한 이상한 이름과 더 많은 외국 해석이 대수(추상적인 유형)와 범주 이론을 깊이 이해해야 하기 때문에 이러한 유형의 구조가 우리의 편안한 일상 자바스크립트 코드에 어떻게 적합한지 알아보기 어려울 것이다.
이 시리즈에서는 JavaScript 프로그래머의 관점에서 aMonad가 무엇인지, 그리고 일상 코드에서 어떻게 사용하는지 연구할 것이다.우리는 배후의 이론이 아니라 이런 유형의 용법에 주목할 것이다.
예를 들어 다음과 같은 정의를 이해하려고 노력하는 것과 같다.

A Monad is a Monoid in the Category of Endofunctors


우리는 더욱 실용적인 정의를 이해하기 위해 노력할 것이다.

A Monad is a data type that allows for sequential application of its "effects" or "embellishments" while mapping its underlying data.


현재 두 번째 정의는 아직 명확하지 않겠지만, 나는 이 단어들이 어떻게 조합되어 왔는지 이해하기 위해 노력하는 것이 이해하기 쉽다고 생각한다.
우리가 자신의 유형을 만들기 위해 모험을 할 때, 첫 번째 정의를 이해하는 것이 중요하다.만약 당신과 내가 비슷한 점이 있다면, 나는 손을 더럽히는 것을 좋아하지만, 먼저 놀고, 그리고 그것을 어떻게 사용하는지에 대해 좋은 직감을 가지고 있을 때, 이론을 활용하여 이해를 쌓는 것을 좋아한다.일련의 유형이 이미 야외에서 이루어져서 우리는 즐겁게 놀 수 있다...그들의 배후의 수학을 모른다.
이 게시물들은 자바스크립트 언어뿐만 아니라 자바스크립트에서'currying','partial application','function composition'을 어떻게 실현하는지 이해해야 한다.만약 네가 이 화제들이 좀 모호하다고 생각한다면, 사이트에 너를 도와 정리할 수 있는 많은 자원이 있다.
따라서 더 많은 번거로움이 필요 없으니 해결을 시작합시다.

섹션 1: 대수 데이터 유형(ADT)
많은 사람들이'나는 이것Monad으로 이것을 하거나 그것Monad으로 그것을 한다'고 말할 때 진정한 뜻은'나는 이 대수 데이터 형식(ADT)으로 이것, 저ADT로 저것을 한다'는 것이다.그들이 보여준 코드를 보면, 그들이 유형Monad 부분에 닿지 않았거나, 어떤 경우에는 유형이 심지어 그렇지 않은 경우Monad를 발견할 수 있습니다.
나는 먼저 나와 이 논쟁점을 해명하고 싶다.이것은 작은 일인 것 같지만, 우리가 Monad와 데이터 유형에 대한 다른 방면의 초기 직각을 세우기 시작할 때, 우리가 진정 일부ADT를 가리킬 때, 사물을 aMonad라고 부르는 것은 종종 혼동을 초래할 수 있다는 것을 나는 발견했다.
대수 데이터 유형 aMonad가 도대체 어떤 구성인지 이해하기 전에 ADT가 무엇인지 알아야 한다.내가 생각할 수 있는 이 화제를 제기하는 가장 좋은 방법은ADT가 무엇인지에 대한 간단한 정의를 제공하는 것이다.그리고 자바스크립트의 ADT를 사용하여 더 익숙한 명령식 구현과 비교하는 방법을 보여 줍니다.
다음 예제에서 처리할 데이터를 살펴보겠습니다.
// data :: [ * ]
const data = [
  { id: '9CYolEKK', learner: 'Molly' },
  null,
  { id: 'gbdCC8Ui', learner: 'Thomas' },
  undefined,
  { id: '1AceDkK_', learner: 'Lisa' },
  { id: 3, learner: 'Chad' },
  { gid: 11232, learner: 'Mitch' },
]
데이터는 혼합Array으로 모든 종류의 값을 포함할 수 있다.이 특정한 실례에서 우리는 세 가지 유형이 있는데 그것이 바로 POJO 서로 다른 형태의 s(일반적인ol'JavaScript 대상), 하나Null 실례와 하나Undefined 실례이다.
우리의 예제에서는 다음 요구 사항 목록을 사용하여 정의합니다.
  • 입력할 때 모든 종류의 값을 수락합니다.
  • 데이터가 최소한 유효한 기록이 있는 경우Array가 아니면 비어 있는 경우Object로 돌아갑니다.
  • 포함된 기록에서 Object개의 유효 기록을 되돌려주고 유효id키로 입력하여 모든 무효 기록을 효과적으로 필터합니다.
  • 유효기록은 Object 키가 있는 Stringid로 정의합니다.
  • 입력이 무엇이든지 이 함수는 던져지지 않으며 되돌아오는 빈 함수Object에 합리적인 기본값을 제공한다.
  • 이러한 요구 사항에 따라 다음과 같은 명령어 기능을 사용할 수 있습니다.
  • 입력이 Array인지 확인하고
  • 이 아니면 공백Object으로 돌아갑니다.
  • 최종 결과를 생성하기 위해 result 누적기를 설명하고 기본값을 비워 둡니다Object.
  • 중복 제공Array 및 각 항목에 대해 다음을 수행합니다.
  • Dell 레코드 표준
  • 에 따라 프로젝트 검증
  • 통과할 경우 레코드를 결과에 추가하고 레코드의 id 값을 입력합니다.안 그러면 아무것도 안 해.
  • 이 되돌아오다result.
  • 몇 명의 조수가 우리가 몇 가지 유형 검사를 진행하는 것을 도와주었는데, 우리는 다음과 같은 실현을 제공할 수 있다.
    // isArray :: a -> Boolean
    const isArray =
      Array.isArray
    
    // isString :: a -> Boolean
    const isString = x =>
      typeof x === 'string'
    
    // isObject :: a -> Boolean
    const isObject = x =>
      !!x && Object.prototype.toString.call(x) === '[object Object]'
    
    // indexById :: [ * ] -> Object
    function indexById(records) {
      if (!isArray(records)) {
        return {}
      }
    
      let result = {}
    
      for (let i = 0; i < records.length; i++) {
        const rec = records[i]
    
        if (isObject(rec) && isString(rec.id)) {
          result[rec.id] = rec
        }
      }
    
      return result
    }
    
    indexById(null)
    //=> {}
    
    indexById([])
    //=> {}
    
    indexById([ 1, 2, 3 ])
    //=> {}
    
    indexById(data)
    //=> {
    //   9CYolEKK: { id: '9CYolEKK', learner: 'Molly' },
    //   gbdCC8Ui: { id: 'gbdCC8Ui', learner: 'Thomas' },
    //   1AceDkK_: { id: '1AceDkK_', learner: 'Lisa' }
    // }
    
    보시다시피, 우리는 우리의 수요를 충족시키고, 우리가 제공한 모든 입력에 예상대로 응답할 수 있는 강력한 실현을 가지고 있습니다.
    우리의 ADT 실현에 대해 우리는 crocks 라이브러리에 매우 의존할 것이다.비록 JavaScript는 기능이 모두 갖추어진 프로그래밍 언어이지만, 이 언어들은 통용 언어가 아니라 엄격한 기능성 언어에 나타나는 구조가 부족하다.따라서 crocks 와 같은 라이브러리는 보통 ADT를 처리하는 데 쓰인다.
    다음은 ADT를 사용하여 요구 사항을 구현하는 것입니다.
    const {
      Assign, Maybe, composeK, converge, isArray,
      isObject, isString, liftA2, mreduceMap, objOf,
      prop, safe
    } = require('crocks')
    
    // wrapRecord :: Object -> Maybe Object
    const wrapRecord = converge(
      liftA2(objOf),
      composeK(safe(isString), prop('id')),
      Maybe.of
    )
    
    // mapRecord :: a -> Object
    const mapRecord = record =>
      safe(isObject, record)
        .chain(wrapRecord)
        .option({})
    
    // indexById :: [ * ] -> Object
    const indexById = records =>
      safe(isArray, records)
        .map(mreduceMap(Assign, mapRecord))
        .option({})
    
    indexById(null)
    //=> {}
    
    indexById([])
    //=> {}
    
    indexById([ 1, 2, 3 ])
    //=> {}
    
    indexById(data)
    //=> {
    //   9CYolEKK: { id: '9CYolEKK', learner: 'Molly' },
    //   gbdCC8Ui: { id: 'gbdCC8Ui', learner: 'Thomas' },
    //   1AceDkK_: { id: '1AceDkK_', learner: 'Lisa' }
    // }
    
    나는 모두가 이 두 가지 실현 사이의 차이 중 하나가 ADT 실현에 익숙한 흐름 제어와 논리 모델이 부족하다는 것을 알아차리기를 바란다.두 번째 실현에서 for 순환과 if 문장은 한 번도 나타나지 않는다.그것들은 여전히 존재한다. 물론 존재하지만, ADT를 사용할 때, 우리는 이러한 흐름/논리를 특정한 유형으로 인코딩한다.
    예를 들어 safe 함수는 몇 곳에서 사용합니까?이 호출된 첫 번째 매개 변수에 전달되는 술어 함수를 봐라.여기서도 같은 검사를 했지만 if 함수가 아니라 safe 함수를 사용했습니다. 이 함수는 Maybe라는 ADT를 되돌려줍니다.
    두 번째 실현 중 어느 곳에도 상태가 부족하다는 것을 알아차릴 수 있습니다.선언된 각 변수는 JavaScript 값이 아닌 함수입니다.최초의 실현에서, 우리는 두 개의 상태 비트 result 를 사용하여 최종 결과와 rec 라는 작은 조수를 조합했다. 이것은 코드를 정리하는 것일 뿐, Array 의 색인 값을 인용할 필요가 없다.
    함수for를 사용하여 모든 기록을 result 유형에 접으면 우리는 mreduceMap 순환과 Assign 변수의 수요에서 벗어날 수 있다.Assignvanilla JavaScript에서 ObjectObject.assign 유사한 방식으로 추적resultObject 같은 누적기가 필요하지 않습니다.현재 우리는 누적된 방법이 생겨서 for제거mreduceMap순환에 의존할 수 있다.Maybe,Assign,접기 등은 이제 이해할 필요가 없다.내가 그것들을 언급한 것은 원시 실현 중의 모든 모델이 ADT 버전에 존재하고 여기에 마법이 없다는 것을 여러분에게 알려주고 싶었기 때문이다.ADT 인코딩을 사용할 때, 우리는 ADT에서 인코딩을 통해 축적, 논리, 흐름 제어, 상태 잡기 등 대량의 기계 위치를 제거하고, 유형이 모든 '파이프'를 처리하도록 합니다.
    내가 가장 듣기 싫은 것은 우리가 afluent api처럼 보이는 것을 어떻게 사용해서 우리의 조작을 함수mapRecordindexById에 연결하는지 하는 것이다.이러한 코드를 보면 우리는 전형적인 대상 프로그래머가 사용할 수 있는 것처럼 전통적인Object과 클래스를 사용하고 있다고 믿을 수 있다.방법이라고 불리는 이 조작 (모든 crocks 문서가 이렇게 되어 있음) 을 들었을 때, 그것은 심지어 강화되었다.이러한 직감적이고 오도적인 설명은 우리가 일상 코드에서 ADT를 사용하는 방식을 이해하는 데 방해가 될 수 있다.
    다음에 우리는 ADT의 사용을 더욱 깊이 연구하고 ADT가 어떻게 Objects가 아닌지 탐색할 것이다. 왜냐하면 대상을 대상으로 하는 프로그래머가 볼 수 있기 때문이다Object.

    오락성 단련
  • for에서 사용할 수 있는 reduce 방법을 사용하여 첫 번째 POJ(순수ol'JavaScript) 함수를 가져오고 삭제Array.prototype 순환을 가져옵니다.result 변수에 무슨 일이 일어났는지, 그리고 {}의 기본값이 어떻게 적용되는지 주의하십시오.
  • 첫 번째 POJ 함수를 예로 들면 타이머setTimeoutsetInterval를 사용하지 않은 상태에서 당신이 생각할 수 있는 가장 낮은 효과의 실현으로 재구성한다.재구성할 때 가장 효과가 없다고 생각하는 것이 무엇인지 생각해 보세요.
  • 첫 번째POJ 함수나 연습1의 재구성을 사용하여 그 자체 함수에 존재할 수 있는 이산 조작/변환을 확정한다.그리고 이 함수들을 만들고 주 함수를 재구성해서 사용하십시오.

  • 추가 단련(재미를 위해서)
  • 우리는 제3자 라이브러리의 유형 검사 술어 함수를 사용하여 유형 검사를 한다.우리가 사용하는 술어를 선택하여 사용자의 버전을 실현하고, 사용자의 구현에서 서로 다른 유형의 값을 던져서 예상한 대로 실행되는지 확인하십시오.
  • 공교롭게도 ramdalodash-fp 같은 라이브러리에 정통하면 익숙한 라이브러리만 사용하여 함수에서 같은 행동을 하십시오.함수 결과를 위의 ADT 버전의 다음 무점 버전과 비교합니다.
  • // wrapRecord :: Object -> Maybe Object
    const wrapRecord = converge(
      liftA2(objOf),
      composeK(safe(isString), prop('id')),
      Maybe.of
    )
    
    // mapRecord :: a -> Object
    const mapRecord = compose(
      option({}),
      chain(wrapRecord),
      safe(isObject)
    )
    
    // indexById :: [ * ] -> Object
    const indexById = records => compose(
      option({ error: true }),
      map(mreduceMap(Assign, mapRecord)),
      safe(isArray),
    )
    

    좋은 웹페이지 즐겨찾기