일상적인javascript에 사용되는 함수식 프로그래밍: 사용

Puedes leer la versión en español .


명세서 들어봤어? 얼마나 좋은데?네가 있을지도 모르지만, 너는 여전히 이해하지 못한다.좋아요.나는 여기서 너에게 그것들이 무엇인지 알려주는 것이 아니라, 나도 그것들을 너에게 팔려고 하지 않을 것이다. 내가 해야 할 일은 너에게 예를 보여주는 것이다. 만약 네가 자바스크립트에서 그것들을 사용한다면 어떤 모습일지.
우리는 불필요한 복잡한 방식으로 상당히 자질구레한 문제를 해결할 수 있는 재미있는 일을 할 것이다.
만약 우리가 json 파일이나 일반 js 대상에 사전을 저장했다고 가정해 보세요.
{
    "accident": ["An unexpected, unfortunate mishap, failure or loss with the potential for harming human life, property or the environment.", "An event that happens suddenly or by chance without an apparent cause."], 
    "accumulator": ["A rechargeable device for storing electrical energy in the form of chemical energy, consisting of one or more separate secondary cells.\\n(Source: CED)"],
    "acid": ["A compound capable of transferring a hydrogen ion in solution.", "Being harsh or corrosive in tone.", "Having an acid, sharp or tangy taste.", "A powerful hallucinogenic drug manufactured from lysergic acid.", "Having a pH less than 7, or being sour, or having the strength to neutralize  alkalis, or turning a litmus paper red."],

     // ... moar words and meanings

    "Paris": ["The capital and largest city of France."]
  }
사용자가 단어 중 하나를 검색하고 그 의미를 표시할 수 있도록 폼이 필요합니다.이것은 매우 간단하다, 그렇지?이 가능하다, ~할 수 있다,...
모든 사람들이 HTML을 좋아하기 때문에 우리는 여기서부터 시작한다.
<form id="search_form">
  <label for="search_input">Search a word</label>
  <input id="search_input" type="text">
  <button type="submit">Submit</button>
</form>

<div id="result"></div>
첫 번째 버전에서는 사용자 입력에 따라 이 값을 가져옵니다.
// main.js

// magically retrieve the data from a file or whatever
const entries = data();

function format(results) {
  return results.join('<br>'); // I regret nothing
}

window.search_form.addEventListener('submit', function(ev) {
  ev.preventDefault();
  let input = ev.target[0];
  window.result.innerHTML = format(entries[input.value]);
});
물론 우리가 검색을 시도한 첫 번째 일은'시다'이다이거 봐. 결과야.

A compound capable of transferring a hydrogen ion in solution.
Being harsh or corrosive in tone.
Having an acid, sharp or tangy taste.
A powerful hallucinogenic drug manufactured from lysergic acid.
Having a pH less than 7, or being sour, or having the strength to neutralize alkalis, or turning a litmus paper red.


지금 우리는'파리'를 검색하는데, 나는 그것이 거기에 있다고 확신한다.우리는 무엇을 얻었습니까?아무것도 없다아무것도 없는 게 아니야, 우리는 얻었어.

TypeError: results is undefined


우리는 예측할 수 없는 제출 단추가 하나 더 있습니다. 때로는 유효하고, 때로는 무효입니다. 그러면 우리는 무엇을 원합니까?우리는 진정으로 무엇을 원합니까?안전하고 응용 프로그램을 붕괴시키지 않는 대상, 믿을 만한 대상이 필요합니다.
우리가 해야 할 일은 용기를 실현하고 실행 흐름을 묘사하며 그것들이 가지고 있는 가치를 걱정할 필요가 없다.듣기에 괜찮은데, 그렇지?자바스크립트로 제 뜻을 보여드리겠습니다.이거 해봐.
const is_even = num => num % 2 === 0;

const odd_arr = [1,3,4,5].filter(is_even).map(val => val.toString());
const empty_arr = [].filter(is_even).map(val => val.toString());

console.log({odd_arr, empty_arr});
그것은 공수 그룹에서 이상을 일으킵니까?정말 알려준다면.이거 좋지 않아요?수조 방법이 올바른 일을 할 수 있다는 것을 알면서도 아무 것도 사용할 수 없다는 것은 따뜻하고 모호하지 않습니까?이것이 바로 우리가 원하는 것이다.
설마 우리가 몇 개 if 의 문장을 써서 그것을 완성할 수 없단 말인가?좋아요.네, 그런데 그중의 즐거움은 어디에 있습니까?우리는 모두 링크 함수가 매우 멋있다는 것을 안다. 우리는 함수 프로그래밍의 팬이다. 우리는 모든 함수 프로그래밍의 고수들이 하는 일을 했다. 함수 아래에 물건을 숨기는 것이다.
따라서 우리는 if 문장 (또는 한 쌍) 을 숨길 것이다. 만약 우리가 계산한 값이 정의되지 않았다면, 우리는 포장기로 돌아갈 것이다. 이 포장기는 어떤 상황에서도 어떻게 조작하는지 알게 될 것이다.
// maybe.js
// (I would like to apologize for the many `thing`s you'll see)

function Maybe(the_thing) {
  if(the_thing === null 
     || the_thing === undefined 
     || the_thing.is_nothing
  ) {
    return Nothing();
  }

  // I don't want nested Maybes
  if(the_thing.is_just) {
    return the_thing;
  }

  return Just(the_thing);
}
적당한 함수식 프로그래밍 언어에서 본 책 Maybe 에 따르면, 이 포장기는 당신의 표준이 되지 않습니다.우리는 편의와 부작용의 이름으로 사기를 칠 것이다.또한 이러한 메서드는 Rust에서 찾은 옵션 유형의 메서드로 지정됩니다.여기가 마술이 일어나는 곳이야.
// maybe.js

// I lied, there will be a lot of cheating and `fun`s.

function Just(thing) {
  return {
    map: fun => Maybe(fun(thing)),
    and_then: fun => fun(thing),
    or_else: () => Maybe(thing),
    tap: fun => (fun(thing), Maybe(thing)),
    unwrap_or: () => thing,

    filter: predicate_fun => 
      predicate_fun(thing) 
        ? Maybe(thing) 
        : Nothing(),

    is_just: true,
    is_nothing: false,
    inspect: () => `Just(${thing})`,
  };
}

function Nothing() {
  return {
    map: Nothing,
    and_then: Nothing,
    or_else: fun => fun(),
    tap: Nothing,
    unwrap_or: arg => arg,

    filter: Nothing,

    is_just: false,
    is_nothing: true,
    inspect: () => `Nothing`,
  };
}
이 방법들의 목적은 무엇입니까?
  • map: 함수funthe_thing에 적용하고 이를 다시 Maybe에 포장하여 모임을 계속합니다...내 말은 대상의 모양을 유지하는 것이다. 이렇게 하면 링크 함수를 유지할 수 있다.
  • and_then: 이것은 주로 탈출석이다.함수fun를 응용하여 운명을 결정한다.
  • or_else: 이 제품은 elsemap 제품입니다.다른 길.없으면요?
  • and_then: 부작용일 뿐입니다.만약 당신이 그것을 보았다면, 그것은 그것의 범위 밖의 물건에 영향을 주었을 수도 있고, tap의 완벽한 위치에 영향을 주었을 수도 있다.
  • 필터: 술어 함수가 진실한 것을 되돌려주면 통과할 수 있습니다.
  • console.log: 이것이 바로 당신unwrap_or이 탈퇴하는 방식입니다.링크 방법을 완성하고 명령식 세계로 돌아갈 준비를 할 때, 그것을 원할 것이다.
  • 우리들은 우리의 형식으로 돌아가서 그것의 행동을 보게 한다.사용자 검색과 일치하는 결과를 찾을 수 없는 함수 the_thing 를 생성합니다.만약 그렇다면, 우리는 보안 상하문에서 실행될 다른 함수를 연결할 것이다
    // main.js
    
    const search = (data, input) => Maybe(data[input]);
    
    const search_word = word => search(entries, word)
      .map(format)
      .unwrap_or('word not found');
    
    현재 우리는 새로운 안전 (r) 함수로 우리의 사악한 낡은 방법을 대체했다.
     window.search_form.addEventListener('submit', function(ev) {
       ev.preventDefault();
       let input = ev.target[0];
    -  window.result.innerHTML = format(entries[input.value]);
    +  window.result.innerHTML = search_word(input.value);
     });
    
    지금 테스트를 해보겠습니다.사고 검색

    An unexpected, unfortunate mishap, failure or loss with the potential for harming human life, property or the environment.
    An event that happens suddenly or by chance without an apparent cause.


    지금은 파리입니다.'파리'검색

    word not found


    그것은 동결 단추가 없다. 이것은 매우 좋다.파리에 있는 거 알아요.만약 네가 한번 조사해 보면, 너는 그것이 파리라는 것을 볼 수 있을 것이다우리는 단지 사용자를 대문자로 입력해야만 그들이 이렇게 할 필요가 없다.우선, 우리는 정확한 입력을 검색하고, 만약 실패한다면, 우리는 대문자 방식을 시도할 것이다.
    // main.js
    
    function create_search(data, exact) {
      return input => {
        const word = exact ? input : capitalize(input);
        return Maybe(data[word]);
      }
    }
    
    function capitalize(str) {
      return str.charAt(0).toUpperCase() + str.slice(1);
    }
    
    검색 기능을 변경합니다.
    - const search = (data, input) => Maybe(data[input]);
    + const search = create_search(entries, true);
    + const search_name = create_search(entries, false);
    -
    - const search_word = word => search(entries, word)
    + const search_word = word => search(word)
    +   .or_else(() => search_name(word))
        .map(format)
        .unwrap_or('word not found');
    
    괜찮은데.이것이 바로 우리가 지금까지 주로 얻은 것이다.전체 화면을 보고 싶으면
    // main.js
    
    const entries = data();
    
    function create_search(data, exact) {
      return input => {
        const word = exact ? input : capitalize(input);
        return Maybe(data[word]);
      }
    }
    
    function capitalize(str) {
      return str.charAt(0).toUpperCase() + str.slice(1);
    }
    
    function format(results) {
      return results.join('<br>');
    }
    
    const search = create_search(entries, true);
    const search_name = create_search(entries, false);
    
    const search_word = word => search(word)
      .or_else(() => search_name(word))
      .map(format)
      .unwrap_or('word not found');
    
    window.search_form.addEventListener('submit', function(ev) {
      ev.preventDefault();
      let input = ev.target[0];
      window.result.innerHTML = search_word(input.value);
    });
    
    그런데 이게 우리가 살면서 원하는 거예요?아니, 물론 아니야. 우리는 사랑하고 싶지만,javascript가 우리에게 줄 수 없으니, 우리는 작은 건의어 기능에 만족한다."accu"를 검색하고 싶은데, 확인 대화상자가 있습니다. "누가기를 말씀하시는 겁니까?"
    우리는 이 방면의 도움을 필요로 한다. 우리는 항목에 대해 모호한 검색을 할 수 있는 의존항을 가져올 것이다. fuzzy-search.그래서 우리는 다음과 같은 내용을 추가했다.
    // main.js
    
    import FuzzySearch from 'https://unpkg.com/[email protected]/src/FuzzySearch.js';
    
    const fzf = new FuzzySearch(
      Object.keys(entries),
      [],
      {caseSensitive: false, sort: true}
    );
    
    그러나 빈 그룹에서 일치하는 항목을 가져오려고 할 때 전체 과정이 붕괴되기 때문에 보안 작업을 수행할 수 없습니다.그럼 우리 어떡하지?우리는 사물을 함수 아래에 숨긴다.
    // main.js
    
    function suggest(word) {
      const matches = fzf.search(word);
      return Maybe(matches[0]);
    }
    
    모호한 검색이 준비되었습니다. 이제 아주 좋은 확인 대화상자를 추가하겠습니다.좋아할 거야.
    // main.js
    
    function confirm_word(value) {
      if(value && confirm(`Did you mean ${value}`)) {
        return value;
      }
    }
    
    우리는 새로운 함수를 우리의 search 와 결합시킬 것이다.
    // main.js
    
    const suggest_word = value => () => suggest(value)
      .map(confirm_word)
      .map(search);
    
    search에 기능을 추가합니다.
     const search_word = word => search(word)
       .or_else(() => search_name(word))
    +  .or_else(suggest_word(word))
       .map(format)
       .unwrap_or('word not found');
    
    정말 쓸모가 있다!그러나 만약에 우리가 search_word 문장에 알레르기가 있다고 가정하면 함수에서 되돌아오는 것은 말할 것도 없고 if 는 거칠다.우리는 더 잘할 수 있다.
     function confirm_word(value) {
    -  if(value && confirm(`Did you mean ${value}`)) {
    -    return value;
    -  }
    +  return confirm(`Did you mean ${value}`);
     }
    
     const suggest_word = value => () => suggest(value)
    -  .map(confirm_word)
    +  .filter(confirm_word)
       .map(search);
    
    어떤 일들은 나를 매우 짜증나게 한다."accu"를 검색하고 대화상자를 팝업합니다. 건의를 확인하면 결과가 나타납니다.그러나 "accu"는 입력에 여전히 존재하기 때문에 매우 난처합니다.정확한 단어로 입력을 업데이트합시다.
    const update_input = val => window.search_form[0].value = val;
    
     const suggest_word = value => () => suggest(value)
       .filter(confirm_word)
    +  .tap(update_input)
       .map(search);
    
    그것의 실제 응용을 보고 싶습니까?여기 있습니다.

    보너스 트랙


    Warning: The main point of the post (which is me showing that codepen example) was already accomplished. What follows is a strange experiment to see if I could make that Maybe function support asynchronous operations. If you are tired just skip everything and check out the last example code.


    지금 당신은 매우 귀엽다고 말할 수 있지만'진실한 세계'에서 우리는 http 요청을 보내고 데이터베이스를 조회하며 각종 비동기적인 내용을 제작한다. 이런 상황에서 이것이 유용할 수 있겠는가?
    들었어.우리의 현재 실현은 정상적인 막힌 임무만 지원한다.undefined이 나타날 때, 너는 반드시 Maybes의 체인을 깨야 한다.
    하지만 만약에...듣다우리는 알기로 약속한다Promise.우리가 할 수 있을까Just?AsyncJust ? 오, 너무 무서워.
    모르면 aJustAsync는javascript가 미래 이벤트를 조율하는 데이터 형식입니다.이를 위해 Promise라는 방법을 사용했습니다. 이 방법은 리셋 (그리고 then 을 받아들여 오류가 발생했을 때) 에 사용됩니다. 따라서 만약에 우리가 catch 의 내용을 납치했다면 우리는nicethen 인터페이스를 보존할 수 있습니다.
    당신은 일련의 답방을 거친 후에 어떻게 행동했습니까?
    나 왔어.보여줄게Maybe.
    // Don't judge me. 
    
    function Future(promise_thing) { 
      return {
        map: fun => Future(promise_thing.then(map_future(fun))),
        and_then: fun => Future(promise_thing.then(map_future(fun))),
        or_else: fun => Future(promise_thing.catch(fun)),
        tap: fun => Future(promise_thing.then(val => (fun(val), val))),
        unwrap_or: arg => promise_thing.catch(val => arg),
    
        filter: fun => Future(promise_thing.then(filter_future(fun))), 
    
        is_just: false,
        is_nothing: false,
        is_future: true,
        inspect: () => `<Promise>`
      };
    }
    
    만약 우리가 소음을 제거한다면, 아마도 우리는 더욱 잘 이해할 수 있을 것이다.
    // In it's very core is callbacks all the way.
    
    {
      map: fun => promise.then(fun),
      and_then: fun => promise.then(fun),
      or_else: fun => promise.catch(fun),
      tap: fun => promise.then(val => (fun(val), val))),
      unwrap_or: arg => promise.catch(val => arg),
    
      filter: fun => promise.then(fun), 
    }
    
  • Future/map: 똑같은 일을 해요. 벗어날 수 없으니까and_then.
  • Promise: 리셋을 or_else 방법에 넣고 catch 행동을 시뮬레이션합니다.
  • else: 를 사용하여 값을 봅니다.부작용을 겨냥한 것이기 때문에 우리는 다시 값을 되돌린다.
  • tap: 사용할 수 있도록 약속을 되돌려줍니다then.만약 모든 것이 순조롭다면, unwrap_or 의 원시 값은 await 때 되돌아오고, 그렇지 않으면 제공된 매개 변수가 되돌아옵니다.어떤 방식이든promise는 오류를 던지지 않습니다. 왜냐하면 Promise 방법이 추가되었기 때문입니다.
  • await: 이것은 특수한 Future, 이것이 바로 catch이 존재하는 원인이다.
  • 거의 모든 방법이 새로운 filter'원인map을 되돌려줍니다.
  • filter_future 이상하게도 내부에서 일어난 일이다.Future 기억나세요?
    function map_future(fun) { // `fun` is the user's callback
      return val => {
        /* Evaluate the original value */
        let promise_content = val;
    
        // It needs to decide if the value of the Promise
        // can be trusted
        if(Maybe(promise_content).is_nothing) {
          Promise.reject();
          return;
        }
    
        // If it is a Just then unwrap it.
        if(promise_content.is_just) {
          promise_content = val.unwrap_or();
        }
    
        /* Evaluate the return value of the user's callback */
    
        // Use Maybe because I have trust issues.
        // For the javascript world is undefined and full of errors.
        const result = Maybe(fun(promise_content));
    
        if(result.is_just) {
          // If it gets here it's all good.
          return result.unwrap_or();
        }
    
        // at this point i should check if result is a Future
        // if that happens you are using them in a wrong way
        // so for now I don't do it 
    
        // There is something seriously wrong.
        return Promise.reject();
      }
    }
    
    현재promise.then.
    function filter_future(predicate_fun) { // the user's function
      return val => {
        const result = predicate_fun(val);
    
        // Did you just returned a `Promise`?
        if(result.then) {
          // You did! That's why you can't have nice things.
    
          // peek inside the user's promise.
          const return_result = the_real_result => the_real_result 
            ? val
            : Promise.reject();
    
          // keep the promise chain alive.
          return result.then(return_result);
        }
    
        return result ? val : Promise.reject();
      }
    }
    
    그리고 마지막으로 하고 싶은 일은 보조 함수를 만들고 정규 값을 Promise 로 바꾸는 것이다.
    Future.from_val = function(val) {
      return Future(Promise.resolve(val));
    }
    
    우리가 지금 해야 할 일은 aFuture에서 amap를 지지하는 것이다.
     function Maybe(the_thing) {
       if(the_thing === null 
         || the_thing === undefined 
         || the_thing.is_nothing
       ) {
         return Nothing();
       }
    -
    -  if(the_thing.is_just) {
    +  if(the_thing.is_future || the_thing.is_just) {
         return the_thing;
        }
    
        return Just(the_thing);
     }
    
    그러나 백만 달러의 문제는 여전히 존재한다.그것은 정말 효과가 있습니까?
    나는 CLI version 개가 있다.이것은 같은 코드 펜의 예입니다. 약간의 조정이 있습니다. 저는 map_future 관련 함수를 추가했습니다. 대화 상자가 실제로 대화 상자(this one인 것을 확인했습니다. 이벤트 탐지기는 현재 비동기 함수입니다. filter_future 결과를 얻을 수 있습니다.

    보너스 편집


    이것이 바로 우리가 부정행위를 할 때의 모습이다.만약 우리가 부정행위를 하지 않았다면, 그것은 마치 this 과 같다.

    기타 리소스

  • The Marvellously Mysterious JavaScript Maybe Monad
  • Option/Maybe, Either, and Future Monads in JavaScript, Python, Ruby, Swift, and Scala


  • 읽어주셔서 감사합니다.만약 당신이 이 문장이 매우 유용하다고 생각하고 나의 노력을 지지하고 싶다면, buy me a coffee ☕.

    좋은 웹페이지 즐겨찾기