INDIES

[Learn You a Haskell For Great Good!] 1. 소개 본문

Haskell/LYAH

[Learn You a Haskell For Great Good!] 1. 소개

jwvg0425 2015. 1. 14. 22:01


Learn You a Haskell For Great Good!


이 게시글은 http://learnyouahaskell.com/chapters 사이트에 올라와있는 글을 한글로 번역한 것입니다.의역이 굉장히 많으니 주의...


1. 소개


이 튜토리얼에 관하여


 Learn You a Haskell for Great Good에 온 것을 환영해! 네가 이 걸 보고 있다는 건, 네가 Haskell을 배우고 싶어한다는 거겠지. 음, 넌 올바른 장소에 잘 찾아왔지만 이 튜토리얼에 대해 먼저 조금 이야기해야할 게 있어.

 난 Haskell에 대한 내 지식을 견고히 하기 위해, 그리고 Haskell을 새로 배우려는 사람들을 내 관점에서 도울 수 있을거라고 생각했기 때문에 이걸 쓰기로 결정했어. 인터넷 상에는 이미 Haskell에 관한 튜토리얼들이 많이 존재하고, 내가 처음 Haskell을 배울 땐 어느 한 곳에서만 배우지 않았어. 나는 Haskell을 다양한 튜토리얼과 기사들을 읽으면서 배웠어. 왜냐하면 각각은 서로 다른 방식으로 같은 개념을 설명하거든. 그렇게 여러가지 강의자료들을 접하면서, 나는 내 머릿속의 지식 조각들을 하나로 모아 정리할 수 있었어. 그리고 이 튜토리얼은 Haskell을 배우기 위한 유용한 자료를 하나 더 늘리고자 하는 시도야. 너는 네 마음에 드는 강의 자료를 찾을 수 있는 더 큰 기회를 갖게 됐다고 할 수 있지.

 이 튜토리얼은 명령형 언어(C, C++, Java, Python..)를 접해본 적 있지만 함수형 언어(Haskell, ML, OCaml...)로는 프로그래밍 해본 적 없는 사람들을 대상으로 만들어졌어. 하지만 네가 어떤 프로그래밍 경험도 없다 하더라도, 스스로 강의를 따라올 수 있고 Haskell을 배우려는 의지가 있다면 충분히 이 강의를 이해할 수 있을거라고 생각해.

 freenode network의 #haskell 채널은 뭔가 막혔을 때 질문하기 아주 좋은 장소야. 그 곳 사람들은 굉장히 멋지고, 새로 배우는 사람들을 끈기있게 이해시켜줘.

 난 Haskell을 제대로 배우기까지 대략 2번정도 실패를 경험했는데, 왜냐하면 이 언어는 처음에 내가 보기에 너무 괴상해보였을 뿐만 아니라 이해하기도 힘들었기 때문이야. 하지만 한 번 그 개념들이 "명확(clicked)"해지고, 그 제일 처음의 허들을 넘고 나자 순풍에 돛단 듯 이해가 되더라고. 내가 말하고 싶은 건 이거야. "Haskell은 멋지고, 만약 네가 프로그래밍에 관심이 있다면, 이게 처음에는 이상해 보일지 몰라도 넌 Haskell을 정말로 반드시 꼭 배워야해." Haskell을 배우는 건 제일 처음 프로그램을 배울 때와 많이 비슷해. 정말 재밌어! 이건 네가 다른 방식으로 사고하길 강요하고, 우리를 새로운 영역으로 나아가게 해주지.


그래서 Haskell이 뭔데?


Haskell은 순수한 함수형 프로그래밍 언어야. 명령형 언어에서 너는 컴퓨터에게 일련의 임무(task)를 주고 그것을 실행시킴으로써 원하는 결과를 얻었지. 그리고 그 임무들은 실행되는 동안 상태가 바뀔 수 있어. 예를 들어서, 네가 a라는 변수의 값을 5로 지정한 다음 뭔가 작업을 하고, 그 다음 a의 값을 다른 값으로 바꿀 수 있지. 너는 어떤 행동들을 여러번 수행하기 위해 흐름의 구조를 제어할 필요가 있어. 순수한 함수형 언어에서 너는 위에서 이야기한 것같은 방식으로 컴퓨터한테 뭘 해야할지 시킬 필요가 없어. 순수한 함수형 언어에서는 대신에 해야할 일이 '뭔지'를 말해줘야 하지. n 팩토리얼이란 1부터 n까지의 곱을 말하는 것이라든가, 숫자 list의 합이란 첫번째 숫자와 list의 나머지 모든 숫자들의 합이라는 것이라든가, 그런 것들 말이야. 넌 이런 걸 함수의 형태로 나타낼 수 있어. 또 Haskell에서 너는 어떤 것에 대한 변수를 만들고 그 값을 나중에 바꾸는 것과 같은 걸 할 수 없어. 만약 네가 a를 5라고 했다면, 이후에 넌 a의 값을 다른 걸로 바꿀 수 없어. 이미 a는 5라고 말해버렸으니까. 어때, 거짓말같아? 순수한 함수형 언어에서, 함수들은 부수효과(side-effect)를 가지면 안 돼. 함수가 할 수 있는 유일한 건 어떤 걸 계산하고 그걸 결과로 돌려주는 거야. 처음에는 이게 어떤 한계를 갖고 있는 것처럼 보이겠지만 실제로는 이 특성은 아주 멋진 결과를 가져와. 만약 함수가 같은 인자들을 가지고 호출됐다면, 이건 언제나 같은 결과를 돌려줄 것이라는 게 보장이 된다는 거지. 이걸 참조 투명성(referential transparency)이라고 부르고, 이건 프로그램의 동작을 컴파일러가 판단하게 해줄 뿐만 아니라 함수가 올바른지 쉽게 파악하고 증명할 수 있게 해주고, 더 복잡한 함수들을 단지 간단한 함수들을 이어붙이는 것만으로 만들 수 있게 해줘.

 Haskell은 게을러(lazy). 무슨 뜻이냐면, 특별히 이야기하지 않는 한 Haskell은 그 결과를 정말로 보여줘야만 하기 전까진 어떤 계산도, 함수의 실행도 하지 않아. 이건 참조투명성 덕분에 잘 동작하고, 네가 프로그램을 일련의 데이터의 변화(a series of transformations on data)로 생각하게 만들어줘. 또 이건 무한대 크기의 데이터와 같은 멋진 것들을 다룰 수 있게 해 주지. 변하지 않는 숫자 list인 xs = [1,2,3,4,5,6,7,8]과 모든 원소의 값을 2배로 늘린 list를 돌려주는 doubleMe라는 함수가 있다고 하자. 명령형 언어에서 네가 xs의 모든 원소의 값을 8배 늘리고 싶다면, doubleMe(doubleMe(doubleMe(xs)))라고 하면 될거야. 그러면 아마 list가 바로 함수에 넘겨져서, list의 값을 복사한 다음 그 값을 2배로 늘린 list를 반환할테고, 다시 두 번째 함수 호출에서 마찬가지로 4배가 되고, 세 번째 함수 호출에서 8배가 되어 반환되겠지. 게으른 언어(lazy language)에서는, 결과를 보여달라는 말 없이 doubleMe를 호출한다면 이건 program이 끝날 때까지 "그래 그래. 나중에 계산할게!"라는 결과만 돌려줄 거야. 하지만 네가 한 번 결과를 보고 싶다고 말한다면, 첫 번째 doubleMe 호출은 두 번째 doubleMe에 대해 지금 당장 결과를 원해! 라고 말할 거고,  두번째 녀석도 세번째 doubleMe에 대해 결과를 원한다고 말하겠지. 세번째 호출은 마지못해서 2배로 값을 증가시킨 list를 리턴할 테고, 두 번째 녀석도 이걸 받아 4배로 값이 증가한 list를 첫번째 녀석에게 돌려주겠지. 그래서 이건 네가 진짜로 꼭 필요할 때 딱 한 번만 리스트를 전달해. 이런 방식으로 게으른 언어에서는 네가 뭔가를 원할 때 단지 초기 데이터와 효율적인 변형을 취해 해결하면 되고, 이와 유사한 방식으로 네가 원하는 것을 얻을 수 있어.

 Haskell은 정적 타입 언어(statically typed)야. 네가 네 프로그램을 컴파일 할 때, 컴파일러는 코드의 어떤 부분이 숫자를 가리키는지, 어떤 부분이 문자열을 가리키는 지 등등을 모두 알고 있어. 이 말은, 컴파일 타임에 많은 오류들을 잡아낼 수 있다는 뜻이야. 만약 네가 숫자와 문자열을 더하려고 한다면, 컴파일러는 그런건 불가능하다고 징징댈거야. Haskell은 타입 추론(type inference)을 포함한 아주 좋은 타입 시스템을 사용해. 그래서 너는 모든 코드 조각에 과도하게 이게 무슨 타입인지 가리키는 라벨을 붙일 필요가 없어. 왜냐하면 타입 시스템이 대부분의 타입을 영리하게 추론해 내거든.  만약 네가 a = 5+4라고 한다면, Haskell에게 a가 숫자라고 말해줄 필요는 없어. Haskell은 알아서 a가 숫자 타입임을 알아내거든. 또 타입 추론은 네 코드를 더욱 일반적으로 만들어줘. 만약 네가 짠 함수가 두 개의 인자를 받아서 그 두개를 더해 돌려주고, 네가 따로 그 타입에 대해 명시하지 않았다면, 그 함수는 숫자와 유사하게 동작하는 어떤 타입의 두 인자에 대해서도 정상적으로 동작할거야.

 Haskell은 우아하고 간결해(elegant and concise). 왜냐하면 Haskell은 다양한 고수준의 개념들을 이용하고, Haskell로 짠 프로그램은 일반적으로 명령형 언어로 짠 동일한 프로그램보다 짧기 때문이지. 그리고 짧은 프로그램은 그렇지 않은 프로그램에 비해 유지보수성이 좋고 버그도 더 적어.

 Haskell은 굉장히 똑똑한 사람들(박사를 포함한)에 의해 만들어졌어. Haskell 작업은 1987년에 개쩌는 언어를 만들기 위해 모인 학회에서 시작했어. 2003년도에 Haskell Report가 발행됐고, 이건 언어의 안정성있는 버젼에 대해 정의하고 있지.


몰두하기 위해 필요한 것


 텍스트 편집기와 Haskell 컴파일러만 있으면 돼. 아마 자기한테 알맞는 텍스트 편집기는 이미 깔려있을테니, 거기에 시간 낭비는 하지 말자고. 이 튜토리얼의 목적을 위해 우리는 GHC라고 불리는, 가장 널리 쓰이는 Haskell 컴파일러를 사용할 거야. 시작하기 가장 좋은 방법은 Haskell Platform에서 다운로드 받는 거야.

 GHC는 Haskell Script(보통 .hs 확장자를 가진)를 컴파일해주며, 상호작용 모드도 제공해줘서 실시간으로 스크립트와 상호작용할 수도 있어. 스크립트로부터 함수를 호출할 수도 있고, 즉시 화면에 그 결과를 띄울 수도 있지. 변화가 생길 때마다 매번 컴파일해서 프로그램을 실행해보는 것보다는 상호작용 모드를 이용하는게 배우기 더 쉽고 빠를거야. 상호작용 모드는 네 프롬프트 창에서 ghci를 타이핑하는 걸로 시작할 수 있어. 만약 네가 어떤 함수들을 myfunctions.hs와 같은 파일에 선언해두었다면, 그 함수들을 :l myfunctions 라고 입력하는 걸로 불러올 수 있지. 물론 myfunctions.hs 파일이 ghci가 실행된 폴더와 동일한 폴더에 있어야하고. 만약 네가 .hs 스크립트에 변화를 줬다면, 그냥 한 번 더 :l myfunctions.hs 명령을 실행하거나, 현재 스크립트를 다시 불러오라는 의미를 가진 :r 명령어를 실행하는 걸로 간단하게 바뀐 내용을 다시 불러올 수 있어. 나는 보통 .hs 파일에 몇몇 함수들을 정의하고, 그것들을 불러와서 느긋하게 즐기다가, 다시 .hs 파일의 내용을 수정하고, 그걸 다시 불러와서 마찬가지 과정을 반복해. 그리고 그게 우리가 여기서 하려는 일이지.