擴充套件C#與超程式設計
擴充套件C#與超程式設計
https://www.cnblogs.com/knat/p/4580393.html
https://www.cnblogs.com/knat/p/4584023.html
擴充套件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 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的"語法糖"。
日光之下,並無新事。超程式設計是個非常"古老"的概念,但在每個"時代"它都能玩出耳目一新的花樣。
欲知後事如何,請聽下回分解。
如果你對Windows Workflow Foundation(WF)一無所知,當看到擴充套件C#與超程式設計(一)中由MW編譯器生成的FirstLook.mw.cs時,也許這麼在想:我KAO,這是C#版的組合語言!
WF到底是什麼?可以這麼認為:WF runtime是高階版的CLR(CLR上的CLR),activity是高階版的MSIL指令(可以勉強這麼比喻),Metah.W是高階版的C#。
Activity可以分為兩類:primitive activity和composite activity。Primitive activity繼承自System.Activities.NativeActivity, System.Activities.NativeActivity<T>等,用來實現流程控制容器,如sequence, if-else, while, foreach, try-catch-finally等:
namespace System.Activities.Statements {
public sealed class Sequence : NativeActivity {
public Sequence();
public Collection<Variable> Variables { get; }
public Collection<Activity> Activities { get; }
//...
}
public sealed class If : NativeActivity {
public If();
public InArgument<bool> Condition { get; set; }
public Activity Then { get; set; }
public Activity Else { get; set; }
//...
}
public sealed class While : NativeActivity {
public While();
public Collection<Variable> Variables { get; }
public Activity<bool> Condition { get; set; }
public Activity Body { get; set; }
//...
}
public sealed class ForEach<T> : NativeActivity {
public ForEach();
public InArgument<IEnumerable<T>> Values { get; set; }
public ActivityAction<T> Body { get; set; }
//...
}
public sealed class TryCatch : NativeActivity {
public TryCatch();
public Activity Try { get; set; }
public Collection<Catch> Catches { get; }
public Activity Finally { get; set; }
//...
}
}
WF這個高階CLR令人拍手稱快的特性之一是,你可以自定義流程控制容器(可以想象成自定義MSIL指令),比如高大上的狀態機:
namespace System.Activities.Statements {
public sealed class StateMachine : NativeActivity {
public StateMachine();
public Collection<Variable> Variables { get; }
public State InitialState { get; set; }
public Collection<State> States { get; }
//...
}
public sealed class State {
public State();
public bool IsFinal { get; set; }
public Collection<Variable> Variables { get; }
public Activity Entry { get; set; }
public Activity Exit { get; set; }
public Collection<Transition> Transitions { get; }
//...
}
public sealed class Transition {
public Transition();
public Activity Trigger { get; set; }
public Activity<bool> Condition { get; set; }
public Activity Action { get; set; }
public State To { get; set; }
//...
}
}
SecondLook.mw展示瞭如何使用狀態機:
一個statemachine至少包含一個common state及一個final state,每個state由唯一的label標識,如InPark, InNeutral等,break關鍵字標明這是一個final state,statemachine關鍵字後的goto clause標明initial common state,當流程進入某common state後,首先執行~>所標識的entry statement,接著執行on關鍵字標識的trigger statement,然後依順序評估if關鍵字標識的condition expression,如果某condition評估為true,則執行<~所標識的exit statement和do關鍵字標識的action statement(SecondLook.mw中未使用),接著跳轉到goto關鍵字所標識的state,如果所有的condition expression都評估為false,則重新執行該state的trigger statement。如果某final state執行完畢,則該statemachine執行完畢。
MW編譯器將activity Drive翻譯成下面的C#程式碼:
//SecondLook.mw.cs, generated by MW compiler
namespace HelloMW.SecondLook
{
class Drive : global::System.Activities.Activity
{
private global::System.Activities.Activity __GetImplementation__()
{
global::System.Activities.Activity __vroot__;
{
var __v__0 = new global::System.Activities.Statements.Sequence();
var isMoved = new global::System.Activities.Variable<bool>();
__v__0.Variables.Add(isMoved);
__v__0.Activities.Add(new global::MetahWActionActivity(__ctx__ =>
{
isMoved.SetEx(__ctx__, false);
}
));
var __v__1 = new global::System.Activities.Statements.While();
__v__1.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => !isMoved.Get(__ctx__));
{
var __v__2 = new global::System.Activities.Statements.Sequence();
{
var __v__3 = new global::System.Activities.Statements.StateMachine();
var action = new global::System.Activities.Variable<DriveAction>();
__v__3.Variables.Add(action);
var __v__4 = new global::System.Activities.Statements.State();
var __v__5 = new global::System.Activities.Statements.Transition();
var __v__6 = new global::System.Activities.Statements.State();
var __v__7 = new global::System.Activities.Statements.Transition();
var __v__8 = new global::System.Activities.Statements.Transition();
var __v__9 = new global::System.Activities.Statements.Transition();
var __v__10 = new global::System.Activities.Statements.State();
var __v__11 = new global::System.Activities.Statements.Transition();
var __v__12 = new global::System.Activities.Statements.State();
var __v__13 = new global::System.Activities.Statements.Transition();
var __v__14 = new global::System.Activities.Statements.State();
{
__v__3.States.Add(__v__4);
__v__4.Entry = new global::MetahWActionActivity(__ctx__ =>
{
Console.WriteLine("Enter InPark");
}
);
__v__4.Exit = new global::MetahWActionActivity(__ctx__ =>
{
Console.WriteLine("Exit InPark");
}
);
__v__5.Trigger = new GetDriveAction().Initialize(__activity2__ =>
{
__activity2__.Result = new global::System.Activities.OutArgument<global::HelloMW.SecondLook.DriveAction>(new global::MetahWLocationActivity<global::HelloMW.SecondLook.DriveAction>(action));
}
);
__v__5.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => action.Get(__ctx__) == DriveAction.Neutral);
__v__4.Transitions.Add(__v__5);
}
{
__v__3.States.Add(__v__6);
__v__6.Entry = new global::MetahWActionActivity(__ctx__ =>
{
Console.WriteLine("Enter InNeutral");
}
);
__v__6.Exit = new global::MetahWActionActivity(__ctx__ =>
{
Console.WriteLine("Exit InNeutral");
}
);
__v__7.Trigger = new GetDriveAction().Initialize(__activity2__ =>
{
__activity2__.Result = new global::System.Activities.OutArgument<global::HelloMW.SecondLook.DriveAction>(new global::MetahWLocationActivity<global::HelloMW.SecondLook.DriveAction>(action));
}
);
__v__7.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => action.Get(__ctx__) == DriveAction.Forward);
__v__6.Transitions.Add(__v__7);
__v__8.Trigger = __v__7.Trigger;
__v__8.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => action.Get(__ctx__) == DriveAction.Reverse);
__v__6.Transitions.Add(__v__8);
__v__9.Trigger = __v__7.Trigger;
__v__9.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => action.Get(__ctx__) == DriveAction.TurnOff);
__v__6.Transitions.Add(__v__9);
}
{
__v__3.States.Add(__v__10);
__v__10.Entry = new global::MetahWActionActivity(__ctx__ =>
{
Console.WriteLine("Enter InForward");
isMoved.SetEx(__ctx__, true);
}
);
__v__10.Exit = new global::MetahWActionActivity(__ctx__ =>
{
Console.WriteLine("Exit InForward");
}
);
__v__11.Trigger = new GetDriveAction().Initialize(__activity2__ =>
{
__activity2__.Result = new global::System.Activities.OutArgument<global::HelloMW.SecondLook.DriveAction>(new global::MetahWLocationActivity<global::HelloMW.SecondLook.DriveAction>(action));
}
);
__v__11.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => action.Get(__ctx__) == DriveAction.Neutral);
__v__10.Transitions.Add(__v__11);
}
{
__v__3.States.Add(__v__12);
__v__12.Entry = new global::MetahWActionActivity(__ctx__ =>
{
Console.WriteLine("Enter InReverse");
isMoved.SetEx(__ctx__, true);
}
);
__v__12.Exit = new global::MetahWActionActivity(__ctx__ =>
{
Console.WriteLine("Exit InReverse");
}
);
__v__13.Trigger = new GetDriveAction().Initialize(__activity2__ =>
{
__activity2__.Result = new global::System.Activities.OutArgument<global::HelloMW.SecondLook.DriveAction>(new global::MetahWLocationActivity<global::HelloMW.SecondLook.DriveAction>(action));
}
);
__v__13.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => action.Get(__ctx__) == DriveAction.Neutral);
__v__12.Transitions.Add(__v__13);
}
{
__v__3.States.Add(__v__14);
__v__14.IsFinal = true;
__v__14.Entry = new global::MetahWActionActivity(__ctx__ =>
{
Console.WriteLine("TurnedOff");
}
);
}
__v__3.InitialState = __v__4;
__v__5.To = __v__6;
__v__7.To = __v__10;
__v__8.To = __v__12;
__v__9.To = __v__14;
__v__11.To = __v__6;
__v__13.To = __v__6;
__v__2.Activities.Add(__v__3);
}
__v__2.Activities.Add(new global::MetahWActionActivity(__ctx__ =>
{
Console.WriteLine("isMoved: " + isMoved.Get(__ctx__));
}
));
__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();
}
}
}
}
"組合語言程式碼"總是冗長無趣的,所以有了Metah.W這樣的高階語言。下面是可能的執行結果:
Enter InPark
!action: Neutral
Exit InPark
Enter InNeutral
!action: Reverse
Exit InNeutral
Enter InReverse
!action: TurnOff
!action: Forward
!action: Neutral
Exit InReverse
Enter InNeutral
!action: Forward
Exit InNeutral
Enter InForward
!action: Forward
!action: Forward
!action: Reverse
!action: Neutral
Exit InForward
Enter InNeutral
!action: TurnOff
Exit InNeutral
TurnedOff
isMoved: True
請按任意鍵繼續. . .
Composite activity直接繼承自System.Activities.Activity或System.Activities.Activity<T>,它由其它primitive activity和/或composite activity組合而成。Metah.W和WF designer只能創作composite activity。
我不知道大家怎麼看待微軟這家公司,多數時候MS是家專業但缺乏想象力的公司,有時它能創造出一些令人眼前一亮的作品,C#從第二版開始就一直閃亮,WF是個極富想象力的技術,不過,WF現在還是個藏在深山中的璞玉,希望此文能激起你研究WF的興趣。關於
Metah.W: A Workflow Metaprogramming的更多資訊請訪問:https://github.com/knat/Metah 。
待續。