Swift:专门针对函数类型的泛型类的方法 [英] Swift: Specialize method of generic class for function types

查看:43
本文介绍了Swift:专门针对函数类型的泛型类的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于泛型自由函数,我可以使用重载, 本质上专门针对函数类型的函数,如下所示:

For generic free functions I can use overloading, to essentially specialize the function for function types, like this:

func foo<T>(_ t: T.Type) { print("T is unknown") }
func foo<P>(_ t: ((P) -> Void).Type) { print("T is a function with one parameter") }

let f: (String) -> Void = { print($0) }    
foo(type(of: f))   //  prints "T is a function with one parameter"

请注意,foo()的第二版不受协议的约束, 主要是因为据我所知,我们无法使函数类型符合协议 (我们不能扩展非标称类型).我可以创建一个OneParamFunction协议, 并可以在受约束的foo()中使用它,但是我无法制作所有一个参数 函数类型符合该协议.

Note the second version of foo() is not protocol-constrained, mainly because as far as I know, we can't make function types conform to protocols (we can't extend non-nominal types). I could create a OneParamFunction protocol, and could use that in a constrained foo(), but I couldn't make all one-parameter function types conform to that protocol.

但是上面的重载在没有协议约束的情况下起作用.

But the above overload works without protocol constraints.

对于泛型类的实例方法,这样的事情可能吗?

Is something like this possible for an instance method of a generic class?

在我看来,这种语法似乎是最自然的,但不支持:

To me, this syntax would seem most natural, but it's not supported:

class Generic1<T> { init(_ t: T.Type) {} }
extension Generic1 { func foo() { print("T is unknown") } }

extension Generic1<P>
    where T == ((P) -> Void) {
    func foo() { print("T is a function with one parameter") }
}

在Generic类上创建受协议约束的扩展的常规"方式 看起来像这样:

The "normal" way of creating protocol-constrained extensions on the Generic class would look like this:

extension Generic1 where T: OneParamFunction { ... }

但是如上所述,我无法使函数类型符合OneParamFunction协议.

but as discussed above, I can't make function types conform to the OneParamFunction protocol.

我也不能只创建一个(没有重载/专业化)实例方法 然后转发到免费功能,这是行不通的:

I also can't just create a single (no overloads / specializations) instance method and then forward to the free function, this doesn't work:

class Generic2<T> {
    init(_ t: T.Type) {}
    func foo() { myModule.foo(T.self) }
}

let f: (String) -> Void = { print($0) }
Generic2(type(of: f)).foo()   //  prints "unknown T"

编译,但总是调用unknown-T版本,我认为是因为类型擦除. 在Generic2内部,编译器并不真正知道T是什么. Generic2没有在T上定义任何有助于编译器的协议约束 正确分派myModule.foo()调用(并且不能具有此类约束,请参见上文).

Compiles, but always calls the unknown-T version, I think because of type erasure. Inside Generic2, the compiler doesn't really know what T is. Generic2 doesn't define any protocol constraints on T that would help the compiler properly dispatch the myModule.foo() call (and it can't have such constraints, see above).

使用泛型类内部的方法重载进行编译,并且看起来很接近, 但仍然不起作用,尽管在这种情况下我不确定为什么.

Using method overloading inside the generic class compiles and seems close, but still doesn't work, although in this case I'm not sure why.

class Generic3<T> {
    init(_ t: T.Type) {}
    func foo() { print("T is unknown") }
    func foo<P>() where T == ((P) -> Void) { print("T is a function with one parameter") }
}

let f: (String) -> Void = { print($0) }
Generic3(type(of: f)).foo()   //  prints "unknown T"

在调用foo()的位置,Generic3的类型参数是众所周知的, 因此在我看来,编译器将拥有所有必要的类型信息 正确分派呼叫,但事实并非如此,它仍会显示未知T".

Here at the site of calling foo() the type parameter of Generic3 is fully known, so it seems to me that the compiler would have all the necessary type information to correctly dispatch the call, but that's not what happens, it still prints "unknown T".

甚至不重复将类型作为foo()的参数也有帮助(无论如何都不理想):

Not even repeating the type as a parameter to foo() helps (wouldn't be ideal anyway):

class Generic4<T> {
    init(_ t: T.Type) {}
    func foo(_ t: T.Type) { print("T is unknown") }
    func foo<P>(_ t: T.Type) where T == ((P) -> Void) { print("T is a function with one parameter") }
}

let f: (String) -> Void = { print($0) }
Generic4(type(of: f)).foo(type(of: f))   //  still prints "unknown T"

我还有其他选择吗?

更新,以回应Rob Napier的回答.

Update, in response to Rob Napier's answer.

我想我想要的不是真正的动态调度,而是静态调度,而是基于呼叫站点已知的所有类型信息,而不是基于Generic.init()之前推断的T的类型擦除值.这确实适用于自由函数,但不适用于成员函数.

I think what I wish for here isn't really dynamic dispatch, I'd like to have static dispatch, but based on all the type information known at the call site, rather than based on the type-erased value for T previously inferred during Generic.init(). And that does work with free functions, but not with member functions.

尝试一下:

func foo<T>(_ t: T.Type) { print("T is unknown") }
func foo<P>(_ t: ((P) -> Void).Type) { print("T is a function with one parameter") }

func g<T>(_ x: T.Type) -> T.Type { return x }
let f: (String) -> Void = { print($0) }
foo(g(type(of: f)))   //  prints "T is a function"

这确实调用了foo的"T是函数"版本,即使T也在g()内部进行了类型擦除.而且我认为,这与Generic(type(of: f)).foo()更像是Rob的示例,其中g<T>()调用foo()(这更类似于从Generic的其他成员调用Generic.foo()的情况–在这种情况下,我确实理解了T未知).

This does call the "T is function" version of foo, even though T gets type-erased inside g() too. And I think this is more similar to Generic(type(of: f)).foo() than Rob's example with g<T>() calling foo() (which is more analogous to calling Generic.foo() from some other member of Generic -- in this case I do understand why T is unknown).

在两种情况下(Generic(type(of: f)).foo()foo(g(type(of: f))))都有两种类型:

In both cases (Generic(type(of: f)).foo() vs foo(g(type(of: f)))) there are two types:

  1. f的原始类型和
  2. 第一次调用返回的类型(Generic.init()/g()).
  1. the original type of f, and
  2. the type returned from the first call (Generic.init() / g()).

但是很明显,当调用自由函数foo()时,随后对foo()的调用是根据类型#1分派的,而类型#2用于分派给成员函数Generic.foo().

But apparently the subsequent call to foo() is dispatched based on type #1 when calling the free function foo(), while type #2 is used for dispatching to member function Generic.foo().

首先,我认为差异与上述示例g()返回T.Type的方式有关,而Generic.init()的结果是Generic<T>,但没有:

First I thought that the difference has to do with how in the above example g() returns T.Type, while the result of Generic.init() is a Generic<T>, but no:

class Generic_<T> {
    init(_ t: T.Type) {}
    func member_foo() { print("T is unknown") }
    func member_foo<P>() where T == ((P) -> Void) { print("T is a function with one parameter") }
}

func free_foo<T>(_ g: Generic_<T>) { print("T is unknown") }
func free_foo<P>(_ t: Generic_<(P) -> Void>) { print("T is a function with one parameter") }

func g_<T>(_ t: T.Type) -> Generic_<T> { return Generic_(t) }

free_foo(g_(type(of: f)))   //  T is function
Generic_(type(of: f)).member_foo()   //  T is unknown

在这种情况下,Generic.initg()均返回Generic<T>.但是,free_foo()调用似乎是基于f的原始类型分派的,而member_foo()调用却没有.我仍然想知道为什么.

In this case both Generic.init and g() return Generic<T>. And yet, the free_foo() call seems to get dispatched based on the full original type of f, while the member_foo() call does not. I still wonder why.

推荐答案

您可能要对泛型类使用多个泛型参数.

You may want to use more than one generic parameter for your Generic class.

class Generic1<P, R> {
    init(_ t: ((P) -> R).Type) {}
}

extension Generic1 where P == Void
{ func foo() { print("T is unknown") } }

extension Generic1{
    func foo() { print("T is a function with one parameter") }
}
let f: (String) -> Void = { print($0) }
Generic1(type(of: f)).foo()   //  prints "T is a function with one parameter"
let v: (()) -> Void = { print($0) } // a bit ugly ;)
Generic1(type(of: v)).foo()   //  prints "T is unknown"

但是最好使用通用类型别名;)

因此,考虑到您对accout的评论,我试图:

So taking your comment into accout I tried to :

  1. 摆脱() s
  2. 找到一种在不向客户提出过多要求的情况下扩大支持的参数数量的方法(尽管这有待辩论)
  3. 找到在非函数类型中使用它的方法
  1. get rid of the ()s
  2. find a way to scale up the number of supported params without asking too much to the client (that's up for debate though)
  3. find a way to use it with non function type

这就是我得到的:

// some generic type aliases
typealias Bar<P, R> = (P) -> R
typealias Foo<P> = Bar<P, Void>
typealias Quux<P, Q, R> = (P, Q) -> R
typealias Qux<P, Q> = Quux<P, Q, Void>
typealias Xyzyy<S, P, Q, R> = (S, P, Q) -> R

// some closures
let fooString: Foo<String> = { print($0) }
let barIntVoid: Bar<Int, Void> = { print($0) }
let quuxStringIntString: Quux<String, Int, String> = { "\($0)\($1)"}
let quuxStringIntVoid: Quux<String, Int, Void> = { print("\($0)\($1)") }
let xyzyyDateStringIntVoid: Xyzyy<Date, String, Int, Void> = { print("\($0): \($1)\($2)") }

// same class as before
class Generic2<G> {
    init(_ t: G.Type) {}
}

// handling any type
extension Generic2 {
    func foo<T>(_ f: T) {
        print("\(T.self) is \(T.self == G.self ? "known" : "unknown")")
    }
}

// these methods are put in an unspecialized extension in order to be "shared"
// I guess if your designing a module you probably won't be able to handle all the possibilities
// but I'm not sure you should anyway.
// it should be possible to extends Generic2 outside it's module to handle custom case though
extension Generic2 {
    func foo<P,R>(p: P.Type, r: R.Type) {
        print("f is a function with one parameter of type `\(P.self)` returning `\(R.self)`")
        print("\(Bar<P,R>.self) is \(G.self == Bar<P,R>.self ? "known" : "unknown")")
    }

    func foo<P, Q,R>(p: P.Type, q: Q.Type, r: R.Type) {
        print("f is a function with two parameter of type `\(P.self)` and `\(Q.self)` returning `\(R.self)`")
        print("\(Quux<P, Q, R>.self) is \(G.self == Quux<P, Q, R>.self ? "known" : "unknown")")
    }

    func foo<S, P, Q,R>(s: S.Type, p: P.Type, q: Q.Type, r: R.Type) {
        print("f is a function with two parameter of type `\(S.self)`, `\(P.self)` and `\(Q.self)` returning `\(R.self)`")
        print("\(Xyzyy<S, P, Q, R>.self) is \(G.self == Xyzyy<S, P, Q, R>.self ? "known" : "unknown")")
    }
}

// you have to create an extension an write an overload of `foo(_:)` for each type you want to support
extension Generic2 where G == Bar<String, Void> {
    func foo(_ f: G) {
        foo(p: String.self, r: Void.self)
    }
}

extension Generic2 where G == Bar<Int, Void> {
    func foo(_ f: G) {
        foo(p: Int.self, r: Void.self)
    }
}

extension Generic2 where G == Quux<String, Int, String> {
    func foo(_ f: G) {
        foo(p: String.self, q: Int.self, r: String.self)
    }
    
    func foo(p: String, q: Int, f: G) {
        foo(f)
        f(p,q)
    }
}

extension Generic2 where G == Quux<String, Int, Void> {
    func foo(_ f: G) {
        foo(p: String.self, q: Int.self, r: Void.self)
    }
    
    func foo(p: String, q: Int, f: G) {
        foo(f)
        f(p,q)
    }
}

我像这样测试它:

print("fooString:")
Generic2(Foo<String>.self).foo(fooString)

print("\nbarIntVoid:")
Generic2(Bar<Int, Void>.self).foo(barIntVoid)

print("\nquuxStringIntString:")
Generic2(Quux<String, Int, String>.self).foo(quuxStringIntString)

print("\nquuxStringIntString:")
Generic2(Quux<String, Int, Void>.self).foo(quuxStringIntString)

print("\nquuxStringIntVoid:")
Generic2(Quux<String, Int, Void>.self).foo(p: "#", q:1, f: quuxStringIntVoid) // prints "#1"

print("\nxyzyyDateStringIntVoid:")
Generic2(Xyzyy<Date, String, Int, Void>.self).foo(xyzyyDateStringIntVoid)

print("\nnon function types:")
Generic2(Foo<String>.self).foo(Int.self)
Generic2(Foo<String>.self).foo(1)
Generic2(Int.self).foo(1)

和输出看起来像这样:

fooString:
f is a function with one parameter of type `String` returning `()`
(String) -> () is known

barIntVoid:
f is a function with one parameter of type `Int` returning `()`
(Int) -> () is known

quuxStringIntString:
f is a function with two parameter of type `String` and `Int` returning `String`
(String, Int) -> String is known

quuxStringIntString:
(String, Int) -> String is unknown

quuxStringIntVoid:
f is a function with two parameter of type `String` and `Int` returning `()`
(String, Int) -> () is known
#1

xyzyyDateStringIntVoid:
(Date, String, Int) -> () is known

non function types:
Int.Type is unknown
Int is unknown
Int is known

编辑

在这一点上,我不确定是否应该保留以前的编辑,但是这要短一些.

EDIT

At this point I'm not sure if I should keep previous edits, but this one is shorter.

我刚刚将您的第二个重载更改为:

I just changed you second overload to :

class Generic_<T> {
    init(_ t: T.Type) {}
    func member_foo() { print("T is unknown") }
    func member_foo<P>(_ type: P.Type) { print("T is a function with one parameter") }

}

free_function的行为不变:

It behavior is unchanged for free_function :

free_foo(g_(type(of: f)))   //  T is function
free_foo(g_(String.self))   // T is unknown

但是现在也可以与Generic_的成员一起使用:

BUT now it also works with Generic_'s members :

let generic = Generic_(Bar<String, Int>.self)
generic.member_foo()   //  T is unknown
generic.member_foo(String.self)   //  T is a function with one parameter

这篇关于Swift:专门针对函数类型的泛型类的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆