Dictionary of protocol conforming values is ambiguous? - swift

I'm trying create a "conversion" Protocol, to which a Dictionary can conform to if its values implement said protocol.
import Foundation
protocol Fooable {
var foo:String { get }
}
extension Double:Fooable {
var foo:String { get { return "number" } }
}
extension Int:Fooable {
var foo:String { get { return "count" } }
}
extension String:Fooable {
var foo:String { get { return "name" } }
}
extension Dictionary:Fooable where Key == String, Value:Fooable {
var foo:String {
get {
var result = "["
self.keys.sorted().forEach { key in
result += key
result += ": "
result += self[key]!.foo
}
result += "]"
return result
}
}
}
["a": 6.28, "b": 42, "c": "boo"].foo
The problem is that the last line is deemed ambiguous:
error: type of expression is ambiguous without more context
["a": 6.28, "b": 42, "c": "boo"].foo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
Is what I'm trying to do even doable? What do I have to change or add to make it work?
UPDATE
Type Erasure seems to be one "solution". So basically, I would need to make something like:
struct AnyFoo<Fooable> { }
At this point, I can now wrap/erase everything in my dictionary literal:
["a": AnyFoo(6.28), "b": AnyFoo(42), "c": AnyFoo("boo")].foo
along with refining the extension of Dictionary to be for Dictionary's where Value is AnyFoo. Is this really any better than just constricting the Dictionary's extension to cases where Value is String instead and just doing:
["a": 6.28.foo, "b": 42.foo, "c": "boo".foo].foo
For a simple "convert" case, I'm not sure what I would gain from using TypeErasure to defer the conversion through a wrapper, vs just converting before dictionary creation. Less actual typing (as in character input) to do the latter. So I haven't really gained any thing.
Unless I'm missing something, for my case, I'll probably just go with downcasts (e.g. as? Fooable) for container elements, and do something like log errors or something.

Related

Why can't Swift infer this return type properly?

I'm trying to do something I think should be pretty simple, but I'm running into trouble with Swift's type inference. I really don't understand why it's falling down here.
I have a type Cocktail, which has other properties, but the only one important here is the name:
struct Cocktail {
// ... other stuff
let name: String
}
Then I have two protocols:
protocol ScrollIndexable {
var scrollIndexTitle: String { get }
}
protocol ScrollIndexProviding {
var scrollIndices: [any ScrollIndexable] { get }
}
along with a simple conformance on String to ScrollIndexable:
extension String: ScrollIndexable {
var scrollIndexTitle: String { self }
}
I want to make it so that I can use an array of Cocktails as a ScrollIndexProviding:
extension Array: ScrollIndexProviding where Element == Cocktail {
var scrollIndices: [any ScrollIndexable] {
let firstCharacters = reduce(into: Set<String>()) { partialResult, cocktail in
guard let firstCharacter = cocktail.name.first else {
return
}
partialResult.insert(String(firstCharacter))
}
// The return line here has two errors:
// Cannot convert return expression of type 'Array<Cocktail>' to return type '[any ScrollIndexable]'
// No exact matches in call to initializer
return Array(firstCharacters)
}
}
This extension fails to build, with two errors:
Cannot convert return expression of type 'Array' to return type '[any ScrollIndexable]'
No exact matches in call to initializer
The second error seems like noise to me, since Set conforms to Sequence, so I should be able to use that init method.
The first error is confusing to me since the firstCharacters array is of type Set<String>, so the error message just doesn't seem to make any sense. Is there something I'm misunderstanding about the any keyword here? What's going on?
The issue is that you're inside an extension of Array where the Element is Cocktail, so when you try to create an array without specifying the element type the compiler will assume you mean for the element type to be Cocktail.
extension Array where Element: Cocktail {
func someMethod() {
// This array is of type `Array<Cocktail>` since the compiler
// assumes the array's element type should be the same as
// Self's element type, which (from the extension) is `Cocktail`.
let array = Array()
}
}
So, to fix this, just explicitly tell the compiler that the array's element type is String, as in:
extension Array: ScrollIndexProviding where Element == Cocktail {
var scrollIndices: [any ScrollIndexable] {
let firstCharacters = reduce(into: Set<String>()) { partialResult, cocktail in
guard let firstCharacter = cocktail.name.first else {
return
}
partialResult.insert(String(firstCharacter))
}
return Array<String>(firstCharacters)
// ^^^^^^^^ add this
}
}

Swift: Same-Type requirement makes generic parameters equivalent?

I'm using swift 5 and try to compile the following code:
protocol BasicProtocol {
associatedtype T
var str: T {get set}
}
struct AItem<U>: BasicProtocol {
typealias T = U
var str: T
init<G: StringProtocol>(str: G) where G == T {
self.str = str
}
}
I got compilation error:
error: Test.playground:10:45: error: same-type requirement makes generic parameters 'G' and 'U' equivalent
init<G: StringProtocol>(str: G) where G == T {
^
How to make them equivalent? or I can't?
Thanks.
Update 1:
This is the problem I encountered: I want to declare struct "AItem", hoping it has a generic type "T". And this generic type will have some restrictions, such as: "T: StringProtocol". Then for some reason, I need to use an array to load these structs, and ensure that the generics of each structure can be set at will.
I learned that there is "type-erase" might can solve this. So I tried this way, but it seemed unsuccessful. The problems mentioned above have occurred.
Update 2:
struct AItem<T: StringProtocol> {
var aStr: T
}
var array: [AItem<Any>] = [AItem(aStr: "asdfasdf")]
Look,If you compile this code, you will get a compilation error:
error: Test.playground:5:13: error: type 'Any' does not conform to protocol 'StringProtocol'
var array: [AItem<Any>] = [AItem(aStr: "asdfasdf")]
^
If I use "var array: [AItem<String>]", I will not be able to put any other non-"String" but implemented "StringProtocol" instance in the array.
This is why I said I want "ensure that the generics of each structure can be set at will".
Update 3:
very thanks for #jweightman, now I update my question again.
protocol ConstraintProtocol {}
extension String: ConstraintProtocol{}
extension Data: ConstraintProtocol{}
extension Int: ConstraintProtocol{}
.......
struct AItem<T = which class has Implemented "ConstraintProtocol"> {
var aPara: T
init(aPara:T) {
self.aPara = aPara
}
}
// make a array to contain them
var anArray: [AItem<Any class which Implemented "ConstraintProtocol">] = [AItem(aPara: "String"), AItem(aPara: 1234), AItem(aPara: Data("a path")), …]
// then I can use any item which in anArray. Maybe I will implement a method to judge these generics and perform the appropriate action.
for curItem in anArray {
var result = handleItem(curItem)
do something...
}
func handleItem<T: ConstraintProtocol>(item: AItem<T>) -> Any? {
if (item.T is ...) {
do someThing
return ......
} else if (item.T is ...) {
do someThing
return ...
}
return nil
}
This is my whole idea, but all of which are pseudo-code.
It seems like type erasure is the answer to your problem. The key idea to the type erasure pattern is to put your strongly typed but incompatible data (like an AItem<String> and an AItem<Data>) inside of another data structure which stores them with "less precise" types (usually Any).
A major drawback of type erasure is that you're discarding type information—if you need to recover it later on to figure out what you need to do with each element in your array, you'll need to try to cast your data to each possible type, which can be messy and brittle. For this reason, I've generally tried to avoid it where possible.
Anyways, here's an example of type erasure based on your pseudo code:
protocol ConstraintProtocol {}
extension String: ConstraintProtocol{}
extension Data: ConstraintProtocol{}
extension Int: ConstraintProtocol{}
struct AItem<T: ConstraintProtocol> {
var aPara: T
init(aPara: T) {
self.aPara = aPara
}
}
struct AnyAItem {
// By construction, this is always some kind of AItem. The loss of type
// safety here is one of the costs of the type erasure pattern.
let wrapped: Any
// Note: all the constructors always initialize `wrapped` to an `AItem`.
// Since the member variable is constant, our program is "type correct"
// even though type erasure isn't "type safe."
init<T: ConstraintProtocol>(_ wrapped: AItem<T>) {
self.wrapped = wrapped
}
init<T: ConstraintProtocol>(aPara: T) {
self.wrapped = AItem(aPara: aPara);
}
// Think about why AnyAItem cannot expose any properties of `wrapped`...
}
var anArray: [AnyAItem] = [
AnyAItem(aPara: "String"),
AnyAItem(aPara: 1234),
AnyAItem(aPara: "a path".data(using: .utf8)!)
]
for curItem in anArray {
let result = handleItem(item: curItem)
print("result = \(result)")
}
// Note that this function is no longer generic. If you want to try to "recover"
// the type information you erased, you will have to do that somewhere. It's up
// to you where you want to do this.
func handleItem(item: AnyAItem) -> String {
if (item.wrapped is AItem<String>) {
return "String"
} else if (item.wrapped is AItem<Data>) {
return "Data"
} else if (item.wrapped is AItem<Int>) {
return "Int"
}
return "unknown"
}
An alternative to type erasure you could consider, which works well if there's a small, finite set of concrete types your generic could take on, would be to use an enum with associated values to define a "sum type". This might not be a good choice if the protocol you're interested in is from a library that you can't change. In practice, the sum type might look like this:
enum AItem {
case string(String)
case data(Data)
case int(Int)
}
var anArray: [AItem] = [
.string("String"),
.int(1234),
.data("a path".data(using: .utf8)!)
]
for curItem in anArray {
let result = handleItem(item: curItem)
print("result = \(result)")
}
func handleItem(item: AItem) -> String {
// Note that no casting is required, and we don't need an unknown case
// because we know all types that might occur at compile time!
switch item {
case .string: return "String"
case .data: return "Data"
case .int: return "Int"
}
}

Swift 3.1 Key-Only Dictionary for lookups [duplicate]

How can we create unique object list in Swift language like NSSet & NSMutableSet in Objective-C.
As of Swift 1.2 (Xcode 6.3 beta), Swift has a native set type.
From the release notes:
A new Set data structure is included which provides a generic
collection of unique elements, with full value semantics. It bridges
with NSSet, providing functionality analogous to Array and Dictionary.
Here are some simple usage examples:
// Create set from array literal:
var set = Set([1, 2, 3, 2, 1])
// Add single elements:
set.insert(4)
set.insert(3)
// Add multiple elements:
set.unionInPlace([ 4, 5, 6 ])
// Swift 3: set.formUnion([ 4, 5, 6 ])
// Remove single element:
set.remove(2)
// Remove multiple elements:
set.subtractInPlace([ 6, 7 ])
// Swift 3: set.subtract([ 6, 7 ])
print(set) // [5, 3, 1, 4]
// Test membership:
if set.contains(5) {
print("yes")
}
but there are far more methods available.
Update: Sets are now also documented in the "Collection Types" chapter of the Swift documentation.
You can use any Objective-C class in Swift:
var set = NSMutableSet()
set.addObject(foo)
Swift has no concept of sets. Using NSMutableSet in Swift might be slower than using a Dictionary that holds dummy values. You could do this :
var mySet: Dictionary<String, Boolean> = [:]
mySet["something"]= 1
Then just iterate over the keys.
I've built an extensive Set type similar to the built-in Array and Dictionary - here are blog posts one and two and a GitHub repository:
Creating a Set Type in Swift
Set Type Follow-up
SwiftSets on GitHub
extension Array where Element: Hashable {
var setValue: Set<Element> {
return Set<Element>(self)
}
}
let numbers = [1,2,3,4,5,6,7,8,9,0,0,9,8,7]
let uniqueNumbers = numbers.setValue // {0, 2, 4, 9, 5, 6, 7, 3, 1, 8}
let names = ["John","Mary","Steve","Mary"]
let uniqueNames = names.setValue // {"John", "Mary", "Steve"}
I thought a struct with an internal Dictionary would be the way to go. I have only just started using it, so it’s not complete and I have no idea on performance yet.
struct Set<T : Hashable>
{
var _items : Dictionary<T, Bool> = [:]
mutating func add(newItem : T) {
_items[newItem] = true
}
mutating func remove(newItem : T) {
_items[newItem] = nil
}
func contains(item: T) -> Bool {
if _items.indexForKey(item) != nil { return true } else { return false }
}
var items : [T] { get { return [T](_items.keys) } }
var count : Int { get { return _items.count } }
}
You actually can create a Set object pretty easy (in contradiction to GoZoner, there is a built in contains method):
class Set<T : Equatable> {
var items : T[] = []
func add(item : T) {
if !contains(items, {$0 == item}) {
items += item
}
}
}
and you maybe even want to declare a custom operator:
#assignment #infix func += <T : Equatable> (inout set : Set<T>, items : T[]) -> Set<T> {
for item in items {
set.add(item)
}
return set
}
Always in such a case the critical factor is how to compare objects and what types of objects go into the Set. Using a Swift Dictionary, where the Set objects are the dictionary keys, could be a problem based on the restrictions on the key type (String, Int, Double, Bool, valueless Enumerations or hashable).
If you can define a hash function on your object type then you can use a Dictionary. If the objects are orderable, then you could define a Tree. If the objects are only comparable with == then you'll need to iterate over the set elements to detect a preexisting object.
// When T is only Equatable
class Set<T: Equatable> {
var items = Array<T>()
func hasItem (that: T) {
// No builtin Array method of hasItem...
// because comparison is undefined in builtin Array
for this: T in items {
if (this == that) {
return true
}
}
return false
}
func insert (that: T) {
if (!hasItem (that))
items.append (that)
}
}
The above is an example of building a Swift Set; the example used objects that are only Equatable - which, while a common case, doesn't necessarily lead to an efficient Set implementations (O(N) search complexity - the above is an example).
So I think creating a Set with an array is a terrible idea - O(n) is the time complexity of that set.
I have put together a nice Set that uses a dictionary: https://github.com/evilpenguin/Swift-Stuff/blob/master/Set.swift
I wrote a function to solve this problem.
public func removeDuplicates<C: ExtensibleCollectionType where C.Generator.Element : Equatable>(aCollection: C) -> C {
var container = C()
for element in aCollection {
if !contains(container, element) {
container.append(element)
}
}
return container
}
To use it, just pass an array which contains duplicate elements to this function. And then it will return a uniqueness-guaranteed array.
You also can pass a Dictionary, String or anything conforms to ExtensibleCollectionType protocol if you like.
Special case for classes derived from NSObject
given that default Equitable (& Hashable) conformance in NSObject is basically trash you'd better make sure you provide a proper
static func == (lhs: YourClassDerivedFromNSObject, rhs: YourClassDerivedFromNSObject) -> Bool {
implementation lest you want plucking the duplicates inserted into
Set

Referencing self in Dictionary extension

I'm trying to extend Dictionary but can't reference self with a key. I'm confused as to why this is.
extension Dictionary {
func foo() {
var result = self["key"]
}
}
I get this error :
Type 'DictionaryIndex' does not conform to protocol 'StringLiteralConvertible'
If anyone has any insight, it would be appreciated.
Dictionary is a Generic struct. It is generic on its Key and Value.
Thus in your extension you have to use Key as the type for the dictionary keys, and Value as the type for dictionary values.
The compiler is complaining because you are using the wrong type for the dictionary key extension.
Here is an example:
extension Dictionary {
func ext(key: Key) {
if let value = self[key] {
// use your value
println("Key is present")
} else {
println("No value for key")
}
}
}
let dic = ["A": 20]
dic.ext("A")
dic.ext("B")
Here is how you can do something similar ... it might make clearer why your test didn't work:
extension Dictionary {
var foo: String? {
if let key = "key" as? Key {
return self[key] as? String
}
return nil
}
}
let dic1 = ["A": "an A", "key": "the value"]
dic1.foo // "the value" as optional
dic.foo // nil since dic value type is Int
Since Dictionary is a generic struct, you might reconsider extending it as if it is a specific concrete type.

How to create array of unique object list in Swift

How can we create unique object list in Swift language like NSSet & NSMutableSet in Objective-C.
As of Swift 1.2 (Xcode 6.3 beta), Swift has a native set type.
From the release notes:
A new Set data structure is included which provides a generic
collection of unique elements, with full value semantics. It bridges
with NSSet, providing functionality analogous to Array and Dictionary.
Here are some simple usage examples:
// Create set from array literal:
var set = Set([1, 2, 3, 2, 1])
// Add single elements:
set.insert(4)
set.insert(3)
// Add multiple elements:
set.unionInPlace([ 4, 5, 6 ])
// Swift 3: set.formUnion([ 4, 5, 6 ])
// Remove single element:
set.remove(2)
// Remove multiple elements:
set.subtractInPlace([ 6, 7 ])
// Swift 3: set.subtract([ 6, 7 ])
print(set) // [5, 3, 1, 4]
// Test membership:
if set.contains(5) {
print("yes")
}
but there are far more methods available.
Update: Sets are now also documented in the "Collection Types" chapter of the Swift documentation.
You can use any Objective-C class in Swift:
var set = NSMutableSet()
set.addObject(foo)
Swift has no concept of sets. Using NSMutableSet in Swift might be slower than using a Dictionary that holds dummy values. You could do this :
var mySet: Dictionary<String, Boolean> = [:]
mySet["something"]= 1
Then just iterate over the keys.
I've built an extensive Set type similar to the built-in Array and Dictionary - here are blog posts one and two and a GitHub repository:
Creating a Set Type in Swift
Set Type Follow-up
SwiftSets on GitHub
extension Array where Element: Hashable {
var setValue: Set<Element> {
return Set<Element>(self)
}
}
let numbers = [1,2,3,4,5,6,7,8,9,0,0,9,8,7]
let uniqueNumbers = numbers.setValue // {0, 2, 4, 9, 5, 6, 7, 3, 1, 8}
let names = ["John","Mary","Steve","Mary"]
let uniqueNames = names.setValue // {"John", "Mary", "Steve"}
I thought a struct with an internal Dictionary would be the way to go. I have only just started using it, so it’s not complete and I have no idea on performance yet.
struct Set<T : Hashable>
{
var _items : Dictionary<T, Bool> = [:]
mutating func add(newItem : T) {
_items[newItem] = true
}
mutating func remove(newItem : T) {
_items[newItem] = nil
}
func contains(item: T) -> Bool {
if _items.indexForKey(item) != nil { return true } else { return false }
}
var items : [T] { get { return [T](_items.keys) } }
var count : Int { get { return _items.count } }
}
You actually can create a Set object pretty easy (in contradiction to GoZoner, there is a built in contains method):
class Set<T : Equatable> {
var items : T[] = []
func add(item : T) {
if !contains(items, {$0 == item}) {
items += item
}
}
}
and you maybe even want to declare a custom operator:
#assignment #infix func += <T : Equatable> (inout set : Set<T>, items : T[]) -> Set<T> {
for item in items {
set.add(item)
}
return set
}
Always in such a case the critical factor is how to compare objects and what types of objects go into the Set. Using a Swift Dictionary, where the Set objects are the dictionary keys, could be a problem based on the restrictions on the key type (String, Int, Double, Bool, valueless Enumerations or hashable).
If you can define a hash function on your object type then you can use a Dictionary. If the objects are orderable, then you could define a Tree. If the objects are only comparable with == then you'll need to iterate over the set elements to detect a preexisting object.
// When T is only Equatable
class Set<T: Equatable> {
var items = Array<T>()
func hasItem (that: T) {
// No builtin Array method of hasItem...
// because comparison is undefined in builtin Array
for this: T in items {
if (this == that) {
return true
}
}
return false
}
func insert (that: T) {
if (!hasItem (that))
items.append (that)
}
}
The above is an example of building a Swift Set; the example used objects that are only Equatable - which, while a common case, doesn't necessarily lead to an efficient Set implementations (O(N) search complexity - the above is an example).
So I think creating a Set with an array is a terrible idea - O(n) is the time complexity of that set.
I have put together a nice Set that uses a dictionary: https://github.com/evilpenguin/Swift-Stuff/blob/master/Set.swift
I wrote a function to solve this problem.
public func removeDuplicates<C: ExtensibleCollectionType where C.Generator.Element : Equatable>(aCollection: C) -> C {
var container = C()
for element in aCollection {
if !contains(container, element) {
container.append(element)
}
}
return container
}
To use it, just pass an array which contains duplicate elements to this function. And then it will return a uniqueness-guaranteed array.
You also can pass a Dictionary, String or anything conforms to ExtensibleCollectionType protocol if you like.
Special case for classes derived from NSObject
given that default Equitable (& Hashable) conformance in NSObject is basically trash you'd better make sure you provide a proper
static func == (lhs: YourClassDerivedFromNSObject, rhs: YourClassDerivedFromNSObject) -> Bool {
implementation lest you want plucking the duplicates inserted into
Set