眾所周知,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 Prompt
的as 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#代碼:
//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的“語法糖”。
日光之下,並無新事。元編程是個非常“古老”的概念,但在每個“時代”它都能玩出耳目一新的花樣。
欲知后事如何,請聽下回分解。