-관심있으면 많이 알아봐야 하는 법.
이제 F#과의 만남도 어느덧 세번째에 접어들었네요. 한 두번쯤 만나면 상대방에 대해 관심이 있는지 없는지 스스로 눈치챌 수 있겠죠. 그리고 관심이 있다면, 더 열심히 상대방에 대해서 알려고 노력하고 있을테구요. F#에 대해서 알려고 노력하다보니, F#의 4차원적인 면을 하나발견하고 잠깐 당황해서 허우적댔습니다. 거기에 대해서 짧게 적어보려 합니다.
-짧은 예제를 통해 까발려본 F#의 4차원적 특징
#light
type TwoNumbers =
{Number1 : int; Number2 : int}
let num = {Number1 = 3; Number2 = 5}
printfn "%d %d" num.Number1 num.Number2
그리고 실행해보면 아래와 같은 결과가 나온다.
음~ 예상했던 대로 결과가 잘 나왔습니다. 그런데 왠지 타입선언과 그 타입을 실행하는 코드는 다른 소스파일에 넣는게 맞는거 같아서 타입선언을 TwoNumbers라는 파일을 생성해서 거기에 넣자는 생각이 들었죠. 그래서 C#에서 하던것 처럼 새로운 파일을 생성해서 거기에 타입선언 코드를 집어넣고, 다시 자신있게 컴파일을 실행했습니다. 그런데! 다음과 같은 오류가 발생하더군요.
오류 1 - The record label 'Number1' is not defined.
오류 2 - Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved
오류 3 - Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved |
즉, Number1이랑 Number2가 선언되어 있는 타입을 찾을 수 없다는 말인데요. 이게 무슨 소리입니까;; 같은 솔루션에 있는 파일에 클래스의 선언을 옮겼는데 못 찾다니요..? 그런데, VisualStudio의 한지붕아래에서 살아서 전혀 눈치를 못채고 있었는데, F#의 솔루션 탐색기에는 다른 언어들과는 다른 뭔가 특별한게 있더군요. 이런 4차원 F#!!!
바로 위 그림에서 볼 수 있는 Move Up, Move Down, Add Above, Add Below말인거죠. 무슨 아이돌 그룹이라도 되는 마냥 4인조가 나란히 모여있죠. 이건 당췌 뭘까요? 아! 혹시 F#은 위에서 부터 아래로 차례대로 파일들을 컴파일 하는걸까요? 그럼 타입선언이 있는 TwoNumbers파일을 Move Up해서 다시 컴파일을 해보죠!! 자신있게 컴파일을 했건만 결과는 똑같은 에러가 납니다. 연애를 해봤거나, 작업을 절박하게 걸어본 사람은 그 사람의 성격을 알았다고 생각해서 그에 맞춰주려다가 오히려 더 곤혹스러웠던 상황이 있을겁니다.
이런. F#은 도대체 왜 이렇게 특이한걸까요? 그래서 F#을 잘아는 사람들에게 정보를 얻어보니, F#은 위에서 부터 차례대로 컴파일을 하는 건 맞는데, 그 파일들이 자동으로 하나의 모듈단위로 컴파일이 되고, 그 모듈을 이용하려면, open을 이용해서 그 모듈의 내용을 열어야 사용이 가능하다는 거 였더군요. 그래서 아래와 같이 모듈의 이름을 open해주었습니다.
#light
open TwoNumbers
let num = {Number1 = 3; Number2 = 5}
printfn "%d %d" num.Number1 num.Number2
이제 컴파일이 잘 됩니다.>_<! 모듈의 이름은 명시적으로 아래와같이,
module MyCompany.MyLibrary.MyModule
(위와 아래의 선언은 동일하다)
namespace MyCompany.MyLibrary
module MyModule =
....... |
이렇게 지정해주지 않는이상 파일이름과 동일하며, 무조건 맨 앞의 문자는 대문자가 됩니다.(즉 파일의 이름이 twoNumbers 였더라도, 모듈의 이름은 TwoNumbers가 된다는 말)
-그냥 추가로 이야기하는 공간-
type Card =
{ Number: int; Shape: int }
이렇게 타입(클래스)를 선언하고, 위 선언을 F# Interactive에서 평가하고 아래와 같은 결과가 나옵니다.
type Card =
{Number: int;
Shape: int;}
그리고 아래처럼 계속 평가식을 입력해보면....
> let v = {Number = 3; Shape = 5};;
val v : Card
> let a = {a = 4; b = 5};;
let a = {a = 4; b = 5};;
---------^^
stdin(4,10): error FS0039: The record label 'a' is not defined.
> v;;
val it : Card = {Number = 3;
Shape = 5;}
>
즉, 타입이름을 명시하지 않아도 멤버의 이름과 타입을 보고 알아서 타입을 찾아서 생성한다는 겁니다. 그래서 a와 b라는 멤버로 생성하려고 했을때, 그런 멤버를 가진 타입이 없어서 에러가 발생하는 거죠. |
-어디까지 4차원일까?
F#은 알면 알수록 4차원인거 같네요. 아... F#에 대해서 더 잘 알게되면 F#의 마음을 얻고 잘 지낼 수 있을까요? 일단 최대한 노력해보는 수 밖엔 없겠군요. 이렇게 짧은 탐색전을 마치겠습니다.
-참고자료
1. Expert F#, Don Syme, Adam Granicz, Antonio Cisternino, APRESS.
2. http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/spec2.aspx#_Toc207785764