Thursday, February 2, 2023
Learning Code
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#
No Result
View All Result
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#
No Result
View All Result
Learning Code
No Result
View All Result
Home Swift

Result builders in Swift – The.Swift.Dev.

learningcode_x1mckf by learningcode_x1mckf
September 13, 2022
in Swift
0
Result builders in Swift – The.Swift.Dev.
74
SHARES
1.2k
VIEWS
Share on FacebookShare on Twitter


2021/12/01

If you wish to make a end result builder in Swift, this text will make it easier to to cope with the commonest instances when making a DSL.

Swift

Swift end result builder fundamentals


The result builder proposal (initially it was known as operate builders) was carried out in Swift 5.4. This characteristic permits us to construct up a end result worth utilizing a sequence of parts. At first sight, you may assume, hey this seems like an array with a sequence of components, besides the coma in between the objects, however nope, that is fully completely different. However why is it good for us?


End result builder can be utilized to create completely new Area-Particular Languages (DSLs) inside Swift. Making a DSL has many benefits, since DSLs are often tied to a selected drawback, the syntax that you simply use to explain the language may be very light-weight, but highly effective and succesful. Since Swift DSLs are kind protected, it’s a lot safer to make use of one as a substitute of manually concatenate objects. Swift DSLs additionally permits us to make use of primary management flows inside these embedded micro-languages. 🤔


Let me offer you an instance: you may write HTML in Swift, you may merely write out all of the tags and glue a bunch of String values collectively, however that would not be so protected, proper?


func buildWebpage(title: String, physique: String) -> String 
    """
    <html>
        <head>
            <title>(title)</title>
        </head>
        <physique>
            <h1>(title)</h1>
            <h1>(physique)</h1>
        </physique>
    </html>
    """


let html = buildWebpage(title: "Lorem ipsum", physique: "dolor sit amet")
print(html)


We are able to all agree that that is ugly and the compiler will not make it easier to to detect the semantic points in any respect. Now if we change the next code with a DSL, we’ll significantly good thing about the Swift compiler options. Swift will give us kind security, so our code can be much less error inclined. A DSL can have many constraints and restrictions that’ll assist others to put in writing higher code. In our case the listing of tags goes to be a predefined set of values, so you will not have the ability to present a improper tag or miss the closing tag, in different phrases your DSL goes to be syntactically legitimate. In fact you continue to can have logical errors, however that is all the time the case, it doesn’t matter what instrument you select. 🧠


import SwiftHtml

func buildWebpage(title: String, physique: String) -> String 
    let doc = Doc(.unspecified) 
        Html 
            Head 
                Title(title)
            
            Physique 
                H1(title)
                P(physique)
            
        
    
    return DocumentRenderer().render(doc)


As you may see the snippet above seems far more Swifty and we had been additionally in a position to take away the duplicate HTML closing tags from the code. We do not have to put in writing the characters in any respect and the compiler can kind test all the pieces for us, so type-o accidents cannot occur. ✅


Earlier than you assume that end result builders are simply syntactic sugar over underlying knowledge sorts, I’ve to guarantee you that they’re way more complicated than this. It’s an especially superior and highly effective characteristic that you must positively learn about.


You may create all types of end result builders, for instance I am utilizing them to construct validators, person interface components and format constraints. In fact SGML (HTML, XML) and CSS can also be a terrific use-case, however the listing is limitless. Let me present you methods to construct a easy end result builder.




Constructing a HTML tree construction


I’ll present you ways I created my SwiftHtml HTML DSL library, as a result of it was a enjoyable challenge to work with and I’ve realized loads about it, it is also going to switch the Leaf/Tau template in my future initiatives. The primary concept behind SwiftHtml was that I wished to comply with the HTML specs as carefully as attainable. So I’ve created a Node construction to signify a node contained in the doc tree.


public struct Node 

    public enum `Sort` 
        case commonplace     
        case remark      
        case empty        
        case group        
    

    public let kind: `Sort`
    public let identify: String?
    public let contents: String?

    public init(kind: `Sort` = .commonplace,
                identify: String? = nil,
                contents: String? = nil) 
        self.kind = kind
        self.identify = identify
        self.contents = contents
    


A node has 4 variants outlined by the Sort. A normal node will render as a regular HTML tag utilizing the identify and the contents. A remark will solely use the contents and empty tag will not have a closing tag and use the identify property as a tag identify. Lastly the group node can be used to group collectively a number of nodes, it will not render something, it is only a grouping factor for different tags.


The trick in my answer is that these Node objects solely include the visible illustration of a tag, however I’ve determined to separate the hierarchical relationship from this stage. That is why I really launched a Tag class that may have a number of youngsters. In my earlier article I confirmed a number of methods to build a tree structure using Swift, I’ve experimented with all of the attainable options and my closing alternative was to make use of reference sorts as a substitute of worth sorts. Do not hate me. 😅


open class Tag 

    public var node: Node
    public var youngsters: [Tag]

    public init(_ node: Node, youngsters: [Tag] = []) 
        self.node = node
        self.youngsters = youngsters
    



Now that is how a Tag object seems like, it is fairly easy. It has an underlying node and a bunch of youngsters. It’s attainable to increase this tag and supply functionalities for all of the HTML tags, akin to the potential of including widespread attributes and I am additionally in a position to create subclasses for the tags.


public closing class Html: Tag 

    public init(_ youngsters: [Tag]) 
        tremendous.init(.init(kind: .commonplace, identify: "html", contents: nil), youngsters: youngsters)
    


public closing class Head: Tag 

    public init(_ youngsters: [Tag]) 
        tremendous.init(.init(kind: .commonplace, identify: "head", contents: nil), youngsters: youngsters)
    


public closing class Title: Tag 

    public init(_ contents: String) 
        tremendous.init(.init(kind: .commonplace, identify: "title", contents: contents))
    


public closing class Physique: Tag 

    public init(_ youngsters: [Tag]) 
        tremendous.init(.init(kind: .commonplace, identify: "physique", contents: nil), youngsters: youngsters)
    


public closing class H1: Tag 

    public init(_ contents: String) 
        tremendous.init(.init(kind: .commonplace, identify: "h1", contents: contents))
    


public closing class P: Tag 

    public init(_ contents: String) 
        tremendous.init(.init(kind: .commonplace, identify: "p", contents: contents))
    


All proper, now we’re in a position to initialize our Tag tree, however I warn you, it will look very awkward.


func buildWebpage(title: String, physique: String) -> Html 
    Html([
        Head([
            Title(title),
        ]),
        Physique([
            H1(title),
            P(body),
        ]),
    ])


It’s nonetheless not attainable to render the tree and the syntax shouldn’t be so eye-catchy. It is time to make issues higher and we must always positively introduce some end result builders for good.




The anatomy of Swift end result builders

Now that we have now our knowledge construction ready, we must always give attention to the DSL itself. Earlier than we dive in, I extremely advocate to fastidiously learn the official proposal and watch this WWDC video about end result builders, since each sources are wonderful. 🤓


Constructing an array of components


The primary factor that I do not like about our earlier buildWebpage operate is that I’ve to continually write brackets and comas, with a purpose to construct our construction. This may be simply eradicated by introducing a brand new end result builder for the Tag objects. We simply need to mark an enum with the @resultBuilder attribute and supply a static buildBlock methodology with the given kind.


@resultBuilder
public enum TagBuilder 
    public static func buildBlock(_ parts: Tag...) -> [Tag] 
        parts
    


It will permit us to make use of an inventory of parts within our DSL constructing blocks, however earlier than we might use it we even have to alter our particular HTML tag init strategies to reap the benefits of this newly created end result builder. Simply use a closure with the return kind that we wish to use and mark the whole operate argument with the @TagBuilder key phrase.


public closing class Html: Tag 
    public init(@TagBuilder _ builder: () -> [Tag]) 
        tremendous.init(.init(kind: .commonplace, identify: "html", contents: nil), youngsters: builder())
    


public closing class Head: Tag 
    public init(@TagBuilder _ builder: () -> [Tag]) 
        tremendous.init(.init(kind: .commonplace, identify: "head", contents: nil), youngsters: builder())
    


public closing class Physique: Tag 
    public init(@TagBuilder _ builder: () -> [Tag]) 
        tremendous.init(.init(kind: .commonplace, identify: "physique", contents: nil), youngsters: builder())
    


Now we will refactor the construct webpage methodology since it might probably now use the underlying end result builder to assemble the constructing blocks primarily based on the parts. When you check out the introduction section contained in the proposal you will get a greater concept about what occurs beneath the hood.


func buildWebpage(title: String, physique: String) -> Html 
    Html 
        Head 
            Title(title)
        
        Physique 
            H1(title)
            P(physique)
        
    


let html = buildWebpage(title: "title", physique: "physique")


Anyway, it is fairly magical how we will remodel our complicated array primarily based code into one thing clear and good by profiting from the Swift compiler. I like this strategy, however there’s extra.


Optionals and additional construct blocks


If you wish to present if assist inside your DSL you need to implement some extra strategies inside your end result builder object. Do that code, nevertheless it will not compile:


func buildWebpage(title: String, physique: String) -> Html 
    Html 
        Head 
            Title(title)
        
        Physique 
            if title == "magic" 
                H1(title)
                P(physique)
            
        
    


The construct an non-compulsory end result with an if assertion we have now to consider what occurs right here. If the title is magic we want to return an array of Tags, in any other case nil. So this might be expressed as a [Tag]? kind however we all the time wish to have a bunch of [Tag] components, now that is straightforward.


@resultBuilder
public enum TagBuilder 

    public static func buildBlock(_ parts: Tag...) -> [Tag] 
        parts
    

    public static func buildOptional(_ element: [Tag]?) -> [Tag] 
        element ?? []
    


However wait, why is it not working? Nicely, since we return an array of tags, however the outer Physique factor was anticipating Tag components one after one other, so a [Tag] array will not match our wants there. What can we do about this? Nicely, we will introduce a brand new buildBlock methodology that may remodel our [Tag]... values right into a plain Tag array. Let me present you actual this fast.


@resultBuilder
public enum TagBuilder 

    public static func buildBlock(_ parts: Tag...) -> [Tag] 
        parts
    
    
    public static func buildBlock(_ parts: [Tag]...) -> [Tag] 
        parts.flatMap  $0 
    

    public static func buildOptional(_ element: [Tag]?) -> [Tag] 
        element ?? []
    



func buildWebpage(title: String, physique: String) -> Html {
    Html 
        Head 
            Title(title)
        
        Physique  
            if title == "magic"  
                H1("Hi there")
                P("World")
             

            
    


I hope it isn’t too difficult, nevertheless it’s all about constructing the correct return kind for the underlying methodology. We wished to have simply an array of tags, however with the if assist we have ended up with an inventory of tag arrays, that is why we have now to remodel it again to a flattened array of tags with the brand new construct block. If you need to check out a extra easy instance, you must read this post. ☺️


If and else assist and both blocks


If blocks can return non-compulsory values, now what about if-else blocks? Nicely, it is fairly an identical strategy, we simply wish to return both the primary or the second array of tags.


@resultBuilder
public enum TagBuilder 

    public static func buildBlock(_ parts: Tag...) -> [Tag] 
        parts
    
    
    public static func buildBlock(_ parts: [Tag]...) -> [Tag] 
        parts.flatMap  $0 
        

    public static func buildOptional(_ element: [Tag]?) -> [Tag] 
        element ?? []
    

    public static func buildEither(first element: [Tag]) -> [Tag] 
        element
    

    public static func buildEither(second element: [Tag]) -> [Tag] 
        element
    


func buildWebpage(title: String, physique: String) -> Html 
    Html 
        Head 
            Title(title)
        
        Physique 
            if title == "magic" 
                H1("Hi there")
                P("World")
            
            else 
                P(physique)
            
        
    


let html = buildWebpage(title: "title", physique: "physique")


As you may see now we do not want extra constructing blocks, since we have already coated the variadic Tag array difficulty with the non-compulsory assist. Now it’s attainable to put in writing if and else blocks inside our HTML DSL. Appears fairly good thus far, what’s subsequent? 🧐


Enabling for loops and maps by means of expressions


Think about that you’ve got a bunch of paragraphs within the physique that you simply’d like to make use of. Fairly straightforward, proper? Simply change the physique into an array of strings and use a for loop to remodel them into P tags.


func buildWebpage(title: String, paragraphs: [String]) -> Html 
    Html 
        Head 
            Title(title)
        
        Physique 
            H1(title)
            for merchandise in paragraphs 
                P(merchandise)
            
        
    


let html = buildWebpage(title: "title", paragraphs: ["a", "b", "c"])


Not so quick, what is the precise return kind right here and the way can we remedy the issue? In fact the primary impression is that we’re returning a Tag, however in actuality we might like to have the ability to return a number of tags from a for loop, so it is a [Tag], ultimately, it will be an array of Tag arrays: [[Tag]].


The buildArray methodology can remodel these array of tag arrays into Tag arrays, that is ok to supply for assist, however we nonetheless want yet one more methodology to have the ability to use it correctly. We now have to construct an expression from a single Tag to show it into an array of tags. 🔖


@resultBuilder
public enum TagBuilder 

    public static func buildBlock(_ parts: Tag...) -> [Tag] 
        parts
    
    
    public static func buildBlock(_ parts: [Tag]...) -> [Tag] 
        parts.flatMap  $0 
    

    public static func buildEither(first element: [Tag]) -> [Tag] 
        element
    

    public static func buildEither(second element: [Tag]) -> [Tag] 
        element
    

    public static func buildOptional(_ element: [Tag]?) -> [Tag] 
        element ?? []
    

    public static func buildExpression(_ expression: Tag) -> [Tag] 
        [expression]
    

    public static func buildArray(_ parts: [[Tag]]) -> [Tag] 
        parts.flatMap  $0 
    


This manner our for loop will work. The construct expression methodology may be very highly effective, it allows us to supply numerous enter sorts and switch them into the info kind that we really want. I’ll present you yet one more construct expression instance on this case to assist the map operate on an array of components. That is the ultimate end result builder:


@resultBuilder
public enum TagBuilder 

    public static func buildBlock(_ parts: Tag...) -> [Tag] 
        parts
    
    
    public static func buildBlock(_ parts: [Tag]...) -> [Tag] 
        parts.flatMap  $0 
    


    public static func buildEither(first element: [Tag]) -> [Tag] 
        element
    

    public static func buildEither(second element: [Tag]) -> [Tag] 
        element
    

    public static func buildOptional(_ element: [Tag]?) -> [Tag] 
        element ?? []
    

    public static func buildExpression(_ expression: Tag) -> [Tag] 
        [expression]
    

    public static func buildExpression(_ expression: [Tag]) -> [Tag] 
        expression
    

    public static func buildArray(_ parts: [[Tag]]) -> [Tag] 
        parts.flatMap  $0 
    


Now we will use maps as a substitute of for loops if we want useful strategies. 😍


func buildWebpage(title: String, paragraphs: [String]) -> Html 
    Html 
        Head 
            Title(title)
        
        Physique 
            H1(title)
            paragraphs.map  P($0) 
        
    


let html = buildWebpage(title: "title", paragraphs: ["a", "b", "c"])


That is how I used to be in a position to create a DSL for my Tag hierarchy. Please be aware that I’d had some issues improper, this was the very first DSL that I’ve made, however thus far so good, it serves all my wants.




A easy HTML renderer


Earlier than we shut this text I would like to indicate you ways I created my HTML doc renderer.


struct Renderer 

    func render(tag: Tag, stage: Int = 0) -> String 
        let indent = 4
        let areas = String(repeating: " ", depend: stage * indent)
        change tag.node.kind 
        case .commonplace:
            return areas + open(tag) + (tag.node.contents ?? "") + renderChildren(tag, stage: stage, areas: areas) + shut(tag)
        case .remark:
            return areas + "<!--" + (tag.node.contents ?? "") + "-->"
        case .empty:
            return areas + open(tag)
        case .group:
            return areas + (tag.node.contents ?? "") + renderChildren(tag, stage: stage, areas: areas)
        
    

    personal func renderChildren(_ tag: Tag, stage: Int, areas: String) -> String 
        var youngsters = tag.youngsters.map  render(tag: $0, stage: stage + 1) .joined(separator: "n")
        if !youngsters.isEmpty 
            youngsters = "n" + youngsters + "n" + areas
        
        return youngsters
    
    
    personal func open(_ tag: Tag) -> String 
        return "<" + tag.node.identify! + ">"
    
    
    personal func shut(_ tag: Tag) -> String 
        "</" + tag.node.identify! + ">"
    


As you may see it is a fairly easy, but complicated struct. The open and shut strategies are simple, the fascinating half occurs within the render strategies. The very first render operate can render a tag utilizing the node kind. We simply change the kind and return the HTML worth in line with it. if the node is a regular or a gaggle kind we additionally render the youngsters utilizing the identical methodology.


In fact the ultimate implementation is a little more complicated, it includes HTML attributes, it helps minification and customized indentation stage, however for academic functions this light-weight model is greater than sufficient. Here is the ultimate code snippet to render a HTML construction:


func buildWebpage(title: String, paragraphs: [String]) -> Html 
    Html 
        Head 
            Title(title)
        
        Physique 
            H1(title)
            paragraphs.map  P($0) 
        
    


let html = buildWebpage(title: "title", paragraphs: ["a", "b", "c"])
let output = Renderer().render(tag: html)
print(output)


If we evaluate this to our very first string primarily based answer we will say that the distinction is big. Truthfully talking I used to be afraid of end result builders for a really very long time, I believed it is simply pointless complexity and we do not actually need them, however hey issues change, and I’ve additionally modified my thoughts about this characteristic. Now I can not dwell with out end result builders and I like the code that I can write by utilizing them. I actually hope that this text helped you to know them a bit higher. 🙏





Source link

You might also like

The abstract Vapor service factory design pattern

SwiftNIO tutorial – The echo server

Introducing – Vapor cheatsheet – The.Swift.Dev.

Share30Tweet19
learningcode_x1mckf

learningcode_x1mckf

Recommended For You

The abstract Vapor service factory design pattern

by learningcode_x1mckf
February 1, 2023
0
The abstract Vapor service factory design pattern

I've written a number of articles about manufacturing unit design patterns on my weblog and this time I might like to speak a couple of particular one, which...

Read more

SwiftNIO tutorial – The echo server

by learningcode_x1mckf
January 27, 2023
0
SwiftNIO tutorial – The echo server

Intoducing SwiftNIO In the event you used a excessive degree net framework, corresponding to Vapor, up to now, you would possibly had some interplay with occasion loops...

Read more

Introducing – Vapor cheatsheet – The.Swift.Dev.

by learningcode_x1mckf
January 23, 2023
0
Introducing – Vapor cheatsheet – The.Swift.Dev.

Out there on Gumroad Thanks for supporting my work by buying the cheatsheet. 🙏 Download now A whole Vapor framework reference for novices. greater than...

Read more

iCloud Programming Tutorial for iOS: An Introduction

by learningcode_x1mckf
January 18, 2023
0
iCloud Programming Tutorial for iOS: An Introduction

Editor’s observe: This week, we work with Ziad Tamim once more to provide you an introduction of iCloud programming. You’ll learn to save and retrieve knowledge from iCloud.On...

Read more

Easy multipart file upload for Swift

by learningcode_x1mckf
January 18, 2023
0
Easy multipart file upload for Swift

I imagine that you've got already heard in regards to the well-known multipart-data add method that everybody likes to add recordsdata and submit type knowledge, but when not,...

Read more
Next Post
The seventh way to call a JavaScript function without parentheses

The seventh way to call a JavaScript function without parentheses

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Related News

DevOps for Java Developers

DevOps for Java Developers

November 24, 2022
What’s new in SwiftUI for iOS 16 – Hacking with Swift

What’s new in SwiftUI for iOS 16 – Hacking with Swift

September 6, 2022
Theo’s Java Club still looking for new owner a year after closing

Theo’s Java Club still looking for new owner a year after closing

September 4, 2022

Browse by Category

  • C#
  • C++
  • Java
  • JavaScript
  • Python
  • Swift

RECENT POSTS

  • Java :Full Stack Developer – Western Cape saon_careerjunctionza_state
  • Pay What You Want for this Learn to Code JavaScript Certification Bundle
  • UPB Java Jam brings coffeehouse vibes to Taylor Down Under | Culture

CATEGORIES

  • C#
  • C++
  • Java
  • JavaScript
  • Python
  • Swift

© 2022 Copyright Learning Code

No Result
View All Result
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#

© 2022 Copyright Learning Code

Are you sure want to unlock this post?
Unlock left : 0
Are you sure want to cancel subscription?