Reputation: 24912
I'm getting a strange type-related error in my Swift code:
type of expression is ambiguous without more context.
This happens even if I provide the full type information.
Here's the code that reproduces it.
I have 2 structures:
struct Person{
let name_ : String
let address_ : Address
}
struct Address {
let street_ : String
let city_ : String
}
I then create a structure that contains 2 functions to get and set the address
of a Person
:
struct Lens<A,B> {
let extract: (A)->B
let create: (B,A) -> A
}
When I try to create an instance of Lens that gets and sets the address (in the later case it returns a new Person with the new address), I get the error in the first closure.
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_))})
Not only the type of the parameter of the first closure is specified in the type for lens, but also in the closure itself.
What's going on????
Upvotes: 4
Views: 2720
Reputation: 437917
While it suggests the error is in extract
, it really was in create
. The $0
and $1
are backwards. You're referring to $0.name_
, but $0
of the create
closure is B
, the Address
, but name_
is a property of Person
. I think you meant:
let lens : Lens<Person, Address> = Lens(
extract: { $0.address_ },
create: { Person(name_: $1.name_, address_: Address(street_: $0.street_, city_: $1.address_.city_)) }
)
Or you can redefine Lens
:
struct Lens<A, B> {
let extract: (A) -> B
let create: (A, B) -> A // note, A & B swapped
}
And then you can do:
let lens : Lens<Person, Address> = Lens(
extract: { $0.address_ },
create: { Person(name_: $0.name_, address_: Address(street_: $1.street_, city_: $0.address_.city_)) }
)
Or, perhaps you meant:
let lens : Lens<Person, Address> = Lens(
extract: { $0.address_ },
create: { Person(name_: $0.name_, address_: $1) }
)
That way, the create
uses both the street and city of the supplied address. (It doesn't make sense to me to use the street of the address, but not the city.)
Upvotes: 3
Reputation: 73196
Your initialization of lens
will work using a form like the following:
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_))})
As Martin R noted, the sole error were in fact in the create
closure. When you call the default initializers for your struct, you need to supply the external names (=internal names) for all arguments (even the first one), by default (this differs from regular functions where you may omit the external name for the first function argument). Also make sure you supply the correct argument types for the initializer.
Note from above that you needn't supply the closure type ((p:Person)->Address in ...
), as Swift can infer (and expects this) from the closure: p in ...
suffices (see example above). Or, even more condensed, omit explicit variable names in you closures and make use of $0
and $1
(expected arguments) references instead, as in Robs solution.
Upvotes: 2