November 26, 2024

Swift 5.9 brengt de kracht van macro-uitbreiding naar de taal

Swift 5.9 brengt de kracht van macro-uitbreiding naar de taal

Gepresenteerd op WWDC 2023Swift 5.9, nu beschikbaar in beta, zorgt voor een belangrijke uitbreiding van de mogelijkheden van de taal door het genereren van code tijdens het compileren met behulp van macro’s te ondersteunen.

Macro’s zijn bedoeld om de mogelijkheden van een programmeertaal uit te breiden door syntaxis te introduceren die vergelijkbaar is met de basisprincipes van de taal en door zoveel mogelijk paradigma’s te elimineren. Snelle macro’s worden uitgevoerd op het AST-niveau (Abstract Syntax Tree). Maakt het mogelijk om tijdens het compileren code te genereren die wordt teruggestuurd naar de compiler. Ze moeten niet worden verward met C/C++-macro’s, die alleen een soort gespecialiseerde tekenreeksvervanging bevatten en dichter bij macro’s in talen als Rust of Scala staan, ondanks hun eigen verfijning.

Macro’s […] Een AST omzetten in een andere AST zonder afhankelijk te zijn van een externe status en zonder wijzigingen aan te brengen in een externe status.

Swift heeft twee soorten macro’s: Op zichzelf staande macro’s, op zichzelf verschijnend, zonder geassocieerd te zijn met een goedkeuring; En Bijgevoegde macro’s, die de PE-aangifte die erop volgt wijzigt. Syntactisch gezien beginnen zelfstandige macro’s met #macro’s bijgevoegd door @:


#aFreestandingMacro("with argument")

@AttachedMacro<T> struct AStruct {
...
}

Om een ​​macro uit te breiden, doorloopt de Swift-compiler een reeks stappen, te beginnen met het maken van het programma AST. Delen van de AST worden verzonden om de macro uit te voeren, die wordt gebruikt om zijn eigen uitgebreide formulier te maken. De compiler gebruikt uiteindelijk de uitgebreide AST om de macro-aanroep te vervangen en zorgt er vervolgens voor dat de resulterende code nog steeds geldig is. Macro’s kunnen in andere macro’s verschijnen, in welk geval de buitenste macro eerst wordt uitgevouwen en de binnenste macro dus kan worden gewijzigd voordat deze wordt uitgevouwen.

In tegenstelling tot andere functies van de taal, vereisen Swift-macro’s dat u een declaratie en een implementatie afzonderlijk definieert.

Macro-declaratie wordt bediend door macro Het sleutelwoord definieert de plaatsen in de code waar het kan worden aangeroepen en het type code dat het genereert. De volgende declaratie is voor de standaardbibliotheekmacro @OptionSetwat bedoeld is als een gemakkelijkere manier om mee te werken OptionSet protocolprobeert het volgende uit te leggen:


@attached(member)
@attached(conformance)
public macro OptionSet<RawType>() =
        #externalMacro(module: "SwiftMacros", type: "OptionSetMacro")

Volgens de aankondiging heeft de @OptionSet De macro voegt nieuwe leden toe aan het object waaraan het is gekoppeld en komt overeen met een of meer protocollen. De declaratie geeft ook aan waar de uitvoering van de macro te vinden is, bijvoorbeeld in de module “SwiftMacros”, en welk type de implementatie ervan biedt, bijvoorbeeld “OptionSetMacro”.

Zoals je misschien al vermoedde, is het uitvoeren van een macro niet het meest eenvoudige proces, hoewel vergeleken met andere talen waar de definitie van een macro kan worden opgenomen in dezelfde code die deze gebruikt. In feite zijn macro’s in Swift geïmplementeerd als een bibliotheek die een type biedt, bijvoorbeeld een struct, en behoort tot zijn eigen doel in uw Xcode-project. Dit kan worden begrepen door te bedenken dat de macro-uitbreidingsstap wordt behandeld als de Xcode-bouwfase, wat een grote hulp is bij het hebben van afzonderlijke bibliotheken om aan te roepen.

De eenvoudigste manier om een ​​macro te maken is met Swift Package manager swift package init --type macro. Dit biedt een mooi sjabloon voor uw macro-aangifte en implementatie.

Zonder al te veel in detail te treden over hoe de macro is geïmplementeerd, kan het interessant zijn om op te merken hoe strikt u zich verhoudt tot de macro’s van uw AST-programma. In het volgende voorbeeld kunt u leren hoe u een type kunt declareren dat macro-uitbreiding implementeert. U moet een bestand aanleveren expansion(of:in:) Methode die AST en macrocontext als argumenten ontvangt en een extensie levert ExprSyntax:


public struct FourCharacterCode: ExpressionMacro {

    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) throws -> ExprSyntax {
...
}

gebruik makend van FreestandingMacroExpansionSyntax argument, heeft de macrotoepassing toegang tot argumenten die als geschreven waarden aan de macro zijn doorgegeven. De geretourneerde waarde is een bestand ExprSyntax, van het SwiftSyntax-project, wat een uitdrukking is in AST. sinds, ExprSyntax Compatibel met StringLiteralConvertibleis de gemakkelijkste manier om zo’n AST te maken, om het als een String vanwaar je terugkeert expansion(of:in:) Het wordt automatisch geconverteerd naar een bestand ExprSyntax.

Als laatste opmerking over Swift-macro’s, als u een uitzondering op een macro gooit expansion implementatie, zal het worden behandeld als een compilatiefout in de vervormde macrolocatie, wat belooft dat het debuggen van macro’s minder hoofdpijn zal opleveren dan in andere talen.

Als u geïnteresseerd bent in het uitproberen van Swift-macro’s, kunt u Xcode 15 beta downloaden of de nieuwste versie installeren Snelle toolchain In Xcode 14.