Add Constraints to Generic Parameters in Extension

Add constraints to generic parameters in extension

Try this code in the Playground:

// make sure only `Optional` conforms to this protocol
protocol OptionalEquivalent {
typealias WrappedValueType
func toOptional() -> WrappedValueType?
}

extension Optional: OptionalEquivalent {
typealias WrappedValueType = Wrapped

// just to cast `Optional<Wrapped>` to `Wrapped?`
func toOptional() -> WrappedValueType? {
return self
}
}

extension Dictionary where Value: OptionalEquivalent {
func flatten() -> Dictionary<Key, Value.WrappedValueType> {
var result = Dictionary<Key, Value.WrappedValueType>()
for (key, value) in self {
guard let value = value.toOptional() else { continue }
result[key] = value
}
return result
}
}

let a: [String: String?] = ["a": "a", "b": nil, "c": "c", "d": nil]
a.flatten() //["a": "a", "c": "c"]

Because you cannot specify an exact type in the where clause of a protocol extension, one way you may detect exactly the Optional type is to make Optional UNIQUELY conforms to a protocol (say OptionalEquivalent).

In order to get the wrapped value type of the Optional, I defined a typealias WrappedValueType in the custom protocol OptionalEquivalent and then made an extension of Optional, assgin the Wrapped to WrappedValueType, then you can get the type in the flatten method.

Note that the sugarCast method is just to cast the Optional<Wrapped> to Wrapped?(which is exactly the same thing), to enable the usage guard statement.

UPDATE

Thanks to Rob Napier 's comment I have simplified & renamed the sugarCast() method and renamed the protocol to make it more understandable.

How do I write an extension method for a generic type with constraints on type parameters?

You need to add the same type parameter constraints on the extension method.

This is my attempt at the closest reconstruction of your example that compiles and runs, without any error:

public class Matrix<T>  where T : new() {
public T[,] values;
}


public static class MatrixExtension {
public static T getCalcResult<T>(this Matrix<T> mat) where T : new() {
T result = new T();
return result;
}
}

class Program {
static void Main(string[] args) {
Matrix<int> m = new Matrix<int>();
int aNumber = m.getCalcResult();
Console.WriteLine(aNumber); //outputs "0"
}

c# extension method for a generic class with interface as type constraint

Yes, this can be done by making the method generic and adding a generic type constraint to the method, as follows:

public static void SomeMethod<T>(
this SomeClass<T> obj, ISomeInterface objParam)
where T : ISomeInterface // <-- generic type constraint
{
...
}

Is it possible to add type constraints to a Swift protocol conformance extension?

EDIT: As noted in the updated question, this is now possible since Swift 4.1


This is not currently possible in Swift (as of Xcode 7.1). As the error indicates, you can't restrict protocol conformance ("inheritance clause") to a type-constrained extension. Maybe someday. I don't believe there's any deep reason for this to be impossible, but it's currently not implemented.

The closest you can get is to create a wrapper type such as:

struct FriendlyArray<Element: Friendly>: Friendly {
let array: [Element]
init(_ array: [Element]) {
self.array = array
}
func sayHi() {
for elem in array {
elem.sayHi()
}
}
}

let friendly: Friendly = FriendlyArray(["Foo", "Bar"])

(You would likely want to extend FriendlyArray to be a CollectionType.)

For a tale of my own descent into the madness of trying to make this work, and my crawl back from the edge, see NSData, My Old Friend.

Nested Generic Constraints: Constrain the T of a generic item inside a generic sequence extension that is constrained to that generic type

You can restrict Iterator.Element to types conforming to
ObservableType and then add another constraint for the associated type E of Iterator.Element:

protocol ObservableType {
associatedtype E
// ...
}

class MyType { }

extension Sequence where Iterator.Element: ObservableType, Iterator.Element.E: MyType {

}

Extension methods for specific generic types

This isn't possible in the current version of F#, unfortunately. See related question here.

Creating an extension method against a generic interface or as a generic constraint?

Yes there is.

The first signature would match any type that can be compared to T, not just T values. So any type that implements IComparable<int> can be used by the first signature, not just int.

Example:

void Main()
{
10.IsFoo(20).Dump();
new Dummy().IsFoo(20).Dump();

IComparable<int> x = 10;
x.IsFoo(20).Dump();

IComparable<int> y = new Dummy();
y.IsFoo(20).Dump();
}

public class Dummy : IComparable<int>
{
public int CompareTo(int other)
{
return 0;
}
}

public static class Extensions
{
public static bool IsFoo<T>(this IComparable<T> value, T other)
where T : IComparable<T>
{
Debug.WriteLine("1");
return false;
}

public static bool IsFoo<T>(this T value, T other)
where T : IComparable<T>
{
Debug.WriteLine("2");
return false;
}
}

Will output:

2
False
1
False
1
False
1
False

I tested this with LINQPad.

How do I constrain a Kotlin extension function parameter to be the same as the extended type?

As mentioned by @Alexander Udalov it's not possible to do directly but there's a workaround where you define the extension method on another type like so:

data class Wrapper<T>(val value: T)

val <T> T.ext: Wrapper<T> get() = Wrapper(this)

fun <T> Wrapper<T>.thing(p: T) {
println("value = $value, param = $p")
}

With the above the following compiles:

"abc".ext.thing("A")

but the next fails

"abc".ext.thing(2)

with:

Kotlin: Type inference failed: Cannot infer type parameter T in fun <T> Wrapper<T>.thing(p: T): Unit
None of the following substitutions
receiver: Wrapper<String> arguments: (String)
receiver: Wrapper<Int> arguments: (Int)
can be applied to
receiver: Wrapper<String> arguments: (Int)

As suggested by @hotkey it seems that it should be possible to avoid the need for explicit Wrapper type with the following extension property:

val <T> T.thing: (T) -> Any? get() = { println("extension body") }

And then use it as "abc".thing("A") but it also fails. Surprisingly the following does compile "abc".thing.invoke("A")



Related Topics



Leave a reply



Submit