ReasonML 에서 Stream 사용하기
- Stream 이란 간단히 얘기해 데이터의 흐름 입니다. ioStream은 자바에서는 굉장히 많이 사용되는 개념으로, 아래처럼 read()등을 사용하여 단위 정보를 읽어올 수 있는데요,
public static void main(String args[]){ try{ FileInputStream in = new FileInputStream("D://somewhere"); byte[] input = new byte[5]; for(int i=0; i<input.length; i++){ int b = in.read(); if(b == -1) break; // rest of the logic... } } }
(
아아 자바여..) ReasonML에서 Stream을 사용한 예를 통해 감을 잡아보겠습니다. -
여기를 보면 next를 하면 자료형이 바로 나오고 peek을 하면 option(‘a)로 감싸져서 나오네요. Stream.from(f)는 f에 의해 생성된 요소를 Stream안에 넣고 Stream을 반환합니다.
- 아래의 코드는 1부터 순차적으로 수를 읽다가, limit이 100이 되었을 때 멈추는 동작을 합니다. 링크 의 Day06.re를 참고했습니다.
let init = ref(1);
let limitFilter = s => {
let rec go = () => {
let v = Stream.next(s);
Js.log2("v=>",v);
switch (v^ >= 100) {
| false => go()
| true => {
Js.log("Reached 100!")
}
};
};
go();
};
let numberStream = input => {
let state = input;
let next = _ => {
state := state^ + 1;
Some(state);
};
Stream.from(next);
};
let answer = numberStream(init)->limitFilter->Js.log;
....
v=> { contents: 96 }
v=> { contents: 97 }
v=> { contents: 98 }
v=> { contents: 99 }
v=> { contents: 100 }
Reached 100!
- 한번 흐름을 따라가보겠습니다.
- let answer = 로 시작하는 pipe operator들을 보면 (1) init으로 시작된 Stream을 (2) limitFilter로 걸러서 (3) Js.log로 출력하라 라고 한 것을 알 수 있습니다.
- 여기서 중요한 점은 Stream을 생성하는 로직과 Stream의 값을 필터링하는 로직이 분리되어 있다는 것입니다.
let numberStream = input => {
let state = input;
let next = _ => {
state := state^ + 1;
Some(state);
};
Stream.from(next);
};
- Stream.from은 Stream이 어떻게 다음(next) 값을 얻어내는지를 정의하는 부분입니다. ReasonML의 모든 let 변수들은 immutable하기 때문에(‘변’수는 아니지만) ref를 사용해서 update할 수 있도록 했습니다. 단순히 input된 값에서 1을 더해서 넣고 있습니다.
let limitFilter = s => {
let rec go = () => {
let v = Stream.next(s);
Js.log2("v=>",v);
switch (v^ >= 100) {
| false => go()
| true => {
Js.log("Reached 100!")
}
};
};
go();
};
-
limitFilter는 안에서 go() 라는 재귀함수를 호출하며 내부에서 Stream.next(s)가 뱉어낸 값이 100 이상인지를 검사합니다. 아니라면 ( false ) 계속해서 찾고, 맞다면 로그를 출력하도록 했습니다. 근데 위의 어느부분에서 다음 수를 생성하고 있는 것일까요?
let v = Stream.next(s);
이 부분입니다. Stream.from(next)에서 다음 값에 대한 정의가 있었기 때문에 Stream.next(s)는 이전 상태에서 발전한 다음 값을 리턴하게 됩니다. ^^
- 이번 ReasonML에서 Stream을 사용하는 모습은 마치 ES6의 generator를 떠올리게 합니다. 아래는 실제 generator JavaScript코드 입니다.
function * naturalNumbers() {
let num = 1;
while (true) {
yield num;
num = num + 1
}
}
const numbers = naturalNumbers();
console.log(numbers.next().value)
console.log(numbers.next().value)
- 매우 흡사하죠? (
근데 훨씬 간단하죠?) 그럼 오늘은 여기까지 하겠습니다.