Skip to content

حالُ الهواتفِ في لغة ص

الخلاصةُ في سطر

لا تطبيقَ هاتفٍ يُبنى من لغةِ ص اليومَ بلا مرورٍ بأدواتِ المنصّة. والموجودُ ليس فراغًا: مولِّدا مصدرٍ يُخرِجانِ من شجرةِ واجهةِ ص نفسِها شفرةَ Jetpack Compose لأندرويدَ وشفرةَ SwiftUI لـiOS.

وهذه الصفحةُ تسمّي ما يوجدُ وما لا يوجدُ وما يفتحُ المجال — لا أكثرَ ولا أقلّ.

ما يوجدُ فعلًا

مولِّدانِ مُعلَنانِ مستقرَّينِ في UICore، ومقيسانِ بالترجمةِ والتشغيل:

sad
متغير الجذر = عمود(
    نص_عنصر("مرحبًا")،
    زر("انقرني")
)

اطبع_سطر(توليد_أندرويد(الجذر، "شاشتي"))
اطبع_سطر(توليد_آي_أو_إس(الجذر، "شاشتي"))

المخرَجُ المقيسُ لأندرويد — دالّةُ @Composable كاملةٌ مع @Preview:

kotlin
@Composable
fun شاشتي() {
    Column(modifier = Modifier) {
        Text(text = "مرحبًا", modifier = Modifier)
        Button(onClick = {}, ...) { Text("انقرني") }
    }
}

ولـiOS — بنيةُ View مع اتّجاهٍ من اليمينِ إلى اليسارِ مضبوطًا:

swift
struct شاشتي: View {
    var body: some View {
        VStack {
            Text("مرحبًا")
            Button(action: {}) { Text("انقرني") }
        }
        .environment(\.layoutDirection, .rightToLeft)

الشجرةُ الواحدةُ تُوصَفُ مرّةً وتُترجَمُ إلى المنصّتَين. وهذا مكسبٌ حقيقيّ: بنيةُ الشاشةِ وخصائصُها وترتيبُها ونصوصُها العربيّةُ تُنقَلُ بلا إعادةِ كتابة.

ما لا يوجدُ — بلا مواربة

مولِّدُ المصدرِ ليس مُصيِّرًا حيًّا

هو يُرجِعُ نصًّا (نوع(…) == "نص" — مقيس) ثمّ يقف. لا يرسمُ، ولا يُشغِّل، ولا يملكُ حلقةَ أحداث. والبناءُ والتصييرُ يبقيانِ على Android Studio أو Xcode. تفصيلُ الفرق.

والمنطقُ لا يُنقَل. مقيسٌ في المخرَجِ أعلاه: الزرُّ المربوطُ بمعالِجٍ في ص يخرجُ بـonClick = {} وaction: {} فارغَين. فالمولَّدُ نقطةُ بدايةٍ بنيويّة، لا تطبيقٌ يعمل.

وطبقةُ المنصّةِ دَينٌ مُعلَن. UIPlatform مُعلَنٌ ٤٣ مدخلًا واثنانِ منها مستقرّان (ارسم_خط · ارسم_مستطيل)؛ والحوارُ (UIDialog) والتخزينُ (UIStorage) والجهازُ (UIDevice، ١٨ مدخلًا) صفرُ مستقرٍّ في كلٍّ منها. فالنوافذُ والمدخلاتُ ودورةُ الحياةِ غيرُ موصولةٍ بالمترجِّم.

enabled: true لا يُقرَأُ قدرة

language-truth/ui_nodes.yaml يُعلِنُ مُصيِّرَي أندرويدَ وiOS enabled: true. وهذا الحقلُ لم يُقَسْ صدقُه، ولا تُشتَقُّ منه دعوى.

والشاهدُ في الملفِّ عينِه: خلفيّةُ ماكِ مُعلَنةٌ فيه بالحقلِ نفسِه enabled: false، وسببُها مكتوبٌ حرفيًّا — تستدعي طبقةَ تفاعلٍ لا تصرّحُ بها ترويستُها فلا تُترجَمُ كما هي. فالحقلُ يصفُ نيّةَ الشفرةِ ويجوزُ أن ينحرفَ عنها.

ولذلك بطاقةُ هذا المجالِ خاليةُ الدعاوى (capabilities: [] في tracks/mobile.yaml) مع أنّ في مصدرِ الحقيقةِ ما يُغري بملئِها. ولو مُلِئَت بدعوى فضاءِ أسماءٍ فيه مدخلٌ مستقرٌّ واحدٌ لانقلبتِ الدرجةُ إلى «ناشئ» بلا أن تتغيّرَ قدرةٌ واحدة — وذلك هو الكذبُ الذي وُجِدَت هذه البنيةُ لمنعِه.

ما يفتحُ المجال — ثلاثُ خطواتٍ محدَّدة

#الخطوةالأثر
وصلُ مدخلاتِ UIPlatform بالمترجِّمِ ورفعُ وسمِ التأجيلِ عنها٤٣ ← تصيرُ الطبقةُ التي تحتَ العنصرِ قدرةً لا دَينًا
بناءُ مُصيِّرٍ حيٍّ لإحدى المنصّتَينِ وتشغيلُه على جهازٍ حقيقيّيصيرُ للمجالِ دعوى قدرةٍ واحدةٌ صادقة
حارسٌ يقيسُ البناءَ فيُحمِّرُ إن أُعلِنَت خلفيّةٌ ولم تُترجَمْenabled يصيرُ حقلًا مقيسًا لا نيّةً مكتوبة

ولماذا هذا ترتيبٌ لا قائمةَ أمنيات

النظيرُ قائمٌ في الشجرةِ فعلًا: scripts/codegen/check_ui_render_completeness.py يقيسُ تغطيةَ العُقَدِ في كلِّ هدفِ تصيير. فالخطوةُ ③ توسيعُ حارسٍ يعملُ لا اختراعُ آلة.

ولحظةَ يُبنى مُصيِّرٌ ويُشغَّلُ على جهاز، تُكتَبُ دعوى واحدةٌ صادقةٌ في tracks/mobile.yaml فتنقلبُ درجةُ البطاقةِ من تلقاءِ نفسِها في البناءِ التالي. هذا هو معنى «قابلةٌ للتوسّع» عمليًّا.

أينَ يُبنى تطبيقُ واجهةٍ اليومَ

على سطحِ المكتب، وهو مُصيِّرٌ حيٌّ مقيس: النافذةُ تُفتَحُ وحلقةُ الأحداثِ تعمل. ابدَأْ من أوّلِ نافذةٍ وزرٍّ يستجيب، ثمّ ولِّدْ من الشجرةِ نفسِها شفرةَ Compose أو SwiftUI حينَ تحتاجُ نقطةَ بدايةٍ على الهاتف.

مُرخَّص بموجب رخصة MIT