MetaCodable icon indicating copy to clipboard operation
MetaCodable copied to clipboard

[Feature Request] 1. Default value by InitializerClauseSyntax 2. Add `GroupedDefault` for multiple bindings 3. Some builtin type infer

Open SouHanaQiao opened this issue 1 year ago • 1 comments

This is an amazing project about swift macros.

In the past, some implementations I needed could only be achieved by customizing official codecs such as JSONDecoder/JSONEncoder. I want to make a small contribution to the improvement of this project. For example, the following features provide discussion.

1. Default value by InitializerClauseSyntax

Provide default value by InitializerClauseSyntax (decl.binding.initializer?.value).

Example:

struct Example {
    @Default(10)
    let int: Int   /// defaul value is 10
    var int2: Int = 10 /// default value is 10
    @Default(10)
    var int3: Int = 20 /// default value is 10 when apply `Default` attribute
    var int4: Int = 4, int5: Int = 5 /// `int4` default value is 4, `int5` default value is 5
}

2. Add GroupedDefault for multiple bindings

I add a GroupedDefault member attribute like Default but attach to multiple bindings:

@attached(peer)
@available(swift 5.9)
public macro GroupedDefault<each T>(_ defaults: repeat each T) =
    #externalMacro(module: "MacroPlugin", type: "GroupedDefault")

Using Example:

struct Student {
    @GroupedDefault("Simon", 22)
    let name: String, age: Int
    @Default(60)
    let grade: Int
}

3. We can infer some types like Int, Bool, String, Float, Double with var age = 10, var string = "hello"refer to MemberwiseInit project.

struct SomeInferTest {
    var int = 10 /// Int
    var float = 0.1 as Float /// Float
    var double = 0.11 /// Double
    var string = "hello" /// String
    var bool = Bool() /// Bool
    var date = Date() /// Date
}

So we can add default value like this:

struct Student {
    var name = "unknown", age = 0
    var grade = 60
}

Same as:

struct Student {
    var name: String = "unknown", age: Int = 0
    var grade: Int = 60
}

and

struct Student {
    @GroupedDefault("unknown", 0)
    let name: String, age: Int
    @Default(60)
    let grade: Int
}

4.Just generate one init function.

@MemberInit
struct Student {
    let name: String, age: Int
    @Default(60)
    let grade: Int
    var isBoy: Bool = false 
    var isGirl: Bool = true
}

Generate 4 init function in past like this:

init(name: String, age: Int, grade: Int = 60) {
    self.name = name
    self.age = age
    self.grade = grade
}

init(name: String, age: Int, grade: Int = 60, isBoy: Bool) {
    self.name = name
    self.age = age
    self.grade = grade
    self.isBoy = isBoy
}

init(name: String, age: Int, grade: Int = 60, isGirl: Bool) {
    self.name = name
    self.age = age
    self.grade = grade
    self.isGirl = isGirl
}

init(name: String, age: Int, grade: Int = 60, isBoy: Bool, isGirl: Bool) {
    self.name = name
    self.age = age
    self.grade = grade
    self.isBoy = isBoy
    self.isGirl = isGirl
}

But now expand 1 init function like this:

init(name: String, age: Int, grade: Int = 60, isBoy: Bool = false, isGirl: Bool = true) {
    self.name = name
    self.age = age
    self.grade = grade
    self.isBoy = isBoy
    self.isGirl = isGirl
}

SouHanaQiao avatar Mar 27 '24 06:03 SouHanaQiao

Thanks @SouHanaQiao for this contribution. Please create separate PRs for each feature contribution, this will help in reviewing PRs and also auto changelog generation happens per PR.

soumyamahunt avatar Mar 27 '24 09:03 soumyamahunt