-너 F#쓰던 넘이자나, Dynamic C#은 뭐냐.
안녕하세요. Welcome to F#이라는 앞뒤도 안맞고 내용도 부실하며, 불친절한 포스트를 남발하고 있는 강보람(워너비)입니다. 원래 하던거에 약간 시들해지면, 새로운 자극을 찾는다고 하던가요. F#의 포스트를 쓰는게 점점 벽에 부딛히니 C#과 자극적인 외도를..... 생각하는건 아닙니다. 하지만, 의문이 생기니 그걸 찾아보고자 하는 호기심이 생기고, 그 호기심을 오래잠재워두면 없어질거 같아서 일단 시리즈를 시작해보자! 해서 일단 무작정 Welcome to Dynamic C#이라는 시리즈를 시작해볼까 합니다. 기존에 동적언어에 대한 경험이 일천하다보니 이 시리즈의 내용역시 상당히 불친절할걸로 예상되지만, 관심있으신 분들께선 따쓰한 피드백을 주시기 바랍니다. 이제 날씨가 더워지는데 따쓰한 피드백을 주면 제가 열사병으로 죽지는 않을까 하는 염려는 고이접어 간직하시고 따쓰한 피드백을....-_-
-Paul Graham아저씨와 실용주의 아저씨들?
Paul Graham을 아십니까? "Hackers and Painters"의 저자이며, 논쟁을 불러일으킬만한 말을 참 많이도 하는 Lisp빠돌이죠. 오랜만에 Hackers and Painter를 읽다가 아래와 같은 구절을 읽고는 '이 책에 이런구절이 있었나~?' 하면서 C# 4.0의 dynamic과 DLR이 떠올랐습니다.
A programming language is for thinking of programs, not for expressing programs you've already thought of. It should be a pencil, not a pen. Static typing would be a fine idea if people actually did write programs the way they taught me to in college. But that's not how any of the hackers I know write programs. We need a language that lets us scribble and smudge and smear, not a language where you have to sit with a teacup of types balanced on your knee and make polite conversation with a strict old aunt of a complier.
프로그래밍언어는 프로그램에 대해서 생각해나가기 위한 것이지, 이미 머리속에 짜여져있는 프로그램을 표현하기 위한 것이 아닙니다. 프로그래밍언어는 펜이 아니라 연필이어야 합니다. 정적타입은 제가 학교에서 배우던 것 처럼만 프로그램을 짠다면 괜찮은 아이이디어 일지도 모르겠습니다. 하지만 제가 아는 해커들중에 그렇게 짜는 사람은 한명도 없더군요. 우리는 명확하지 않은 아이디어를 흐릿한 형태 그대로 빠르게 묘사할 수 있게 해주는 언어가 필요합니다. 무릎에 타입이라는 찻잔을 떨어지지 않게 올려놓고, 나이많고 엄격하신 이모님이랑 공손하게 대화하려고 애쓰는 것 처럼 컴파일러와 대화할 필요가 없다는 거죠. |
그리고는 예전에 사두었던 "Programming Ruby"를 꺼내서 읽어봤습니다. "실용주의 프로그래머"를 읽으면서 실용주의 학파로 유명한 데이비드 토머스와 앤디 헌트가 펄에서 루비로 관심사를 옮겼다는 사실은 알고 있었지만, 이 책을 읽으면서 이들의 동적언어에 대한 사랑이 확실하게 드러나더군요.
반면에 루비를 잠깐이라도 사용해본다면, 동적 타입을 갖는 변수가 많은 점에서 실질적으로 생산성을 향상시킴을 알게 될 것이다. 오랜 기간 실행되는 커다란 루비 프로그램이 중요한 작업을 수행하면서도, 타입과 관련된 오류는 하나도 던지지 않고 잘 돌아간다. 왜 일까? ..... 결론적으로 말해 '타입 안전성'에서 말하는 '안전성'이란 대개 허상에 불과하며, 루비 같은 동적 언어로 개발하는 것은 안전하면서도 생산적이다. |
이런 호기심이 생기자, F#때문에 미뤄오던 C#4.0의 dynamic에 대한 탐구를 더 이상 미룰 수 없다는 생각이 들었습니다. 그래서 가끔 생각날때 마다 dynamic에 대한 생각을 정리해서 올리려고 지금 포석을 깔고자 하는 것이죠. 대뜸 F#에 대해서 쓰다가 C#으로 옮겨가면, 읽으시는 분들도 혼란을 느끼시지 않을까 해서 말이죠. 물론 그런분덜 한분도 없을거 압니다-_-.
-반갑다! dynamic!
일단, dynamic에 대해서 조금 알아보도록 하겠습니다. dynamic은 말 그대로 동적이라는 거구요, 타입체크를 컴파일 타임이 아니라 수행순간에 즉, 런타임에서 하겠다는 말이 됩니다. 여기서 Duck typing이라는 개념이 들어가게 되는데요, Duck typing은 객체의 타입에 따라서 가능여부를 결정하는 게 아니라, 그저 요건을 갖추고 있다면 모두다 가능하다고 보는 거죠. 즉, 비유를 하나 하자면 조선시대의 어떤 모임을 생각해 보겠습니다.
이 모임에서 양반집의 지체높은 어르신들과 그 어르신들의 자제들만 회원으로 받아준다면 이 모임은 정적타입검사(Static type check)를 하고 있는셈입니다. 특정 계급(Type)만 들여보내주기 때문이죠. 타입이 틀리면 아예들어갈 수가 없습니다. 하지만, 어떤 모임에서는 그림에 관심이 있는 사람이라면, 양반이나 상놈 가리지 않고 모두다 받아들여줬습니다. 이 모임은 바로 Duck typing을 충실히 따르고 있는셈입니다. 계급(타입)에 상관없이 그저 공통점(그림을 사랑하는 뜨거운 가슴!)만 있으면 들여보내주기 때문이죠. Duck typing은 이처럼 타입으로 가르지 않고, 요건을 갖췄다면 모두 묻지도 따지지도 않고 받아들여주는 걸 이야기 합니다.(Duck이 들어가 있는 이유는 애초에 이 개념을 설명하신 분께서 오리처럼 걷고 꽥거리면 전부다 오리로 봐주자고 말씀하셨기 때문이죠. 제가 안그랬습니다-_-. 오히려 제가 만들었다면, '동호회 타이핑' 같은용어를 썼을거 같은데... 제가 안만든게 다행이군요.)
그럼 첫 예제를 한번 볼까요?
static void Main(string[] args)
{
dynamic num = 4;
Console.WriteLine(num);
Type type = num.GetType();
MethodInfo method = type.GetMethod("ToString", new Type[] {});
if (method != null)
{
Console.WriteLine(method.ToString());
}
}
위 예제는 dynamic타입의 num에 숫자인 4를 입력하고, 출력을 합니다. 그리고 WriteLine에 의해서 불려지는 ToString메서드의 유무를 검사하기 위해서, 타입에서 ToString메서드를 찾아서 출력하는 프로그램입니다. 결과는 아래와 같습니다.
WriteLine에선 출력할 객체의 ToString을 호출하죠. 그래서 num의 값이 출력되는 것을 보면 런타임에 num이 ToString을 가지고 있는지 확인해서 있으니까 출력을 했다고 볼 수 있습니다. 그리고 확인을 위해서 dynamic타입인 num의 타입에서 ToString메서드를 찾아서 해당 메서드를 출력해봅니다. 결과를 보면 ToString이 있는 것을 볼 수 있습니다. 그리고 WriteLine의 오버로딩중에서 dynamic타입을 인자로 받아들이는 오버로딩이 없는걸로 봐서는, 런타임에 해당 dynamic객체가 ToString을 가지고 있는지 봐서 있으면 출력하고, 없으면 Microsoft.CSharp.RuntimeBinder.RuntimeBinderException을 발생시키는 것을 유추할 수 있습니다.
-양반과 상놈의 이야기
그럼 위에서 이야기 했던, 양반과 상놈의 이야기를 한번 구현해볼까요?
class Yangban
{
public string Name {get; set;}
public void YangbanSound()
{
Console.WriteLine("Yo, it's the yangban sound, baby :) ");
}
}
class YangbanChild : Yangban
{
public void ILikeDrawing()
{
Console.WriteLine("I like Drawing");
}
}
class Sangnom
{
public string Name { get; set; }
public void SangnomSound()
{
Console.WriteLine("Yo, it's the sangnom sound, baby ;) ");
}
public void ILikeDrawing()
{
Console.WriteLine("I like Drawing");
}
}
class Program
{
public void EnteringYangbanClub(Yangban yangban)
{
Console.WriteLine("Welcome yangban {0}!", yangban.Name);
}
public void EnteringDrawingClub(dynamic person)
{
try
{
person.ILikeDrawing();
Console.WriteLine("Welcome {0}! who like drawing",person.Name);
}
catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException you_do_not_like_drawing)
{
Console.WriteLine("{0}. You can't come in. I'm sorry about that :)", person.Name);
}
}
static void Main(string[] args)
{
Yangban yangban1 = new Yangban { Name = "chulsoo" };
YangbanChild yangbanChild1 = new YangbanChild { Name = "chulsooAdeul" };
Sangnom sangnom1 = new Sangnom { Name = "boram" };
Program program = new Program();
program.EnteringDrawingClub(yangban1);
program.EnteringDrawingClub(yangbanChild1);
program.EnteringDrawingClub(sangnom1);
}
}
위 코드를 보시면, 양반과 양반자식, 상놈이 있죠? 모두 이름을 가지고 있구요, 양반은 간지나는 양반사운드를 구사하며, 상놈은 상놈사운드 그리고 양반자식과 상놈은 자기가 그림그리는걸 좋아한다고 합니다. 그리고 두개의 클럽이 있습니다. 하나는 양반만 들어갈 수 있는 클럽이구요, 하나는 그림을 좋아하는 사람이 들어갈 수 있는 클럽입니다. 자 그럼, 양반과 양반자식인 철수와 철수아들, 상놈인 보람을 데리고 이야기를 해보겠습니다. 양반클럽에 철수와 철수아들, 보람 이렇게 셋이 들어가려고 합니다. 그런데, 양반클럽은 양반이거나 양반의 자제가 아니면 못들가게 엄격하게 막아놨습니다. 그래서 일까요? 아래 그림을 보면, 들어가고 싶다는 의사표시조차도 허용되지 않는 슬픈 모습입니다.
뭐, 계급으로 딱 막혀있기 때문에 들어가는거 자체가 불가능하니까요. 이렇게 타입으로 조건을 걸게되면, 해당타입이거나 해당타입을 상속한 타입이 아니면 안됩니다. 그러면, 그림애호가 클럽에 들어가보도록 하겠습니다. 그림애호가클럽은 일단 다 들여보내주는 군요. 하지만, 들어가게되면 그림을 좋아하는지 크게 외쳐보라고 시킵니다. 근데, 철수는 그림을 좋아하지 않는군요. 그래서 철수는 쫓겨나고 철수아들과 보람은 환영받습니다. 실행결과는 아래와 같죠.
그렇습니다. 계급으로 나누것이 아니라 무엇을 할 수 있는지에 따라 니편 내편을 나누니깐 철수는 못들어가고 보람은 들어가게 되는 것이죠.
-근데 dynamic이거 어따 쓰면 조으까?
도대체 이걸로 무엇을 할 수 있다는 걸까요? 왜 Paul Graham아저씨는 그토록 다이나믹을 부르짖었으며, 실용주의 아저씨들은 또 왜그랬을까요? 불행히도 저는 그동안 정적인 명령형 언어만 다뤄봤기 때문에 잘 모르겠습니다. 뭐 좀 알아보려고 하는데, 그 과정에서 느끼는 점들을 이곳을 통해 나눠보려고 하는거구요. 그럼 F#뿐만 아니라 C#을 통해서 최대한 자주 찾아뵙겠습니다.
-참고자료
1. Hackers and Painters, Paul Graham, O'Reilly
2. Programming Ruby, 데이비드 토머스, 앤디 헌트, 차드파울러, 인사이트
3. http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=csharpfuture&DownloadId=3550