Visual Studio 2010 Beta 1 설치부터 살펴보기

Visual Studio 2010 2009. 6. 15. 17:30 Posted by 알 수 없는 사용자

Visual Studio 2010 Beta 1이 일반에 공개된지도 벌써 한달이 다 되갑니다.
미래의 Visual Studio의 모습을 보기 위해 설치 해서 사용을 해보신 분들도 있으시겠지만 시간이 없거나 아직 Beta 1 단계라서 완성되지 않은 부분이 있어서 다음 기회에 사용 해보시려는 분들도 있으실겁니다.
설치 과정을 보면서 어떠한 구성요소가 업데이트 되었고 설치가 되는지 알아보고 기본적으로 변화된 IDE의 모습을 살펴보겠습니다.



먼저 오토런 화면입니다.
첫번째 인스톨 메뉴를 선택하고 설치를 시작 해 보겠습니다.



기본적인 설치 파일 작업이 끝나고 다음으로 넘어갑니다.



현재 설치되어 있는 구성 요소를 체크 하고 설치 될 요소를 알려줍니다.
VC 런타임이 9.0과 10.0 두개가 모두 설치되는 것을 볼 수 있습니다.
그리고 핵심이 되는 .NET Framework 4 Beta 1과 Visual Studio 2010 Beta 1 등이 설치 되는 것을 알 수 있습니다.
그럼 라이센스에 동의를 하고 다음으로 넘어갑니다.



지금까지는 이전과 별 다를건 없었는데 여기서 약간 달라집니다.
기존에는 전체 구성요소가 트리 구조로 한번에 나왔는데 이제 바로 나오지 않고
.NET 개발 환경과 C++ 개발 환경으로 나뉘어져 있습니다.
자신의 개발 환경에 맞게 선택을 할 수 있고 체크를 한 후 아래 보이는 Customize 버튼을 누르면 세부 선택이 가능합니다.



보시는 것처럼 이전과 같이 트리 구조로 선택 할 수 있게 나오게 되고 원하는 형태로 설치가 가능합니다.
그리고 SQL Server 2008 Express 버전이 기본적으로 선택이 됩니다.
Install 버튼을 눌러 설치를 시작하겠습니다.



.NET Framework 4 Beta 1이 설치되면 재부팅을 요구합니다. 재부팅을 하고나면 나머지 설치 과정을 이어서 진행합니다.



드디어 설치가 완료되었습니다. Windows Server 2008 SP2 VPC에서 설치를 진행했는데 중간에 디지털 서명 관련 에러가 나서 재부팅하고 다시 하니 잘 설치되었습니다.
혹시 비슷한 에러나 나면 참고하세요.

그리고 설치되는 MSDN은 베타1에 포함되지 않았습니다. 온라인 MSDN 라이브러리에서 2010에 관련된 내용을 확인 할 수 있습니다.


제어판에서 설치된 목록을 보면 다음과 같습니다.
현재 베타1 단계에서는 이정도가 설치되고 앞으로 베타2 이후에는 변화 될 수도 있습니다.



시작 메뉴에 등록된 프로그램은 다음과 같습니다. Microsoft Test and Lab Manager 등 새롭게 추가된 요소를 볼 수 있습니다.




VS2010 베타1을 실행 해 보면 시작 페이지가 많이 달라진것을 볼 수 있습니다.
전체적인 IDE에도 색도 들어가고 화려한 모습을 보여주고 있습니다.




About 메뉴에서 구성 요소를 다시 한번 보면 Silverlight도 기본으로 추가된것을 볼 수 있고 F# 등 새로운 언어도 포함되었습니다.


새 프로젝트를 열어보면 이전과 조금 다른 형태가 보입니다.
2008에서 처럼 멀티 타겟팅을 지원하고 업그레이드 된 .NET Framework 4.0도 보입니다.



F# 프로젝트 템플릿은 다음과 같습니다. .NET Framework 4.0에서 새롭게 지원하는 것이므로 3.5 이하를 선택하면 나타나지 않습니다.



새롭게 추가된 온라인 템플릿 탭을 선택하면 기본 템플릿 외에 다양한 템플릿을 추가 할 수 있습니다.



WPF 프로젝트를 생성 해 보았습니다. 전체적인 IDE가 푸른색을 기본으로 새롭게 변화되었고 WPF로 구성되었다는걸 느낄 수 있습니다.
그런데 WPF에서 폰트 처리 문제때문에 좀 뿌옇게 보이는 경향이 있는데 앞으로 개선될거라 예상됩니다.



Visual Studio 2010에서는 멀티 모니터를 지원 한다고 하는데 보시는 것처럼 탭으로 분류된 창을 분리 할 수 있습니다. 창을 드래그 해서 다른 모니터로 옮겨서 여러 모니터에서 작업을 할 수 있습니다.

지금까지 설치부터 IDE의 모습까지 간략히 살펴보았는데 여러가지 향상된 점이 많다는 것을 알 수 있습니다.
관심 있으신 분들은 설치해서 사용 해보시가 여건이 안되신다면 궁금한 점을 말씀하시면 더 자세히 알려드리도록 하겠습니다.

Welcome to F#(9) - 메이져 데뷰.

F# 2009. 6. 12. 16:25 Posted by 알 수 없는 사용자
-뒷북이냐

어느덧 비주얼스튜디오 2010 베타1이 나온지도 거의 한달이 다되어 가네요. 그동안 뭐가 바뀌었는지 간략한 소개도 못드렸었군요. 뒷북이라 느끼실 수도 있겠지만, 간략하게 비주얼스튜디오 2010 베타1에서 F#이 어떻게 무명시절의 설움을 딛고 일어서고 있는지 소개해드리려고 합니다.




-메이저데뷰

개인적으로 가장 중요하다고 생각하는 점은 F#이 무명시절의 설움을 딛고 메이져데뷰를 했다는 거죠. 이제 당당하게 비주얼 스튜디오 시작시에 나오는 스플래시 화면에도 이름을 올리고, 비주얼 스튜디오를 깔고 처음실행하면 나오는 기본개발환경설정 선택창에서도 Visual F#을 보실 수 있습니다.




캬. 그리고 새 프로젝트를 눌러서 들어가보면 Visual F#이 메인에 나오고요 C#과 VB.NET은 "Other Languages" 카테고리로 분류가 됩니다. 좋네요. 참고로 F# Interactive도 기본으로 뜹니다. 


그리고 비주얼 스튜디오 2010 베타1에 포함된 F#은 약간 버전업이 되었는데요(1.9.6.2 -> 1.9.6.16) 아래 그림을 비교해보시면, F#의 버전과 닷넷프레임워크 버전이 바뀌었음을 확인해볼 수 있습니다.

(2008)
(2010)


그리고 이번버전부터 PowerPack을 따로 받아서 깔도록 바뀌었습니다. 그래서 F# Interactive에서 #r 해서 PowerPack.dll을 로드하려고 해도 dll을 찾아오지를 못하는데요, 그래서 따로 다운을 받아서 복사를 해줘야 합니다.  여기에서 받을 수 있구요~ "C:\Program Files\Microsoft F#\v4.0" 이 경로에 복사해서 넣어주면, F# Interactive에서 기존처럼 #r "FSharp.PowerPack.dll";; 구문으로 추가할때도 자동으로 찾아서 추가해줄 수 있습니다. 그리고 기존처럼 비주얼스튜디오 2008에서도 베타1에 추가된 1.9.6.16버전을 사용할 수 있는데요, 여기에서 다운받으실 수 있습니다.


그동안 6개월동안 커뮤니티의 피드백을 받아서 F#언어와 컴파일러의 버그를 잡고 구조와 성능을 개선하는데 집중했다고 합니다. #light가 이제는 기본으로 설정되는 것과 .NET 4.0의 BingInteger와 연동이 잘되고, params타입을 사용하는 메서드를 호출하거나 만들수있게 되었고, 라이브러리의 이름이나 표현을 표준에 맞게 수정하면서 타입파라미터가 대문자로 바뀌는 등의 변화가 있었습니다.


(소문자였던 타입 파라미터가)

(대문자로 바뀌었음)



-이게 다야?

사실, 더 자세하게 하나하나 분석해드릴 수 있으면 좋으련만, 공부한지 얼마 안되는지라 뭐가 자세하게 바뀐건지 감은 잡혀도 확실하게 설명드리지 못하는 점이 조금은 죄송스럽군요;;;
여기에서 추가되거나 바뀐 내용들을 자세하게 보실 수 있습니다.

F#이 도대체 왜 비주얼스튜디오에 정식으로 추가된 걸까요? 기존에도 J#등과 같은 언어가 추가되었다가 소리소문없이 사라지기도 했었습니다만, F#도 그런걸까요? 사실 저는 함수형언어에 기존의 언어들이 갖지 못한 장점들이 있고 C#이나 VB.NET이 멀티패러다임을 지향하면서 함수형,동적 언어들의 장점을 취합하고 있지만 함수형언어 고유의 영역을 제대로 커버하는건 힘든게 아닌가 싶었습니다. 그래서 F#이 추가되는 걸 그런 점에서 봤었습니다만, 스터디를 이끌고 계시는 K님의 말씀을 듣던 중 새로운 점을 하나 발견하게 됐습니다.

패션쇼를 예를 들어주셨는데요, 제대로 옮기는지는 확실치 않지만 대략 '패션쇼에서 독특한 옷들 입고 나와서 모델들이 걸어다닌다고 해서 그 옷이 바로 백화점이나 옷가게 진열장에 걸리는건 아니다, 앞으로의 가능성과 방향을 제시하는 의미가 있는거다' 같은 말씀이였습니다. 그리고 함수형언어가 그런면이 있어서 그동안 함수형언어의 발전적인 개념들이 현업에서 쓰는 언어에 하나씩 추가되기도 하는거라고 말이죠(LINQ같이).

이 말을 듣고 나닌 Paul Graham이 왜 그토록 Lisp을 최고로 치면서 시간이 지나면 사라질 언어말고 언어계층의 뿌리에 해당하는, 그래서 시간이 지나도 가치가 변하지 않을 Lisp같은 언어에 집중하라고 말을 했는지 알것 같기도 했습니다.

F#이 메이져 데뷰를 하면서 그걸통해 닷넷프레임워크에서 돌아가고 서로간에 호환이 가능한 함수형언어를 확보하는 동시에, F#을 통해서 시험적인 요소들의 가능성도 보고 닷넷을 더 발전적인 형태로 가져가기 위한 시도가 아닌가 하는 생각이 들었습니다.

제대로 설명한건지는 확신이 안서지만 어쨌든, 기존의 함수형언어의 팬들이 F#을 통해 뭔가를 많이 시도하고 즐긴다면 닷넷이 더욱 풍성해지는 거겠죠~~~~.

-참고자료
1. http://blogs.msdn.com/dsyme/default.aspx

Welcome to Dynamic C#(2) - Wanna be a polyglot.

C# 2009. 5. 17. 16:25 Posted by 알 수 없는 사용자

- 자넨 왜 그렇게 언어에 집착하는고?

넵, 확실히 저는 언어에 쫌 집착하는 편이긴 합니다. 우리가 말하고 쓰는언어도 꽤나 집착하는 편입니다. 언어를 제대로 배우려고 노력하다보면, 더 재밌는 걸 많이 접할 수 있게 되고 그 언어뿐만 아니라 언어를 쓰는 사람들의 사고방식도 아주 조금씩 이해하게 되기 때문이죠. 뭔가 보고싶은게 있는데 그게 제가 모르는 언어로 되어 있어서 못보는건 조금 슬픈일인거 같습니다. 개인적으로 부족한 실력이지만, 미드&일드를 아주 재밌게 즐기고 있습니다.

프로그래밍 언어도 비슷한 의미에서 집착하게 되는게 아닐까 싶습니다. 프로그래밍 언어에는 그 언어를 만들고 지지하는 사람들의 사고방식도 같이 배울 수 있게 되고, 점점 재밌게 할수 있는게 늘어나기 때문이죠. 최근에 언어에 대해서 아주 부족한 의견이지만 글을 썼던 적이 있습니다. 관심있으신 분은 보시고 따쓰한 피드백 주시면 완전 감사하겠습니다. 

아무튼 그런의미에서 F#에 C#에 Axum까지 건드려보고 있는거지요. 줏대가 없다거나 바람기가 있다거나 뭐 그런건 아닙니다. 그럼 본론으로 들어가서, 지난번엔 동적C#에 대한 이야기를 조금 드렸었습니다. 하지만 정작 알고 싶은건 dynamic키워드란게 생겼고 대충 오리꽥꽥 어쩌구 저쩌구 하는건 알겠는데, 어디다 써먹는거란 말이더냐? 뭐 그런거겠죠. 그래서 쌩초보이지만, 최대한 그런관점에서 접근해보고자 합니다. 그 첫번째가 실제로 프로젝트를 하다가 하나 느낀게 있어서 그걸 적어보고자 합니다. 



- 시나리오
(제가 모르는 해결방법이 있을수도 있습니다. 그럴땐 따쓰한 피드백을!)

LINQ to SQL(이하 L2S)로 프로젝트를 진행중입니다만, 데이터가 추가되거나, 수정되거나 삭제될때 그 값들의 이전/이후 데이터를 포함해서 그 데이터의 고유번호(seq)를 같이 저장하는 뭐 그런 시나리오입니다. 그래서 L2S에서는 아래와 같은 방법을 제공합니다.

 

ChangeSet changeSet = db.GetChangeSet();

foreach (Customer c in changeSet.Inserts)
{
	.......
}

 

하지만, 위와 같은 코드는 한번의 SubmitChanges로 변경이 일어나는 대상 테이블이 하나라면 Generic파라미터로 처리할 수도 있겠지만(ChangeSet에서 리턴되는 객체의 타입이 object입니다), 주문과 상세주문같이 한번에 여러개의 테이블에 변경이 일어난다면, 그닥 친절하지 못한 시나리오가 되겠습니다. 그래서 그때 들었던 생각이 "아 이거 dynamic키워드를 이용해서 오리꽥꽥타이핑(duck typing)을 이용하면, 쫌 쉽게 될거 같은뎅..." 였습니다. 그래서 한번 살짝 구현해봤습니다.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace FirstDynamic
{
    class Customer
    {
        public int Seq{get; set;}
        public string Name {get; set;}        
    }

    class Company
    {
        public int Seq{get; set;}
        public string Name{get; set;}
    }

    class Project
    {
        public int Seq{get; set;}
        public string Name{get; set;}
    }

    class NoneSeqThing
    {
        public string Name{get; set;}
    }

    class Logger
    {
        public void WriteLog(dynamic entity)
        {
            try{
                Console.WriteLine(entity.Seq);
            }
            catch(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex)
            {
                Console.WriteLine("Exception : " + ex.Message);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {            
            Logger logger = new Logger();

            Customer customer = new Customer
            {
                Seq = 1,
                Name = "Boram"
            };

            Company company = new Company
            {
                Seq = 6,
                Name = "NanoSoft"
            };

            Project project = new Project
            {
                Seq = 108,
                Name = ".NET 99.9"
            };

            NoneSeqThing noneSeqThing = new NoneSeqThing
            {
                Name = "None Seq Thing"
            };

            object[] entities = new object[] { customer, company, project, noneSeqThing };

            foreach (object entity in entities)
            {
                logger.WriteLog(entity);
            }
        }
    }
}

 

위에서는 몇가지 객체를 선언해서 DTO를 흉내냈고, Seq가 있는 타입과 없는 타입이 있는 것을 볼 수 있습니다. 그리고 ChangeSet에서 반환되는 객체를 그냥 dynamic으로 Logger에게 넘겨주고 있습니다. 그리고 Seq멤버변수가 있으면 남기고 없으면 안남기는 식으로 다른 처리를 해주고 있습니다. 그리고 결과는 아래와 같습니다.



그렇습니다, 이런식으로 dynamic을 이용한다면 지난 포스트에서 Paul Graham아자씨가 이야기 했던 나이들고 엄한 이모 컴파일러와 좀더 편하게 대화할 수 있는 법이죠. 써놓고 보니깐 포스트가 별거 없네요. 기분 탓이 아니라 제 내공 탓이겠죠. 여담이지만, 제가 해결한 방식은 리플렉션으로 Seq프로퍼티를 검색해서 유/무를 판별하는 방식을 이용했습니다. 확실히 손이 좀더 많이 가는 방법이죠.

이렇게 여러가지 패러다임에 익숙해지고 잘 활용할 수 있게 되면, 점점 더 빠르고 경쾌한 리듬으로 코드를 작성하는 일도 가능하지 않을까 생각해봅니다. 이제 프로그래밍을 하시는 분들도 세계화에 발맞추는 속도에 걸맞게 프로그래밍언어에 대해서도  ployglot이 되어야 하지 않을까 생각합니다. 다만, 제 생각엔 그러한 노력은 프로그래머에게 족쇄를 채우는게 아니라 오히려 더 큰 자유를 주는 일이라고 생각합니다. 그럼, 또 개발하다가 dynamic의 헬프가 필요한 순간이 오면 나누도록 하겠습니다.

여담이지만, polyglot하니깐 중학교때 "핸들 이빠이 돌려"라는 한문장으로 3개국어를 자유자재로 구사하시던 한문선생님이 떠오르는 군요. ㅋ


-참고자료
1. Pro LINQ: Language Integrated Query in C# 2008, Joseph C. Rattz, Jr. , APRESS.

Welcome to Parellel world(1) - Here comes a new challenger!

C# Parallel Programming 2009. 5. 16. 16:04 Posted by 알 수 없는 사용자

-신 캐릭터의 등장.

언제나 새로운 캐릭터의 등장은 가슴을 설레이게 하는거 같습니다. 디아블로 같은 액션 RPG도, 스타크래프트같은 전략시뮬에서도, 미연시에서...... 그리고 비주얼스튜디오에서도 말이죠. Axum은 딱히 비주얼스튜디오 2010에 포함되기로 이야기 되는 언어는 아닙니다만, 왠지 2010이나 그 이후버전에선 정식으로 편입되어서 중요한 역할을 할 것같은 느낌이 들어서 아주 조금씩 소개를 드려볼까 합니다. 물론 Welcome to Parallel world에서는 Axum만 다루진 않을 거구요 기타 다른 병렬프로그래밍에 대한 이슈들을 쌩초보의 입장에서 공부하면서 정리해보고자 합니다. 신기한게 보이면 그냥 못지나치는 성격이라 시간은 부족해지는데 일은 계속해서 늘리고 있군요. 아무튼, 관심있는분들의 많은 따쓰한 피드백을 기다리며 시작해보겠습니다. 

-Axum????

Axum은 위에서 설명드린것 처럼 딱히 아직 구체적인 계획은 없고 그저 실험적으로 개발되고 있는 새로운 언어입니다. 가이드문서에서는 아래와 같이 설명을 하고 있군요.

Writing a parallel program typically requires partitioning the solution into a number of parallel tasks. Some problems are easily amenable to parallelization because the tasks can run independently of each other. In other problems the tasks have interdependencies and require coordination. ......... With Axum, we offer a language that allows programmers to arrange coordination between components in a way that is close to their natural conception of the solution. In other words, if you can model your solution in terms of interactive components, encoding it in Axum will be straightforward, and you will likely avoid many common concurrency-related bugs. 

병렬 프로그램을 작성하려면, 프로그램을 여러개의 작은 병렬작업들로 쪼개는 작업이 필요합니다. 몇가지 문제들은 애초에 서로 독립적으로 수행될 수 있으니깐 쉽게 병렬화시킬 수 있습니다. 다른 몇가지 문제들은 서로 연관되어 있기 때문에 순서를 잘 정해줘야 합니다. .... Axum은 그러한 수행순서등을 솔루션이 원래 동작해야 하는 시나리오에 근접하게 잘 정리하도록 도와주는 새로운 언어입니다. 부연 설명을 하자면, 문제해결을 위한 솔루션을 컴포넌트들의 상호작용모델로 그려낼 수 있다면, 이런 솔루션을 Axum으로 표현하는 작업은 매우 직관적이고 쉬울 것입니다. 그래서 프로그래머들은 매우 흔히 발생하는 동시성과 관련된 버그를 피할 수 있게 되는 것이죠.


즉, 병렬프로그래밍을 지원하기 위한 새로운 DSL이라고 생각하면 될 것 같기도 하군요. Axum은 C#과 비슷한 점이 많기도 하고 틀린점도 있는데 대강정리해보면 아래와 같습니다.

-C#과 Axum이 가진 공통점은 :
      모든 C# 3.0의 람다와 LINQ쿼리 같은 표현식들
      모든 C# 3.0의 yield와 yield break같은 구문들
      메서드와 필드 정의들
      델리게이트와 이뉴머레이션 타입들 

-Axum에서 볼 수 없는 C#의 요소들
      클래스, 인터페이스와 구조체
      연산자 정의들
      프로퍼티
      상수 필드와 상수지역변수
      정적 필드와 정적 메서드 

-Axum에서만 볼 수 있는 요소들
      에이전트 와 도메인
      채널
      스키마
      네트워크
      Interleaved control flow


그렇다는 하는 군요. 아무튼, 정리해보면 'Axum은 C#과 비슷한 점도 있고 틀린점도 있으며 병렬프로그래밍을 지원하기 위해서 새롭게 실험적으로 개발되고 있는 DSL이다' 정도가 되려나요?

DSL이라는 말은 제가 붙인겁니다만, 아무튼 모냥을 보니 병렬프로그래밍이라는 부분에 특화된 모습을 보이면서 class정의는 C#, F#, Managed C++, Visual Basic.NET등의 언어를 통해서 하거나 C#을 통해 직접 정의할 수 있다는 걸로 봐서는 DSL이라고 보는게 맞는거 같기도 하군요. 여담이지만 위에서 언급한 4개의 언어의 순서는 제 맘대로 한건 아니구요 문서에 있는 순서를 그대로 언급한 겁니다. F#도 이제 MS내에서도 중요하게 생각하고 있다는 걸까요? 왠지 급흐뭇해 지는 군요.
(찾아보니 이전에 Maestro라는 코드네임으로 개발되던 언어였고 DSL이 맞군요 ㅋ)


여담2 이지만 이런 구절도 있었습니다. "Axum is a .NET language and can use libraries written in other .NET language such as C#, Visual Basic or F#." 이제 F#도 C#과 VB.NET으로 대표되는 주요언어에 포함되는 느낌이로군요 캬캬캬.

아무튼, Axum은 여기서 받으실 수 있습니다. 그리고 그 사이트에서 문서도 보실 수 있구요. 좀 더 자세한 이야기는 다음포스트로 넘기기로 할까요?

-나만 몰랐던가? concurrency와 parallel은 다른 것이다!

일단, 사전적 정의를 보자면 아래과 같습니다.

concurrent - 무언가가 시간적으로 동시에 일어나는 이미지
parallel - 무언가 동일한게 시간&공간적으로 다른곳에 존재하는 이미지

그리고 여기에 대한 좋은 설명이 있어서 참고해봤는데요, 싱글코어 CPU에서 실행되는 두개의 concurrent한 스레드는 concurrent하지만, parallel하지는 않다는 이야기이고... parallel은 멀티코어, 멀티CPU, 분산환경에서 얻을 수 있는 장점이라는 이야기더군요 즉, concurrent하다고 해서, parallel한것은 아니라는 것입니다.

 

- 참고자료

1. Axum Programmer's Guide, Microsoft Corporation
2. AXUM LANGUAGE OVERVIEW, Niklas Gustafsson
3. http://blogs.sun.com/yuanlin/entry/concurrency_vs_parallelism_concurrent_programming


Welcome to F#(8) - 은총알과 엄친아.

F# 2009. 5. 10. 20:56 Posted by 알 수 없는 사용자
-넌 제목이 왜 맨날 이 모냥이냐.

은총알과 엄친아. 과연 전 무슨생각으로 제목을 맨날 이렇게 짓는 걸까요? 개인적으로 포스트에서 이야기 하고자 하는걸 비유적으로 설명하는걸 좋아합니다. 그래서 일까요? 늘 글을 쓰기 전에 이야기 하고자 하는 내용을 심사숙고합니다. 그렇게 해서 나오는게 고작 요정도 인거죠.

은총알. 은초딩도 아니고 이건 뭘까요? 들어보신분들도 많으시겠지만, 소프트웨어 공학에서 이야기하는 모든 문제가 이거하나면 해결된다고 하는 그런 어떤 경우에도 쓸 수 있는 만능의 해결책? 그정도 느낌을 생각하시면 될거 같습니다. 즉, 많은 사람들을 파닥파닥 낚이게 만드는 주범이지요. 엄친아도 못하는게 없는 만능의 엄마친구아들을 뜻하죠. 적어도 프로그래밍 세계에선 은총알이나 엄친아 같은 프로그래밍언어는 없는거 같습니다. 아무리 C#이 발전했다 한들, 하자면 할 수 있지만 어색한 부분이 있다는 거죠. 그럼 오늘은 거기에 대해서 좀 이야기 해보겠습니다.


- 이거슨 F# 코드!

아래 코드는 Expert F#에 나오는 코드입니다.

open System.Collections.Generic

let devideIntoEquivalenceClasses keyf seq =
    let dict = new Dictionary<'key, ResizeArray<'a>>()
    
    seq |> Seq.iter(fun v ->
        let key = keyf v
        let ok, prev = dict.TryGetValue(key)
        if ok then prev.Add(v)
        else 
            let prev = new ResizeArray<'a>()
            dict.[key] <- prev
            prev.Add(v))
            
    dict |> Seq.map(fun group -> group.Key, Seq.readonly group.Value)

즉, 어떤 데이터의 집합과 그 데이터에 대해 수행할 함수를 받아서 각각의 데이터에 대해 그 함수를 수행합니다. 그리고 같은 결과 끼리 집합을 만들어서, 결과값과 결과값이 같은 데이터들의 집합을 Dictionary로 만들어서 리턴해주는 프로그램이죠. 즉, 함수형언어의 장점이 잘 드러나는 데이터처리부분의 예제입니다. 실행결과는 아래와 같습니다. 

> devideIntoEquivalenceClasses (fun x -> x % 3) [1..10];;
val it : seq<int * seq<int>>
= seq [(1, seq [1; 4; 7; 10]); (2, seq [2; 5; 8]); (0, seq [3; 6; 9])]

숫자를 3으로 나눈나머지를 구하는 함수와, 1부터 10까지의 숫자를 넘겨주면, 이렇게 나머지값이 같은 숫자별로 그룹을 지어서 리턴해줍니다. 여기서 Dictionary는 닷넷의 클래스를 그대로 사용한 거구요, ResizeArray는 닷넷에서의 System.Collections.Generic.List<T>의 줄임말입입니다. 이름그대로 mutable한, 사이즈를 조절가능한 Array라는 거죠. 

코드를 보면, 전체데이터를 포함할 dict라는 Dictionary를 선언하구요, 입력으로 들어온 seq의 각요소에 대해서 사용자가 넘겨준 함수(keyf)를 수행합니다. 그리고 그 결과값이 이미 존재하는지 확인해서 존재한다면, 그 결과값을 key로 하는 ResizeArray에 숫자값을 추가하고 존재하지 않는다면, 새로운 ResizeArray를 만들어서 숫자를 추가하고, 그 ResizeArray를 dict에 추가해주는 방식입니다.

그리곤 마지막으로 key에 해당하는 ResizeArray를 읽기전용인 시퀀스로 만들어서 리턴해주고 있습니다. 이 예제는 어떤 함수에서 내부적으로는 side-effect를 가진 자료구조를 쓰더라도 그 side-effect를 오직 내부에서만 나타나게 격리한다면, 그 함수는 pure하고 functional한 함수라는 이야기를 하면서 나온 예제입니다. 그래서 마지막에 읽기전용 시퀀스로 리턴을 해주고 있는거지요. 


- 이거슨 C#코드!

이 코드를 C# 3.0버전으로 그대로 옮겨 보겠습니다. 

class Program
    {
        public Dictionary<T2, List<T1>> DivideIntoEquivalenceClasses<T1, T2>>(Func<T1,T2> func, List<T1> list)
        {
            Dictionary<T2, List<T1>> dict = new Dictionary<T2, List<T1>>();

            list.ForEach(l => {
                T2 key = func(l);
                if (dict.ContainsKey(key))
                {
                    dict[key].Add(l);
                }
                else
                {
                    List<T1> prev = new List<T1>();
                    dict[key] = prev;
                    prev.Add(l);
                }
            });

            return dict;
        }

        static void Main(string[] args)
        {
            Program program = new Program();

            var dict = program.DivideIntoEquivalenceClasses((x => x % 3), new List<INT> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });

            var resultSet = dict.Select(item => new { item.Key, item.Value });

            foreach (var result in resultSet)
            {                
                Console.Write("{0},", result.Key);
                result.Value.ForEach(item => Console.Write(" {0} ", item));
                Console.WriteLine();
            }
        }
    }

실행결과는 아래과 같구요.



역시 func<>를 이용한 higher-order와 LINQ덕분에 코드의 길이자체가 별로 차이가 없고, 오히려 C#의 문법에 익숙해서 그런지 더 깔끔해 보이는 부분도 있네요. 다만, 눈에 상당히 거슬리는게 있다면, T1,T2를 비롯한 파라미터타입과 리턴타입등을 명시한 부분이 상당히 거슬리는군요. 사실 짜면서도 아직 익숙하지 않아서 일까 좀 헷갈렸습니다. F#에서는 Type inference가 제대로 적용되어서 파라미터에도 타입을 명시할 필요가 없죠. 물론 C#에서도 변수수준에서는 var타입을 이용해서 Type inference를 지원하지만, 파라미터와 리턴타입에 대해서는 지원하지 않기 때문에 쫌 복잡해질 수 있습니다. 함수형 프로그래밍을 C#에서 하느라 복잡해지는 모냥은 여기에서도 확인하실 수 있습니다. @_@.

 
아래구절은 Expert F#에서 인용한 구절인데요, 제가 설명하려는 바를 아주 잘 요약해주시고 있습니다.

 A key feature of F# is the automatic generalization of code. The combination of automatic
generalization and type inference makes many programs simpler, more succinct, and more

general. It also greatly enhances code reuse. Languages without automatic generalization force programmers to compute and explicitly write down the most general type of their functions, and often this is so tedious that programmers do not take the time to abstract common patterns of data manipulation and control.

F#의 핵심기능중의 하나가 바로 코드를 자동으로 일반화시켜주는 것이다. 자동일반화와 타입유추를 조합하게 되면 많은 프로그램을 간단하고, 좀더 명확하게 그리고 좀 더 일반화시켜서 작성할 수 있다. 당연히 코드 재사용도 아주 편리해 진다. 자동일반화를 지원하지 않는 언어는 프로그래머가 명시적으로 일반적인 타입을 계산해서 명시해야 만 한다. 그리고 이런 것들은 데이터처리와 그 처리의 흐름을 공통적인 부분을 묶어서 추상화하는 작업을 매우 손이 많이가고 짜증나는 작업으로 만든다.



- 그래서~?

즉, LINQ를 통해 C#에서의 데이터처리와 흐름의 추상화가 가능해졌으며, 그 덕에 C#으로도 부분적으로 함수형언어와 버금가는 생산성을 내는일이 가능해졌습니다. 하지만, 명령형 언어의 한계점이 이런부분일까요? 일반화를 하기 위해서 무척이나 까다롭게 인자들의 타입을 잘 명시해줘야 하는거죠. 물론, 저렇게 하는게 옳은지에 대해서는 잘 모르겠습니다. 개인적으로는 저런부분을 굳이 C#을 이용해서 구현하려고 애쓰기 보다는 F#을 이용해서 조합하는게 더 옳지 않나 싶습니다. 사실 F#을 닷넷에 편입시키려는 의도도 그런부분을 가장 기대하는게 아닌가 싶기도 하구요. 


- 참고자료

1. Expert F#, Don Syme, Adam Granicz, Antonio Cisternino, APRESS
2. http://weblogs.asp.net/podwysocki/archive/2009/04/26/functional-c-forward-functional-composition.aspx

 

Welcome to Dynamic C#(1) - 첫만남.

C# 2009. 5. 4. 14:37 Posted by 알 수 없는 사용자

-너 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

[Better Code]Visualize Code Relationships

Agile Development 2009. 5. 2. 18:40 Posted by kkongchi

image

위 그림처럼 이번 Visual Studio Team System 2010에서는 Analyze(분석) 메뉴에 새로운 기능이 하나 더 추가되었습니다. 바로 Visualize Code Relationships 메뉴입니다. (아직 한글로는 어떻게 번역될 지 잘 모르겠네요)

이 기능은 제목 그대로 코드의 관계를 시각화해서 보여 줍니다. UML 다이어그램과도 조금 다르고, 모델링 용의 DSL 다이어그램과도 조금 다르긴 하지만, 꽤 괜찮은 그림을 보여줍니다.

그림을 한 번 보기 위해서, 아래와 같은 아주 간단한 Visual Studio 솔루션을 한 번 구성해봤습니다.

image

구조는 간단합니다. WebApplication1이 WebService1을 Web Reference를 해서 호출하고, 다시 WebService1은 ClassLibaray1을 내부적으로 Reference해서 호출하는, 아주 흔한 형태의 Application 구조입니다. 그리고 ClassLibrary1 내부에서 Class2는 Class1을 사용합니다.

이 구조가 어떻게 시각화 되는지 한 번 볼까요?

1. Visualize Call Dependency – By Assembly

image

오른쪽 옆의 버튼을 누르면, 내부로 펼쳐지면서, 내부에 들어있는 클래스도 보입니다.

image 

2. Visualize Call Dependency – By Namespace

image

3. Visualize Call Dependency – By Class

image

프로젝트가 성숙하고 발전되면, 코드도 따라서 많아지면서 구조도 복잡해지기 마련입니다. 복잡도가 높아지면 높아질 수록 유용한 툴이 되지 않을까 싶습니다.

하지만, 지금 그림을 보시면 아시겠지만, WebApplication1이 Web Service1를 Web 참조를 하고 있습니다만 그림에는 그 참조 관계는 표시가 제대로 되질 않네요. 결국 직접적인 참조 외에는 다이어그램에서 보기가 힘든 것 같습니다. 쉽지 않겠지만, 그런 부분들도 표시할 수 있었더라면 정말 환상적이었을 텐데 하는 생각이 듭니다.

안녕하세요, 정재원이라고 합니다.

C++ 관련 글을 올리고 계신 흥배님과 마찬가지로 게임 개발자입니다.

그간 여러분들이 올려주시는 좋은 글들을 읽고만 있다가...; 무언가 올리지 않으면 짤리겠다는 위기감에 소재거리를 뒤져보았습니다;;

흥배님과 가능한한 겹치지 않는 주제와 방식으로 무엇이 적당할까 생각하던 중... 본 동영상을 발견하고는, 그래 여기에 자막을 달아보자! 생각하게 되었습니다. 작년 PDC에서 발표된 Parallel Pattern Library에 대한 동영상입니다.

왠지 간단할듯 여겨졌으나... 처음 해보는 자막 작업 쉽지 않더군요 ㅠㅠ 도구를 찾고 익히는데 시간이 더 들었습니다.

한시간이 넘는 것을 한번에 번역해 올리려면, 날새겠다는 생각이 들더군요; 유투브 업로드가 10분 미만의 동영상으로 제한된다는 것에 안도감(?)을 느꼈습니다...

이를 변명삼아 일단 10분 분량을 올립니다. 포스트 수를 늘리기 위한 수작 아니냐 라는 비난의 소리가 들립니다만... 맞습니다 ㅠㅠ 툴에 좀 더 익숙해지면 자주라도 올리겠습니다.

부족한 번역과 분량에 대해 건설적 충고 부탁드리고요, 기타 제 포스트에 제안 사항 있으시면 댓글 달아주십시오.

 

지난 번 글에서 Visual Studio Team System 2010의 향상된 Unit Test 기능에 대해서 살펴 본 바가 있었습니다. 오늘 소개드릴 PEX는 Microsoft Research 그룹에서 개발한 Automated Whitebox Testing Framework 입니다. 꽤나 긴 정의입니다만, 간단히 말하자면 자동으로 코드를 분석해서 WhiteBox Unit Test를 만들어주는 Visual Studio Extension입니다.

Blackbox Test – Blackbox Test는 우리가 코드 내부에 대한 지식이 없다는 것을 전제로 합니다. Blackbox Test에서는 코드의 노출된 API를 소비하는 Consumer의 입장에서 Function을 테스트하게 됩니다. 비교적 초기에 작성되는 Unit Test들은 이 Blackbox Test가 됩니다. 아직 Code가 성숙되지 않은 상태에서 API의 Requirements, Specification 등만을 가지고 테스트를 작성하게 되기 때문입니다. 하지만 코드 개발이 점점 진행됨에 따라서 Unit Test는 Whitebox Test로 이행되게 됩니다.

Whitebox Test – Whitebox Test는 코드 내부를 알고 있다는 것을 전제로 하는 테스트입니다. 우리는 코드를 알기 때문에 모든 가능한 성공/실패 시나리오를 모두 알 수가 있고, 특정 데이터나 입력 조건이 어떤 Flow를 따르는지에 대해서 충분하게 알 수 있습니다. 이런 가능한 모든 시나리오에 대해서 Unit Test를 작성하고 수행하게 되면 그것을 Whitebox Test라고 말할 수가 있습니다.

* 이는 Blackbox Test, Whitebox Test에 대한 일반적인 정의가 아니라, Unit Test관점에서 바라본 정의입니다. 일반적인 정의에 대해서는 Wikipedia의
Whitebox Testing, Blackbox Testing 항목들을 참고하시기 바랍니다.

Unit Test를 수행할 때에 대개 Code Coverage Test를 같이 수행하는 이유가 바로 우리가 작성한 Unit Test가 어느 정도 수준의 Whitebox Testing에 도달했는지를 알 수가 있기 때문입니다. 당연히 우리가 작성한 모든 코드에 대해서 완벽한 Whitebox Testing을 Unit Test를 통해서 수행했다면 Code Coverage Test의 결과는 100%가 되어야 합니다. 하지만, 실제로 Unit Test건 Manual Test Case건 Code Coverage Test결과가 100%에 도달하기는 참으로 힘듭니다. 일반적으로 80%정도를 목표로 하지만, 그 이하가 되는 경우가 대부분입니다. 그것은 복잡성의 문제이기도 하고, 비용 대비 효과의 문제이기도 합니다. 코드의 모든 경로를 통과하는 Unit Test를 작성하려면 엄청난 시간이 소요될 테니까요.

이 점이 PEX라는 제품이 나오게 된 배경이라고 할 수 있습니다. PEX는 Visual Studio의 코드를 분석해서, Input과 output 조합을 찾아서 자동으로 테스트 코드를 만들어 줍니다. 자동으로 생성된 테스트 코드이지만, 높은 Coverage를 보여 줍니다. PEX를 사용하면, Unit Test 작성에 들이는 수고를 상당히 줄일 수 있을 것 같습니다.

 

PEX 홈페이지는 http://research.microsoft.com/en-us/projects/pex/default.aspx 이며, 현재 Visual Studio Team System 2008과 2010에서 사용할 수 있다고 되어 있습니다. 즉, 현재 VSTS 2008을 쓰고 계시는 개발자 분이라면 다운로드 받아서 써 보실 수 있다는 의미입니다. 현재 정식 버전은 아니고 Pre-release 버전입니다. 그리고 Visual Studio 2008 Professional 버전을 위한 비 상업용 Academic 버전도 다운로드 받을 수 있습니다. 다운로드 링크는 http://research.microsoft.com/en-us/projects/pex/downloads.aspx 입니다.

이번 글은 개요이니까, PEX Tutorial에 나오는 간단한 샘플만 잠시 보도록 하겠습니다.

   1:  [PexMethod]
   2:  public void ParameterizedTest(int i)
   3:  {
   4:       if (i == 123)
   5:           throw new ArgumentException("i");
   6:  }

Integer 타입의 매개 변수를 받는 간단한 메소드입니다. 매개 변수가 123일 때에는 ArgumentException을 일으키는 딱 두 줄의 구현만 있을 뿐입니다. PexMethod Attribute는 이 메소드가 PEX Test method라는 것을 의미합니다. 이 메소드에 대고 마우스 오른쪽 클릭을 하게 되면 컨텍스트 메뉴에 다음과 같이 PEX 관련 메뉴들이 보이게 됩니다.

여기서 Run Pex Explorations를 선택하게 되면, PEX가 자동으로 코드를 분석해서 가능한 모든 매개 변수를 대입해 본 다음 그 결과를 바탕으로 아래처럼 결과를 보여주게 됩니다.

이 부분 – Run Pex Exploration 실행 - 에서 저는 예상치 못했던 에러를 만났습니다.

[critical] unexpected failure during exploration
System.IO.FileLoadException: Could not load file or assembly 'Microsoft.Z3, Version=2.0.30325.1, Culture=neutral, PublicKeyToken=9c8d792caae602a2' or one of its dependencies.


위와 같은 에러였는데, 이 에러는 Visual C++을 설치하지 않았을 때에 발생하는 것으로 보입니다. 아직 PEX 설치 파일이 어떤 조건에서도 완벽하게 모든 필요한 파일들을 설치하지 못하는 것 같습니다. 이 에러는 Visual C++ 2008 SP1 Redistributable Package를 다운로드해서 설치하는 것으로 해결할 수 있습니다. 이 정보는 http://social.msdn.microsoft.com/Forums/en-US/pex/thread/5862d522-0c2e-481c-b537-864e7427a7e5 에서 얻었습니다. 혹시 Visual C++ 가 설치되지 않은 분들은 참고하시기 바랍니다.

예상대로 123이라는 매개변수가 입력되었을 때에 ArgumentException이 발생한 것을 볼 수가 있습니다. 그리고 여기서 또 다시 마우스 오른쪽 컨텍스트 메뉴를 통해서 Go To를 선택하게 되면 실제 자동으로 생성된 테스트 코드를 아래 그림처럼 볼 수가 있습니다.

   1:  [TestMethod]
   2:  [PexGeneratedBy(typeof(HelloWorldTest))]
   3:  public void ParameterizedTest01()
   4:  {
   5:      this.ParameterizedTest(0);
   6:  }
   7:   
   8:  [TestMethod]
   9:  [PexGeneratedBy(typeof(HelloWorldTest))]
  10:  [PexRaisedException(typeof(ArgumentException))]
  11:  public void ParameterizedTest02()
  12:  {
  13:       this.ParameterizedTest(123);
  14:  }

간단한 샘플이긴 하지만, PEX의 강력한 자동 테스트 코드 생성 기능을 볼 수 있으셨을 거라고 생각합니다. 다음 포스팅에서는 PEX에 대해서 더 깊이 들어가 보겠습니다. 아 물론, Code Analysis에 대한 글들도 계속 올릴 예정입니다. 읽어주셔서 감사합니다.

Welcome to F#(7) - 클리프 행어.

F# 2009. 4. 26. 18:22 Posted by 알 수 없는 사용자

-거인의 어깨에 위태롭게 매달려서 안간힘-_-.

여전히 함수형언어를, F#을 이해하기 위해서 몸부림 치고 있습니다. 제 짧은 생각인지 모르겠으나, F#을 처음배우는 프로그래밍언어로 생각하시는 분은 없을거라고 생각합니다. 호기심이든 다른 목적이 있어서든 기존에 주력으로 쓰는 명령형 언어가 있고 추가로 공부하려고 하시는 분이 거의 대부분이 아닐까 합니다.(게다가 F#은 C#이나 VB.NET을 하시던 분이 거의 대부분이 아닐까 싶네요) 저 역시 C#을 주력으로 쓰면서 F#을 공부하고 있는거니까요.

그래서 단순히 문법적 차이를 말씀드리기 보다는 함수형언어에 깔려있는 사상&접근법&관점의 차이에 대해서 말씀드리고자 하는데, 부족한 지식으로 글이 들쭉날쭉하고 앞내용과 뒷내용이 연결이 안되는, 한글이 생긴이래 진급을 못하고 있는 불상사도 자주 보게되는거 같습니다. 

오늘도 함수형언어를 이해하기 위한 노력의 일환으로 한 논문의 내용을 바탕으로 이야기 하도록 하겠습니다. Anders hejlsberg가 언어설계에 대한 이야기를 하면서 자신은 다른 거인의 어깨위에 서있다고 말을 했었는데, 저는 거인의 어깨에 선게 아니라 위태롭게 매달려 있다는 느낌이 드는군요.-_-;  아놔, 오늘도 서론이 길군요-_-;;

 

-무슨 논문이냐.

제가 한번 
"Why Functional Programming Matters?"라는 논문에 대해서 언급한적이 있었습니다. 오늘 포스트의 바탕이 될 이 논문에서 모듈화에 대한 이야기를 하면서 다음과 같은 이야기를 합니다. 

However, there is a very important point that is often missed. When writing a modular program to solve a problem, one first divides the problem into subproblems, then solves the sub-problems and combines the solutions. The ways in which one can divide up the original problem depend directly on the ways in which one can glue solutions together. Therefore, to increase ones ability to modularise a problem conceptually, one must provide new kinds of glue in the programming language. Complicated scope rules and provision for separate compilation only help with clerical details; they offer no new conceptual tools for decomposing problems. 

하지만, 그런 이야기에서 중요한 점이 무시되곤 한다. 어떤 문제를 해결하기 위해서 모듈화된 프로그램을 작성할때, 프로그래머는 우선 문제를 작은 문제로 분리해내야 하고, 각각의 작은문제를 해결해서 하나의 솔루션으로 조합해야 한다. 문제를 어떻게 분리할지는 전적으로 해결된 문제들을 어떻게 하나의 솔루션으로 조합할지에 달려있다. 그러므로 프로그래머가 조금더 능숙하게 문제를 모듈화해서 해결하게 해주려면, 프로그래밍언어에서 문제를 붙이기 위한 새로운 접착제를 제공해줘야 한다.  복잡한 스코프룰이나 분리컴파일을 좀 더 편리하게 할 수 있게 도와주는 것들은 그저 사무적인일(문제를 해결하는일 자체보다는 코드작성을 도와주는 부차적인일)정도를 도와줄 뿐이다. 즉, 문제를 잘개 쪼개기 위해선 아무 도움도 안되는 것이다.


그러면서 저자는 함수형언어에서 문제를 붙이기 위해 쓰이는 두가지 접착제를 소개합니다. 그 중 하나가 지난포스트에서도 언급했었던 higher-order function입니다. 기억나시나요? 함수를 인자로 받고 함수를 리턴가능한 함수가 higher-order function이었죠? 이 논문은 Miranda라는 언어를 사용하면서 Haskell을 쓰지 못해서 죄송하다고 언급하고 있습니다. 하지만, 조금 된 논문이라 F#으로 못해서 죄송하다는 말은 없군요. 크하하하-_-;;

그럼 저자가 higher-order function의 예로 언급한 reduce를 F#으로 구현해보면서 설명드리도록 하겠습니다. reduce는 MapReduce때문에 유명해지기도 했고, fold라는 이름으로도 불리는데요, 아마도 fold나 reduce나 둘다 뭔가를 하나씩 줄여나가는 이미지를 연상하면서 붙인 이름이 아닌가 싶습니다.


우선, 원문을 보실분들을 위해서 Miranda에 대해서 아주초큼 설명을 드리자면, Miranda는 ML에서 ML은 Lisp에서 출발한 언어라고 합니다. Lisp에서는 리스트를 Pair의 조합으로 표현하는데요, 여기서도 그런거 같습니다. Pair의 조합이란 cons(construct의 준말이라는 군요)라는 함수를 통해서 만들어지는데요(Lisp이나 Scheme하신분들은 많이 보셨을듯~) 인자를 두개 받아서 그 두 인자를 하나의 쌍으로 묶어서 리턴하는 함수입니다. 즉 Lisp에서 (1 2 3 4)라는 리스트는,

 

 (cons 1

(cons 2

(cons 3

(cons 4 nil)))) 


nil은 Lisp에서 빈값을 뜻합니다, (1 2)는 (cons 1 2), (1 2 3)은 (cons 1 (cons 2 3))의 결과 인거죠. F#이 OCaml에서, OCaml은 ML에서 출발했으니, F#에서도 리스트는 비슷하게 구현되지 않았을까 싶습니다.(List.hd, List.tl같은 함수를 보면 그런거 같습니다.) 그리고 두개의 값이 하나의 쌍으로 이루어 져있기 때문에, 값에서 head부분과 tail부분으로 나눠서 생각할 수 있습니다. (1 2 3)의 head는 1이고 tail은 (2 3)입니다. (4 5 6 7)의 head는 4이고 tail은 (5 6 7)인거죠

 

-그래 그래 얼른 reduce가 뭔지나 좀 보자.

일단, 숫자의 리스트를 모두더하는 sum을 생각해보도록 하죠. 빈값에 대한 sum은 0이겠죠? 그래서 아래와 같이 정의합니다. 

sum [] = 0 

그리고 리스트의 합은 head의 값과 나머지 tail의 합으로 정의할 수 있고, 그 tail의 합은 다시 head의 값과 나머지인 tail의 합으로 정의할 수 있습니다. 즉, 

sum list = List.hd(list) + sum List.tl(list) 

이렇게 정의할 수 있습니다. List.hd는 head의 약자로 List에서 head를 리턴하고, tl은 tail의 약자로 List에서 head를 제외한 나머지 부분을 리턴합니다. 이런 과정을 어떤 함수가 들어와도 가능하게 일반화 하려면, 위에서 빨간색(?)으로 음영표시한 두부분이 중요해 집니다. 즉, '리스트의 마지막에 도달했을때 어떤 값을 리턴할 것인가'와 '리스트의 head와 tail에 대해 어떤 함수를 수행할 것인가'하는 부분입니다. 예를 들면, 리스트에 대해서 곱셈을 수행하는데, 빈리스트에 대해서 0을 리턴하게 한다면 리스트의 곱셈값은 0이 되겠죠-_-;; 그래서 이럴땐 빈리스트의 값을 곱셈에 영향을 안주는 1로 설정해야 합니다. 물론, 위의 경우에서는 합에 영향을 주지 않는 0으로 선택한 것이구요. 

그럼, 각 리스트의 요소에 대해 수행할 함수를 아래와 같이 정의합니다. 

let add x y = x + y 

그리고 다음과 같이 reduce함수를 작성해줍니다. 

 let rec reduce f x list =
    if list = [] then x

    else f (List.hd list) ((reduce f x) (List.tl list)) 

즉, reduce는 자기자신의 정의속에 자신을 호출하는 부분이 있는 제귀함수이고, 인자로는 f, x, list를 받으며 list가 빈리스트라면 연산에 영향을 주지않는 값으로 x를 리턴하고, 빈 리스트가 아니라면 head와 나머지리스트에 함수를 f를 적용한 값을 가지고 f를 수행합니다. 위의 메서드를 좀 더 읽기 쉽게하기 위해서 타입을 명시해주면 아래과 같습니다.

let rec reduce (f : 'a -> 'b) (x : 'c) (list : 'd list) =
    if list = [] then x

    else f (List.hd list) ((reduce f x) (List.tl list))

즉, f는 함수이고, x는 제네릭한 값하나, list는 제네릭한 리스트인거죠. 일단, 수행결과를 보고 무슨 소리인지 더 풀어보도록 하죠. 위 두 함수의 정의를 평가해주고나서, reduce add 0 [1;2;3;4] 이렇게 실행을 하면 아래와 같은 결과가 나옵니다. 

val add : int -> int -> int

>

val reduce : ('a -> 'b -> 'b) -> 'b -> 'a list -> 'b 

> reduce add 0 [1;2;3;4];;
val it : int = 10 

리스트의 값을 모두 더해서 10이라는 값이 나왔군요. 이 값이 어떻게 나왔는지 reduce가 정의를 확장하고 줄여나가는 모습을 자세히 적어보면 아래와 같습니다.

 

즉,
add x y = x + y이고
(reduce add 0) [1;2;3;4] 라면..
= add 1 ((reduce add 0) [2;3;4])
= add 1 (add 2 ((reduce add 0) [3;4]))
= add 1 (add 2 (add 3 ((reduce add 0) [4])))
= add 1 (add 2 (add 3 (add 4 ((reduce add 0) []))))
= add 1 (add 2 (add 3 (add 4 0)))
= add 1 (add 2 (add 3 4))
= add 1 (add 2 7)
= add 1 9
= 10

음영으로 강조한 부분이 reduce가 확장되는걸 보기 쉽게 만든 부분입니다. 자신의 정의를 이용해서 빈리스트가 나올때까지 값을 확장하고, 하나씩 줄여가면서 값을 게산해 가는거죠. 사실 add는 이미 F#에 구현되어 있습니다. 아래와 같이 쓸 수도 있습니다. 

reduce (+) 0 [1;2;3;4] 

그리고 람다를 이용해서 아래와 같이 써도 똑같은 결과를 낼 수 있습니다. 

reduce (fun x y -> x + y) 0 [1;2;3;4] 

그리고 reduce를 이용해서 아래와 같은 연산들도 가능합니다. 

reduce (&&) true [true; true; true; false; true] //전부다 true인지 검사하는 함수
let append a b = reduce (fun x y -> List.Cons(x, y)) b a //두 리스트를 붙이는 함수 

그리고 아래는 append의 실행결과입니다.

> append [1;2] [3;4];;

val it : int list = [1; 2; 3; 4] 

List.Cons는 위에서 말씀드렸던 cons의 F#의 List버전입니다. 위 메서드를 확장하고 줄여가나는 모습을 자세히 그려보면 아래와 같습니다.

List.Cons(1, reduce (fun x y -> List.Cons(x, y)) [3;4] [2])

= List.Cons(1, (List.Cons (2, reduce (fun x y -> List.Cons(x, y)) [3;4] [])))

= List.Cons(1, (List.Cons (2, [3;4])))

= List.Cons(1, [2;3;4])

= [1;2;3;4]

 

즉, 리스트에 대해서 특정함수의 수행을 누적시켜서 결과를 얻는(Aggregate) 과정을 쪼개고 그 쪼갠 부분을 붙이는 방법으로 higher-order function을 사용해서 만든 reduce로 이렇게 다양한 연산이 가능한 것입니다. 물론 논문의 저자는 여기서 더 나아가고 있습니다만, 제 실력부족으로 더 이상 나가지 못하는 점 양해바랍니다.-_-;;; 아, 그리고 추가로 말씀드리자면 reduce는 F#에 이미 구현되어 있습니다. reduce가 fold라고 불리기도 한다고 위에서 말씀드렸었죠? 아래와 같이 하면 reduce add 0 [1;2;3;4]와 같은 결과를 낼 수 있습니다. 

List.fold_right (+) [1;2;3;4] 0

List.fold_left (+) 0 [1;2;3;4] 

둘의 차이점은 아래와 같습니다. 

(출처 : http://en.wikipedia.org/wiki/Fold_(higher-order_function))


아래그림은 fold_left이고, 윗그림이 fold_right입니다. 그리고
각각 그림의 왼쪽은 리스트이고, 오른쪽은 어떻게 펼치느냐에 대한 도식입니다. 즉, 왼쪽으로 펼쳐서 접느냐, 오른쪽으로 펼쳐서 접느냐에 따라 틀린거지요. 위에서 구현된 reduce는 fold_right라고 보시면 됩니다.

 

- 마치면서

사실, 진정한 함수형언어의 강점과 F#의 강점은 아직 맛도 못봤습니다. 이거 빨리 제가 맛을 보고 잘 전해드려야 할텐데;; 개인적인 사정이 점점 안좋아지는군요-_-. Anders hejlsberg의 인터뷰에서 발췌한 글을 끝으로 남기면서 글을 마치겠습니다.(왜 Anders hejlsberg이야기가 많이 나오냐면, 전  Anders hejlsberg빠돌이거든요-_-;) 이 글을 읽다보니, 왜 함수형언어의 힘을 빌린 LINQ를 추가해서 데이터를 다루는 부분을 추상화 했는지 확 다가오는 군요. 그리고 앤더스가 선언형 언어와 함수형언어의 장점을 점점 더 중요하게 생각하고 있다는 생각이 듭니다.(물론, 원문을 읽어보시면 더 자세한 내용을 보실수 있겠져~.) 그리고 언제나 그렇듯이 제 맘대로 번역-_-.

 

I think one of the things that are true of most programming languages today is that they force you to overspecify the solution to your problem. You're down there writing nested for loops and if statements and whatever, and really all you wanted to do was a join between two pieces of data. But there's nothing that allows you to say that. You have to get down and dirty and do hash tables and dictionaries, blah, blah, blah.

난 요즘 대부분의 프로그래밍언어가 프로그래머에게 문제를 해결하기 위한 솔루션을 너무 필요이상으로 서술하게 만든다고 생각합니다. 물론 사실이구요. 프로그래머는 그저 두 데이터집합을 조인하고 싶을 뿐인데, for루프랑 if문을 여러개 중첩시키고 어쩌고 저쩌고 해야만 하는거죠. 즉, 그냥 조인하게 해달라고만 말할 수 있는게 없다는 겁니다. 진흙탕으로 내려가서 해시테이블이랑 딕셔너리랑 기타등등, 뭐 그런것들이랑 열심히 뒹굴어야 하는거죠.



-참고자료

1. Expert F#, Don Syme, Adam Granicz, Antonio Cisternino, APRESS
2. Programming Language Pragmatics 2nd Edition, Michael L. Scott, Morgan Kaufmann Publishers
3. Structure and Interpretation of Computer Programs, 2nd Edition, Harold Abelson, Gerald Jay Sussman, Julie Sussman, The MIT Press
4. 구글을 지탱하는 기술, 니시다 케이스케, 멘토르
5. http://en.wikipedia.org/wiki/Fold_(higher-order_function)
6. http://broadcast.oreilly.com/2009/04/an-interview-with-anders-hejls-1.html
7. http://en.wikipedia.org/wiki/Miranda_(programming_language
8. http://www.math.chalmers.se/~rjmh/Papers/whyfp.pdf

Welcome to F#(6) - 비교본능.

F# 2009. 4. 23. 09:27 Posted by 알 수 없는 사용자
-지난시간에 이어서.
잘들 지내셨는지요. 어느새 수요일이 다 지나가는 군요. 오늘은 어제에 비해서 일이 잘 되셨나요? 그렇습니다. 사람은 자연스럽게 비교를 하려는 본능비스무리 한게 있는거 같습니다. 여친과 다른 사람들을 비교하고, 자기 자식이랑 친구아들을 비교하고, 심지어는 별 찌질한것까지도 비교하면서 그 우위에서 오는 만족감을 느끼려는게 사람이 아닐까요.

하지만, 비교는 좋은 이해의 틀이기도 합니다. 뭔가가 틀리다는 건, 다른 것들과 부딪히면서 비로소 틀린점들이 눈에 들어오기 시작하고 형체를 갖추기 시작합니다. 이번 포스트에서는 지난 포스트에서 봤었던 F#예제와 같은 일을 하는 C#예제를 통해서 차이점을 좀 찾아보고자 합니다. ........늘 느끼는 거지만, 참 서두를 번지르르하게 써놓고 시작을 하네요.-_-



- C#코드부터 보자!

넵. 보여드리겠습니다. 보여드려야죠~. 다만, 아래의 코드는 제가 짠 코드일뿐 모범적인 코드라고는 볼 수 없다는 걸 생각하고 봐주시기 바랍니다.


class Demo
    {
        private string destDir;
        private List<STRING> finalFiles;

        public Demo(string destDir)
        {
            this.destDir = destDir;
            finalFiles = new List<STRING>();
        }

        private List<STRING> GetSthAll(Func<STRING, string[]> GetSth, string path)
        {
            return GetSth(path).ToList();
        }

        public void PrintAllFilesCount(string ext)
        {
            GetAllFiles(destDir);
            List<STRING[]> filesWithExt = finalFiles.Select(file => file.Split('.')).ToList();
            int countOfExt = filesWithExt.Where(file => (file.Length == 2) && (file[1] == ext)).Count();

            Console.WriteLine(countOfExt);
        }

        private void GetAllFiles(string destDir)
        {
            finalFiles.AddRange(GetSthAll(Directory.GetFiles, destDir));
            foreach (string currentDir in GetSthAll(Directory.GetDirectories, destDir))
            {
                GetAllFiles(currentDir);
            }
        }
    }

그리고 위 코드는 아래처럼 실행할 수 있습니다.


class Program
    {
        static void Main(string[] args)
        {
            Demo demo = new Demo(@"c:\ATI");
            demo.PrintAllFilesCount("txt");
        }
    }

위의 코드는 이전 포스트의 코드와 같은 결과를 내는 코드입니다. F#에서는 중간결과를 따로 저장하지 않고 |>를 통해서 바로바로 다음함수로 연결했었기 때문에 중간값의 상태저장에 대한 고려가 필요없었습니다. 그 때문에 제귀호출 과정의 데이터를 어디에 저장해야 할지 역시 고려하지 않아도 되었습니다. 즉, 데이터가 어디에서 어디로 가는지에 대한 데이터의 흐름이 추상화 되어 있었던 것이죠. 위의 코드를 보시면, 명령형언어에서 오는 한계점이 분명히 존재하지만, 특별히 복잡해보인다거나 코드가 길어졌다거나 하는 모습은 볼 수 없습니다. 왜그럴까요?

바로 LINQ때문입니다. 함수형언어의 장점을 녹인 LINQ덕분에 데이터추출과정이 추상화되어서 코드가 큰 차이없이 작성될 수 있었습니다. 최근 추세는 명령형언어와 함수형언어가 조금씩 융화되어 가는 것 같습니다. C#에도 점점 함수형언어의 장점과 동적언어의 장점이 녹아들어가고 있습니다. 물론 C#의 설계자인
Anders Hejlsberg는 static한 언어가 가지는 강점을 여전히 높게 평가하고 있습니다만, 필요하다면 점점 융화되어 가는거겠죠. 그럼, 어디한번 계급장떼고(?)...아니아니 LINQ떼고 한번 해보죠!


- 계급장떼고 붙여봐!
넵, 계급장테고 붙여보겠습니다. LINQ없이 C#2.0의 익명메서드 기능을 이용해서 동일한 코드를 작성해 보겠습니다.


class Demo2
    {
        private string destDir;
        private List<STRING> finalFiles;

        public Demo2(string destDir)
        {
            this.destDir = destDir;
            finalFiles = new List<STRING>();
        }

        private List<STRING> GetSthAll(Func<STRING, string[]> GetSth, string path)
        {
            return GetSth(path).ToList();
        }

        private List<STRING[]> GetSplitedList(List<STRING> data, Func<STRING,STRING[]> splitMethod)
        {
            List<STRING[]> splitedList = new List<STRING[]>();
            foreach (string str in data)
            {
                splitedList.Add(splitMethod(str));
            }
            return splitedList;
        }

        private int GetFilesCountWithExt(List<STRING[]> data, Func<STRING[], bool> filterMethod)
        {
            List<STRING[]> filteredList = new List<STRING[]>();
            foreach (string[] fileStr in data)
            {
                if (filterMethod(fileStr))
                {
                    filteredList.Add(fileStr);
                }
            }
            return filteredList.Count;
        }
        public void PrintAllFilesCount(string ext)
        {
            GetAllFiles(destDir);
            List<STRING[]> filesWithExt = GetSplitedList(finalFiles, delegate(string str) { return str.Split('.'); });
            int countOfExt = GetFilesCountWithExt(filesWithExt, delegate(string[] fileStr) { return (fileStr.Length == 2) && (fileStr[1] == ext); });
            Console.WriteLine(countOfExt);
        }

        private void GetAllFiles(string destDir)
        {
            finalFiles.AddRange(GetSthAll(Directory.GetFiles, destDir));
            foreach (string currentDir in GetSthAll(Directory.GetDirectories, destDir))
            {
                GetAllFiles(currentDir);
            }
        }
    }

어떻습니까? 이젠 차이가 좀 드러나기 시작합니다. 익명메서드를 이용해서 람다를 흉내냈지만, 데이터추출과정을 추상화하진못해서 그 추출과정의 중간값저장이나 추출루프를 직접 제어해줘야 합니다. 이렇게 되면, 문제가 조금씩 복잡해질수록 코딩도 어려워 지겠지요.(Func<>는 .NET 3.5에서 추가된거지만 봐주셈요-_-)

즉, 단순히 LINQ라는 기술을 쓰고 안쓰고정도의 문제가 아니라, 추상화 단계가 낮아지는 것이기 때문에 생각의 복잡도는 높아질 수 밖에 없는 것입니다. 데이터의 흐름을 추상화해주는 함수형언어의 특징이 이런부분에서 굉장한 강점으로 작용하게 됩니다.



- 할말다했냐?
네 별로 대단치않은 예제이지만, 최선을 다해서 한번 비교를 해봤습니다. 물론, 예제가 완전히 적절한건 아니고 크기나 복잡도 역시 높은 수준이 아니라 명확한 비교라고 볼 수는 없지만, 하나의 예제가 될 수 있다고 생각합니다. 이번 포스트에서 함수형언어인 F#의 장점을 못 느끼셨다면 모두 제 책임이나 따뜻한 질타를 주시기 바랍니다. 끝으로 Expert F#의 저자인 Don Syme의 블로그에 올라온 사례하나를 소개하고 마치겠습니다.

-포스트 원문-

The first application was parsing 110GB of log data spread over 11,000 text files in over 300 directories and importing it into a SQL database. The whole application is 90 lines long (including comments!) and finished the task of parsing the source files and importing the data in under 18 hours; that works out to a staggering 10,000 log lines processed per second! Note that I have not optimized the code at all but written the application in the most obvious way. I was truly astonished as I had planned at least a week of work for both coding and running the application.

Ralf Herbrich, Microsoft Research
(http://blogs.msdn.com/dsyme/archive/2006/04/01/566301.aspx)


-제 맘대로 해석-_-;;;-

첫번째 프로그램은 300개가 넘는 디렉토리에 있는 11,000개의 텍스트파일에 저장되어있는 100GB나 되는 로그 데이터를 가져와서 분석해서 SQL데이터베이스에 저장하는 거였습니다. 어플리케이션은 전체 길이가 주석을 포함해서 딱 90줄이었고, 로그파일들을 읽어들여 분석하고 저장하는데 18시간이 채 안걸렸습니다. 1초에 1만줄의 로그를 처리한 셈이죠! 더 중요한건 코드를 최적하려고 하지도 않았고 그냥 제일 명확하게 보이도록 짰습니다. 제가 진짜 놀랐던건, 솔직히 전 그 프로그램 짜고 돌리는데 일주일은 걸릴 줄 알았었거든요.



- 참고자료

1. Expert F#, Don Syme, Adam Granicz, Antonio Cisternino, APRESS

2. http://blogs.msdn.com/dsyme/archive/2006/04/01/566301.aspx


 

Welcome to F#(5) - 아주 조금씩 심화되는 탐색전.

F# 2009. 4. 20. 08:50 Posted by 알 수 없는 사용자

- 벌써 다섯번째네?


네, 맞습니다;; 벌써 다섯번째 포스트입니다. 그런데 영~ 포스트는 나아질 기미를 보이진 않는군요. 모든게 제 잘못입니다;; 계속 노력중이니 좋게 봐주시길 부탁드립니다.^^ 오늘은 지난번에 했던 거 보다 조금 더 복잡한 코드를 가지고 이야기를 해보려고 합니다. 그리고 그 코드를 바탕으로 C#과의 비교를 통해서 좀 더 의미있는 것들을 이야기 해보고 싶습니다. 읽어보시고 의미가 없다면 따뜻한 피드백으로 질타를...;;

 

- 알았으니까 예제부터 보자!

open System.IO

let rec getAllFiles dir =
    List.append 
      (dir |> Directory.GetFiles |> Seq.to_list) 
      (dir |> Directory.GetDirectories |> Seq.map getAllFiles |> Seq.concat |> Seq.to_list)
 
//여기서는 그렇게 가져온 파일리스트를 '.'을 기준으로 짤라낸다.
let splitedAllFiles = getAllFiles @"C:\ATI" |> List.map (String.split ['.'])
 
//여기서는 각 파일을 돌면서, 확장자가 사용자가 원하는 확장자인거만 걸러내고, 개수를 리턴한다.
let Count ext = splitedAllFiles |> List.filter (fun file -> if file.Length = 2 then file.[1] = ext else false) |> List.length

 

그리고 위의 예제를 F#인터렉티브에서 확인해본 결과입니다.(PowerPack.dll을 로딩하는 부분빼곤 전부 Alt+Enter를 이용했습니다.)


 > #r "FSharp.PowerPack.dll";;

--> Referenced 'C:\Program Files (x86)\FSharp-1.9.6.2\bin\FSharp.PowerPack.dll'

>

 

val getAllFiles : string -> string list
val splitedAllFiles : string list list
val Count : string -> int


> Count "txt";;
val it : int = 129
>

 

위 코드는 명시된 디렉토리를 모두 훑어서 거기에 있는 파일들의 경로를 가져오고, 그 파일들 중에 사용자가 명시한 확장자를 가진 파일이 몇개나 되는지를 출력하는 코드로 Expert F#에 나온코드를 조금 수정하고 덧붙인 것입니다. 일단 이 예제를 가지고 C#과의 비교를 통해서, 함수형언어로서의 F#이 가지는 장점에 대해 조금알아보고자 합니다. 그럼 우선 위의 코드에 대해서 알아보겠습니다.

 

- 첫번째는?

우선, 위의 코드에서는 두가지의 자료구조가 사용되었는데요, Seq과 List가 그것입니다. Seq는 C#에서의 IEnumerable<>타입을 의미하구요, List는 List<>을 의미합니다. 특성도 비슷합니다. 조금 더 익숙하실 C#을 통해 먼저 알아보죠. LINQ에서 보면, 쿼리를 날렸을때,  IEnumerable<>타입이 리턴되는데요, 이 IEnumerable<>로 리턴된 시퀀스는  아직 데이터를 가지고 있는게 아닙니다. 실제로 IEnumerable<>의 각요소에 접근해야 할때가 되서야 LINQ의 쿼리가 실행됩니다. 예제코드를 먼저 보시져~!

 

class Program
    {
        static void Main(string[] args)
        {
            int[] list1 = new int[] { 98, 34, 5, 134, 64, 57, 99, 320, 21, 55, 62, 39 };
            IEnumerable<int> queriedList1 = list1.Where(num => num <= 100);

            foreach (int num in queriedList1)
            {
                Console.WriteLine(num);
            }

            list1[0] = 198; //98 -> 198
            list1[1] = 134; //34 -> 134

            Console.WriteLine("---------------second call-------------");

            foreach (int num in queriedList1)
            {
                Console.WriteLine(num);
            }
        }
    }

 

위코드를 보시면, int형 배열에 대해서 100보다 작은 수만 골라서 쿼리를 날려서 IEnumerable<int>형으로 리턴을 받습니다. 그리고 처음에 모든 수를 돌면서 출력을 하고, 그 다음에 쿼리의 대상이었던 배열의 값중에 처음 두개를 100보다 크게 바꾼뒤에 다시 시퀀스돌면서 숫자를 출력하고 있습니다. 여기서 주목하실건, 쿼리를 두번 날린게 아니라 그저  IEnumerable<int>시퀀스 안의 요소를 한번더 돌면서 출력한 것 뿐이라는 겁니다. 결과를 보시죠.

 

 

위의 결과를 보시면, 두번째 foreach에서는 바꿔준 값들이 나오지 않는 걸 알 수 있습니다. 즉, 쿼리는 만들어지긴 하되 그 실행시기는 직접 요청될때로 연기(Deferred)된 것입니다. 이런 IEnumerable의 특성으로 인해, 변경된 내용을 가져오는데 쿼리를 두번날릴 필요가 없이 그저 전체요소를 한번더 돌기만 하면 자연스럽게 변경된 내용을 읽어오는게 되는 것입니다. 이와는 반대로 List는 즉시 모든 값을 읽어옵니다. 그래서 위의 코드에서,

 

IEnumerable<int> queriedList1 = list1.Where(num => num <= 100);

위코드를 아래와 같이 수정하고,

List<int> queriedList1 = list1.Where(num => num <= 100).ToList();


다시 예제를 돌려보면, 아래와 같은 결과가 나오는 걸 확인할 수 있습니다.

 

 

즉, 이미 수행시점에서 모든 요소를 미리(Immediately)가져 왔기 때문에, 원본값이 수정된 뒤에서 쿼리로 가져온 값에는 변화가 없는 것이죠. 이런 특성은 F#에서도 여전히 유효합니다.

 

> seq { 1 .. 100000000 };;
val it : seq<int> = seq [1; 2; 3; 4; ...] 
 

F# 인터렉티브에서는 위와같은 결과가 나옵니다. 즉, 1부터 1억까지의 숫자의 시퀀스를 선언하면, 모든 숫자가 다 즉시 만들어지는게 아니라는 거죠. 결과에서 보듯이 1부터 4까지만 나온걸 보실 수 있습니다. 즉, F#인터렉티브가 네번째 요소까지만 미리 읽어오도록 하고 있는거지요. 그래서 주의 하셔야 할점도 있습니다. List는 미리다 읽어오므로 메모리에 다 올라갈 수 있을지를 미리 생각해봐야 하고, Seq는 미리 다 읽어오는게 아니므로 읽어오기 전에 데이터변경이 되면 다른 결과가 나올 수 있습니다. 이 부분을 유념해야 합니다.

 

- 두번째는?


그럼 맨 처음에 파일들을 읽어오는 코드를 계속 보기로 하죠. 위에서 "|>"라는 기호가 보이는데 이 건 뭘까요? 뭐 리눅스같은데서 shell을 좀 다뤄보셨거나 하면 pipe겠구나 하는건 금방 아실 수 있겠죠.^^;; 이건 정방향 파이프 연산자(forward pipe operator)라고 하는데요, |> 앞쪽의 리턴값을 |> 뒤쪽의 메서드에 넘겨주는 역할을 합니다. 즉 위의 예제를 보자면, Directory.GetFiles의 인자로 dir를 넘겨주고, 그 결과를 Seq.to_list 의 인자로 넘겨주는 거죠. 굉장히 심플하게 중간 결과를 어디에 담아둘지 고민하지 않고 함수들을 연결할 수 있게 도와주는 연산자입니다.

아, 그리고 여기서 주목하실 부분이 있다면, 닷넷프레임워크의 메서드인 Directory.GetFiles나 Directory.GetDirectories를 F#에서도 First-class function으로 사용할 수 있습니다. 이런 부분이 F#의 진정한 강점이 아닌가 싶네요. 그리고 기존의 C#이나 VB.NET을 이용하시던 분들이 좀더 자연스럽게 F#이용해서 효율적인 함수형 프로그램을 작성할 수 있게 해주는 원동력이기도 하구요.


- First-class function?


Fisrt-class function이란 함수형언어의 특징중 하나로서, 함수가 다른 함수의 파라미터로 들어갈 수 있고, 연산결과로 리턴될 수 있으며 명령형언어에서는 추가적으로 변수에 대입가능한(자바스크립트를 떠올리시면 됩니다.) 함수라는 걸 의미합니다. Higher-order function은 함수를 인자로 받거나 함수를 결과로 리턴하는 함수를 의미하구요.

 

그 다음줄도 역시 Directory.GetDiretories에 dir을 넘겨줘서 현재 디렉토리의 하위 디렉토리들의 시퀄스를 가져오고 그걸 Seq.map에 넘겨줘서 각 디렉토리별로 getAllFiles를 실행해서 모든 하위 디렉토리의 파일경로를 가져오도록 합니다. 그리고 그 결과를 Seq.concat을 통해서 하나의 시퀀스로 합치구요, 그 다음에 그 시퀀스를 list로 변환합니다. 그리고 모든 결과를 하나의 list로 합쳐서 리턴합니다.

 

그리고 splitedAllFiles에서는 그렇게 만든 리스트의 모든 요소를 대상으로 '.'을 기준으로 파일경로와 확장자로 분리합니다. 그리고 마지막 Count함수에서는 그 결과를 List.filter함수에게 넘겨줘서 확장자가 사용자가 명시한 확장자와 일치하는 것만 추려서 하나의 리스트로 만든뒤, 그 리스트의 길이를 리턴합니다. 이렇게 해서 해당 디렉토리와 그 하위 디렉토리에서 특정 확장자를 가지는 파일의 개수를 읽어오는 프로그램이 완성되는 것입니다.

 

 

- C#과 비교한대매?


이번 포스트에서는 F#의 예제코드를 통해서 F#의 Seq, List의 특성을 알아봤고, |>연산자를 통해서 함수들을 쉽게 연결하는 것을 보았습니다. 다음 포스트에서는 이 F#의 코드와 동일한 C#코드를 통해서 비교를 조금 해보고자 합니다. 글의 내용이 여전히 만족스럽진 않지만, 저도 부족한 머리 박아가면서 쓰는 글이니 좀 봐주시구요;; 따뜻한 피드백 부탁드립니다.

 

- 참고자료

1. Expert F#, Don Syme, Adam Granicz, Antonio Cisternino, APRESS

2. Pro LINQ: Language Integrated Query in C# 2008, Joseph C. Rattz, Jr. , APRESS.

3. Programming Language Pragmatics 2nd Edition, Michael L. Scott, Morgan Kaufmann Publishers


지난 번 포스팅에서 말씀 드렸던 것처럼 Visual Studio Team System 2010에서 이루어진 Code Analysis 기능 개선에 대해서 더 깊게 다뤄 보겠습니다. 그 첫 번째로 새롭게 추가된 Rule Sets 개념에 대해서 알아보도록 하겠습니다.


Visual Studio 2005, 2008에서의 코드 분석 설정

Visual Studio의 코드 분석 시스템에는 약 200개 정도의 규칙이 있습니다. 하지만, 사실 이 규칙들을 모두 준수를 해야 하는 것은 아닙니다. 모든 규칙이 나름의 이유와 정당성을 갖고 있다고 해도, 프로젝트의 성격에 따라, 모듈의 성격에 따라서 꼭 지켜야 할 규칙, 그렇지 않은 규칙, 심지어는 어쩔 수 없이 위반해야 하는 규칙도 있을 수 있습니다.

그래서 코드 리뷰를 할 때, 대부분의 경우 모든 규칙을 다 검사하지는 않습니다. 각각 케이스 바이 케이스로 규칙을 적절하게 조절하게 됩니다.

Visual Studio Team System 2005와 2008 버전에서는, 이런 설정을 Visual Studio Project Configuration에서 각 프로젝트 별로 할 수가 있습니다.

이 규칙 설정은 프로젝트 파일(.vbproj 혹은 .csproj)에 CodeAnalysisRules라는 Property 이름으로 아래와 같이 저장되게 됩니다. 내용을 보면 아시겠지만, “-“ 기호와 규칙 ID를 붙여 놓은 모양으로 되어있습니다. 즉, 거기에 열거된 규칙 ID를 Analysis Rules에서 빼는 형태로 저장이 되는 것입니다. 아래 샘플은 그래서 이 프로젝트는 코드 리뷰를 할 때에 Design Rule의 CA1000번 규칙을 검사하지 않는다라는 의미가 됩니다.

<CodeAnalysisRules>-Microsoft.Design#CA1000</CodeAnalysisRules>

이런 형태로 Visual Studio 프로젝트마다 다른 규칙을 적용할 수 있다는 것은 대단히 유연한 디자인이긴 하지만, 많은 수의 Visual Studio Project가 존재한다면, 관리가 참으로 어렵습니다. 그래서 예전에 Visual Studio Team System 2005 를 사용했던 프로젝트에서 전체 Visual Studio 프로젝트의 분석 규칙들을 통일하기 위해서, 개별 개발자들에게 위임하지 않고 제가 직접 모든 프로젝트를 텍스트 에디터로 열어서 저 부분만 Copy & Paste로 넣어서 Check-In하는 엄청난 단순 반복 작업을 한 적도 있습니다.

 

Visual Studio Team System 2010의 Rule Sets Feature

그래서 새롭게 출시될 Visual Studio Team System 2010에서는 Rule Sets라는 개념이 도입되었습니다. 이 Rule Sets 기능은 이름에서도 알 수 있듯이 Rule들을 미리 정해진 Set으로 관리할 수 있는 기능입니다. 이제 Visual Studio 프로젝트 별로 Rule을 하나 하나 빼는 수고를 할 필요가 없이 Visual Studio 프로젝트에 Rule Set을 지정하는 것으로 Code analysis를 커스터마이징할 수 있게 된 것입니다. 아래 그림에서 볼 수 있듯이, 하나 이상의 Rule Set을 선택하는 것도 가능합니다. (가장 아래의 Choose Multiple Rule sets를 선택하면 됩니다.)


Visual Studio Solution 전체에 Rule Set을 적용하는 것도 가능합니다. Solution Property에서 아래와 같이 설정할 수 있습니다.

 

위 그림에서 보시는 것처럼 마이크로소프트는 기본적으로 7개의 기본 Rule Set을 제공합니다. 이 기본 Rule Set의 목록은 다음과 같습니다.

  • Microsoft All Rules – 전체 Rule이 모두 포함된 Rule Set입니다.
  • Microsoft Basic Correctness Rules – 로직 에러와 자주 저지르는 실수를 예방하는 규칙들로 이루어진 Rule Set입니다.
  • Microsoft Basic Design Guideline Rules – Framework API 작성 상의 Best Practice에 집중된 Rule Set입니다.
  • Microsoft Extended Correctness Rules – Basic Correctness Rules를 확장하여, COM Interop이나 Mobile Application에도 사용 가능하도록 만들어진 Rule Set입니다.
  • Microsoft Extended Design Guideline Rules – Basic Design Guideline Rules를 확장해서, 사용성이나 유지 보수성도 체크할 수 있도록 만들어진 Rule Set입니다.
  • Microsoft Globalization Rules – Application의 Globalization에 있어서 문제가 있는지 검증하는데 집중된 Rule Set입니다.
  • Microsoft Minimum Recommended Rules - MS가 제안하는 최소한의 Rule Set입니다. 50개 정도의 규칙으로 이루어져 있습니다. 다른 대부분의 Rule Set들이 이 Rule Set을 포함하고 있습니다.
  • Microsoft Security Rules – 이름 그대로 보안에 집중된 Rule Set으로, 모든 Security 관련 규칙들이 모두 포함되어 있습니다.

 

물론 이 외에도 사용자가 직접 Rule Set을 편집할 수 있습니다. Rule Set은 .ruleset이라는 확장자를 가지는 XML 파일 포맷으로 되어 있습니다. 기본적으로 Visual Studio 2010에서 아래와 같은 UI를 통해서 편집할 수 있도록 되어 있습니다.


 

아래는 Rule Set 파일을 직접 Notepad로 열어본 것입니다.

<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Microsoft Basic Correctness Rules" Description="These rules focus on logic errors and common mistakes made in the usage of framework APIs. Include this rule set to expand on the list of warnings reported by the minimum recommended rules." ToolsVersion="10.0">
  <Localization ResourceAssembly="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.dll" ResourceBaseName="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.Localized">
    <Name Resource="BasicCorrectnessRules_Name" />
    <Description Resource="BasicCorrectnessRules_Description" />
  </Localization>
  <Include Path="minimumrecommendedrules.ruleset" Action="Default" />
  <Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
    <Rule Id="CA1008" Action="Warn" />
    <Rule Id="CA1013" Action="Warn" />
    <Rule Id="CA1303" Action="Warn" />
    <Rule Id="CA1308" Action="Warn" />
    <Rule Id="CA1806" Action="Warn" />
    <Rule Id="CA1816" Action="Warn" />
    <Rule Id="CA1819" Action="Warn" />
    <Rule Id="CA1820" Action="Warn" />
    <Rule Id="CA1903" Action="Warn" />
    <Rule Id="CA2004" Action="Warn" />
    <Rule Id="CA2006" Action="Warn" />
  </Rules>
</RuleSet>

 

Team Foundation Server Check-In Policy

이 Rule Set 기능은 Team Foundation Server의 Check-In Policy에도 쓰이게 됩니다.

 

맺으며..

이번 Rule Set Feature를 통해서 확실히 이전 버전보다는 Code Analysis를 사용하는 것이 쉬워지고 편리해진 것 같습니다. 하지만, Code Analysis에서 중요한 것은 편의성보다는, Rule의 문제라고 생각됩니다. 그런 면에서 아직 FxCop이 처음 나왔을 때의 200개 정도의 Rule에서 많은 추가가 없다는 것은 굉장히 아쉽습니다. 특히 직접적인 경쟁 제품이라고 볼 수 있는 Compuware의 DevPartner Code Review가 처음 버전부터 600개 이상의 Rule을 가지고 있다는 점에 비교해 본다면 더욱 그렇습니다.

이어지는 포스팅에서는 새롭게 Code Analysis에 추가된 Phoenix 엔진과 그에 따라 다시 복귀한 Data Flow 관련 규칙들에 대해서 한 번 알아보겠습니다.

Welcome to F#(4) - 과거와 배경을 좀 더 알고싶어.

F# 2009. 4. 18. 21:48 Posted by 알 수 없는 사용자

- I remember back in the day

우리는 어떤 사람이 마음에 들면, 그 사람에 대해서 작은 거라고 할지라도 더 알고 싶어서 발버둥 칩니다. 멀리 갈것도 없습니다. 연예인에 삘꽂혀서 그 연예인의 나이, 취미, 혈액형 그리고 별 정확하지도 않은 키랑 몸무게 찾아다니느라 애쓰던 시절을 떠올려 보시지요. 그리고 노트북을 살때, 노트북에 꽂혀서 사소한 것 까지도 다 찾아내고 물어보고 알아내서 자신에 마음에 조금이라도 더 드는 상품을 사지요.
 
아이팟도 그 좋은 예인데요, 아이팟을 사고나서 온갖 해킹이며, 어플다 깔다가 하루가 지나갔던 그런 경험을 하신분도 있을 겁니다. 서론이 너무 기네요. 이번 포스트에서는 함수형언어를 이해하려고 무진장 애를 쓰다가 머릿속에 떠오른 게 있어서 적어보려고 합니다. 엄밀하지 못한 글일 수도 있으나, 따뜻한 피드백 많이 부탁드립니다. 차가운 피드백은... 아시져? ^^
 

- 기초학문?

개인적인 생각으로 우리가 흔히 기초학문이라 부르는 것들은 공통적인 특징이 있는 것 같습니다. 그리고 기초학문은 아마도 존재하지 않던 새로운 것들을 만들어 내는 쪽보다는 기존에 있던 것들에 새로운 의미를 부여하고 새로운 발견을 해나가는 쪽이 더 맞지 않나 합니다. 그래서 그 새로운것이 어떤건지 성질을 밝혀내고 알아내서 어떻게 응용이 될 수 있을지에 대한 단서를 제공하는게 아닌가 합니다. 

- 그래서 뭐? 수학?

그런 기초학문중에 대표적인 하나를 꼽자면 수학을 꼽을 수 있을 것 같습니다. 수학이라는 학문도 사람들이 살면서 자연스럽게 뭔가를 세기 위해서 군만두만 먹으면서 벽에 작대기를 그으면서 표시하던게 점차 발전하여 지금의 우리가 쓰는 숫자도 아랍쪽 어디선가 발견이 되고  퍼지면서 사용되는거고, 그 도구에 '수'라는 이름을 붙이면서 시작된게 아닌가 합니다. 뭔가 분명히 존재하지만 명확하게 정의할 수 없을때, 거기에 이름을 붙이게 되면 우리는 그에 대한 연구를 시작할 수 있게 됩니다. 이름을 붙인다는 건 우리에게 상당한 지배력을 제공해 주기 때문입니다. 무진장 가깝지만 사귀자고 누구도 명확하게 이야기 하지않은 상태라면, 서로 뻘쭘하고 넘을 수 있는 선의 범위도 모호하지만 서로의 관계를 공식적으로 '연인'이라고 이름붙이게 되면 더 자연스럽게 애정행각(!)을 벌일 수 있는 법이지요. ^^;; 

- 그래서 하고 싶은 이야기가 머꼬?

자, 다들 중~고등학교때 수학공부를 하고 문제를 풀던 때를 떠올려 보시지요. 수학은 정의와 공식으로 시작해서 정의와 공식으로 끝납니다. 정의나 공식은 이미 존재하는 무언가를, 그리고 그 무언가에 나타나는 패턴을 명확하게 나타내 보일 수 있는 수학적 도구입니다. 그리고 그런 정의와 공식에서 시작해서 또다른 정의와 공식이 그위에 쌓이게 되고 그런 것들이 계속 되면서 각각의 수학분야가 하나로 모이게 되는 대통합수학도 이루어 지는 거고 그렇게 하다보니 페르마의 마지막 정리 같은 난제도 해결이 될 수 있었습니다. 

즉, 수학문제를 풀때 그 문제를 이루고 있는 각 요소가 뭔지 공식으로 정의를 해내고, 그 정의를 모아서 문제에 대한 공식을 도출해 냅니다. 그리고 그 공식을 통해서 문제를 해결하고, 동일한 패턴을 보이는 문제들을 풀어나갑니다. 가만히 생각해보면, 중간중간에 도출되는 값을 x,y같은 부호에 묶어주고, 도출된 공식을 부호로 표시하거나, 공식에 있는 부호를 공식으로 풀어내서 문제를 해결하곤 했습니다. 수학문제를 해결한다는 건 무엇을 어떻게 정의하느냐에 따라서 금새 풀리기도 아무리 싸매고 고생해도 안풀리곤 했던 거지요. 우리가 풀던 수학에선 x에는 하나의 값이나 공식만 정의할 수 있었고, 상태를 저장하는 거 역시 없었습니다. 

- 그게 함수형 언어랑 뭔 상관?

최대공약수를 정의할 때 명령형 프로그래머는,

a와 b의 최대공약수를 계산 해내려면, 일단 a랑 b가 같은지 보는거야. 만약에 그렇다면, 둘중에 하나를 출력하고 끝나면 되는거지. 안 그렇다면, 둘중에 큰수를 두수의 차(빼기)로 바꾸고 다시 하면 돼

그리고 함수형 프로그래머는,

a와 b의 최대공약수는 a와 b가 같다면 a이고, a와 b가 같지 않다면 c와 d에 대한 최대공약수를 구하게 되는데 여기서 c는 a와 b중에 작은수가 되고, d는 그 두수의 차가 된다. 주어진 쌍의 숫자의 최대공약수를 구하려면, 이 공식을 세부적으로 계속 확장해 나간다음에 식이 끝날때까지 하나씩 수행하면서 줄여나가면 된다.

사실 이 글을 쓰게 된데에는 "Programming Language Pragmatics"에서 위의 문장을 보고서 든 생각을 바탕으로(제 맘대로 해석했습니다-_-) 기존에 수학에 대해서 생각해오던 것들을 추려서 쓴 것입니다. 위의 문장이 많은 것을 말해주는 것 같다는 생각이 듭니다. 명령형 프로그래머는 "어떻게"에 집중하는 반면에 함수형 프로그래머는 최대공약수 "무엇"인지를 정의하는데 중점을 둔다는 것입니다. 물론 F#은 Haskell과 같은 순수한 함수형언어는 아닙니다. 물론, 순수한 함수형 언어로도 사용할 수 있지만, 그건 F#의 역할이 아니라는게 개인적인 생각입니다. 

- 아 거 말 되게 많네.

깊이도 없는 내용을 쓸데없이 길게말하느라 쓰는 저도 수고했고, 읽어주신 여러분도 수고하셨습니다.-_-;; 사실 함수형언어는 "왜 함수형 언어인가?"에 대한 질문에 답을 할 수 있어야 함수형언어와 친하게 지낼 수 있지 않을까 하는 생각이 듭니다. 명령형나라의 프로그래머와 함수형나라의 프로그래머는 서로 문제를 바라보는 관점이 다르기 때문이죠. "Why Functional Programming Matters?"와 같은 좋은 논문도 있어서 공부중이지만, 부족한 제머리와 부족한 시간은 깝깝하기만 하군요;; 아무튼, 제가 느끼는 것들은 이 공간을 통해서 계속 머리박아가면서 쓰겠습니다. 좋은 피드백으로 격려해주세요. 앞으론 차가운 피드백 이야긴 안하겠습니다.^^ 


-참고자료

1. Expert F#, Don Syme, Adam Granicz, Antonio Cisternino, APRESS.
2. Programming Language Pragmatics, 2nd Edition, Michael L. Scott, Morgan Kaufmann Publishers
3. 거짓의 사람들, 스캇 펙, 비전과 리더십
4. 페르마의 마지막 정리, 사이먼 싱, 영림카디널
5. 사고력을 키우는 수학책, 오카베 츠네하루, 을지외국어



ps. 왜 연재 계획도 없고, 이야기 했던 계획이랑도 틀린 포스트가 계속 올라오냐?

아직 아무도 지적하신 분은 없지만... 네 지당하신 지적입니다. 제가 연재라는 개념이 희박하고 분야에 대한 지식이 얉은 탓에 그렇게 되는 점 혹시 안좋게 보신 분들이 있다면 사과드립니다;;; 앞으로도 계속 그럴지도 모르기 때문에 지금쯤에는 사과를 해 드려야 나중에 한꺼번에 욕을 먹는 사태를 방지할 수 있을거 같아서 말이죠-_-;;;


Visual Studio Team System 2005 에서 코드 분석(Static Code Analysis) 기능이 처음 소개된 바가 있습니다. 이 코드 분석은 Microsoft에서 제안하는 닷넷 프레임워크에서의 디자인 가이드라인과 성능이나 보안, 신뢰성 등의 요소에 대한 Best Practice 등으로 이루어진 200개 이상의 Rule을 기반으로 Code를 검사하고 결함을 발견해 줍니다.

 

원래 이 Code Analysis는 FxCop이란 이름의 독립된 툴로써 세상에 먼저 선을 보인 바 있습니다.

이 FxCop이 Visual Studio 2005 Team System에서 통합되면서 현재의 코드 분석 기능이 나오게 된 것입니다. 실제로 비주얼 스튜디오에서 코드 분석 기능을 담당하는 실행 파일의 이름은 아직도 FxCopCmd.exe입니다.

앞으로 나오게 될 Visual Studio Team System 2010에서는 현재까지 알려진 바로는 다음 세 가지 부분에서 코드 분석의 기능 향상이 이루어 졌습니다. (출처: http://blogs.msdn.com/fxcop/archive/2008/10/30/new-code-analysis-features-in-visual-studio-2010-september-08-ctp.aspx)

  1. Rule Sets – 드디어, 규칙을 프로젝트 별로 하나 하나 빼던 수고스러움을 덜 수 있게 된 것 같습니다. DevPartner Code Review와 같은 상용 툴에서는 진작에 제공되던 기능이었지만, 이제 드디어 Visual Studio 2010에서는 Rule들을 Set으로 만들고 그 Rule Set 기반으로 코드 리뷰를 할 수 있게 되었습니다.
  2. Check-In Policy – 당연한 말이겠지만, 바로 위에서 소개한 Rule Sets 기능이 Team Foundation Server의 Check-In 정책에도 적용할 수 있게 됩니다.
  3. 8개의 새로운 Data-Flow 규칙MSDN Code Gallery의 VS2005와 VS2008 코드 분석 비교 문서를 보시면 아시겠지만, VS2008에서 7개의 규칙이 빠진 바 있습니다. Data-Flow Analysis Engine의 각종 버그와 낮은 성능으로 말미암은 결과라고 하는데, 이번 Visual Studio Team System 2010에서는 Phoenix라는 새로운 분석 엔진의 탑재와 함께 다시 7개의 규칙들이 복귀했고, 새롭게 추가된 하나를 합쳐서 총 8개의 새로운 규칙이 추가되었습니다. 그리고 이번에 추가된 Phoenix 분석 엔진은 당연히 기존 엔진이 가지고 있었던 버그나 성능 문제를 해결한 새롭고 강력한 엔진입니다.

그러면 다음 포스팅을 통해서 이 세 가지 개선점들에 대해서 더 자세히 다뤄보도록 하겠습니다.

안녕하세요. 이번에 Visual Studio Team System 2010 공식 블로그에 새롭게 참여하게 된 LazyDeveloper.Net의 kkongchi라고 합니다. Better Code 시리즈를 통해서 Code Analysis, Unit Test 등에 대한 포스팅을 해보도록 하겠습니다. 부족한 부분 많이 지적해 주시길 바랍니다.

 

TDD?

eXtreme Programming의 창시자 중 하나인 Kent Beck의 eXtreme Programming explained라는 책을 보면 Test를 작성하는 방법에 대해서 이렇게 기술하고 있습니다.

  • If the interface for a method is at all unclear, you write a test before you write the method. (메서드의 인터페이스가 클리어하지 않다면, 메서드를 작성하기 전에 테스트를 먼저 작성해라)

  • If the interface is clear, but you imagine that the implementation will be the least bit complicated, you write a test before you write the method. (메서드의 인터페이스가 클리어하더라도 당신이 생각하기에 구현이 조금 복잡할 것 같다면, 메서드를 작성하기 전에 먼저 테스트를 작성해라)

  • 이런 eXtreme Programming의 Test-Before-Writing 전략을 개발 프로세스에 전면적으로 도입하는 것을 Test Driven Development, 즉 TDD라고 합니다. TDD에 관한 위키피디아 페이지에서 소개하는 TDD의 개발 사이클은 다음과 같습니다. “Red, Green, Refactor”라고 표현하기도 합니다.

    다들 아시다시피, Visual Studio에서는 2005 버전에서부터 Team System의 일부로써 Testing Framework을 제공하고 지원해왔습니다. 그리고 드디어 이번 Visual Studio Team System 2010에서는 완벽하게 TDD의 개념이 Visual Studio Team System안으로 녹아 들어가게 된 것 같습니다. 바로 새롭게 추가된 기능 “Generate” 기능을 통해서, Test를 먼저 작성한 후에 그 Test로부터 코드를 자동으로 Generate해주는 기능이 추가된 것입니다.

     

    TDD Development in VSTS 2010 by “Generate”

    지난 11월에 나온 Visual Studio 2010 and .NET Framework 4.0 Training Kit의 Lab을 통해서 이 기능에 대해서 좀 더 자세히 알아보도록 하겠습니다.

     

    당연히 먼저 테스트를 작성하는 것부터 시작합니다. 하지만, 아직 만들어지지 않은 클래스이기 때문에 아래 그림처럼 빨간색 물결 라인으로 경고가 뜹니다. 여기서 마우스 오른쪽 버튼을 눌러보면, 새로운 “Generate” 기능을 볼 수가 있습니다.

    Generate class를 선택하면 같은 프로젝트에 Class가 추가됩니다. 하지만 Generate other..를 선택하면 아래와 같은 팝업 윈도우가 나옵니다. 클래스를 만들 수 있는 Wizard 개념이라고 보시면 되겠습니다.

    이 Wizard를 통해서 클래스의 Access 한정자, Type, 그리고 파일을 만들 프로젝트까지 설정을 할 수가 있습니다. 이 과정을 통해서 우리는 완벽하게 Test로부터 시작해서 뼈대 코드를 만들어 낼 수가 있습니다. 아래 그림처럼 말이죠..

    이제 이 자동으로 만들어진 뼈대 코드에 구현을 추가하게 되면 여러분들은 다음과 같이 Test를 통과했다는 기분 좋은 화면을 보실 수 있으실 것입니다.


    위에서 보신 Demo Code의 시연은 http://channel9.msdn.com/shows/10-4/10-4-Episode-5-Code-Focused-in-Visual-Studio-2010/#Page=4 에서 Video로도 감상하실 수 있고, http://www.microsoft.com/downloads/details.aspx?FamilyID=752CB725-969B-4732-A383-ED5740F02E93&displaylang=en 에서 Lab Document와 소스 코드도 얻으실 수 있습니다.
     

    지금까지 보신 것처럼 앞으로 출시될 VSTS 2010에서는 IDE 자체에서 완벽한 TDD 지원 기능이 통합되었습니다. TDD가 만능의 도구는 아닙니다. 하지만, 적어도 개발자가 자신의 코드에 대한 이해도가 통상적인 개발 방법보다는 훨씬 크고 깊을 것이라 기대합니다. 어설픈 문서보다는 잘 만들어진 테스트 코드들이 오히려 실제 구현 코드를 이해하는 데 더 도움이 되는 경우도 많습니다. Visual Studio Team System 2010은 효율적인 Test Driven Development를 가능하게 해주는 최고의 도구가 될 것 같습니다.

    부족한 글 읽어주셔서 감사하고, 많은 의견 부탁 드립니다.

    Welcome to F#(3) - 사소한 탐색전.

    F# 2009. 4. 12. 22:10 Posted by 알 수 없는 사용자

    -관심있으면 많이 알아봐야 하는 법.
    이제 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

    Welcome to F#(2) - 두번째 만남.

    F# 2009. 4. 8. 23:40 Posted by 알 수 없는 사용자

    -애프터
    첫번째 만남이 끝난뒤에, 어떻게 애프터를 이어나가야 할지 무처이나 고민했습니다. 첫만남이야 가볍게 서로의 얼굴도 보고, 성격도 맛보는 정도에서 끝난다지만 애프터에서는 서로에 대해서 천천히 자세히 알아가야 할텐데, 갑자기 참여중인 프로젝트의 상황이 악화되서 시간이 잘 나지 않고, 이해부족과 실력부족이 겹쳐서 어떻게 글을 계속 써나가야할지 고민이 많았습니다. 그러다가 직장동료에게 물어봤습니다.

    "만약에 튜토리얼을 쓴다면, 어떻게 쓰는게 좋을까?"
    "글쎄, 나는 그런게 좋던데. 왜 작은 예제에서 시작해서 하나씩 추가되어 가면서 설명해주는 그런거 있잖아."

    이거다 싶었습니다. 그래서 제 내공이 받쳐줄지는 모르겠지만, 최대한 예제를 통해서 F#과의 애프터를 진행해볼까 합니다. 언제나 그렇듯이 제 글에 어색하거나 잘못된 내용이 있을수 있으니 언제나 따뜻한 피드백으로 격려해주시기 바랍니다.(말씀드렸듯이 차가운 피드백은 바로 반사-_-) 그럼 일단 애프터 장소로 가서 F#을 좀 더 만나보도록 하죠~.

    -F#에서는 타입을 안쓰는거 같더라?
    일단 F#에서 타입을 결정하는데 쓰이는 타입유추(Type Inference)에 대해서 알아보겠습니다. 타입유추는 F#에서만 쓰이는 개념은 아닙니다만, 저 같이 함수형언어에 친숙하지 않은 경우에는 생소함을 느끼게 됩니다. 우선 아래와 같은 예제를 작성해서 아래 두줄을 드래그 해서 F# Interactive로 보내보죠. F# Interactive를 띄우는 방법과 F# Interactive로 보내는 방법은 잊지 않으셨죠? 잊으셨다면, 이전 포스트를 참조해주세요~.

    #light

    let count = 1
    printfn "%d" count


    실행결과에 대해서 이야기 하기전에 #light에 대한 설명을 조금 드리자면, 지난 포스트에서 말씀드린 것 같이 이 지시어는 F#에서 좀더 간략한 문법을 사용할 수 있도록 해줍니다. 그런데, 이런 키워드를 명시적으로 꼭 선언해야 하는 이유는 다음과 같습니다. F#은 OCaml으로 부터 영감을 받아서 설계되고 핵심적인 부분중에 일부분을 공유하고 있씁니다. 그리고 OCaml의 코드를 컴파일할 수 있는 기능을 갖추고 있는데, 간단한 OCaml코드의 경우는 수정없이 컴파일 가능하게 되어있습니다. 그래서 그 경우를 위해서 #light라고 명시해줘야만 F#의 간단한 문법이 적용되는 것입니다.

    그러면, 다시 소스코드로 넘어가서 그 소스의 결과는 아래와 같습니다.

    val count : int
    1
     
    즉, 한줄씩 평가 돼서 그 결과가 나오는 건데요, 첫번째 줄은 count라는 value를 선언했고 그 타입은 int라는 말입니다. 그리고 그 다음줄은 count의 값을 출력한 결과인거죠. 눈치 빠른 분이라면, 아마 여기서 뭔가를 눈치채셨을 겁니다. 타입을 정해주지 않았는데, count의 타입이 int라는걸 문맥을 통해 유추해냈습니다. 그리고 아래의 printfn의 출력패턴을 보면 "%d", 즉 C와 동일하게 정수형값을 출력하겠다는 말인데요, 앞의 평가식에서 count가 정수형이라는 걸 유추해냈기 때문에 에러없이 1을 출력하고 프로그램을 종료합니다. 이런걸 타입 유추라고 부릅니다.(원문으로는 Type Inference인데, inference에 대한 딱히 다른 용어를 찾지 못해서 일단 유추라고 하겠습니다.-_-)

    타입유추는 코드를 분석해서 제약사항들을 모아서 이루어집니다. 여기서 제약사항이란, 매개변수가 가질 수 있는 타입을 명시해줌으로써 제약을 가하는 개념인데요, 문맥에서 타입을 유추할때 +,-,*,/같은 경우는 기본적으로 매개변수를 int타입으로 유추됩니다. 그리고 아래의 예제들을 F# Interative에 평가해보죠.

    let printInt x = printfn "%d" x

    let printString x = printfn "%s" x

    그러면, 아래와 같은 결과가 나오죠.

    val printInt : int -> unit

    val printString : string -> unit

    즉, 문맥상 정수형을 출력하니까 x는 int형이 된거고, 문맥상 스트링을 출력하니깐 x는 스트링으로 유추된 거죠. 그리고 아래의 예제를 더 보시죠.(그리고 참고로 unit은 많은 분들이 기대하시는 것 처럼 unsigned int는 아니구요, 정확하게 같진 않지만 void를 나타내는 F#의 타입입니다.-_-)

    let plusplus x = (x + x)
    let plusplus2Times (x) = plusplus x + plusplus x
    -----결과----
    val plusplus : int -> int
    val plusplus2Times : int -> int

    그리고 아래와 같이 바꿔서 다시 평가해보죠.

    let plusplus x = (x + x)
    let plusplus2Times (x:float) = plusplus x + plusplus x
    -----결과----
    val plusplus : float -> float
    val plusplus2Times : float -> float

    역시 사용되는 문액에 따라 plusplus를 호출하는 plusplus2Times에서 int형 x를 넘겨주면, plusplus의 매개변수도 int타입이 되고, float을 사용하겠다고 타입에 제약을 정해주면(즉, 매개변수 x가 될 수 있는 타입에 명시적으로 제약을 주는 것) 그에 따라서 plusplus에 넘어가는 매개변수와 리턴타입역시 그에 따라 float타입으로 유추되는 것을 볼 수 있죠. 즉, F#에서는 타입을 명시해줄 수도 있지만, 이런 작업을 직접 해주지 않으면 기본적으로는 타입유추를 통해 타입을 알아내고 적용한다는 겁니다.

    -타입유추라는거 왠지 불안해보이는데?
    이런 생각을 해볼 수 있습니다. '과연 이렇게 컴파일러가 타입을 추측해주는게 무슨 장점이 있는거냐?' 이 포스트에서 관련된 내용을 많이 찾아볼 수 있었습니다. 저자는 타입유추의 광팬이로군요. 언제든지 C#같은 언어에서 제공하는 var타입이나 익명메서드등을 사용해서 자기 대신 컴파일러가 타입에 대한 문제를 다루도록 한답니다. 즉, '타입유추는 타입안정성을 해치지 않고 그저 컴파일러가 대신 타입을 찾을뿐이다', '타입을 일일이 적지 안아도 되니깐 리팩토링시에도 편리하다', '타이핑을 줄일 수 있다'랍니다. 그리고 반론도 만만치 않습니다. 아주 긴 댓글로 서로 의견을 주고 받고 있군요. 즉, 한마디로 좋다 나쁘다를 정할 수 없는 취향의 문제인것 같기도 합니다.(우주 끝날때까지도 결론이 안 날문제들이 바로 취향에 관련된 문제죠-_-)

    -F#은 어떤 타입이야?
    그리고 타입이야기가 어쩌다 새어나왔으니, 타입을 한번 적어보겠습니다.
     타입  예  설명
     int  int  그냥 32비트 정수입니다.
     type option  int option, option<int>  선언된 타입의 값이 있거나, 혹은 값이 없는 걸 의미하는 None을 가집니다. Optional parameter와 비슷한 개념입니다.
     type list  int list, list<int>  선언된 타입의 immutable한 값들의 linked list입니다. 리스트의 각 값들은 [5;2;88]처럼 같은 타입을 가져야 합니다.
     type1 -> type2  int -> string  함수타입입니다. 즉, type1을 받아서 결과값으로 type2를 리턴한다는 거죠.
     type1 * ... * typeN  int * string  한쌍(a pair), 두쌍 그리고 그 이상의 타입들의 조합이 가능한 튜플타입입니다. (1, "3")같은 경우 말이죠.
     type []  int[]  배열타입이죠. 1차원이고, 고정된 크기의 mutable한 값들의 모음입니다.
     unit  unit  하나의 값을 나타내는 ()을 나타내는데, 명령형 언어의 void와 비슷한 의미입니다.
     'a, 'b  'a, 'b, 'Key, 'Value  제네릭하게 어떤 타입이든지 올 수 있는 변수타입입니다.

    Expert F#에 나오는 예제를 마지막으로 보겠습니다.

     String.split [' '] "hello world"

     String.split ['a';'e';'i';'o';'u'] "hello world"

    일단 위의 코드를 F# Interactive에 보내서 평가해보면 "The value, constructor, namespace or type 'split' is not defined. A construct with this name was found in FSharp.PowerPack.dll,......"이런 에러가 뜹니다. 즉, String클래스에 split이라는 값이나, 생성자, 네임스페이스 혹은 타입이 존재하지 않는데, FSharp.PowerPack.dll에서 발견이 되었다는 친절한 메세지 입니다. CTP버전으로 오면서 많은 기능이 FSharp.PowerPack.dll로 옮겨졌기 때문입니다. String은 Microsoft.FSharp.Core.String을 참조하는데요 Microsoft.FSharp안에 있는 Core,Collection,Text,String같은 네임스페이스는 String.split이나 open String같이 한 단어로 참조가 가능하고 합니다. 이럴땐, FSharp.PowerPack을 참조추가해줘야겠죠. 참조추가후에 open 지시어를 통해서 해당 네임스페이스의 내용을 알아내야 하는데, F# Code에선 기본적인 Core나, Operators, Collections같은 네임스페이스는 암시적으로 open을 한다고 합니다. 그리고 비주얼 스튜디오가 아닌 F# Interactive에서 참조추가를 하려면, 아래 처럼하면 됩니다.

     > #r "FSharp.PowerPack.dll";;

    --> Referenced 'C:\Program Files\FSharp-1.9.6.2\bin\FSharp.PowerPack.dll'


    그리고 다시 F# Interactive로 보내서 평가해보면 아래와 같은 결과나 나옵니다.

     val it : string list = ["hello"; "world"]
    >
    val it : string list = ["h"; "ll"; " w"; "rld"]

    이 예제에서 ' ', 'a'는 그냥 문자고, "hello world"같은건 문자열, ['a';'e';'i';'o';'u']는 문자의 리스트, ["hello"; "world"]는 문자열의 리스트라는 걸 알 수 있습니다.

    -애프터의 만족도는?
    변명이지만, 시간도 조금은 부족했고, 아는것도 부족하다보니 포스트의 내용이 썩 만족스럽지는 못한 것 같습니다. 애프터가 만족스럽지 못하셨다면, 제탓이 가장크겠군요-_-. 그래도 마음에 드신분이 있다면, 몇번 애프터를 더 해보고 손이라도 잡아봐야 겠죠. 다음번엔 이어서 기본적인 타입에 대해서 좀 더 알아보고, .NET공원에서 놀이기구를 몇개 타보는 걸로 해볼 생각입니다. 그리고 이번포스트에서 제대로 조사하지 못한 부분역시 조사해볼 생각입니다. 데이트장소는 고전적이긴 하지만 놀이공원도 괜찮죠. 그럼 도움이 되는 분들이 있길 바라면서 다음 포스트에서 뵙겠습니다.

    -참고자료
    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
    3. http://blogs.msdn.com/jaredpar/archive/2008/09/09/when-to-use-type-inference.aspx

    Welcome to F#(1) - 첫만남.

    F# 2009. 4. 5. 20:58 Posted by 알 수 없는 사용자

    What is F#?
    F#에 대해서 들어본 적이 있으신가요? 아니면, Functional programming language 혹은 함수형 언어라고는 들어본적이 있으신가요? 아마도 최근에 부각되었었던 Erlang이나 기존의 Lisp, Scheme등을 들어보셨거나, 공부해보신 분이라면 알고계시리라 생각합니다. F#은 닷넷에서 돌아가는 함수형언어이고 현재 CTP(Community Technical Preview)버전이 공개된 상태입니다. 차후에 VSTS 2010에 포함되어서 나올 예정이구요. 


    함수형 언어는 왜 배워야 하는데?
    C#이나 Java와 같이 입력을 받아서 어떻게 그걸 처리할지 주저리주저리 이야기 해줘야 하는 언어는 명령형 언어(Imperative programming language)라고 합니다. 하지만, 그와 반대로 어떻게 할건지 보다는 무엇을 처리할지에 더 관심을 두는 형태를 선언형 언어(Declarative programming language)라고 합니다. 정규식이나 함수형언어 같은 언어가 포함되지요. 예를 들어보면 정규식같은 경우는 어떤 패턴을 잡을지에 관심이 집중되어 있습니다. 하지만, 어떻게에 대해서는 관심이 없지요. 즉, 명령형 언어와 선언형 언어는 서로 관점이 틀리기 때문에 서로 유용하게 쓸 수 있는 분야도 틀릴 수 있다는 것입니다.

    물론, Anders Hejlsberg가 JAOO의 강연에서 이야기 한것처럼 함수형언어와 기존의 명령형언어(Imperative programming language)는 서로 다른 장점이 있기 때문에, 어떤 상황에서 어떤 언어를 선택하는 것이 옳은지 명확하게 이야기 할 수 는 없습니다. 하지만, 함수형언어는 나중에 더 자세히 다룰 수 있겠지만 Immutable, 즉 한번 값을 입력하면 다시는 값이 변경될 수 없는(C#에서 readonly를 생각하면 되겠죠.)걸 이야기 하는데, 이때문에 병렬적으로 실행되는 환경에서 흔히 문제가 되는 여러 스레드가 하나의 값에 접근할때 값이 잘못변경될 위험이 없습니다. 그래서 병렬 프로그래밍에서 가장 유용한 프로그래밍 모델로 생각되고 있으며 실제로 얼마전에 Erlang이 이슈가 되었던 것도 그런이유 때문이었던 것 같습니다.

    근데 왜 F#을 공부해야 하는데?
    개인적으로는 점차 플랫폼의 영향력이 커진다는 느낌을 받습니다. 그리고 기존의 플랫폼에 워낙 많은 기능이 이미 구현되어 있고, 그런 언어의 실행환경에 대해서는 신경을 쓰지 않고, 자신의 언어가 가질 특징들에 집중해서 새로운 언어를 만드는 것도 점차 중요해지는 것 같구요. 꽤나 히트했던 Ruby가 JRuby와 IronRuby로 자바와 닷넷에 각각 편입되는 것을 보더라도 그런 것 같습니다. 단순히 플랫폼의 덕만 보는게 아니라, 그 플랫폼에서 사용되는 다른 언어들과도 교차적으로 사용될 수 있다는 점도 꽤나 큰 이익이 아닌가 생각합니다. 이 주제에 대해서 잘 설명을 해주신 장성진님의 글을 읽어보시면 더 좋을 것 같습니다.

    F#을 배워야 하는 이유는 하나의 어플리케이션을 개발할때, C#을 메인언어로 놓고 개발하면서 F#으로 개발했을때 이익이 되는 부분이라면, F#으로 개발해서 조합을 할 수 있겠죠. 저 역시도 그런 가능성을 가장 기대하고 있습니다.

    앞으로의 방향은 어떻게 되니?
    개인적으로 기존에 Scheme을 공부해본적이 있긴하지만, 워낙 잠깐이라 딱히 많이 아는건 없습니다. 그래서 공부해가면서 고민해가면서 여기에 적어볼 생각입니다. 그래서 내용이 엄밀하지 못하거나 심도가 없을 수도 있는데, 그런부분은 피드백으로 잘 훈계해주시면 완전 감사하겠습니다.

    F#을 설치해보자
    시작이 반이라고 했으니, F#을 설치하면서 빨리 반을 끝내보도록 하죠. F#은 현재 CTP버전이 나와있고, 비주얼 스튜디오 2008에서도 사용할 수 있습니다. 그리고 비주얼 스튜디오가 없더라도 명령행툴이 있기 때문에, 거기서 작업을 하면서 공부하실 수 있습니다. 물론 비주얼 스튜디오가 있음 더 편하겠죠. 일단 여기에서 F# CTP버전을 받을 수 있습니다. msi파일을 받아서 실행하시면 되구요, zip파일은 윈도우가 아닌 운영체제에서 압축을 풀어서 readme를 보라고 하는데, 별로 보고싶지 않아서 확인은 안 해봤습니다.(역시 글로쓰면 농담이 안살아나는 군요-_-)

    F#과 만나서 Hello를 외쳐보자
    Hello를 외치려면 일단 만나야 겠죠. 그럼, F# Interative를 실행해보죠. 그러면 아래와 같은 창이 하나 뜹니다.


    궁금한게 있으면 #help;;를 쳐보라는 친절한 말까지 남겨주시죠. 여기서 우리는 F# Interative에게 뭔가 부탁하려면 '# + 부탁할내용 + ;;'을 해야 한다는 사실을 알 수 있죠. 참고로 F# Interactive에서 나가게 해달라고 부탁하는 말은 "#quit;;"입니다. 그러면, 언제나 처음 언어를 접하면 서로 인사를 나누어야 하니 Hello World를 한번 외쳐보도록 하죠.



    위와 같이 한번 쳐보시죠. let HelloWorld~~;;는 선언부이고, 그 아래는 함수호출 결과입니다. 언어가 다르다 보니 모양도 좀 틀립니다. 우선 F# Interative에서는 하나의 의미를 갖는 문장이 끝난다는걸 ';;'을 통해 알립니다. 그래서 위문장은 ';;'를 만날때 까지 하나의 단위로 인식해서 함수를 선언해주는 것이고, 아래 문장은 호출이라는 것을 파악하는 것이죠. 그러면 이제 나가게 해달라고 부탁하시고 비주얼 스튜디오에서도 Hello World를 외쳐보도록 하겠습니다.


    새프로젝트를 눌러보시면 윗 그림과 같이 Visual F#이 추가된걸 보실 수 있습니다. 그럼 프로젝트를 생성해서 아래그림 처럼 코드를 쳐보시죠.



    그리고 정말 친숙한 명령 Ctrl+F5를 누르면, 정말 친숙한 콘솔창에 아래 그림과 같이 결과가 나옵니다.(너무 친숙해서 마치 집에 있는거 같군요. 이래서 집나가면 개고생이라나 봅니다.)



    그리고 비주얼스튜디오에서 F# Interative창을 띄워서 평가하고 싶을때는 아래 그림과 같이 "보기 -> 다른 창 -> F# Interactive"를 눌러줍니다.



    그러면, 아래 그림과 같이 하단부에 F# Interactive가 뜨게됩니다.



    그리고 이제 비주얼 스튜디오에서 작업한 내용을 F# Interactive에서 확인해볼 수 있습니다. F#같은 함수형 언어에서는 모든 코드가 컴파일되는 것이 아니고 한줄 한줄 평가(Evaluation, 혹은 그냥 Eval)하게 되는데요, 작업 부분중에 평가 해보고 싶은 부분만 드래그해서 F# Interative에서 결과를 확인해볼 수 있습니다. 평가하는 방법은 우선 평가하고 싶은 부분을 정확하게 드래그 해서 마우스 오른쪽 버튼을 누르고 "Send to F# Interative"를 누르거나 "Alt + Enter"를 누르시면 됩니다. 아래 그림은 함수선언부를 먼저 F# Interactive로 보내서 평가하고 그 다음에 함수호출부분을 다시 평가해서 얻은 결과물입니다.



    이제는 우리가 헤어져야 할시간, 다음에 다시 만나요
    자 이제, F#과의 첫 인사를 마쳤습니다. 어떠셨나요? 제가 실력이 부족하고 글솜씨가 딸려서 좀 설명이 부족한 부분이나, 틀린 부분이 있을 수도 있습니다. 그럴땐 따뜻한 피드백으로 화답해주셨으면 좋겠습니다.(차가운 피드백은 반사하겠습니다-_-) 저 역시 아직 함수형언어 자체에 대한 이해도 부족하고, F#도 잘 아는 상태가 아니라서 인지 앞으로 어떻게 계속 글을 이어나가야 겠다는 명확한 생각은 없는 상태고 그저 글을 계속 쓰면서 정해나갈 생각입니다. 다음번 포스트에서는 간단한 예제를 통해 F#에 대해서 좀 더 자세한 설명을 드리고 명령형언어와 함수형언어의 차이에 대해서 좀 더 알기 쉽게 설명해볼까 하는 생각을 갖고 있습니다.(말이 길어지면, 둘중 하나만 하고요-_-) 이 글이 도움이 되는 분들이 있길 바라고, 다음 포스트로 뵙겠습니다.

    참고자료
    1. Expert F#, Don Syme, Adam Granicz, Antonio Cisternino, APRESS.
    2. http://blog.java2game.com/229
    3. http://jaoo.blip.tv/file/1317881/
    4. http://en.wikipedia.org/wiki/Functional_programming
    5. http://en.wikipedia.org/wiki/Imperative_programming
    6. http://en.wikipedia.org/wiki/Declarative_programming