在Swift闭包中,“表达式类型含糊不清,需要更多上下文”错误

4

我在我的Swift代码中遇到了一个奇怪的类型相关错误:

表达式的类型在没有更多上下文的情况下是不明确的。

即使我提供了完整的类型信息,这种情况仍会发生。

以下是重现此错误的代码。

我有两个结构体:

struct Person{
    let name_ : String
    let address_ : Address
}

struct Address {
    let street_ : String
    let city_ : String
}

然后我创建了一个结构体,其中包含两个函数,用于获取和设置一个人的地址

struct Lens<A,B> {
    let extract: (A)->B
    let create: (B,A) -> A
}

当我尝试创建一个可以获取和设置地址的Lens实例时(在后一种情况下,它返回一个带有新地址的新Person),我在第一个闭包中遇到了错误。

let lens : Lens<Person, Address> =
Lens(
    extract: {(p:Person)->Address in
        return p.address_}, // here's the error
    create: {Person($0.name_,
        Address(street_: $1, city_: $0.address_.city_))})

不仅第一个闭包的参数类型在镜头类型中被指定,而且在闭包本身中也被指定。

发生了什么???


"create:" 闭包中有几个错误:缺少参数标签(Person(name_: ..., address_: ...)),而且 street_:$1 没有意义,因为 $1 不是字符串。 - Martin R
2个回答

3
虽然它提示错误在于extract,但实际上是在create中。 $0$1位置颠倒了。您正在引用$0.name_,但create闭包的$0B,即Address,而name_Person的属性。我想你的意思是:
let lens : Lens<Person, Address> = Lens(
    extract: { $0.address_ },
    create: { Person(name_: $1.name_, address_: Address(street_: $0.street_, city_: $1.address_.city_)) }
)

或者您可以重新定义Lens

struct Lens<A, B> {
    let extract: (A) -> B
    let create: (A, B) -> A   // note, A & B swapped
}

然后你可以执行以下操作:

let lens : Lens<Person, Address> = Lens(
    extract: { $0.address_ },
    create: { Person(name_: $0.name_, address_: Address(street_: $1.street_, city_: $0.address_.city_)) }
)

或者,也许你想表达的是:
let lens : Lens<Person, Address> = Lens(
    extract: { $0.address_ },
    create: { Person(name_: $0.name_, address_: $1) }
)

那么,create使用提供的地址的街道和城市两个信息。(只使用地址的街道信息没有意义,必须同时使用城市信息。)

2
您的lens初始化将使用以下形式的表单工作:
let lens = Lens<Person, Address>(
    extract: {p in p.address_},
    create: {(a,p) in Person(name_: p.name_, address_: Address(street_: a.street_, city_: p.address_.city_))})

正如Martin R指出的那样,唯一的错误实际上在于create闭包。当您调用结构体的默认初始化程序时,您需要为所有参数(甚至是第一个参数)提供外部名称(=内部名称),默认情况下(这与常规函数不同,您可以省略第一个函数参数的外部名称)。还要确保为初始化程序提供正确的参数类型。

请注意,从上面可以看出,您无需提供闭包类型((p:Person)->Address in ...),因为Swift可以从闭包中推断出来(并期望这样做):p in ...就足够了(参见上面的示例)。或者,更加简洁的方法是,在您的闭包中省略显式变量名称,并使用$0$1(预期的参数)引用,就像Robs解决方案中一样。


"extract:"参数是正确的,{(p:Person)->Address in return p.address_}是正确类型的闭包。错误只在"create:"参数中。 - Martin R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接