擴展C#與元編程(一)

衆所周知,Roslyn project已經開源一年多了。簡單的說,Roslyn是:1)用C#/VB寫的C#/VB的編譯器,以及與IDE集成;2)編譯器的功能以API的方式暴露出來(即一組DLL)。
Roslyn對大多數開發者來講沒啥用處,你關心的是你的應用程序的邏輯與構建而不須要關心編譯器是怎麼運行的。有時你以爲C#/VB有須要加強的地方,因而你興致勃勃的跑到Roslyn論壇去發表一個proposal,MS的guy會給你的issue打上Area-Language Design的標籤,網友們也許會來討論一下,而後呢?就沒有而後了,這有點像在求雨,下不下、何時下、下多大全憑神仙們(MS guys)的「心情」。
求人不如求己,既然Roslyn已經以MIT方式開源,我們就站在巨人的肩膀上本身動手豐衣足食吧,也就是,擴展C#,作(屌絲級的)complier guy!
衆所周知,C#是門general-purpose的編程語言,你想添加的feature既能夠是general的,也能夠是specific的。好比,XML literal是個general的C# feature,下兩圖展現了一個specific的feature,讓C#支持Windows Workflow Foundation(WF)的activity:


其實,這不應叫「擴展C#」,而該叫作「建立一門衍生自C#的DSL」,若是你對WF感興趣,請訪問
Metah.W: A Workflow Metaprogramming Language

姑且就用「擴展C#」這個叫法。如上兩圖所示,Metah.W(MW)在C#中加入了activity的構造,在概念上,activity是C# class和function的合體,在圖一中,public sealed class搖身一變成爲public sealed activity,接着宣告activity的名字,小括號中宣告parameters,如string BookmarkName, string Text,接着宣告可選的return type,如activity Promptas int宣告返回類型是int,若無as XXX宣告則返回void。Activity的body中是變量的宣告和statement的使用,statement能夠是:1)C# expression statement,如圖一中的target = new Random().Next(1, MaxNumber) + 1;;2)well-known statement,如if-else, while, do-while, foreach, try-catch-finally等;3)special statement,如圖二中的statemachine, delay。由於activity是class和function的合體,能夠調用它並將返回值賦給變量/參數,如guess = new Prompt().Invoke(...)
將FirstLook.mw送進MW編譯器,將產生下面的C#代碼:git

//FirstLook.mw.cs, generated by the MW compiler
namespace HelloMW.FirstLook
{
    public sealed class SequentialNumberGuess : global::System.Activities.Activity
    {
        public global::System.Activities.InArgument<int> MaxNumber { get; set; }
        public global::System.Activities.OutArgument<int> Turns { get; set; }

        private global::System.Activities.Activity __GetImplementation__()
        {
            global::System.Activities.Activity __vroot__;
            {
                var __v__0 = new global::System.Activities.Statements.Sequence();
                var target = new global::System.Activities.Variable<int>();
                __v__0.Variables.Add(target);
                var guess = new global::System.Activities.Variable<int>();
                __v__0.Variables.Add(guess);
                __v__0.Activities.Add(new global::MetahWActionActivity(__ctx__ =>
                {
                    target.SetEx(__ctx__, new Random().Next(1, MaxNumber.Get(__ctx__)) + 1);
                }
                ));
                var __v__1 = new global::System.Activities.Statements.DoWhile();
                __v__1.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => guess.Get(__ctx__) != target.Get(__ctx__));
                {
                    var __v__2 = new global::System.Activities.Statements.Sequence();
                    __v__2.Activities.Add(new Prompt().Initialize(__activity2__ =>
                    {
                        __activity2__.BookmarkName = new global::System.Activities.InArgument<string>(new global::MetahWFuncActivity<string>(__ctx__ => "EnterGuess"));
                        __activity2__.Text = new global::System.Activities.InArgument<string>(new global::MetahWFuncActivity<string>(__ctx__ => "Please enter a number between 1 and " + MaxNumber.Get(__ctx__)));
                        __activity2__.Result = new global::System.Activities.OutArgument<int>(new global::MetahWLocationActivity<int>(guess));
                    }
                    ));
                    __v__2.Activities.Add(new global::MetahWActionActivity(__ctx__ =>
                    {
                        Turns.SetEx(__ctx__, __val__ => ++__val__, true);
                    }
                    ));
                    var __v__3 = new global::System.Activities.Statements.If();
                    __v__3.Condition = new global::System.Activities.InArgument<bool>(new global::MetahWFuncActivity<bool>(__ctx__ => guess.Get(__ctx__) != target.Get(__ctx__)));
                    var __v__4 = new global::System.Activities.Statements.If();
                    __v__4.Condition = new global::System.Activities.InArgument<bool>(new global::MetahWFuncActivity<bool>(__ctx__ => guess.Get(__ctx__) < target.Get(__ctx__)));
                    __v__4.Then = new global::MetahWActionActivity(__ctx__ =>
                    {
                        Console.WriteLine("Your guess is too low.");
                    }
                    );
                    __v__4.Else = new global::MetahWActionActivity(__ctx__ =>
                    {
                        Console.WriteLine("Your guess is too high.");
                    }
                    );
                    __v__3.Then = __v__4;
                    __v__2.Activities.Add(__v__3);
                    __v__1.Body = __v__2;
                }
                __v__0.Activities.Add(__v__1);
                __vroot__ = __v__0;
            }
            return __vroot__;
        }

        private global::System.Func<global::System.Activities.Activity> __implementation__;
        protected override global::System.Func<global::System.Activities.Activity> Implementation
        {
            get
            {
                return __implementation__ ?? (__implementation__ = __GetImplementation__);
            }
            set
            {
                throw new global::System.NotSupportedException();
            }
        }
    }

    public sealed class Prompt : global::System.Activities.Activity<int>
    {
        public global::System.Activities.InArgument<string> BookmarkName { get; set; }
        public global::System.Activities.InArgument<string> Text { get; set; }

        private global::System.Activities.Activity __GetImplementation__()
        {
            global::System.Activities.Activity __vroot__;
            var __v__0 = new global::System.Activities.Statements.Sequence();
            __v__0.Activities.Add(new global::MetahWActionActivity(__ctx__ =>
            {
                Console.WriteLine(Text.Get(__ctx__));
            }
            ));
            __v__0.Activities.Add(new ReadInt().Initialize(__activity2__ =>
            {
                __activity2__.BookmarkName = new global::System.Activities.InArgument<string>(new global::MetahWFuncActivity<string>(__ctx__ => BookmarkName.Get(__ctx__)));
                __activity2__.Result = new global::System.Activities.OutArgument<int>(new global::MetahWLocationActivity<int>(Result));
            }
            ));
            __vroot__ = __v__0;
            return __vroot__;
        }

        private global::System.Func<global::System.Activities.Activity> __implementation__;
        protected override global::System.Func<global::System.Activities.Activity> Implementation
        {
            get
            {
                return __implementation__ ?? (__implementation__ = __GetImplementation__);
            }
            set
            {
                throw new global::System.NotSupportedException();
            }
        }
    }
}

這就是元編程,把語法糖解糖的過程,即把高級抽象的描述翻譯成低級具體的實現。我以爲,「語法糖」是個膚淺的認識,實際上,多數的「語法糖」都涉及到語義,不單單是簡單的語法轉換。元編程的另外一個例子,早期某些C++編譯器能將C++代碼翻譯成等價的C代碼,即C++是門元編程語言,它是C的「語法糖」。
日光之下,並沒有新事。元編程是個很是「古老」的概念,但在每一個「時代」它都能玩出耳目一新的花樣。
欲知後事如何,請聽下回分解。github

相關文章
相關標籤/搜索