[Scala] Coursera – Functional Programming in Scala 강의 후기

스크린샷 2015-03-13 오후 1.55.42

드디어 다 들었다. 지난 2월 첫째주부터 듣기 시작했던 Coursera 의 Functional Programming Principles in Scala. 목표는 1주일에 듣는 것이었는데 한달이 넘었다. 솔직히 말해 내가 게으른 것도 있었고, 영어의 문제도 있었고..(Martin Odersky의 약간 느린 유럽식 발음..) 가장 중요한건 개념이 도통 잘 이해가 안가는 것이 가장 컸다. JavaScript를 할 줄 안답시고 함수형 프로그래밍을 조금 무시했는데, 정말 스칼라는 프로그래밍 언어론의 개념이 풍만하게 들어간 언어이다. 강의를 다 들은 지금 시점에서도 아직까지 잘 100% 이해가 가지는 않지만, 한가지는 확실히 알겠다. 왜 스칼라가 OOP와 함수형 프로그래밍의 조합이라고 말하는 것인지를 말이다.

내가 배운 것 중에 가장 큰 것은 결국 함수다. 클래스나 함수, 심지어 변수의 인자로 함수를 넘길 수 있다는 것이다. OOP에서 내가 가장 중요하게 생각한 것은 정의된, 혹은 사용중인 Class를 다른 클래스의 인자로 넘기면서(Call by Reference) 클래스들 간의 이원화가 가능하고, 이를 통해 진정한 OOP로써의 구조를 형상화 할 수 있다는 것이다. A클래스와 B클래스 간의 데이터에 대한 통신을, 특히 내가 주로 다루는 자바에서는 레퍼런스를 넘김으로써 어떠한 데이터에 대한 흐름이 끊김 없이 각 클래스를 거쳐가며 해당 기능에 맞게 가공되기 때문에 말이다.

그런데 스칼라는 이와 비슷하게, 혹은 조금 더 다르게 들어간다. Call by Reference는 맞다. 하지만 정말 함수를 인자로 넘길 수 있다는 것. 함수 자체가 인자가 되기도 하고, 심지어 리턴형이 함수가 되기도 한다. 처음에는 정말 정신없을 정도로 이게 무슨말인지 잘 이해가 되지 않았는데, 강의를 들으며 exercise를 따라서 코딩하다 보니 어느정도 코딩에 대한 감은 잡히더라.

스크린샷 2015-03-13 오후 1.51.28

그래, 많이 배우긴 했다. 하지만 아직도 명확히는 알지 못한다. 더 공부를 해야하는데, 그러자니 실무를 진행할 수가 없다. 그래서 내 나름대로 중요한 개념을 정리한다.

  • Higher-order Functions: 함수에서 인자로 함수를 사용하는 것을 Higher-order function이라고 한다. 함수가 first-class value로써 제공된다는 어떠한 틀에 대한 설명이다. 참 신기한 개념이기도 하고, 중요하기도 하다. 강의에서는 제대로 이해하지 못했는데, Scala by Example 을 보고 이해했다. 🙂
    • def filter(p: T => Boolean): Array[T] 와 같은 함수 정의에서 인자 p가 T 타입의 Boolean을 return 하는 함수를 갖는 함수 filter와 같은 관계.
    • 조금 복잡해 보이긴 해도, 인자에 함수가 들어간다는 것은 결국 클래스에서의 레퍼런스 통일 처럼 함수 자체도 구태어 전역 함수로 빼지 않고 (보통 자바에서 Util이라는 패키지를 만들어서 사용하곤 했는데..) 필요한 함수를 가공해서 넘겨줌으로써 보다 더 최적화된 통일을 가져올 수 있다는 장점이랄까.
  • Call-by-name/Call-by-value: 스칼라는 기본적으로 call-by-value을 취함으로써 induction(귀납)단계를 최소화 한다. 하지만 call-by-value가 가끔은 쓸때없는 repeated evaluation을 발생시키기 때문에 => 를 사용해서 call-by-name방식으로 바꿀 수 있다.
    • 한 예로 def first(x: Int, y: Int) = x 같은 함수 정의에서 y인자를 call-by-name으로 바꾸려면 def first(x: Int, y: => Int)와 같은 식으로 하면 된다.
  • Case Classes and Pattern Matching: case class는 getter/setter가 기본적으로 제공되는 일종의 모델 클래스이고 pattern matching은 기존의 switch문에서 더 나아가서 Case를 함수/변수/상수 의 개념까지 확장한 것이라고 생각한다.
  • Trait vs Abstract Class: 둘다 추상 클래스는 맞긴 한데, 사실 차이가 좀 애매하다. 중요한 차이점은 트레잇은 생성자가 없지만 다중 상속이 가능하다는 것이다. To trait, or not to trait? 에서는 클래스에 대한 효율성이나 중요성이 크지 않다면 Trait으로 하면 알아서 해준다는 식으로 설명되어 있다. 반대로 중요하다면 Abstract Class를 사용하라는.. 결국 추상클래스는 상속이나 효율성을 위해 보통 사용하니깐. 하지만 나는 아직 코딩 레벨이 그리 크지 않아서 실상 인터페이스 이외에는 잘 사용을 하지 않는다.. Trait개념이 있어서 개인적으로는 좀 효율성 측면에서는 편하다고 생각된다. (결국 Effective Java에서 있던 대부분을 효율적으로 끌어올린게 스칼라라는 말인가..)
  • For문: 자바의 For문과 다르게 정말 스칼라의 For문은 정말 하나의 구문이다. for(p<-1 to 10 if p%2 == 0) yield(p) 와 같이. 혹은 for{} 와 같이 중괄호를 사용해 다양한 구문을 넣을 수도 있다. 아래와 같은 것 보면 좀 신기하긴 하다.. 확장된 포문이랄까.
    • for {
      i <- List.range(1, n)
      j <- List.range(1, i)

      if isPrime(i+j) } yield {i, j}

  • Immutable Collections: 다양한 collection에 대해. 특히 Set과Sequence에 대해 속성 위주로 설명한다. 특히 스칼라에서의 Map은 기본적으로 튜플 개념이라서 DB의 튜플을 이해한 상태에서 이에 대한 가공은 뭔가 상당히 DB스럽다고 할까.. 그리고 기본적인 속성들 또한 매력적이다.
    • map: 개개 원소에 대해 패턴에 따라 가공해준다. 뭐 val abc = List(“a”,”b”,”c”) 에서 abc.map(_.toUpperCase) 로 하면 자연스래 List(“A”,”B”,”C”) 가 되는것처럼..
    • foreach: map이랑 비슷한데 리턴값이 없다. 위에서처럼 abc.foreach(_.toUpperCase) 를 하면 abc 자체가 변하고, map으로 하면 새로운 리소스에 변환값이 할당된다.
    • filter: 인자의 패턴에 따라 참/거짓으로 나뉘어 참인 값들만 따로 리턴한다.
    • zip: 인자의 리스트와 함께 배합한 새로운 튜플 단위의 리스트를(쌍으로) 리턴한다.
    • partition: 인자에 따른 상태에 따라 참/거짓으로 리스트를 나뉜다. filter가 참값만 가지는 것에 비해 partition은 전부 가진다.
    • find: 인자의 상태를 만족하는 첫번째 요소를 리턴.
    • drop(i): 첫 i개를 떨군다.
    • foldRight/foldLeft: 리스트를 트리로 쭉 나눈 다음, 인자값을 왼쪽->오른쪽 혹은 오른쪽->왼쪽 으로 적용하여 합친다. 이건 강의를 들어보면 더 자세히 알 수 있다.
    • flatten: 나뉘어진 리스트를 합쳐서 상위 리스트로 합친다. val abc = List(List(1,2),List(3,4)) 를 abc.flatten 하면 abc = List(1,2,3,4) 로 만든다.
    • flatMap: flatten할때 map처럼 개개 인자에 패턴을 적용시킨다. 위에서 abc.flatMap(x => x.map(_ * 2)) 하면 res4: List[Int] = List(2, 4, 6, 8) 가 리턴된다.
    • 기타 자세한 예제는 http://twitter.github.io/scala_school/ko/collections.html 를 참조한다.
  • Lazy Val: Lazy val은 함수에 evaluation이 되는 시점을 def처럼 늦춰준다. val 은 할당 시점에 초기값이 할당되며 업데이트 되지 않지만, lazy val은 호출 시점에 evaluation이 되기 때문에, 함수 등의 리턴값을 가지는 변수를 받는 경우에는 혼선을 줄일 수 있고, 반복적인 evaluation을 줄여서 리소스 낭비를 줄인다.

 

스크린샷 2015-03-13 오후 1.54.22

 

 

결국 따지고 보면 스칼라란, 자바의 최신, 그리고 효율성을 강조한 버전이랄까. 물론 자바 8버전 부터는 함수형 프로그래밍을 지원하긴 하지만, 결국 자바란 자체가 애플릿에서 발전한 만큼 대규모 분산처리를 진행하기에는 효율성이 저조한 면이 있는 것은 사실이다. 느낌은 지금까지의 자바가 큰 패러다임의 변화 없이 응급처치 방식으로 지속해서 기능을 추가했다는 느낌이다. 아마 그래서 패러다임 자체를 바꾼 스칼라 라는 언어를 마틴 오더스키가 창시한 것일지도..

특히 Akka와의 조합을 통해 Actor방식으로 작동하는 것은 O/S시간에도 배웠지만 Mailbox식의 C/S처리 구조는 앞으로의 대규모 자원처리에서는 필수적인 요건이 아닐까 싶다. 기존처럼 Listen방식의 소켓 통신으로는 한계가 있기 마련. 게다가 확장도 어렵고.. 단순히 자바 소켓통신만 생각해도 그렇다. 이를 웹과의 연동으로 적용하기가 얼마나 어려운지..

아직 DSL이나 SOA등에 대해 공부하지는 않았지만, 분명 최근의 프로그래밍을 반영한 스칼라라면 효율적인 설계 혹은 상위 라이브러리가 적용되어 있을것이라고 생각한다. 허나 병렬처리에 있어서는 아직 잘 모르겠는게 현실이기도 하고..

스크린샷 2015-03-13 오후 1.52.07

 

자바 좀 한다고, 그리고 자바스크립트 좀 한다고 OOP랑 함수형 프로그래밍 이해한다 생각하면 오산이다. 단순히 콘솔에서 몇 가지 명령어쯤이야 쳐보겠지만 이를 이용한 Play Framework에서의 설계부터 코딩까지 잘 이해하지 못한다. 얼마전 임백준 님의 칼럼 ‘개발자 왜 에서 시작하라.” 에서 본 것과 같이, 프로그래머가 새로운 언어를 습득하는 데에는 두 가지 방법이 있다고. 첫번째는 부딪쳐 보면서 배우는 것과 두번째는 충분한 공부를 통해 이해하는 것. 솔직히 가슴에 손을 얹고, C++ 이외에 내가 레퍼런스 북을 참조해서 하나 둘 습득해간 언어는 거의 전무하다 시피 하다. 자바프로그래머로써 부끄럽게도 자바 레퍼런스 북 한번 떼지 못했다. 토비의 스프링 책을 볼 때에는 의존성 주입 등의 개념이 너무 신기해서 몇 일간 보긴 했는데 그런 프레임워크적인 것을 제외하면 언어에 대해 의구심을 가져본 적이 참으로 부끄럽게도 없다는 것이다.

하지만 스칼라라는 언어는 정말 힘들다. 코세라 강의를 다 들은 지금 시점에서, 중요한 것이 무엇인지는 알겠는데 아직도 이를 세세하게 적용하자니 두려움이 앞서는 것은 사실이다. 게다가 5주라는 희생의 시간도 있었고.. 프로젝트 마감일도 다가오는 시점에 더 이상 늦출 수도 없는 실정이다. 아마 많은 개발자들이 이런 생각으로 새로운 언어를 충분히 학습하는 데에 Cookbook류의 책을 선호하는 것이 아닐까. 게다가 한글로 된 책도 없으면 더더군다나 cookbook류의 이미지 많은 책이 더 쉬우니깐..

그런 함정속에서 살짝 놀아난 것 같다. 결국 모든 학문이 그렇듯, 왜 라는 것에 대한 의구심을 가지고 뿌리깊은 패러다임의 원칙을 공부해야 한다. 그나마 대학 4학년을 마치고 최근에 와서야 석사과정 공부를 결심한 지금, 공부의 필요성을 깊게 느낀 실정에서 그런 생각을 가진 것을 다행이라고 여긴다.

개인적으로 이 coursera강의는 이런 생각을 가지고 접근했으면 좋겠다. 우리가 기존에 봐왔던 무작정 따라하기 식의 Lecture가 아니니깐. 판서를 충분히 활용해서 귀납법과 수학적 추론을 적절히 사용해서 실효성에 대해 증명하는 것이 이 강의의 핵심이다. 결론은, 공부해야 한다! 아직도 스칼라스쿨의 수 많은 예제와, 150장의 Scala by Example, 게다가 Programming in Scala 라는 책까지 남아있으니. 공부할께 산더미 🙂 그래도 일단 큰 틀과 핵심 기능은 익힌 셈이니, 이를 적용한 프로젝트는 진행할 수 있을 것이라고 기대된다!

5주의 시간, 길다면 길고 짧다면 짧았다. 하지만 5주의 투자로 패러다임을 이해한 나는 5년을 번 셈이다.

이제는 작품을 만들 때! 조금씩 공부도 하되, 적용할 수 있는 기쁨의 시간이 도래할 것 같다 🙂