2020-02-18 TIL

할 일 목록

  • 코루틴 정리.
  • 리액티브, 펑터 모나드 정리.
  • 코딩 도장

완료하지 못한 목록

완료 목록

5Fs

1. Fact

1. 코루틴

  • 우리가 비동기 non-blocking으로 짜지 못하는 가장 큰 이유는 예측이 불가능하기 때문이다.
  • 일반적으로 우리가 사용하는 sync-blocking(싱글 스레드 콜백) 방법 밑의 코드와 같이 함수를 선언하고, main 에서 함수를 사용하는 경우는 함수가 어디서 반환될지 우리는 예측이 가능하다.
fun runFunc() {
    println("함수 실행")
}

fun main() {
    println("메인을 실행한다.")
    runFunc()
    println("메인을 종료한다.")
}
  • 하지만 콜백으로 짠다면 동작을 예측을 할수가 없다. 밑의 코드를 보면 현재 object의 속성 count를 1씩 증가하는 코드를 작성하고 있다. 하지만 count가 2가 되는 시점에 async-nonblocking 함수(스레드를 새로 생성하여 메인 스레드를 멈추지 않고 1초 홀딩 후에 직접 2를 넣어주는 함수)로 직접 2를 넣어주었다. 우리가 기대한 결과값은 1,2,3,4 순서대로 나오는 것을 기대했지만, 1,2,3,2라는 결과값을 받았다. 멀티스레드 콜백(비동기 논블로킹) 방식은 예측하기가 힘들어진다. 그러다 보니 우리는 위와같이 싱글스레드 콜백 방식으로 코드를 작성한다.
const object = {count:0};
const async_nonblocking_fun = (callback) => {
    setTimeout(callback ,1000)
}
const addCount = (object) => {
    object['count'] = object['count'] + 1;
    console.log(object['count']);
}
const inputTwo = () => {
    object['count'] = 2;
    console.log(object['count']);
}

addCount(object);
async_nonblocking_fun(inputTwo,object);
addCount(object);
addCount(object);
  • 그래서 자바의 future는 async blocking 방식이다. 자바의 future을 사용할 경우, 멀티 스레드를 생성하여 완료되면 callback을 호출하지만, 실제로 제어권이 main으로 넘어오지 않고, blocking을 하게된다. 그러면 사실상 싱글스레드를 사용하는 것과 마찬가지이다..
  • 위의 문제점을 해결하기 위하여 콜백이 호출되는 시점을 우리가 원하는 시점에 제어하는 방법을 사용한다.

    1. 로딩이 끝난 상태와 끝나지 않은 상태로 구분할 수 있다.
    2. 로딩이 끝난 상태에서 실행하면 바로 호출이 될것이고
    3. 로딩이 끝나지 않은 상태라면, callback 처럼 대기해서 완료되면 실행이 되도록 한다.
    4. 그렇게 되면 async-nonblocking 코드를 sync-blocking 코드 처럼 우리가 보기 편하게 작성할 수 있다.
  • 위의 한가지 예는 completableFuture이다. 자바스크립트에선 promise와 동일하다고 생각하면 된다.

    1. 한 가지 예를 들어보자. 공장에서 자동차를 조립하는데 타이어, 프레임, 휠, 운전대, 라이트, 엔진, 등을 모두 제조해서 조립을 한다고 생각해보자.
    2. 그렇게 되면 차동차의 조립이 모두 성공하기 위해서는 각각의 부품들이 각 라인에서 생산되어 모두 나와야 완성된 자동차를 만들수가 있다.
    3. 여기서 각각 부품들은 각각의 다른 스레드에서 생성하고, 모두 완성 되면 자동차를 조립하는 코드를 작성해보자.
  • promise가 강력한 이유

    • 만약에 result는 비동기 논블로킹으로 요청을 하고, 밑에 코드를 동기적으로 작성할 수 있다. 그리고 나중에 result가 필요할 때 callback을 호출하게 하여 동작이 가능하다.(callback의 호출 시점을 제어할 수 있기 때문에 )
const engineLine = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("engine finish")
        resolve("engine")},1000);
})
const frameLine = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("frame finish")
        resolve("frame")},1000);
})
const wheelLine = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("wheel finish")
        resolve("wheel")},1000);
})
const tireLine = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("tire finish")
        resolve("tire")},1000);
})
const finishLine = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("finish")
        resolve("tire")},1000);
})

engineLine.then(engineLine)
.then(frameLine)
.then(wheelLine)
.then(tireLine)
.then(finishLine);
  • 멀티 스레드 패턴

    • IOC
    • background 입력 스레드가 항상 버퍼를 갱신하고 다른 멀티스레드는 버퍼를 감시한다.
    • suspended pattern이며 읽고 쓸때 syncronize를 할 필요가 없기 때문에 굉장히 빠르다.
  • 컴퓨터는 사실 비동기가 없다. 우리는 노이만 머신을 사용하기 때문에 한번에 명령이 적재되면 명령을 중간에 멈출 수 있는 방법이 없다. 무조건 모두 실행되야 하고 무조건 블로킹 당한다. 그러기 때문에 우리가 알고있는 비동기는 멀티스레드로 만든것이다. 멀티스레드는 왜 비동기가 될까? 명령을 멈출 수 있기 때문이다. 원래 노이만 머신은 메모리에 적재된 명령을 한번에 소진해야 되는데 스레드를 사용하면 1번 스레드가 자기에 적재된 명령을 실행하다 끊고 다른 스레드로 넘어가서 코드를 실행한다. 실질적인 시분할이 일어난다. 따라서 동기적인 명령을 멈출 수 있는 유일한 방법은 프로세스나 스레드밖에 없다. OS 차원에서 그외에 우린 동기적 명령을 멈출 수 있는 방법은 없다.
  • 그럼 언어에서는 가능할까? for문 돌때 break 같은 전용 flow 명령을 쓰지 않고, 3번 돌다가 멈췄다가 4번 돌다가 이렇게 하는 방법은 없다. 스레드만 os차원에서만 가능하다.
  • 그럼 언어 차원에서 그것을 흉내내기 위해 만든 문법이 yield 중간에 빠졌다가 들어가는데,

2. 코루틴

리스코프 치환원칙 공부하기

  • 코틀린 컬렉션은 왜 자바의 컬렉션이 있는데도 불구하고 새로 짰을까 ?

    • mutable이 mutable 아닌것에 의존하고 있다는게 불안정함 형변환이 되기 때문에 우리는 lsp 계층에 의해서 뮤터블 셋이 그냥 셋으로 부모로 대체할 수 있다. 객체지향의 대체가능성은 자식은 부모로 대체할 수 있다. 여기서 보면 mutableList는 List를 대체하게 되니까 언제나 list처럼 쓸 수 있다는 이야기다. 그래서 항상 이 코틀린의 현재 계층구조에서는 굉장히 불안정하다. list 는 mutable로 mutable은 list로 언제나 타입 캐스팅이 가능함. 걔다가 얘낸 다 인터페이스다 스테레오 타입으로 되어있는 그래서 얘는 구현체 조차도 아님 실제 구현체는 따로 있다는 이야기다. 실제 구현체가 mutable interface를 상속 받았다는 이야기는 list interface를 상속 받았다는 이야기이도 하다. list나 mutableList는 둘다 서로를 타입캐스팅할 수 있음. mutable list를 list로 타입 캐스팅 가능. 여기선 list를 구현한 구상 객체가 보이지 않음. list만 구현한 구상객체조차도 mutablelist로 타입 캐스팅이 된다. 구현체가 list를 구현했을때 mutable list는 구현하지 않도록 해야하는데 실제 현재 코틀린 구현체는 mutable list가 list까지 구현하는 구현체를 가진다. 254 페이지는 클래스 다이어그램이 아니다. 좋은 계층 구조는 인터페이스만으로 되있지 않다. 타입을 확립시킬때는 구상클래스가 개입하게 되있다.OL을 방지하기 위해서 OL이란
    • 여기 더 좋은 그림은 리스트를 list를 mutable 리스트로 화살표가 가는데 이렇게가 아니라 abstract list라는 클래스가 list 인터페이스를 가르키고있는 별도의 박스가 있는 것임. 그럼 mutable list로 내려갈 수 없음. mutablelist 밑에도 absctact mutable list라는 클래스가 따로있는게 나을 수도 있다. 이런 경우엔
  • 근데 왜 이런 위험을 감수하고 왜 따로 코틀린 콜렉션을 만들었을까 ?

    1. 가장 중요한 이유는 mutable, imutable 구분 할 수 있는 인터페이스를 추가하기 위해서

      • 자바 컬렉션의 가장큰 문제를 mutable, immutable로 보는 것이다.
    2. 코틀린 컬렉션은 컴파일러의 다양한 기능에 대해서 지원을 받는다

      • 최상단에interface iterable이란게 있는데 이것의 정체 때문이다. iterable에는 next와 hasNext가 있을것 같지만 그러면 map ,set, list 가 모두 for a in iterable에서 사용할 수 있을것 같지만 하지만 코틀린에서는 iterable을 operator 로 hasNext(), next() 구현했음. 아무 객체나 인터페이스를 사용하지 않고 오퍼레이터로 위의 두개만 구현하면 무조건 iterable이 된다.
      • operator는 객체의 상속 구조를 갖지 않아도 컴파일러가 해석할 수 있음. 컴파일러는 for안에서 아무 객체나 돌아갈 수 있도록 만들어줌 operator로 hasNext와 next만 구현하면 특정 객체 상속 받지 않아도 된다.
      • 이런 혜택이 컬렉션에 적용되어져 있다.
      • 그럼 왜 iterable을 상속을 받고 있을까? 실제로 foreach가 어떻게 구현 되있을까 native foreach가 list나 set에 구현되어져 있는데 우리는 여기에 람다를 넘겨 처리하는데 람다를 foreach란 함수는 안에 무엇으로 구현되어져 있을까? hasNext와 next로 구현되어져 있음. for in으로 구현해도 똑같아짐. 컴파일러의 지원을 받음. 우리는 혜택을 보는 마지막의 클라이언트 함수만 쓰고 있을 뿐이다. 그럼 컴파일러가 인라인 화도 할 수 있다. 컴파일러 상의 인라인 최적화 같은 경우에 다 버프를 받는다. hasNext와 next가 있다는 걸로 for in 을 컴파일러가 hasNext와 next로 바꿀거라 생각하면 안된다.
    3. 이게 추상화된 코틀린 프레임워크이기 때문에 특정 언어의 컬렉션 프레임워크가 아니라도 아무 언어로나 인식하기 편하다.
  • 그리고 컬렉션 시스템의 혜택은 여기 나와있는 map, set, list 만 있지만 컬렉션 프레임워크의 메서드는 array도 사용을 한다. 하지만 배열은 이것을 직접적으로 상속 받는 객체는 아니다. 리스트를 상속받지 않으니까. 하지만 그 위에서 array로 연결되지 않는 이유는 array는 native 타입으로 처리되기 때문이다. native타입(integer와 같은놈임 래핑 타입이 아니라)은 메소드를 가질 수 없음. 래핑 타입만이 메소드를 가질 수 있음 그래서 자바에선 boxing unboxing이다. 그래서 컬렉션 프레임웍에 있는 메소드를 사용하기 위해 배열은 어떤 짓을 해야할까 ? 코틀린은 기본적으로 boxing unboxing을 하지 않지만 배열이 native 타입이라면 거기에 있는 컬렉션 프레임워크의 메소드를 쓰기 위해 ((((((((boxing)))))) 타입과 unboxing 타입이 있을수밖에 없다. 따라서 array는 제네릭으로 서브타입을 받지만 원소이다. boxing unboxing 타입을 만들어 놓은 것임. 그게 바로 typed array이다. 그래서 코틀린은 IntArray LongArray 등등이 있는것임. 코틀린에 있는 IntArray와 Array<Int> 는 호환이 될까 ? typed array가 array<>를 소유하고 있는 것이다. 래핑하고 있음. 박싱하고 있음. typedArry가 array<>(얘는 wrapper임)를 래핑 하고 있는것이다. 원래는 typedArray라고 얘기하면 안되고 typedArray가 array<integer> 쪽의 typedArray이다. 공식 용어는 IntArray가 typedArray가 아니라 얘낸 array 객체라고 불리고, typedArray라 불리는 애들이 Array<Int> 이다. 근데 내부엔은 boxing type인 IntArray형이 있음. 그래서 IntArray 형으로 부터 Array<Int>를 얻어내려면 toTypedArray를 해줘야한다. 이것이 언박싱 하는것이다.
  • 그래서 우리는 언제 IntArray를 써야한다? 박싱을 너무 많이 할것 같을때 사용한다. foreach 했다 map 했다가 난리 칠거면 매번 박싱하는 것 보다 처음부터 박스 객체를 갖는것이 유리하다. Array<Int>는 박싱이 필요 없을때 사용한다. 마지막엔 stringBuilder 의 최후가 toString인 것 처럼 얘한태 toTypedArray으로 마무리한다. IntArray가지고 실컷 괴롭힌 다음 마지막에 toTypedArray를 하면 된다.

    1. 컬렉션을 바꿔도 코드 전체를 바꾸지 않아도 된다.
  • 그래서 collection framework이 kotlin 전용인 덕분에 자바에 비해 코드가 좋아짐. 자바는 Array를 다룰 때 메소드가 상이다. length만 해도 array는 length ,list는 size이다. 배열은 index에 할당해야 하고 list는 n으로 할당 ? 하지만 코틀린은 그런 문제가 없음. 다 똑같은 메소드로 처리됨. 필터나 foreach같은 고급메소드 같은 경우에도 array에 쓸 수 있고, 코드가 더욱더 형을 넘어서 일관성을 갖게됨. 기존의 collection framework의 한계는 컬렉션 타입을 바꾸면 코드를 다 갈아엎었어야 됬는데 이제는 set list array를 뛰어넘는 그 윗쪽에 추상메서드가 있어서 진정한 의미에서 내가 리스트를 어레이로 교체할 수 있음. list를 set으로 교체할 수 있음 코드 하나도 안바뀐다.
  • 자바는 왜 그렇게 못했을까 ?

    1. 이유는 레거시 때문 순차적으로 추가 했기 때문이다. 그당시에 필요한 메서드를 그당시의 객체가 갖고 있었고 컬렉션프레임 워크의 상단을 정의할땐 그것밖에 필요없다고 생각 했던것임 다음 자료구조가 등장할때마다 레거시랑 호환해야 되기때문에 추가할 수 없으니까 구상 클래스마다 각 다른 클래스를 갖게 되는것임. 하지만 자바 플랫폼에서는 캐스팅이됨. 코틀린 컬렉션을 자바 컬렉션으로 캐스팅이 가능. 대표적으로 맵을 linkedHashMap으로 list는 ArrayList로 set은 linkedHashSet으로 캐스팅된다.
    2. 우리가 코틀린의 컬렉션 메소드를 사용하더라도 컴파일 될땐 근접하면 자바 메소드를 호출하는 거으로 모두 바뀜. 코틀린에서 list를 만들면 자바의 arrayList로 만들어냄. 그럼 이걸 래핑하는 객체로 무겁게 만드는게 아니라 진짜 해석해서 다 자바코드와 메소드로 바꿈 . 컴파일러가 무거운 추상층을 이용해서 플랫폼간 호환을 시키는게 아니라 플랫폼별 컴파일러가 굉장히 최적화를 많이함. 내이티브 코드에 근접하게.
    3. 코틀린 collection framework의 가장큰 차이점은 추상화된 객체의 세트이긴 하지만 그건 우리한태 주는 프로그래밍 인터페이스인거고 컴파일러는 클래스 라이브러리를 알고 있기 때문에 컴파일 시에 네이티브코드로 바꿀때 이 추상화 프레임 워크를 그대로 가져가는게 아니라, 해체해서 네이티브 코드로 바꾸는 것임.
    4. 그래서 기존에는 불가능했던 오버헤드와 사용성 간에 교환이 없어졌음. 둘다 좋게만듬.
  • 각각 (mutable)list, (mutable)set, (mutable)map 는 상응하는 of함수가 있음 그걸 이용해 인자를 여러개 보내서 한꺼번에 만들어낼 수 있음.
  • set은 각 항목들을 hash값으로 구분한다. set에서 그항목의 한개만 나온다고 보장하는 방식은 그 항목의 hash값이다. hash로 구분에서 같은게 으면 한개만 나오게 하는게 셋의 의미. 근데 우리가 만들었던 데이터 클래스는 해쉬를 계산할 때 안에있는 field의 값으로 계산한다. 따라서 값만 다르면 같은 객체더라도 값만 다르면 여러번 들어갈 수 있다. 즉 데이터 클래스 인스턴스 하나의 값을 A로 설정한 다음에 set에 넣고 B로 설정한 다음에 set에 넣으면 두개를 다른 값을 본다.그래서 데이터 클래스는 셋에 넣으면 안된다. 기본 적인 정책이다.
data class MyDataClass(val someNumericValue: Int, val someStringValue:String)

main(){
    val dataClassSet = setOf()
    var element = MyDataClass(1,"첫번째')
    dataClassSet.add(element)
    element['someNumericValue'] = 2
    element['someStringValue'] = "두번째"
    dataClassSet.add(element)
    //두개를 따로 구분해서 나올것임.
}
  • Spring이나 java에서 data class에서 문제가 많이 나옴. 해쉬코드 때문이다. 그래서 kotlin 1.3.6에서는 코드가 바뀌었음. 해쉬 코드 자체에는 고유 식별자가 들어 있고, 플러스 필드값도 들어있고 equals를 평가할 때 고유식별자를 제외한 부분을 equals로 평가하는 것으로 바뀌었음.
  • 함수는 다 익히고 이것 외에 10개 정도 더 있는데, first last, fold, foldright, reduce, take, dropWhile, takeWhile, zip, groupBy가 있음.
  • kotlin에서 지원하는 기본 함수에서 filter는 sql 의 where에 해당함. 일반 적인 sql문의 동작에 똑같이 배치하는게 좋다. 가장먼저 where나오고 groupBy가 나온다.
  • collection이 나오면 가장 먼저 해야하는게 filter이다. 그다음 group을 때린다. 그다음 sort 그다음 limit. drop take first last는 가장 마지막에 등장. 마지막에 map이 온다.
  • 코틀린의 모든 컬렉션은 corecursion이다. 그래서 corecursion 메서드인 hasNext와 next를 갖는다.
  • 컬렉션은 그 자체가 corecursion인 것임. 우리는 컬렉션이 아닌 corecursion에 익숙하지 않은것이지 컬렉션인 corecursion에는 익숙하다. 사실 아님 익숙하지 않음.
  • 통계함수(sum, count 등의 쿼리)를 사용하는 특징은 두가지로 나누는데 group by를 사용하는 경우와 group by를 사용하지 않는 경우 group by를 사용하지 않으면 전체 레코드셋을 한줄의 레코드로 축약한다. 이것을 어그리게이션(집합으로 모았다.)이라고 부름. 이 개념을 정확하게 fold와 reduce로 구현하는 것임.
  • fp와 oop

    • 리액티브 프로그래밍이 나오는 이유는 코리커젼에 대해 반응하는 어떤 리스너를 만들고 싶은 것임.
    • corecursion이 현재 모든 emit을 끝내지 않기 때문이다. corecursion이 값을 하나씩 만들어내는 과정을 emitting이라고 함.
    • emiter가 언제 끝날지 모르기 때문에 emitting 할때마다 반응을 하는수 밖에 없다. 근데 반응하는 emitter가 함수형 fp라고 불리냐면 반응하는 emitter가 함수로 줬기 때문에가 아니라 실제로 진짜 함수형이 되기 위해선 내가 코리커젼으로 받는 값이 함수가 되야한다. 내가 코리커전으로 받고있는 값이 계속 emitting해서 옵저버가 받고 있는 값이 함수인 것임. 그럼 저쪽에선 함수만 가득히 만든거지 실제 실행은 내가 그걸 인수해서 쓸때 실행됨. 지연 실행이 거기까지 이어져야 fp임. 근데 우리가 쓰는 rx는 값이 넘어온다. 값을 이미 만들어 오는데 이쪽이 아니라 저쪽에서 이미 비용을 지불한것임. 원래는 지불하지 않고 함수로 감싸서 와야되는데 그래서 rx와 fp는 다른것임. fp는 함수로옴 . 끝까지 지연이 가능함. emitting을 하는데 emtting이 다 함수로 되어져 있기 때문에 실행하는 코드가 하나도 없기 때문에 부하가 하나도 없다. 옵저버가 받아들이고 실행할때 진정으로 실행이 되는것임. 그전까진 연산을 통한 누적으로 함수를 쌓아두기만 했는데 지금은 함수를 해소하는 함수 쪽으로 있어서 해소하는 쪽의 사정에 따라서 함수가 실행되기 시작한다. 그전엔 설계에 있는 연산에 따라서 순차적인 실행이 되는건데 지금은 그렇지 않고 수행하는 쪽에서 걔를 하나하나 깔지 말지를 결정이 가능함. 심지어 skip도.
  • 리액티브 프로그래밍

    • 리액티브 메니페스토 : 선언문
    • 밑의 것들은 rx의 특징이 아니라 RX를 쓰는 이유가 이것들임. rx를 도입하고도 밑의 4가지를 달성하지 못하면 안쓰는게 낫다는 말임.
    • 반응
    • 복원
    • 탄력
    • 메시지 중심
    • rx쓰는데 파편화 안되고 일관적이려면 rx를 쓰는 stream 처리기가 그 어플리케이션의 전부이면 된다. 그래서 서버를 마이크로 서비스로 만드는 것임. 마이크로 서비스 안에 observer stream을 observer stream 하나로 이 마이크로서비스를 구성할 수 있는 단위로 쪼개면 된다. 그럼 마이크로서비스 하나가 observable stream이다.
    • 그리고 우리는 emiter에 반응하는게 쉽지 않음. 반응형 프로그래밍은 반응형박에 못씀. 가끔씩 취합해야할 경우엔 예를 들어 addEvent가 발생할때 마다 나한태 stream이 온다고 생각해 보자. 3번 오면 이벤트가 3개 들어왔으니 3개의 아이템이 쌓인걸 알아야 하는데 어케알지 ? 반응형인지 난 어케 상태를 갖지 상태가 없는데 어케알까? addItem이 올때마다 수신해서 어떻게 아이템 총개수를 갱신하는지? emitter가 2번 아이템과 2번 아이템까지 포함하는 총합도 같이 보냈기 때문에 알 수 있는 것임.따라서 observable의 반응형이 되는게 클라이언트라면 observerable로 짤때 많은 부하가 다 observerable 쪽으로 간다는 말임. obserbable이 emitting할때 자기가 갖고 있는 것임. sum 값도 같이 보내주는것임. 그래서 rx의 observerble을 쓸때 반응형 코드 쪽에 상태가 없으려면 상태를 포함한게 전부다 나한태 날라와야된다. emitting으로 그럼 상태는 다 observable쪽이 갖고 있어야한다. 사실 클라이언트는 서버와의 관계를 보면 일종의 옵저버다. 서버가 주는걸 받아드리는. 우리는 이 관계에서 똑같이 반응형에 대한 개념을 쓸 수 있음. 아이템을 나한태 던져주면 클라가 받아서 sum을 때릴거냐 아니면 서버가 아이템도 던지고 sum까지 던질거냐. 만약에 서버가 sum을 던지면 우리는 표시만 하면 되니까 반응형 클라이언트의 부담은 준다. 하지만 서버가 sum 값을 던지지 않으면 우리가(클라가?) 아이템을 던져야함. 아이템 더하는게 쉽지 않음. 이게 솔드아웃 됬는지 이런걸 다 확인 해봐야 되기 때문임.
    • 그래서 observable이 쓰기 어려운 이유는 emitter 즉 코리커전에 익숙하지 않기 때문이다. 코리커전이 emitting하는 정보를 어떻게 만드는지 익숙하지 않아서 서버가 리액티브해야하는데 클라가 상태를 갖고 접근하는것임. 그래서 이게 망가지는 것임. 서버에서 아이템만 주고 sum은 내가 더해야 되는거다 그러면 클라는 상태를 가질 수 밖에 없음. 얘는 반응형이여야 하는데 상태 의존적이기 때문임. 그럼 순수 함수가 아니지 순수함수가 되려면 상태가 없어야함. 에미터쪽이 상태를 가져야 한다는 말이지 . 그래서 옵저버블쪽이 다 상태를 가져야함. 그래야 rx의 클라이언트가 되는 함수쪽이 순수함수가 되는것임. 근데 이게 어렵단 말이지.
    • fp는 실현하기 굉장히 어렵고, observable도 rx를 써보면 그 효과가 잘 안나는 이유가 클라이언트 쪽에서 상태를 갖기 때문이다. 그래서 디버깅도 어려움.
    • 일반적으로 최종 클라이언트만 순수함. 채인에 있는 모든애가 순수하진 않음. 아까 sum 을 던지는 애도 상태를 가짐. 순수하지 않다는 것임. 이렇게 마지막에 순수성을 확보하기 위해 중간에 상태를 갖는 subject를 엄청 끼워넣게됨. 그래서 subject가 observable인 동시에 observer이다. 이 특수 관계 때문에 나온것. 최종 옵저버를 보호하기 위해서.
    • 코틀린의 확장함수는 rx의 기본적인 메소드를 기본적으로 제공한다.(297페이지)
  • 펑터, 어플리커티브 ,모나드

    1. 펑터

      • 코틀린의 펑터는 펑터가 아님 왜냐면 시퀀스로 바꾸지 않았기 때문이다.p.306
      • 리스트를 sequenceOf로 바꾸고 이걸 하면 펑터가될 가능성이 있음.
      • string이란 클래스가 생성자의 인자로 integer를 받는다면 toString을 내부에서 실행함.
      • 어짜피 string객체에는 getvalue란 메소드가 있어서 생성자로 다른 타입을 받아들일 수 있는데 toString이나 valueOf를 실행 시키면 string값이 나온다고 생각해보자.
      • 객체지향에선 굉장히 쉬움. 왜냐면 생성자로 받아들이는 애들을 field로 잡을 생각을 하고 있음. 그래서 메소드 호출하면 field를 이용할 생각을 하고 있기 때문이다. 즉 객체를 상태를 받는 상자라고 생각하고있기 때문에 그 상자안에 있는 값을 기억하고 있음으로써 메소드가 상태를 이용해서 얼마든지 만들어낼 수 있다는 사고방식을 갖는다. 이 모든게 상태를 가짐으로써 가능하게 되는것.
      • 그럼 함수는 대체 String이란 클래스를 어떻게 찾을까 ? 함수이다 그러면 String이라는 함수는 대체 무슨 함수일까 ? 내가 String이란 함수에게 A값을 넘겼는데 A가 integer나 boolean일 경우 String 함수는 나에게 뭘 반환해야할까 ? 함수를 반환해야함. 인자로 보냈던 값을 문자열로 바꿔서 출력해주는 함수를 반환하는 함수가 된다. 얜 객체가 아니라 함수를 사용함. 함수란 실행하기 전까지 연산을 지연시키는 놈임.따라서 함수형에서는 언제나 값을 평가하지 않고 지연할 수 있는 함수를 감싼게 그 type이되기도 하고 값이 되기도 함. 그러면 이 String 함수는 특정값 v를 받아들여 return으로 v.toString을 하는 함수를 반환하는 함수이다. 그럼으로써 객체와 굉장히 비슷한 일을 할 수 있음. 그함수를 호출해서 return값으로 함수를 들고 있다가 원할때 호출하면 문자열 값이 나오게 되는것임. 함수에서 모든 형은 맵함수가 반환되는 것임. map = type이 되는것임. 내가 아무값이나 보내면 string으로 바꿔주는(map)을 하는 함수를 나에게 반환하는게 형(type)임. 왜 map이 펑터가 되느냐 함수형의 기본형이 되느냐는 함수형에서 형이란건 String함수가 반환하는 함수 걔가 바로 타입(형)인데 걔는 한마디로 map임. 그래서 맵이 펑터이다. 그래서 내가 실행하기 전까지 실행 안된다. 그래서 IntegerArray에 맵을 걸어서 진짜 함수형에서 받으면 맵안에는 다 함수가 들어가 있음. 내가 map.{::toString}이란걸 걸어서 새로운 배열을 받아보면 그안에 나중에 호출해보면 string을 반환할 수 있는 함수가 들어가있을 것임. 하나하나의 형도 당연히 map이지만 컬렉션에 대한 맵도 그 컬렉션 전체를 string 타입으로 바꾼것임.
      • 펑터는 맵이다. 어떤걸 받아들여서 그 함수가 뭔갈 반환하는 함수니까 리턴값은 하나잔아 그럼 무슨 지랄을 했던간에 맵이다. 원래 본래의 값이 있는게 다른게 나왔잔아 걍 맵이야
    2. 모나드

      • 플랫맵은 배열안에 배열을 하나의 배열로 플랫화 시켜주는건데 그럼 배열로 보면 배열안에 배열을 다시 배열로 바꾸는건?
      • 모나드란 맵이 펑터라고 불렀는데, 펑터를 받는 펑터도 있겠지? 일관적인 값을 받아들이는 펑터도 있겠지만 펑터를 갖는 펑터도 있음. 근데 어떤 펑터는 펑터 string도 가질 수도 있고 펑터 integer도 받아들일 수 있는 a라는 펑터가 있다고 생각해보면 a는 내부에 string 또는 integer라는 타입을 가질 수 있고 나는 펑터를 반환할건데 이 펑터는 최초에 발현하기를 integer 펑터나 sting 펑터를 가질 수 있는 펑터임. 그럼 애를 까보면 integer펑터나 string펑터가 나옴 하지만 이걸 감싸고 있는것임. 둘중에 하나일지 모르는 애를 하나로 감싸고 있는것임. 안에 까보면 둘중 하나 나오겠지만 두개를 합쳐서 하나가 감싸고 있는것임. 감싸고 있는 펑터는 둘중 하나를 가질 수 있음. 이걸 ADT라고 하는데, 얘의 실체를 알기 위해선 실행해서 진짜 펑터를 알아야함. 근데 그 펑터를 얻어버리면 걔는 더이상 감싸고 있는 펑터와는 호환이 되지 않음. 그래서 얘한태 값을 얻는건 이펑터를 호출해서 안에 있는 펑터를 꺼낸다음에 이 펑터로 호출하는 값을 얻었지만 이건 끝나지 않고 값인건 둘째 치고 펑터가 풀려서 나오면 이 펑터가 더 이상 감싼 펑터랑 호환되지 않음 . 얘를 잠깐 풀어서 값을 얻은다음에 얘를 다시 감싸서 그 형같이 아까 감싼 펑터로 바꿔야지만 원래의 펑터 형과 호환이 되는것임. 이걸 모나드라고 부른다.다른 펑터를 내부에 감추고 있는 펑터를 모나드라고 한다.
      • 모나드를 특정타입의 펑터를 받아들이는 펑터라고 생각하면 되고 그 펑터안에는 A타입 또는 B타입의 펑터가 들어올 수 있는 펑터라고 생각해 봐라. 그럼 이 펑터안에는 반드시 A타입 또는 B타입 중 한만 들어가 있음. 우리는 이 펑터 안에서 A타입 또는 B타입 펑터를 추출한다음에 이 값을 얻어야 함. 이렇게 추출해 버리면 얘는 더이상 (A타입과 B타입을)감싸고 있는 펑터가 아님. 그냥 A타입 또는 B타입의 펑터일 뿐이다. 이러면 원래 감싸진 펑터형에 묻는다. 꺼내서 값을 넣은 다음 다시 감싸서 그 펑터를 다시 만들어 줘야한다. 그래야 다음번에 펑터연산을 다시할 수 있음. 펑터연산은 펑터에서 꺼내서 연산하는거임. 다시 집어넣을려면 다시 감싸놔야함. 그래서 모나드에 대표적인게 optional이다. 이때 모나드가 펑터라고 생각하면 이펑터는 뭘 받을 수 있따 ? null 펑터 또는 값 펑터를 받을 수 있음. 그래서 플랫맵이 왜 필요하냐면 모나드를 풀어서 값을 얻기 위해 필요한 것이다.
      • 원래 큰 배열안에 있는 값들은 펑터인거고 배열안에 있는 것들이 다 펑터인건데 배열을 풀어야 펑터를 얻을 수 있음.([a,b,[c,d]])
      • 그래서 플랫맵은 모나드로부터 값을 얻을때 쓰는거다. 플랫맵을 쓰면 모나드로부터 값을 바로 얻을 수 있음.
    3. 어플리커티브

      • 펑터를 이용하는 함수는 전체 다 어플리커티브이다. 내가 어떤 값을 직접 받지 않고 값 대신 펑터를 받아서 뭔갈 하는 함수들은 다 어플리커티브이다. 근데 왜 귀찬게 맵형 펑터를 만들어? 함수엔 형이 그것밖에 없다. 왜 펑터를 만드는지 이해를 못하는 이유는 상태가 존재한다고 생각하기 때문이다. 함수는 지연 연산의 연속이다. 함수형으로 프로그래밍을 짜면 메인함수가 호출되는 순간 내가 걸어놨던 모든 함수가 다 실행 되고있는것임. 하나도 상태가 미리 만들어지는게 없다. 다 미래에 이렇게 할거야라는 약속들을 계속 만들어갈 뿐이다. 타입 조차도 함수라서 미래에 너가 호출하면 내가 값을 줄게라는 것임.그러면 이 펑터를 어플리커티브에 주면 펑터를 받아들여서 어플리커티브를 실행하는 순간 풀어서 값을 얻어 연산을 한다. 내가 +2 라는 어플리커티브를 만들면 모나드로 인자로 뭘 받냐면 펑터 integer를 받을 것임 그럼 그 함수를 실행하는 순간 +2라는 함수를 실행하는 순간 펑터 인티저로부터 getValue를 그때 때리고 +2를 한걸 나한태 리턴하게 될것임. 그럼 무슨 이득을 얻냐면 펑터()해서 값을 얻는 행위를 어플리커티브를 실행할때까지 지연이 가능하다. 어플리커티브는 펑터의 메소드이다.
  • 코틀린은 당연히 그 함수를 타입취급하는 형태가 없는게 당연하겠지. 그함수만으론 표현이 안되. 펑터 인티저는 함수로 표현하면 ()integer밖에 안된다. 이게 타입인지 구분을 할수가 없음. 이게 특정 타입인지 구분할 수 없음 함수인것처럼 보이는거지. 이걸 상류타입이라고 하고 코틀린은 상류타입을 지원하지 않음. 상류타입은 함수의 시그니처를 기술하는 타입이 아니라 펑터타입을 기술하는 타입인데, 그럼 이제 함수형에서 상속이고 뭐고 공용객체는 뭐냐 물어보면 맵 형식이 똑같은 애들 다 맵형식이 하나에 인터페이스가 되는거고 각각의 펑터들을 만들어내는 함수들은 구상클래스가 되는것임. 내가 인티저를 받아 스트링을 반환하는 함수를 생각해보자 여기까지가 클래스 인터페이스의 정의다. 실제 구현체들은 구상클래스에 해당하는 것이다.
  • 309 페이지의 listOf의 1,2,3을 펑터라고 생각해보자 그럼 플랫맵안에 있는 함수가 하는건 i값을 이용해서 걔를 감싸는 펑터를 만들고 있는 것임. 펑터를 이용한 펑터를 만들고 있는 것임. 이게 모나드임.
  • 리스트가 펑터값을 다시 매핑하는 펑터를 가졌잔아. 펑터를 받아 들인 펑터를 갖고 있는 (리스트가 모나드라고 얘기한건 잘못된거고 ) i * 2가 모나드고 i*3이 모나드이다. 이 둘을 추출해 낼래면 flatmap으로 추출하게 되는것임. 근데 얘가 지연연산을 함수로 하지 않고 값으로 하기 때문에 값이 바로 나오는 것이다. 함수형이 었으면 함수로 왔을거잔아 그러면 flatmap으로 전개할때 까지는 실행이 안됨 . 플랫맵은 모나드의 메소드중에 하나임 값을 추출하기 위한. 플랫맵이 중요한게 아니라 모나드에서 표준적으로 값을 얻을려면 플랫맵이 필요한것 뿐임.
  • 310 페이지 보면 플랫맵이 모나드가 아니라 모나드의 메소드가 플랫맵임. 펑터펑터 두번 풀어야 되서 . 그래서 그 밑에 option을 보면 여기엔 none 또는 some이 들어갈 수 있는데 얘내 둘다 펑터다. 둘중 하나가 들어온다는 이야기임 option이란 펑터엔. 얘내를 깠더니
  • 인자 T가 들어와 타입이 다른 것을 리턴하면 그냥 맵이라고 생각하면 된다. 이 코드는 option이라는 모나드로 값을 감싸서 그렇지 T를 R바꾸는 맵하는 함수이다.
  • option 타입을 반환 할건데 Option.none이라는 모나드는 안에 다른 펑터로 none을 갖는것임. 근데 option.some이라는 모나드는 안에 some이라는 애를 갖고 있는것임. some이 value를 갖는데 플랫맵을 이용해서 value를 추출할수가 있다. 그렇지만 다시 이 프랫맵을 옵션으로 감싸서 반환 해야한다. 그래서 플랫맵이 있어야지만 맵을 쓸 수가 있따.
  • 밑 맵함수를 보면 T를 R로 바꾸는 tranform 함수가 오는데 플랫맵을 통해서 플랫맵을 통해 자기가 option.none 이던 option.some이든 그안에 fm함수를 통해 value를 추출해서 주면 걔를 transform이 T로 받아서 바꾸면 값이 될거니까 무조건 some으로 바꿔서 내부로 다시 들어옴. 그래서 맵을 할수가 있음. 플랫맵을 돌려야지만 펑터안에 펑터를 꺼낼 수 있기 때문에
  • 함수형 자바스크립트 책 걍 봐라

HTTP

2.Feelings

  • 책만 읽는게 아니라 코드로 쳐보면서 공부를 하다 보니 더 깊게 공부를 할 수 있는것 같다.
  • 물리적 시간이 절대적으로 부족하다.. 코틀린 공부할 시간도 부족하고, http 책읽는 시간도.. 직접 만들어보는 시간도 다 부족하다.
  • 시간이 너무 부족하다 .. 공부할 시간이 부족하다.

3.Findings

  • 평소에 너무 자주 기억해내려고 집착하지 말고, 내가 만들어놓은 패턴을 이용하여 적당한 망각을 이용하여 한다면, 더 효율적일 것이다.
  • 비동기 논블로킹의 문제로 콜백이 언제 호출되는지 예측이 불가능 한데, 우리는 반제어를 할 수 있는 프로미스나 completablefuture를 사용한다. 이것을 사용한다면, 콜백이 호출되는 시점을 우리가 제어할 수 있다.
  • 코틀린을 공부한다면 작은 코드를 직접 짜보고 하는 시간이 절대적으로 필요하다는 것을 알게되었다.

4.Future Action Plan

  • 내일은 반드시 리액티브 코틀린과 코틀린 데이터 컨테이너 펑터 모나드 어플리커티브 부분을 다시 복습한다.
  • 모두 끝내고, 코틀린 이번주 내용을 공부한다.

5.FeedBack


Written by@Zero1
This blog is for that I organize what I study and my thinking, feeling and experience.

GitHub