abonglog logoabonglog

Chapter 12: Traversing the Stone [번역] 의 썸네일

Chapter 12: Traversing the Stone [번역]

mostly-adequate-guide
프로필 이미지
yonghyeun

해당 게시글은 mostly-adequate-guide 의 시리즈들을 번역했습니다.

해당 게시글의 저작권은 mostly-adequate-guide 의 저작권인 Attribution-ShareAlike 4.0 International 을 준수합니다.

🤖 AI가 요약한 글이에요!
이 장에서는 Traversable 인터페이스와 그 주요 함수인 sequencetraverse를 소개합니다.
sequence는 중첩된 펑터 타입의 순서를 바꾸는 ([Task a] -> Task [a]) 역할을 합니다.
traversemapsequence를 결합한 것으로, 함수를 적용한 후 타입 순서를 변경하여 여러 효과(effect)를 효율적으로 관리합니다.
예를 들어, 여러 비동기 작업을 병렬로 처리하고 결과를 하나의 컨테이너로 모으는 데 유용합니다.
이를 통해 코드의 구조를 개선하고 비동기 로직 등을 더 깔끔하게 처리할 수 있습니다.

Chapter 12: 돌 위를 횡단하기 (Traversing the Stone)

지금까지, 우리의 컨테이너 서커스에서는...

우리는 사나운 펑터를 길들여, 우리의 변덕스러운 어떤 작업이든 수행하도록 만드는 것을 보았습니다. 함수 적용을 사용하여 여러 위험한 효과(effect)를 한 번에 저글링하여 결과를 수집하는 것에 눈부셨을 것입니다. 컨테이너들을 함께 결합하여 허공으로 사라지는 것을 놀랍게 지켜보았습니다. 부수 효과(side effect) 사이드쇼에서는, 그것들이 하나로 합성되는 것을 보았습니다. 그리고 가장 최근에는, 자연스러운 것을 넘어 여러분의 눈앞에서 한 타입을 다른 타입으로 변환하는 모험을 했습니다.

그리고 이제 다음 묘기를 위해, 우리는 순회(traversals) 를 살펴볼 것입니다. 마치 우리의 가치를 그대로 유지하는 곡예사처럼 타입들이 서로 위를 날아다니는 것을 볼 것입니다. 틸트-어-휠(tilt-a-whirl)의 트롤리처럼 효과의 순서를 재정렬할 것입니다. 우리의 컨테이너들이 곡예사의 팔다리처럼 얽힐 때, 우리는 이 인터페이스를 사용하여 상황을 정리할 수 있습니다. 우리는 다른 순서를 가진 다른 효과들을 목격할 것입니다. 제 판탈롱과 슬라이드 휘슬을 가져다주세요, 시작합시다.

타입과 타입 (Types n' Types)

좀 이상하게 가봅시다:

여러 파일 읽기 예시
// readFile :: FileName -> Task Error String
 
// firstWords :: String -> String
const firstWords = compose(intercalate(" "), take(3), split(" "));
 
// tldr :: FileName -> Task Error String
const tldr = compose(map(firstWords), readFile);
 
map(tldr, ["file1", "file2"]);
// [Task('hail the monarchy'), Task('smash the patriarchy')]

여기서 우리는 여러 파일을 읽고 쓸모없는 태스크 배열로 끝납니다. 이들 각각을 어떻게 포크(fork)할 수 있을까요? 타입을 바꿔서 [Task Error String] 대신 Task Error [String]을 가질 수 있다면 가장 좋을 것입니다. 그렇게 하면, 여러 개의 미래 값이 제멋대로 도착하는 것보다 모든 결과를 담고 있는 하나의 미래 값을 갖게 되어 우리의 비동기 요구에 훨씬 더 적합할 것입니다.

타입 풍수 (Type Feng Shui)

Traversable 인터페이스는 sequencetraverse라는 두 개의 영광스러운 함수로 구성됩니다.

sequence를 사용하여 타입을 재배열해 봅시다:

sequence 함수 사용 예시
sequence(List.of, Maybe.of(["the facts"])); // [Just('the facts')]
sequence(Task.of, new Map({ a: Task.of(1), b: Task.of(2) })); // Task(Map({ a: 1, b: 2 }))
sequence(IO.of, Either.of(IO.of("buckle my shoe"))); // IO(Right('buckle my shoe'))
sequence(Either.of, [Either.of("wing")]); // Right(['wing'])
sequence(Task.of, left("wing")); // Task(Left('wing'))

여기서 무슨 일이 일어났는지 보셨나요? 우리의 중첩된 타입은 습한 여름밤의 가죽 바지처럼 안팎이 뒤집힙니다. 내부 펑터는 외부로 이동하고 그 반대도 마찬가지입니다.

타입의 왈츠 (Waltz of the Types)

초기 예제를 다시 방문하고 정리할 시간입니다.

traverse 함수 사용 예시
// readFile :: FileName -> Task Error String
 
// firstWords :: String -> String
const firstWords = compose(intercalate(" "), take(3), split(" "));
 
// tldr :: FileName -> Task Error String
const tldr = compose(map(firstWords), readFile);
 
traverse(Task.of, tldr, ["file1", "file2"]);
// Task(['hail the monarchy', 'smash the patriarchy']);

map 대신 traverse를 사용하여, 우리는 제멋대로인 Task들을 결과의 멋지고 조정된 배열로 성공적으로 몰아넣었습니다. 이것은 익숙하다면 Promise.all()과 비슷하지만, 단지 일회성의 사용자 정의 함수가 아닙니다. 아니요, 이것은 모든 Traversable 타입에 대해 작동합니다.

연습 문제 (Exercises)

다음 요소들을 고려해 봅시다:

연습 문제용 헬퍼 함수
// httpGet :: Route -> Task Error JSON
 
// routes :: Map Route Route
const routes = new Map({ "/": "/", "/about": "/about" });

이제 다음 유효성 검사 함수를 정의합니다:

연습 문제용 유효성 검사 함수
// validate :: Player -> Either String Player
const validate = (player) =>
  player.name ? Either.of(player) : left("must have name");

마지막으로, 몇 가지 파일 시스템 헬퍼를 고려합니다:

연습 문제용 파일 시스템 헬퍼
// readfile :: String -> String -> Task Error String
// readdir :: String -> Task Error [String]