1. 하스켈은 main 펑션이 함수가 아니라 IO () 타입이다.
이 IO a 타입은, 부작용을 담고 있는 데이터야.
그리고 IO a 타입을 특별한 함수(위험한 함수라서 안전한 하스켈이 아님) 즉, 부작용발생기에 집어넣으면, 부작용이 발생하고 결과값으로 a 타입이 나온다.
즉, main 의 IO () 는, 부작용 발생기에 집어넣으면 부작용이 발생하고 결과값으로 () 즉 void가 나온다는 거다.
그런 타입이다.
부작용발생기에 집어넣어서 부작용을 발생하고 보이드로 반환되는 녀석
2. IO 는 모나드다.
이 모나드라 함은 >>= 연산을 모나드라는 클래스 안에 가지고 있고,
그것의 슈퍼클래스인 어플리카티브 클래스에 <*> 연산을 가지고 있고,
그것의 슈퍼클래스인 펑터 클래스에 <$> 연산을 가지고 있다.
여기서 모나드 연산이란 결국 무엇일까?
모나드는 일종의 구조물이다.
데이터와 구조물. 이걸 잘 구분하시라.
여기서 구조물은 자바로 따지면 제네릭에 해당하는 부분이고, 데이터는 제네릭 파라미터타입의 인스턴스라고 보면 된다.
즉,
[1, 2, 3] 은 길이 3짜리 구조물에 데이터 1, 2, 3 이 들어있는 거다.
Optional Int 의 경우는, Some 3 은 Some 이라는 구조물 안에 데이터 3이 들어있고,
None 은 None이라는 구조물 안에 데이터가 안들어있다
3. 모나드의 의미.
모나드란 결국, >>= 연산을 하기 위해 태어난 족속들이다.
그리고 >>= 연산이 올바르게 정의된다면, 우리는 그것을 모나드라고 부른다.
모나드는, 구조물과 >>= 연산으로 정의된다.
[] 즉 리스트는 모나드다.
4. >>= 의 의미
앞으로 모나드를 M이라고, 타입을 A라고 불렀을때, M A 타입 (즉 자바로 따지면 M<A> 타입)을 ma 라고 부르겠다.
>>=
는
ma >>= (a -> mb) = mb
인 함수다.
즉, ma타입의 구조물 m + 데이터 a 를, 데이터 a를 가지고 와서 mb모나드 타입으로 반환하는 함수와 연산해서
mb모나드 타입을 얻어낸다.
자 이걸 존나 존나 쉽게 말해보자
이거 그냥 존나 쉬운거다. 사실 이게 모나드의 전부였던거다. 수학적인 배경은 따로 공부하고 프로그래머 관점에서의 모나드가 무엇인가. 바로 이거다.
일단 모나드는 전부 "실행기" 라는 것을 가지고 있다.
ma 와 (a -> mb) 를 결합해서 만든 mb 모나드를 실행시키는 것은
ma 모나드를 실행시켜서 a를 얻어낸 후,
그 a를 (a -> mb) 에 넣어서 mb 를 얻어낸 후
마지막으로 그 얻어진 mb를 실행시키는것.
이게 전부다.
너무 쉽지않나? 그냥 시그니처를 읽었을 뿐인데 모나드가 끝이났다.
5. 그래서 IO 모나드의 경우?
IO 모나드의 경우 실행기 안에 집어넣으면 부작용을 발생시키고 결과를 산출해낸다.
이거를 그대로 >>= 에 넣고 이해해보자.
(IO Int : 표준입력으로 숫자를 받아오세요) >>= (Int -> IO () : 숫자 -> 표준출력으로 해당숫자의 제곱을 출력하세요)
라는 모나드를 생각해보자
이 결합된 모나드는,
먼저 IO Int 를 실행기에 넣어서 표준입력에서 숫자를 받아온 후, (이미 여기서 부작용으로 숫자를 받아왔음)
그 숫자를 Int -> IO () 에 넣는다.
그 값을 10이라고 하면,
그 결과로 우리의 Int -> IO () 는
IO () : 표준출력으로 100을 출력하세요
를 반환한다.
그리고 그 반환한 모나드를 또 실행기에 집어넣는다.
그러면 출력으로 100이 출력된다.
즉, 우리가 만든 결합 모나드는 이렇게 순차적으로 하나의 프로그램을 이룬다.
하스켈은 이런 방식으로 모나드를 결합하고 또 결합하고 하나의 거대한 모나드를 만들어낸 후,
그 모나드를 실행기에 집어넣는 방식으로 한번에 부작용을 처리한다
실행기에 집어넣기전까지 부작용은 아무것도 없으며,
실행기에 명시적으로 집어넣어야지만 부작용이 발생한다.
그리고 모든 부작용은, 어느 실행기에 집어넣어야 작동가능한지 명시가 됨으로,
모든 부작용은 사용하고 싶다면 실행기 안에서 실행되어야한다.
참고로, IO는 부작용중의 일부일뿐, 유일한 부작용이 아니다.
다른 부작용들도 모나드라는 이름하에 만들어진다.
그리고 또한, 모든 모나드는 부작용이 아니다.
모나드는 부작용을 컨트롤하기 위한 장치라기보단, 굉장히 함수적으로 프로그래밍을 하는 기법이다.
순수한 모나드들이 많다. 실행기가 순수한 함수인 애들.
6. 순수한 모나드 : 리스트에 대해서도 이것을 부작용실행기라는 모나드의 관점으로 바라볼 수 있을까.
결국 핵심이 이거다
모나드는 부작용이랑 관련있다.
그렇다면, 순수한 모나드인 리스트에 대해서도, 모나드의 퓨어한, 부작용실행관점으로 인식할 수 있어야하지 않을까?
사실 이번 고찰은 전부 이 6번때문에 비롯되었다.
이걸 모나드를 몰랐던 너희들은 이해조차 못하겠지만, 굉장한 깨달음이었다.
리스트의 경우, 실행기는 "뽑는다"
즉,
[1, 2, 3] >>= (\x -> [x, x*10, x *100]) 의 경우
[1, 2, 3] 에서 뽑아서 1을 반환하고
그 반환값을 함수에 넣어서 [1, 10, 100] 을 얻어내고
여기서 뽑아서 1을 반환한다.
어? 마지막 결과물이 1이네?
이게 뭐냐고?
바로 [1, 2, 3] >>= (\x -> [x, x* 10, x * 100]) 의 첫번째 원소다.
그리고 두번째 원소는 10, 세번째 원소는 100
네번째 원소는, [1, 2, 3] 에서 두번째 원소인 2가 뽑히고, 그것을 함수에 집어넣어서 [2, 20, 200]
세번째는 [3, 30, 300]
그렇게 [1, 10, 100, 2, 20, 200, 3, 30, 300] 이라는 원소가 나열되어서 뽑히고
저 배열자체가 모나드 연산의 결과물이다.
"뽑는다" 가 이 모나드의 부작용인 것이다.
나열한다 까지도 부작용인거같지만
거기까지 철학적으로 갈 생각은 없고
여튼 모나드라는게 이렇다
ma를 실행시키고
그 결과를 함수에 집어넣고
결과물인 mb를 실행시키고
그 결과가 나의 결과인
일종의 스트리밍 구조이다.
조금만 더 나아가보자
7. 어플리카티브
어플리카티브 <*> 의 경우
m(a -> b) <*> ma = mb
이다.
이것은
나를 실행기에 넣었을때
m(a -> b) 의 부작용을 실행시켜서 a -> b를 얻고
ma 의 부작용을 실행시켜서 a를 얻고
그 두 결과물을 a -> b 안에 a 를 집어넣어서 b를 얻는
이게 나의 결과물이다!!
라고 한것임.
이 결과물은 b가 아니라 mb임.
실행시켰을때 부작용과 b를 내뱉는거니까 나 자신은 mb임
나를 실행시키면 위와같이 b가 나오는것.
이것은
do
(a -> b) <- m(a -> b)
a <- ma
(a -> b) a
라고 표현할수 있는데 패스패스...
8.
마지막 펑터
펑터는
a -> b <$> ma = mb
임 조낸 단순하지.
얘는
나를 실행시키는 것은
ma를 실행시키고
그 결과에 a -> b 함수를 실행시킨 것이 내 반환값이다
라는 것임.
모나드로는
ma >>= return.(a -> b)
라고 표현됨.
하하하하하하!!!
난 그냥 시그니처를 읽었을 뿐이라구....
기반지식이 없어서 이해가 안되는 1인 ㅇㅅㅇ
사랑해 나토리~
ㅇㅅㅇ
이걸 다 외웠네 와! 기립박수
하스켈은 변태나쓰는거 아닌가요 - dc App