Swift Macros အကြောင်း တစေ့တစောင်း
Intro
ဒီနေ့ article က macros တစ်ခုဘယ်လိုရေးမလဲ (how to write swift macros) မဟုတ်တဲ့အတွက် ဒီအကြောင်းသိချင်တယ်ဆိုရင်တော့ ဆက်မဖတ်လို့ရပါတယ်။ ဒီနေ့မှာ swift macro ဆိုတာဘာလဲ macro ကြောင့် ဘာတွေကောင်းသလဲ ဘာတွေဆိုးသလဲ ဘယ်သူတွေလဲ စတဲ့အကြောင်းအရာတွေကို တစ်ကိုယ်ရည်အမြင်နဲ့ ဆွေးနွေးသွားမှာဖြစ်ပါတယ်။
What is Swift Macros?
Swift macros ကို WWDC23 မှာ တရားဝင်မိတ်ဆက်သွားတာဖြစ်ပြီး Xcode 15 မှာပဲ စမ်းလို့ရပါမယ်။ အခုဒီစာကိုရေးနေတဲ့အချိန်မှာ Xcode 15 beta 4 အထိရောက်နေပါပြီ။
Swift macros ဆိုတာ Swift language တစ်ခုတည်းမှာရှိတာတော့မဟုတ်ပါဘူး။ C, C++ နဲ့ Objective-C မှာလည်း macros ရှိပါတယ်။ Macros ဆိုတာဘာလဲဆိုတော့ wikipedia မှာ ဒီလိုရေးထားပါတယ်။
A rule or pattern that specifies how a certain input should be mapped to a replacement output.
လူနားလည်အောင် လွယ်လွယ်ပြောရရင်တော့ compile time မှာ code တွေကို generate ထုတ်ပေးတာပါ။ Compiler pre-processor လို့ပြောလို့ရပါတယ်။ ဒီနေရာမှာ compiler အကြောင်း ခဏမေ့ထားကြဦးစို့။ Macros ကဘာလုပ်သလဲဆိုတော့ code ကို build လုပ်လိုက်တဲ့အခါ ကိုယ့် code ကို compile မလုပ်ခင် ကိုယ်မရေးထားတဲ့ code တွေကို နောက်ကွယ်ကနေ လျှို့ဝှက်ဆန်းကြယ်တဲ့နည်းလမ်းတွေသုံးပြီး ထည့်ပေးသွားတယ်ပေါ့။ အဲ့လို code တွေနောက်ကွယ်ကထည့်လိုက်တာကို expand လုပ်တယ်လို့ခေါ်သဗျ။ စာသံပေသံနဲ့ပြောရရင် macros expansion ပေါ့။ Swift macros တွေ ဘယ်လို pre-compile လုပ်လဲ ဘယ်လို expand လုပ်လဲဆိုတာကတော့ ဒီ article ရဲ့ out-of-scope ဖြစ်နေတဲ့အတွက် မပြောတော့ပါဘူး။
Before Swift macros
Swift macros မတိုင်ခင်က ယေဘူယျအားဖြင့် code generate ထုတ်ဖို့ လိုအပ်တဲ့ meta data တွေကို file တစ်ခုနဲ့ရေးထားပြီး build phase မှာ shell script နဲ့ ခုနက meta data တွေပါတဲ့ file ကိုပြန်ညွှန်းပေးရပါတယ်။ Xcode build လုပ်လိုက်တဲ့အခါ build phase ထဲက script ကို run သွားပြီး code file တွေ generate ထုတ်သွားတာဖြစ်ပါတယ်။ တစ်ခြားကျွန်တော်မသိတဲ့နည်းလမ်းတွေလည်း ရှိကောင်းရှိနိုင်ပါသေးတယ်။ ဒါပေမယ့် shell script တွေတော့ သေချာပေါက်ရေးရမှာဖြစ်ပါတယ်။ ဥပမာအနေနဲ့ Sourcery တို့ R.Swift တို့ SwiftGen တို့ကိုကြည့်ပါ။ ဒီလို custom script တွေရေးရတာဟာ Swifty လည်းမဖြစ်သလို ကြားထဲမှာ လိုအပ်တဲ့ steps တွေကျန်ခဲ့နိုင်ချေ မှားသွားနိုင်ချေလည်း အရမ်းများပါတယ်။
Why Swift macros?
Swift macros ပေါ်လာတဲ့အခါ Xcode မှာ built-in ပါလာတဲ့အတွက် အလုပ်ရှုပ်သက်သာလာပါတယ်။ ဒါ့အပြင် type safety, compile time type checking စတဲ့ Swift feature တွေကိုလည်း အပြည့်အဝရရှိမှာဖြစ်ပါတယ်။ Swift macros ကို ဘာကြောင့်သုံးသလဲဆိုတော့ ထပ်ကာထပ်ကာရေးနေရတဲ့ repetitive code တွေကိုမရေးချင်တဲ့အတွက် compiler plug-in တစ်ခုရေးပြီး macros expansion လုပ်ခိုင်းလိုက်တာဖြစ်ပါတယ်။ ဥပမာအနေနဲ့ Swift Data ရဲ့ @Model
, @Attribute
, @Query
စတဲ့ macros တွေဟာ နောက်ကွယ်မှာ relational mapping တွေ schema တွေ database query တွေကို generate ထုတ်ပေးသွားပါတယ်။ ဒီလိုဘဲ Swift Observation, SwiftUI preview စတဲ့နေရာတွေမှာလည်း macros တွေကိုသုံးပြီး developer တွေရေးရတဲ့ code အကြောင်းအရေကို လျော့ကျစေပါတယ်။
Freestanding Vs Attached macros
Macros type နှစ်မျိုးရှိပါတယ်။ Freestanding macros နဲ့ attached macros တို့ဖြစ်ပါတယ်။ Again, ဒီ article က macros တွေဘယ်လိုရေးမလဲဆိုတဲ့အကြောင်းမဟုတ်တဲ့အတွက် how to တွေပါမှာမဟုတ်ပါဘူး။
Freestanding macros ဆိုတာကတော့ type declaration တစ်ခုကို မီခိုမှုမရှိဘဲနဲ့ သူ့ဘာသာ သီးခြားရပ်တည်နေတဲ့ macros မျိုးကိုခေါ်တာပါ။ Freestanding macro ကို pure function တစ်ခုလိုမြင်ကြည့်လို့ရပါတယ်။ တစ်ခြားဘာ side-effect မှမလုပ်ဘဲ သူ့ထဲကိုထည့်ပေးလိုက်တဲ့ input ကို process လုပ်ပြီး output (macros မှာတော့ generate လုပ်ရမယ့် code block ပေ့ါ) တစ်ခုပြန်ထုတ်ပေးလိုက်တာပါပဲ။ ဉပမာ SwiftUI ရဲ့ #Preview
macros ဟာ freestanding ဖြစ်ပါတယ်။ PreviewProvider
ကို implement လုပ်တဲ့ struct တစ်ခုကို generate ထုတ်ပြီး preview ပြချင်တဲ့ view ကို ငုံပြီး generate ထုတ်လိုက်ပါတယ်။
Attached macros တွေကတော့ type declaration တစ်ခုကို attach လုပ်ပြီးတော့မှ code တွေ generate ထုတ်တာဖြစ်ပါတယ်။ ဘာတွေလုပ်လို့ရလဲဆိုတော့ ကျွန်တော်တို့ရဲ့ type တစ်ခုကို တစ်ခြား type တစ်ခုကို comform လုပ်စေတာ ကျွန်တော်တို့ရဲ့ type ထဲကို property အသစ်ထပ်တိုးတာ function အသစ်ထပ်ထည့်တာ protocol implementation code တွေထည့်တာ ကျွန်တော်တို့ type နဲ့ same level မှာ type အသစ်တွေ (class, struct, enum) ထည့်တာ စတာတွေလုပ်လို့ရပါတယ်။ Function name, property name တွေကအစ attached အလုပ်ခံရမယ့် type ရဲ့ decalred symbol တွေအပေါ်မူတည်ပြီး ပေးလို့ရပါတယ်။ ဉပမာ Swift Data နဲ့ Swift Observation framework တွေကတော့ attached macros တွေကို သုံးထားတာဖြစ်ပါတယ်။
Swift macros known limitations
Swift macros တွေက ဒီလို type တစ်ခုထဲ variable အသစ်တွေ function အသစ်တွေတိုးလို့ရပေမယ့် type ထဲမှာရှိရင်းစွဲ (existing) code ကို modify လုပ်တာ delete လုပ်တာတွေ လုံးဝ လုံးဝ လုပ်လို့ရမှာမဟုတ်ပါဘူး။ ဒါကြောင့် safe ဖြစ်တယ်လို့ပြောလို့ရပါတယ်။ ဒါ့အပြင် အခုဒီစာကိုရေးနေတဲ့အချိန်ထိ attach လုပ်ခံရမယ့် type အတွက် type extension တွေ generate ထုတ်လို့မရသေးပါဘူး။ ဒါကတော့ extension function တွေ ရေးချင်တယ်ဆိုတာမျိုးရှိတဲ့အတွက် နောက်ပိုင်း update တွေကျ type extension တွေပါ generate ထုတ်လို့ရရင်ရလာပါလိမ့်မယ်။
Swift macro isn’t for everyone
Swift macros ဟာ developer တိုင်းအတွက်မဟုတ်ပါဘူး။ ဘာကြောင့်ဒီလိုပြောရသလဲဆိုတော့ macros တွေဟာ advanced feature တစ်ခုဖြစ်တဲ့အပြင် low level ဆန်တဲ့အတွက် compiler တွေအလုပ်လုပ်ပုံကို အကြမ်းဖျင်းနားမလည်ရင် အနည်းဆုံး compiler frontend လောက်ကို မလေ့လာဖူးရင် ဘယ်လိုမှနားလည်သဘောပေါက်မှာမဟုတ်ပါဘူး။ Swift macros ဆိုတာ macro declare လုပ်ထားတဲ့ node တစ်ခုရဲ့ AST (abstract syntax tree) တစ်ခုလုံးကို developer တွေလက်ထဲထည့်ပေးလိုက်တာဖြစ်ပြီး ကျွန်တော်တို့က AST ကို ပြန် modify လုပ်ပေးရတာဖြစ်ပါတယ်။
ဆိုတော့ ကိုယ်က experienced developer တစ်ယောက်မဟုတ်ဘူး compiler design တွေ programming language တွေ compile လုပ်ပုံတွေ နောက် semantics လောက်အထိ သေချာမသိဘူးဆိုရင် macros ဘယ်လိုမှရေးဖို့မလွယ်ပါဘူး။ Macros ရေးတော့မယ်ဆိုရင် ကိုယ်ကအနည်းဆုံး programming language တစ်ခုအလုပ်လုပ်ပုံကို အတွင်းကျကျနားလည်ထားပြီး ကိုယ့် idea ကို code အနေနဲ့ မှန်မှန်ကန်ကန်ပြောင်းလဲရေးသားနိုင်တဲ့သူဖြစ်မှ ခံသာပါမယ်။ နောက်တစ်ချက် application developer တွေအနေနဲ့ ကိုယ်ပိုင် macros ရေးမယ့်အစား library developer တွေရေးထားတဲ့ macros တွေကိုယူသုံးတာကပိုပြီး အချိန်ကုန်သက်သာပါမယ်။
Swift macros ဆိုတာ တစ်ကယ်တော့ abstraction တစ်မျိုးပဲဖြစ်ပါတယ်။ Right layer, right abstraction လိုဆိုပါတယ်။ Abstract မလုပ်သင့်တဲ့အပိုင်းတွေကို abstract လုပ်မိရင် code complexity တက်ပါတယ်။ Learning curve လည်းရှည်သလို debug လိုက်ဖို့လည်းခက်ပါတယ်။ ဒါကြောင့် macros တွေကိုရေးတော့မယ် ပြန်သုံးတော့မယ်ဆိုရင် အနည်းဆုံး ဆယ်ကြိမ်မှာ ခုနှစ်ကြိမ်လောက် generated code ကို ဂရုစိုက်စရာမလိုဘူးဆိုတော့မှသာ ရေးသင့်တယ်လို့မြင်ပါတယ်။
ဉပမာအနေနဲ့ Apple ရဲ့ iOS 17 framework တွေမှာ သုံးသွားတဲ့ macros တွေနဲ့ စဉ်းစားပြပါမယ်။ အလွယ်ဆုံးကတော့ freestanding macro ဖြစ်တဲ့ #Preview ပါ။ သူဆိုလို့ရှိရင် Xcode preview အတွက်ပဲသုံးတာဖြစ်ပြီး real device မှာ run တဲ့အခါ အဲ့ code ကလုံးဝအလုပ်လုပ်မှာမဟုတ်ပါဘူး။ ဒါ့အပြင် preview ဆိုတာကလည်း preview ထွက်အောင် generate ထုတ်တဲ့ code တွေကို ဆယ်ကြိမ်မှာ ကိုးကြိမ်လောက်က ဘယ်သူမှစိတ်မဝင်စား ဖတ်စရာလည်းမလိုပါဘူး။
နောက်တစ်ခါ Swift Data မှာဆိုရင် @Model
ဆိုတဲ့ macro က class တစ်ခုကို database entity တစ်ခုအဖြစ်ပြောင်းနိုင်ဖို့ လိုအပ်တဲ့ code တွေအကုန်ကို generate ထုတ်ပေးပါတယ်။ အဲ့တော့ database entity model တစ်ခုမှာ နောက်ကွယ်က persistence framework က ယူသုံးဖို့အတွက်လိုအပ်တဲ့အရာတွေကို ဘယ်နှစ်ခေါက်ဖတ်ဖို့လိုလည်းဆိုတော့ ဆယ်ကြိမ်မှာ ခုနှစ်ကြိမ် လိုမယ်မထင်ပါဘူး။ ဟုတ်တယ်လေ သူက ORM သက်သက်ပဲ။ ကိုယ်ကဒါလိုချင်တယ်လို့ပြောလိုက်ရင်ရပြီ။ ကျန်တာ သူ့ဘာသာ generate ထုတ်သွားမယ်ပေါ့။ တစ်ခါတလေ ကိုယ်လိုချင်သလိုမဖြစ်တော့မှသာ expanded code ကိုဝင်ဖတ်မှာ။ ပုံမှန်က စိတ်လည်းမဝင်စားသလို ဂရုလည်းစိုက်မနေသင့်ဘူး။ ဒီလိုအနေအထားမှာဆိုရင် right layer, right abstraction ဖြစ်တယ်။
Client ဘက်က (macro ကိုသုံးမယ့် developer ဘက်က) သိစရာမလိုတဲ့ code တွေကို abstract လုပ်လိုက်တဲ့အတွက် မျက်စိနောက်လည်းသက်သာမယ်။ ဖတ်ရတာလည်း ပိုရှင်းမယ်။ အလုပ်လည်းပိုတွင်မယ်။ အဲ့လိုမဟုတ်ဘဲ လူအထင်ကြီးပြီးရော ရေးရပြီးရော macros တွေ ရေးတတ်တယ်ဆိုတာကြွားချင်ယုံသက်သက် လျှောက်ရေးမယ်ဆိုရင်တော့ wrong abstraction ဖြစ်နိုင်ပြီး တစ်ကယ်အရေးကြီးတဲ့ information တွေကို ကိုယ့်ဘာသာ ရှာမရအောင်ဖွက်ထားသလိုမျိုး ဖြစ်သွားနိုင်တယ်။
စကားပြန်ကောက်ရရင် macros က developer တိုင်းအတွက်မဟုတ်ဘူး။ Programming semantics နဲ့ AST လောက် compiler fronted လောက်ကို နည်းနည်းတီးမိခေါက်မိမရှိထားရင် ဘယ်လိုမှ make sense ဖြစ်မှာမဟုတ်သလို ရေးဖို့လည်းခက်လိမ့်မယ်။ ကိုယ်က အဲ့တာတွေသိတယ် ရေးနိုင်တယ်ဆိုရင်တောင် နေရာတိုင်းမှာ မရေးသင့်ဘူး။ တစ်ကယ် macro ရေးသင့်ရဲ့လား တစ်ကယ်လိုအပ်ရဲ့လား သေသေချာချာစဉ်းစားသင့်တယ်။ နောက် community ကလည်း အသုံးများမယ့် common ဖြစ်မယ့် macros တွေ ရေးနေ share နေကြပြီ။ အဲ့ကျရင်လည်း စွတ်မသုံးသင့်သေးဘူး။ ရှေ့မှာပြောခဲ့သလိုဘဲ wrong abstraction က တအားအန္တရာယ်များတဲ့အတွက်ဖြစ်တယ်။
Resources
Swift AST ကိုကြည့်ချင်တယ်ဆိုရင် Kishikawa Katsumi ရေးထားတဲ့ https://swift-ast-explorer.com/ မှာကြည့်လို့ရပါတယ်။
ဒါကတော့ Apple ရဲ့ Swift Macros documentation ပါ။
Outro
Swift macros ဟာ အင်မတန်အစွမ်းထက်တဲ့ language feature တစ်ခုဖြစ်တယ်။ Swift language မှာ ပါလာတာကိုလည်း ကြိုဆိုတယ်။ စိတ်ဝင်စားဖို့ကောင်းတဲ့ usecase တွေ အများကြီးပေါ်ထွက်လာလိမ့်မယ်။ Code တွေ generate ထုတ်နိုင်တယ် အဲ့တာကလည်းသူ့ tooling နဲ့ built-in support ရှိတယ်ဆိုတာ ကောင်းတဲ့အချက်ဖြစ်တယ်။ ဒါပေမယ့် တစ်ဖက်က သူ့ဆိုးကျိုးတွေလည်းရှိနေသေးတယ်။ အသစ်အဆန်းတစ်ခုအနေနဲ့ စမ်းသုံးသင့်တယ်။ တီးမိခေါက်မိရှိထားသင့်တယ်။ ကိုယ်တိုင်မရေးနိုင်သေးရင်တောင် သူများရေးထားတာကိုဖတ်နိုင်တဲ့အဆင်လောက်တော့ရှိသင့်တယ်။ ကိုယ့် app သေးသေးလေးတွေမှာစမ်းသုံးကြည့်သင့်တယ်။ တစ်ကယ့် live project တွေမှာသုံးမယ်ဆိုရင်တော့ စဉ်းစားရမယ့်အချက်တွေများတယ်။ ကိုယ်တိုင်ရေးနိုင်ရင် ပိုကောင်းတယ်။ ကိုယ်ကိုတိုင်လည်း swift semantics ပိုင်းကို တော်တော်လေးသိသွားမယ်။ ဒါပေမယ့် ကိုယ်မရေးခင် သူများရေးထားပြီးသားရှိနေလား တစ်ချက်ကြည့်ပေါ့ဗျာ။ ကိုယ့်ဘာသာအကုန်အသစ်လုပ်တာထက် သူများလုပ်ထားပြီးသားရှိရင် အဲ့တာကိုသွားပေါင်းတာက ပိုရှေ့ရောက်ပါလိမ့်မယ်။