Functional Programming과 Lambda Calculus의 관계

람다대수(Lambda Calculus)가 왜 자꾸 함수형 프로그래밍(Functional Programming)에서 언급이 될까?

FP를 공부하면 끊임없이 쫓아오는 것이 람다대수 이야기다. 처음 공부할 때는 람다가 익명함수고 람다 대수는 그 연산이니까 함수형 프로그래밍이랑 비슷한 개념정도로 막연하게 이해하고 있었다. 하지만 공부를 하면서 조금씩 그 의미와 그로 인해 이해되는 것들이 있어 몇가지 정리해본다. 물론 아직도 공부 중이기에 더 얻는 깨달음이 있으면 업데이트는 말고 추가로 글을 쓰자.

우선 Labmda는 수학에서 함수를 의미한다. 우리가 중학교때부터 배워왔던 y=ax+b 와 같은 함수. 이것을 람다로 쓰면 𝜆x.ax+b 로 된다. 그래서 수학에서 사용하던 함수는 람다였고, 보는 것처럼 수학적 표현에서 함수명을 표기할 방법이 없다는 것을 보면, 프로그래밍상 익명 함수가 수학에서 사용한 함수 즉 람다가 된다. 함수형 프로그래밍은 수학식의 프로그래밍을 표방하고 있고, 이 수학적 표현을 모두 안고 만들어진 언어가 Haskell이다. Haskell 소개의 처음에 나오는 것이 Haskell == Lambda Calculus, 즉 함수의 연산을 표현하는 언어란거다.

그래서 함수형 프로그래밍에 최적화된 언어인 Haskell이 람다대수를 표현한 언어이기에 함수형 프로그래밍이란 람다대수를 표현하기 위한 것으로 이해해도 무방해보인다.

함수형 프로그래밍을 위한 함수형 언어의 특징은 뭘까?

함수형 프로그래밍은 위에 내가 이해한 바에 따르면 함수들의 연산을 프로그래밍으로 만들어내는 것이다. 이미 익숙한 언어들도 따지고 보면 함수로 만들어지는 것인데 뭐가 다르냐라는 반문을 나도 했었다. 하지만 여기에는 수학적 함수 표현을 위한 몇가지 조건들이 있다. (퓨어함수라던가, 일차함수라던가 하는 함수형 프로그래밍에서의 함수 기본 조건은 설명된 다른 글들이 많으니 그건 여기서 스킵)

아래 수식을 프로그래밍해보면서 설명해보자

f(x)=x+1

위 함수는 입력된 값 x에 1을 더한 값을 반환하는 함수이다. 이걸 단순하게 코드로 치환하면 아래처럼 될거다.

fun f(x: Int) = x + 1

근데 우린 수학 함수를 변경하는 것이다. 수학에서는 또 아래같은 일이 발생한다.

x=2*z

x가 우리가 생각했던 것 처럼 하나의 값이 아닌 함수인 경우다. 그렇다 람다대수는 함수와 함수의 연산이다. 그래서 기본적으로 함수를 인자로 받을 수 있는 함수의 선언이 가능한 언어만이 함수형 프로그래밍을 지원할 수 있는 것이다. 그리고 우리도 함수의 파라메타를 함수로 받도록 함수를 작성해야 진짜 함수형 프로그래밍(람다대수가능 프로그래밍)을 한다고 볼 수 있다. 당연히 모든 함수 파라메타를 함수를 받도록 만드는 일은 어렵고 현실에서는 비효율적이거나 불합리할 수 있다. 가능한 파라메타 선언을 함수로 하는 것이 함수형 프로그래밍의 장점을 활용할 수 있다는 것 정도로 이해하자.

그래서 다시 아래 처럼 만들어질 수 있겠다.

fun f(x: (Int) -> Int) : (Int) -> Int = { x(it) + 1 }

fun xfunc(z: Int) = 2 * z

fun main(args: Array<String>) {
    println(f(::xfunc)(2)) //5
}

위의 것도 상황에 따라다를 수 있으니 일단 코드가 맞고 틀리고 보다는 어떻게 하는 것이 함수형 스럽게 만드는 것인지 이해하는데 집중하자.

함수형 프로그래밍에 필요한 함수 조건을 이해했으면 이와 연관되어 한가지 추론이 더 가능하다. 함수전달이 되어진다는 것은 값을 인자로 받는 것과 함수의 실행 시점의 시점이 변경됨을 알 수 있다. 그렇다 느낌이 온다. 함수형 프로그래밍을 통해 최종 연산되는 시점은 인자가 함수가 입력되는 시점이 아닌 leaf 함수에서 최종적으로 실제 값이 들어오는 시점이다. 일반적인 함수 실행 시점이 함수 호출 시점인 반면에 함수형 프로그래밍에서는 함수의 호출이 되더라도 인자가 값이 아닌 함수인 경우에는 해당 시점에 연산되지 않는다. 많이 들어본 lazy evaluation이 된다.

이렇게 함수형 프로그래밍이 가능한 언어는 최소한 함수를 인자로 받을 수 있는 함수 선언이 가능해야하고 lazy evaluation이 지원되야한다. 근데 지금 작성하고 보니 함수를 인자로 받을 수 있다는 것이 lazy evaluation을 지원 한다는 의미로 받아들여도 되지 않을까 한다.

함수형 프로그래밍은 수학수식으로 표현하기 위해 시작된 만큼 람다대수를 이해하는 것이 함수형 프로그래밍의 특장점을 살린 프로그래밍을 할 수 있는 것이라 생각된다. 사내에서 스터디중인 Haskell이 적응되면 될 수록, 함수형 프로그래밍의 장단점이 보이기 시작한다. 함수형 프로그래밍이 수학 함수의 표현을 위한 것이라고는 하지만 함수응용을 통한 프로그래밍이 가지는 프로그래밍적(?) 장점들이 있기에 이제는 꼭 수학 함수의 표현을 위해 프로그래밍 한다기 보다 함수형 프로그래밍의 장점때문에 함수형 프로그래밍이 인기가 있어진게 아닌가 생각된다.

프로그래밍 패러다임은 어느 것이 최고다라는 것이 없고, 내가 해결하려는 문제에 가장 적합한 패러다임인지에 대해서 평가한다. 다음 글에서는 내가 체득하고 있는 함수형 프로그래밍 패러다임의 장점들을 적어보고 어떤 상황에서 사용하면 좋은지에 대해 적어보겠다.

---------------------------------------- "Functional Programming과 Lambda Calculus의 관계" 끝 ----------------------------------------
2019년 새해 목표

나이의 앞자리가 바뀌며 불혹의 시기에 접어들었다. 세살 버릇 여든까지 간다고 나의 게으름이 단번에 고쳐지지는 않겠지만, 새해이기 때문에 또 목표를 정해본다.

금년의 목표는 아래와 같다.

  • 88kg 이하로 감량 & 유지 : 건강이 많이 안좋아졌다. 가족들을 위해서라도 건강하자.
  • 중국어 회화 가능하도록 공부하기 : 사실 제대로 하는 외국어가 하나도 없지만 아예 못하는 중국어를 초급 수준까지만이라도 만들어 보는게 목표. 내년엔 스페인어? ㅋ
  • 일주일에 한번 글쓰기 : 프로그래밍 관련햇 뭔가를 학습하는 것을 목표로 하고 싶었지만, 목표가 특정 기술이나 언어에 한정되는 것이 싫어서 좀 더 큰 방법으로. 글쓰기가 학습에 제일 도움이 되고 내가 쓴다면 아무래도 하고 싶은 공부 관련 일 테니까

이렇게 일주일에 한번 글쓰기 하나를 했다고 치고 싶다. 사실 목표를 위해 편집기를 켜고 계속 딴 짓 중이라 진도가 안나가서 일단 이거라도 먼저! 형식적으로라도 지키기 시작하면 뭔가가 나올거라 믿는다.

시작이 반!

---------------------------------------- "2019년 새해 목표" 끝 ----------------------------------------
생소해 Kotlin

생소해 Kotlin #1

Kotlin은 JVM 언어이고 Java와 호환성을 가지고 있기 때문에 Java와 비슷한 맥락을 가지고 있을 것이란 어설픈 추축들을 해왔다. 하지만 보면 볼 수록 익숙하지 않은 부분들이 눈에 들어와 그것들을 정리해보자.

생소한 Type

Kotlin 코드를 보면서 생소한 Type들에 대해 모아봤습니다.

Unit

  • Javadoc 설명 : The type with only one value: the Unit object. This type corresponds to the void type in Java.
    • Java의 void와 같은 것이라고 함. 값 하나에대한 타입이 void? 역시 이런 것은 예제가 필요함.
  • Unit-retruning functions
  fun printStr(value: String): Unit {
    println(value)
  }

  // ^^^ vvv 위아래 동일한 것임.

  fun printStr(value: String) {
    println(value)
  }
  • 아마도 함수 레퍼런스를 선언할때 void 함수의 표현을 위해 Unit type이 사용되어 Java의 void와 동일하다고 하는 것 같다.
var voidFunc: () -> Void = {} //요런게 없기 때문에
var voidFunc: () -> Unit = {} //요렇게 하는 것.

Any

  • Javadoc 설명 : The root of the Kotlin class hierarchy. Every Kotlin class has Any as a superclass.

  • Java의 ‘Object’와 매칭되는 super class. Object와 다른 것은 Any가 apply let run to 등을 extension으로 가지고 있다는 점.

Nothing

  • Javadoc 설명 : Nothing has no instances. You can use Nothing to represent “a value that never exists”: for example, if a function has the return type of Nothing, it means that it never returns (always throws an exception).

  • 함수가 명시적으로 return이 존재하지 않는다고 표기하기위한 class. 한마디로 종료되지 않는 blocking 함수 같은 것. 이것을 종료하려면 exception 만이 가능

  //일반적인 return 값이 없는 void 함수
  fun noReturn() {
    println("This method returns nothing.")
  }

  //return이 존재하지 않는다고 선언한 함수....
  fun noReturn() : Nothing {
    println("This method never returns.")
  }
  • 위의 Nothing 반환 함수는 Nothing type의 값을 return 하라고 error가 발생한다. 하지만 목적은 return을 안하는 것이기 때문에 선언 시에 생각했던 로직이 변경되었거나 처음 디자인과는 다르게 잘못 만들어진 부분이 있는 것이니 return Nothing 선언을 변경하거나 코드 수정을 해야한다. 얼마나 유용할지는 아직 코알못이라 잘 모르겠네.

Pair / Triple

  • Javadoc 설명 : There is no meaning attached to values in this class, it can be used for any purpose.
    • 말 그대로 다양한 목적으로 쓸수 있게 단순히 object에 값을 더하는 것 뿐. Pair와 Triple의 차이는 단지 그 값의 개수가 2개냐 3개냐 차이
  • 사실 이 기능은 함수 디자인이나 실제 구현시 정말 많은 편의성을 부여한다. 함수 구현시에 한 개 이상의 값을 반환하는 경우에 방법이 없어 Bean을 따로 생성했던 경험이 있는 사람이라면, 그 확장성이 더 없는 Bean class를 선언하고 getter/setter를 만들고 헀던 귀차니즘을 단박에 줄일 수 있다. Generic으로 각 값의 Type도 지정이 가능하니 거의 완벽! 이것만 봐도 Kotlin은 Java에 개발 편의성을 많이 고민했단 생각이 든다. 정말 개발자 친화적이야. 개인적으로 이 Type은 완소!
  • Lombok은 어차피 kotlin의 data class와 마찬가지이니 단순 getter/setter줄이는 것 이외에도 장점이 많다.
  //Java
  public Profile getProfile() {
    Profile a = new Profile();
    a.setName("nurinamu");
    a.setAge(10);
    return a;
  }

  public static void main(String args[]) {
    Profile me = getProfile();
    System.out.println(me.getName());
    System.out.println(me.getAge());
  }

  class Profile{
    private String name;
    private Integer age;

    public void setName(String name) {
      this.name = name;
    }

    public String getName() {
      return this.name;
    }

    public void setAge(String age) {
      this.age = age;
    }

    public Integer getAge() {
      return this.age;
    }
  }

  //kotlin - wow!
  fun getProfile(): Pair<String,Int> = Pair("nurinamu", 10)

  fun main(args: Array<String>) {
    var (name,age) = getProfile()
    println(name)
    println(age)
  }

이 글에서는 생소한 Type들만 쓰는 것으로. 사실 Keyword, Syntax도 한번에 쓰려고 했는데 static blog에서 글이 길어지면 호흡도 길어지고 publishing 타이밍도 놓쳐서 늘어지느라 나눠 쓰기로!

---------------------------------------- "생소해 Kotlin" 끝 ----------------------------------------
Kotlin 첫 Commit Push

오늘 회사 코드에 Kotlin 첫 commit을 push 했다.

Kotlin 파일 하나 작성한 것 뿐인데. 왜이리 호들갑인가 할 수 있겠지만, 근래에 다시 떨어지고 있는 코딩에 대한 재미를 다시 찾을 수 있을 것 같은 언어이고 이 기회에 회사 업무에 써볼 수 있게되는 초석이되었기에 기념을 하려고 글 한 조각 남긴다.

Kotlin과 나

  • Kotlin에 대한 설명은 굳이 안해도 너무 유명.
  • Google Appengine를 써오면서 Java7에 생각이 고착화되고 많이들 회자되는 RX나 FP등의 Modern Language Code들의 트랜드를 많이 놓치고 있었다. Kotlin을 공부 함으로써 뒤쳐진 부분을 좀 채워나갈 생각.
  • 사실 Kotlin 보다 먼저 진행하려했던 Python은 이상하게 계속 진도를 뽑지를 못하고 있다. 원인은 동기부여 부족? ML을 공부하고 TensorFlow를 쓰면서 Python을 습득하려 했는데 오히려 ML 공부에 빠져들면서 정작 TF는 근처에도 못가고 있다. 결국 Python 시작도 못하는 아이러니.
  • Kotlin push 기념을 핑계 삼아 다시 블로그에 글을 올리고, kotlin 공부하면서 느껴지는 java와의 비교 점들을 남겨보고 싶다. 어느 언어가 뛰어나다의 이야기보다는 써보니 이런 점이 편하고 불편하다의 개인 감상을 남길 계획.
---------------------------------------- "Kotlin 첫 Commit Push" 끝 ----------------------------------------
2016.08.08 오늘의 구글링

오늘의 구글링

class안 private 변수 선언만 있고 setter가 없는데 잘되는 코드가 있다. 뭐지?

  • query : project lombok
    • Project Lombok
    • Java코드의 반복작업이되는 Getter/Setter 뿐아니라 toString/hashcode 등 자주 overwrite되는 함수들을 다이어트 시켜주는 Annotation base processor다.
    • 이제야 이런 것을 알다니; 다른 사람 코드 보다가 너무 늦게 입문.
    • 역시 무식하면 손발이 고생이다를 꺠우침.
  • 결과 : lombok을 써보고 혹시모를 단점들도 좀 찾아보자
---------------------------------------- "2016.08.08 오늘의 구글링" 끝 ----------------------------------------