Generic protocol functions & lvalue in swift - swift

I am encountering the following error when I try to call a stored closure. I get this error when I try to build:
'(T) -> Void' is not convertible to '#lvalue (T) -> Void' (aka '#lvalue (T) -> ()')
public protocol DataSourceProtocol {
associatedtype DataSourceItem
func item(indexPath: IndexPath) -> DataSourceItem?
func update<T>(sender : T)
}
public class AnyDataSourceSimple<T> : DataSourceProtocol {
private var itemClosure : (IndexPath) -> T?
private var updateClosure: (T) -> Void
public init<I: DataSourceProtocol>(_ concrete: I) where I.DataSourceItem == T {
self.itemClosure = concrete.item
self.updateClosure = concrete.update
}
public func item(indexPath: IndexPath) -> T? {
return itemClosure(indexPath)
}
public func update<T>(sender: T) {
// '(T) -> Void' is not convertible to '#lvalue (T) -> Void' (aka '#lvalue (T) -> ()')
updateClosure(sender)
print("update")
}
}
Is this somehow related to a generic function definition within a protocol?

As in the comments, your T for generic function is separate for the T in the definition of the generic class.
To make it compile you have to do it like this, not sure if this is what you meant
import Foundation
public protocol DataSourceProtocol {
associatedtype DataSourceItem
associatedtype UpdateSender
func item(indexPath: IndexPath) -> DataSourceItem?
func update(sender : UpdateSender)
}
public class AnyDataSourceSimple<T> : DataSourceProtocol {
private var itemClosure : (IndexPath) -> T?
private var updateClosure: (T) -> Void
public init<I: DataSourceProtocol>(_ concrete: I) where I.DataSourceItem == T, I.UpdateSender == T {
self.itemClosure = concrete.item
self.updateClosure = concrete.update
}
public func item(indexPath: IndexPath) -> T? {
return itemClosure(indexPath)
}
public func update(sender: T) {
updateClosure(sender)
print("update")
}
}

Related

Swift 4 extension function on generic protocol which takes a generic function as parameter

swift --version
Swift version 4.1 (swift-4.1-RELEASE)
Target: x86_64-unknown-linux-gnu
Given a simple protocol which defines a generic producer (Source):
protocol Source {
associatedtype T
func produce() -> T
}
And a mapping capable of converting between source types:
struct Mapping<T, U : Source> : Source {
typealias Transformation = (U.T) -> T
private let upstream : U
private let block : Transformation
init(_ upstream: U, _ block : #escaping Transformation) {
self.upstream = upstream
self.block = block
}
func produce() -> T {
return block(upstream.produce())
}
}
And a sample source which produces static text:
struct TextSource : Source {
private let text : String
init(_ text: String) {
self.text = text
}
func produce() -> String {
return text
}
}
I can use this to, e.g., count characters...
let t = TextSource("Hi!")
let f = Mapping(t, { (text: String) -> Int in
return text.count
})
print(f.produce()) // output: 3
But I'd rather like to use a generic map extension function on Source such that transformations can be chained, e.g.:
let t = TextSource("Hi!").map { (text: String) -> Int in
return text.count
}
Approach A
extension Source {
func map<T, U : Source>(_ block: #escaping Mapping<T, U>.Transformation) -> Source {
return Mapping(self, block)
}
}
That is rejected by the swift compiler:
error: generic parameter 'U' is not used in function signature
func map<T, U : Source>(_ block: #escaping Mapping<T, U>.Transformation) -> Source {
^
Approach B
extension Source {
func map<T>(_ block: #escaping Mapping<T, U>.Transformation) -> Source {
return Mapping(self, block)
}
}
In this case the compiler complains about the missing type parameter:
error: use of undeclared type 'U'
func map<T>(_ block: #escaping Mapping<T, U>.Transformation) -> Source {
^
Question
Which type parameters and constraints need to be specified on the map extension function in order to satisfy the compiler?
You cannot use Source as a concrete return type for map because it is a protocol with an associated type requirement.
To solve this, you can have the map function return Mapping<X, Self>:
extension Source {
func map<Result>(_ transform: #escaping (T) -> Result) -> Mapping<Result, Self> {
return Mapping(self, transform)
}
}
The function now has a Self requirement. The resulting Mapping type has a generic type parameter Self that is replaced by the concrete implementation of Source, e.g. Mapping or TextSource.

Swift generic without force unwrap downcast

I try to create some generic based code:
protocol ViewModelsCreator {
associatedtype T: EditItemViewModelType
func editItemViewModel<T>() -> T
}
class PlacesListViewModel: ViewModelsCreator {
typealias T = EditPlaceViewModel
func editItemViewModel<T>() -> T {
return EditPlaceViewModel()
}
}
class EditPlaceViewModel: EditItemViewModelType {}
protocol EditItemViewModelType {}
The playground shows error:
cannot convert return expression of type 'EditPlaceViewModel' to
return type 'T'
and suggest to use
return EditPlaceViewModel() as! T
Is there any solution to avoid this (as! T) force unwrap code? I think compiler should figure out that EditPlaceViewModel is EditItemViewModelType and should satisfy this generic.
You need to remove the <T> in the ViewModelsCreator protocol and the PlacesListViewModel class.
protocol ViewModelsCreator {
associatedtype T: EditItemViewModelType
func editItemViewModel() -> T
}
class PlacesListViewModel: ViewModelsCreator {
typealias T = EditPlaceViewModel
func editItemViewModel() -> T {
return EditPlaceViewModel()
}
}
You can also remove the typealias and replace -> T with -> EditPlaceViewModel in the PlacesListViewModel class. It works either way but this is more explicit.
class PlacesListViewModel: ViewModelsCreator {
func editItemViewModel() -> EditPlaceViewModel {
return EditPlaceViewModel()
}
}

Cannot convert value of type '(T) -> Void'

Example:
struct Wrapper<T> {
var key: Int = 0
var listeners: [Int: (T) -> Void] = Dictionary()
mutating func add(_ handler:#escaping (T) -> Void) {
self.key += 1
self.listeners[self.key] = handler
}
func get(key: Int) -> (T) -> Void {
return self.listeners[key]!
}
}
Test protocol:
protocol CommonProtocol {
}
Class that create Wrapper of test class
class C {
var wrapper: Wrapper = Wrapper<CommonProtocol>()
func add<T: CommonProtocol>(_ handler: #escaping (T) -> Void) {
self.wrapper.add(handler) //Cannot convert value of type '(T) -> Void' to expected argument type '(CommonProtocol) -> Void'
}
}
Image with error
I get error:
Cannot convert value of type '(T) -> Void' to expected argument type '(CommonProtocol) -> Void'
Question:
Why (T) -> Void can't be casted to (CommonProtocol) -> Void ? The T
is explicitly declared as <T: CommonProtocol>
This is my first question, if you have some suggestions please don't hesitate to contact me
You don't need to make func add generic.
When you specify in func add<T: CommonProtocol>... you explicitly telling the compiler that your function accepts all Types that inherit CommonProtocol but your Wrapper specifies that accepts CommonProtocol not inherited types.
Solution
Either type-erase class C:
Class C<T: CommonProtocol> {
var wrapper: Wrapper<T>
....
}
or if type T doesn't actually matter to you then:
func add(_ handler: #escaping (CommonProtocol) -> Void)
but second one doesn't make sense at all. You have to downcast it every-time you'll use this method (and downcasts are very bad :D)
Note: It's actually not related to this question, but one of your options is to type-erase the CommonProtocol too.

Contextual type inference for Type in Swift 2.2+

I want to use first order citizen Type in Swift to decide which function to call.
func sf(v: [Float]) -> Float{
}
func df(v: [Double]) -> Double {
}
func f<T: RealType>(seq ls: [T]) -> T {
if T.self == Float.self {
return sf(ls) // 1. where sf: Float -> Void
} else if T.self == Double.self {
return df(ls) // 2. where df : Double -> Void
}
}
The type inference system couldn't notice that under one branch T == Float and Double in the other ?
Is here a missing feature, complex feature or bug ?
Edit:
typealias RealType = protocol<FloatingPointType, Hashable, Equatable, Comparable, FloatLiteralConvertible, SignedNumberType>
for my prototype but will become a protocol
You are trying to combine static resolution given by generic with runtime decisions, and this is not possible.
You can simply overload f for both Float and Double to obtain what you need:
func f(seq ls: [Float]) -> Float {
return sf(ls) // 1. where sf: Float -> Void
}
func f(seq ls: [Double]) -> Double {
return df(ls) // 2. where df : Double -> Void
}
However, if you want RealType to be a generic placeholder that you can use over other types than Float or, Double, then you can do something like this:
protocol RealType {
static func processArray(v: [Self]) -> Self
}
extension Float: RealType {
static func processArray(v: [Float]) -> Float {
return sf(v)
}
}
extension Double: RealType {
static func processArray(v: [Double]) -> Double {
return df(v)
}
}
func sf(v: [Float]) -> Float{
return 0
}
func df(v: [Double]) -> Double {
return 0
}
func f<T: RealType>(seq ls: [T]) -> T {
return T.processArray(ls)
}
This will give you both type safety, which is one of Swift's main advantages, and scalability as whenever you need to add support for f over another type, you need to only declare that type as conforming to RealType, and implement the processArray method.

Swift compiler is unable to resolve recursive use of generics

I am trying to implement the chain of responsibility pattern in Swift.
public class Chain<T, U> {
private var command: (T?, (U?) -> Void) -> Void
private var runCommand: (() -> Void)?
private var nextCommand: ((U?) -> Void)?
private init(command: (T?, (U?) -> Void) -> Void) {
self.command = command
}
private func next(u: U?) {
self.nextCommand?(u)
}
func then<V>(command: (U?, (V?) -> Void) -> Void) -> Chain<U, V> {
let c = Chain<U, V>(command: command)
self.nextCommand = { command($0, c.next) }
c.runCommand = self.runCommand
return c
}
func endWith(command: (U?) -> Void) {
self.nextCommand = command
self.runCommand!()
}
static func build<V>(command: ((V?) -> Void) -> Void) -> Chain<AnyObject, V> {
let c = Chain<AnyObject, V>(command: { _, next in command(next) })
c.runCommand = { command(c.next) }
return c
}
}
My class does not raise any compilation error but a simple use case (such as the one below) does not work. It raises the following error: error: cannot invoke 'endWith' with an argument list of type '((_?) -> ()) ; expected an argument list of type '((U?) -> Void)'
Any thought?
Chain.build { next in
print("Foo")
next("Bar")
}
.then { o, next in
print(o)
next(15)
}
.endWith { o in
print(o)
}
I know it is an edge case of generics usage in Swift. However, as it is not possible to explicitly specialize a generic type, I have not found any solution so far.
The compiler isn't able to infer the types in your example. You just need to specify them where they're ambiguous:
Chain<String,Int>.build { next in
print("Foo")
next("Bar")
}
.then { (o: String?, next: Int? -> Void) in
print(o)
next(15)
}
.endWith { o in
print(o)
}