Can you explain/solve these 'generic constraint' compiler errors?
This is an incorrect use of a protocol. Protocols associated types are determined by the implementer of the protocol, not the user of the protocol. Generics are determined by the user of the protocol. There is no automatic wrapping of protocols into generics (that feature will be called "existential containers" and we don't know when it will come if ever).
[Listener]
is not a complete type. what is the ValueType
?
For this particular case, there is no reason for a protocol. It captures exactly one function. You should just pass the function:
class Binding<T> {
var value: T?
var listeners:[(Binding<T>, T) -> ()] = []
func fire() {
if let value = value {
listeners.forEach { $0(self, value) }
}
}
init() {}
}
If you really need the protocol, you can lift it into a type eraser (AnyListener
):
protocol Listener {
associatedtype ValueType
func call(_ binding:Binding<ValueType>, value:ValueType)
}
struct AnyListener<ValueType>: Listener {
let _call: (Binding<ValueType>, ValueType) -> ()
init<L: Listener>(_ listener: L) where L.ValueType == ValueType {
_call = listener.call
}
func call(_ binding: Binding<ValueType>, value: ValueType) {
_call(binding, value)
}
}
class Binding<T> {
var value:T?
var listeners: [AnyListener<T>] = []
func fire() {
if let value = value {
listeners.forEach { $0.call(self, value: value) }
}
}
}
let listener = ... some listener ....
binding.listeners.append(AnyListener(listener))
Or you could just make AnyListener
into a more concrete struct and get rid of the protocol. But for such a simple case I'd pass the function usually.
Protocol function with generic type
Status of the features needed to make this work:
- Recursive protocol constraints (SE-0157) Implemented (Swift 4.1)
- Arbitrary requirements in protocols (SE-0142) Implemented (Swift 4)
- Generic Type Aliases (SE-0048) Implemented (Swift 3)
Looks like this is currently not possible without introducing boxed types (the "type erasure" technique), and is something looked at for a future version of Swift, as described by the Recursive protocol constraints and Arbitrary requirements in protocols sections of the Complete Generics Manifesto (since generic protocols are not going to be supported).
When Swift supports these two features, the following should become valid:
protocol Parser {
associatedtype Result
associatedtype SubParser: Parser where SubParser.Result == Result
func parse() -> ParserOutcome<Result, SubParser>
}
enum ParserOutcome<Result, SubParser: Parser where SubParser.Result == Result> {
case result(Result)
case parser(P)
}
With generic typealias
es, the subparser type could also be extracted as:
typealias SubParser<Result> = Parser where SubParser.Result == Result
How to pass a class type as a function parameter
You are approaching it in the wrong way: in Swift, unlike Objective-C, classes have specific types and even have an inheritance hierarchy (that is, if class B
inherits from A
, then B.Type
also inherits from A.Type
):
class A {}
class B: A {}
class C {}
// B inherits from A
let object: A = B()
// B.Type also inherits from A.Type
let type: A.Type = B.self
// Error: 'C' is not a subtype of 'A'
let type2: A.Type = C.self
That's why you shouldn't use AnyClass
, unless you really want to allow any class. In this case the right type would be T.Type
, because it expresses the link between the returningClass
parameter and the parameter of the closure.
In fact, using it instead of AnyClass
allows the compiler to correctly infer the types in the method call:
class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: T.Type, completionHandler handler: ((T) -> ())) {
// The compiler correctly infers that T is the class of the instances of returningClass
handler(returningClass())
}
Now there's the problem of constructing an instance of T
to pass to handler
: if you try and run the code right now the compiler will complain that T
is not constructible with ()
. And rightfully so: T
has to be explicitly constrained to require that it implements a specific initializer.
This can be done with a protocol like the following one:
protocol Initable {
init()
}
class CityInfo : NSObject, Initable {
var cityName: String?
var regionCode: String?
var regionName: String?
// Nothing to change here, CityInfo already implements init()
}
Then you only have to change the generic constraints of invokeService
from <T>
to <T: Initable>
.
Tip
If you get strange errors like "Cannot convert the expression's type '()' to type 'String'", it is often useful to move every argument of the method call to its own variable. It helps narrowing down the code that is causing the error and uncovering type inference issues:
let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self
CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/
}
Now there are two possibilities: the error moves to one of the variables (which means that the wrong part is there) or you get a cryptic message like "Cannot convert the expression's type ()
to type ($T6) -> ($T6) -> $T5
".
The cause of the latter error is that the compiler is not able to infer the types of what you wrote. In this case the problem is that T
is only used in the parameter of the closure and the closure you passed doesn't indicate any particular type so the compiler doesn't know what type to infer. By changing the type of returningClass
to include T
you give the compiler a way to determine the generic parameter.
Java two-level parameterized type inference
For the first compilation issue, your problem comes from the fact that you try to assign it to some variable, whose type does not match.
The simplest approach is to start from the useList()
call to make sure that this simple code compiles:
List<? extends Number> numList = makeList();
useList(numList);
If you check this code, it compiles already.
Now ask your IDE to assign the result to a local variable. The IDE will compute the type of the expression for you to declare the variable, and voilà:
Set<? extends NumberLists<? extends List<? extends Number>, ? extends Number>> paramSet =
useList(numList);
(If possible, get rid of the L
type on NumbersList
, that will simplify things a lot)
For the paraNL1.add(numList);
call, it cannot work because of the wildcard: the compiler has no way to check that paraNL1
accepts your unknown type of list.
Even if you fix the declaration by following the same process:
NumberLists<? extends List<? extends Number>, ? extends Number> paraNL1 =
paramSet.iterator().next();
paraNL1.add(numList); // will still not compile
You see that you are trying to use paraNL1
as a consumer (it consumes numList
), and PECS tells you that you must declare it with super
for that to work.
Indeed, the compiler knows that there must exist some type L extends List<? extends Number>
for your NumberLists
, but it does not know it and has no way to check that it matches the type of numList
. L
could be for example LinkedList<Float>
while numList
could be ArrayList<Integer>
.
Edit: to make it work, you might be able to use a wilcard capture helper method like this:
private static <T extends Number, L extends List<T>> void helper(L numList) {
Set<NumberLists<L, T>> paramSet = useList(numList);
NumberLists<L, T> paraNL1 = paramSet.iterator().next();
paraNL1.add(numList);
}
(this even allows you to get rid of the ? extends NumerLists
in the Set
declaration)
and call it with
List<? extends Number> numList = makeList();
helper(numList);
On Java generics lower bound usage: ? super T
Here's an example:
The following snippet passes compilation with the signature <T> void copy(List<? super T> dest, List<? extends T> src)
but doesn't work with the signature <T> void copy(List<T> dest, List<? extends T> src)
:
YourClass obj = new YourClass ();
List<HashMap<String,String>> lhm = new ArrayList<>();
List<Map<String,String>> lm = new ArrayList<>();
obj.<HashMap<String,String>>copy (lm,lhm);
Defining generic type based on method (structural typing)
The problem is that
def getC = 100
is not the same function signature as
def getC() = 100
A
and B
have getC
without ()
so you need to remove the ()
from the type constraint:
def readC[T <: {def getC: Int}](obj: T) = {
obj.getC
}
The better way to do this is to use a typeclass. Check online for a number of good tutorials on how to do this.
Type constraint for higher-kinded type in Scala
The reason why your attempt did not work is because you have a kind mismatch.
The following:
def functorLaw[A, F[_] :Arbitrary] (fn :Functor[F]) :Prop = forAll { (fa :F[A]) => true }
is just a syntactic sugar for:
def functorLaw[A, F[_]] (fn :Functor[F])(implicit evidence: Arbitrary[F]) :Prop = forAll { (fa :F[A]) => true }
So in essence, the problem is that your method expects an implicit value of type Arbitrary[F]
where F is an higher-order type (F[_]
), but that does not make sense because Arbitrary
does not take an higher order type:
// T is a first order type, it has the kind *
// Simply put, it is not a type constructor
class Arbitrary[T]
For your code to compile as is (and make sense), Arbitrary
would have to be declared something like this:
// T is a type constructor, it has the kind * -> *
class Arbitrary[T[_]]
Now for how to fix it.
In your case, the actual arbitrary values that you want are of type F[A]
, not F
(which should go without saying as it's not a concrete type, but a type constructor), so the implicit you need is of type Arbitrary[F[A]]
:
def functorLaw[A, F[_]] (fn :Functor[F])(implicit arb: Arbitrary[F[A]]) :Prop = forAll { (fa :F[A]) => true }
And because F[A]
does not occur in the type parameter list (there is A
and F
, but not F[A]
), the "context bound" syntactic sugar can not be used and we have to leave it at using an explicit (!) implicit parameter list.
Related Topics
What Is the Shortest Way to Run Same Code N Times in Swift
How to Change Uitextfield Keyboard Type to Email in Swift
Differencebetween Sequencetype and Collectiontype in Swift
How to Use a Switch Statement with a Nested Enum
How to Make Uislider Default Thumb to Be Smaller Like the Ones in the iOS Control Center
Tableview Accessories Don't Load Correctly
Parse.Com Pfquery Order by Time (Swift)
Error While Using Property 'Cgrectgetwidth', It Says It Was Replaced with 'Cgrect.Width'
How to Call Swiftui Navigationlink Conditionally
Image Disappears When Styling Class to Make a Round Image
Ios8: Auto-Layout and Gradient
How to Stop Tokbox Screen Sharing in Swift
Realm Error Domain=Io.Realm.Unknown Code=89 "Operation Canceled"
How to Decode Utf8-Literals Like "\Xc3\Xa6" in Swift 5
New Value Is Only Available in Sendasynchronousrequest - Swift