是否可以在 Swift 中编写表达式宏来柯里化任何函数?

问题描述 投票:0回答:1

假设我有这个功能:

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"],
)

是否可以编写一个表达式宏来完成此任务?

swift macros currying
1个回答
0
投票

不可能为此制作宏,因为不可能手动编写它。考虑一个更简单的函数:

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"
必须是源代码中的字符串文字。

我认为没有比这更有活力的东西存在了。同样,不是因为宏,而是因为闭包的语法。您只是不能在其中包含命名参数,这会破坏您的目标。

© www.soinside.com 2019 - 2024. All rights reserved.