假设我有这个功能:
enum HttpMethod: String { case get, post, put, patch, delete }
func makeRequest(
method: HttpMethod,
baseURL: URL,
pathSegments: [CustomStringConvertible] = [],
headers: [String:String] = [:],
) -> Response {} // body is not important
如果可能的话,我想要的是一个名为
#curry
的表达式宏,它可以执行以下操作:
// Every underscore signifies that that parameter
// should still be fillable in the newly generated function
let makeRequestToMyApi = #curry(makeRequest(
method: _,
baseURL: "https://example.com/api",
pathSegments: _,
headers: _,
))
let me = makeRequestToMyApi(
method: .get,
pathSegments: ["users", "me"],
headers: ["Authorization": "Bearer blabla"],
)
如果在函数定义中定义了默认值的参数(例如
headers
),在柯里化过程中not给出下划线,那么应该假设该参数应该只用它的默认参数填充。因此以后不再可以填补。像这样,例如:
let makeRequestToMyApi = #curry(makeRequest(
method: _,
baseURL: "https://example.com/api",
pathSegments: _,
// notice that headers is missing here,
// since it was not marked with an _,
// it will be assigned its default value
))
let me = makeRequestToMyApi(
method: .get,
pathSegments: ["users", "me"],
// This would cause an error, because headers is not available to fill,
// because headers was already assigned its default value of [:]
headers: ["Authorization": "Bearer blabla"],
)
是否可以编写一个表达式宏来完成此任务?
不可能为此制作宏,因为不可能手动编写它。考虑一个更简单的函数:
func f(x: Int, y: String) -> String { "\(x)\(y)" }
我想部分应用
x
,留下一个功能(y: String) -> String
:
func f(x: Int) -> ((y: String) -> String) { { y in f(x: x, y: y) } }
有了这个,我可以打电话:
let fx = f(x: 1)
fx(y: "one")
但这不是有效的 Swift。它会给出两个错误:
Function types cannot have argument labels; use '_' before 'y'
然后:
Extraneous argument label 'y:' in call
Swift 不支持函数类型中的参数标签。
var f: (Int) -> String // This is allowed
var g: (x: Int) -> String // This is not
所以没有任何一种表达式宏可以构建这个。它没有什么可以扩展的。
我一直在思考如何将其编写为声明宏,但即使这是可能的(我仍在探索),它也不符合您的示例的精神。也许有一个像这样工作的宏:
// This particular syntax is probably impossible, but as an idea
#partial("makeRequestToMyApi", makeRequest, baseURL: "https://example.com/api")
在最好的可能世界中,这可能会扩展到:
func makeRequestToMyApi(method: HttpMethod,
pathSegments: [CustomStringConvertible] = [],
headers: [String:String] = [:],
) -> Response {
makeRequest(method: method,
baseURL: "https://example.com/api",
pathSegments: pathSegments,
headers: headers)
}
但即使这是可能的,您也会注意到它声明了一个函数。这意味着它必须在编译时完成,因此
"https://example.com/api"
必须是源代码中的字符串文字。
我认为没有比这更有活力的东西存在了。同样,不是因为宏,而是因为闭包的语法。您只是不能在其中包含命名参数,这会破坏您的目标。