Types 在Swift中实施关闭协议

Types 在Swift中实施关闭协议,types,enums,swift,prototype,Types,Enums,Swift,Prototype,我想在我的Swift项目中创建一些函数,这些函数可以接受对象,也可以接受返回该对象类型的闭包。当然,我可以在同一个函数的每个位置定义多个签名,但这太冗长了。我还希望能够创建这些对象/对象返回闭包的类型安全列表,但如果没有一些公共类型来描述这两种情况,就无法做到这一点 这就是我想做的 但这不起作用,因为我实际上无法扩展我的StringClosure类型来实现Stringable。我可以制作stringableList任何类型的列表,但这不是类型安全的 枚举解决方案 一种解决方案是,我可以创建一个枚

我想在我的Swift项目中创建一些函数,这些函数可以接受对象,也可以接受返回该对象类型的闭包。当然,我可以在同一个函数的每个位置定义多个签名,但这太冗长了。我还希望能够创建这些对象/对象返回闭包的类型安全列表,但如果没有一些公共类型来描述这两种情况,就无法做到这一点

这就是我想做的 但这不起作用,因为我实际上无法扩展我的
StringClosure
类型来实现
Stringable
。我可以制作
stringableList
任何类型的
列表,但这不是类型安全的

枚举解决方案 一种解决方案是,我可以创建一个枚举类型,但这意味着我必须显式地在我使用这些类型的任何地方对枚举进行注释,这是很蹩脚的。看起来是这样的:

enum StringableEnum {
    case Str(String)
    case Fun(StringClosure)
}

func printStringableEnum(a : StringableEnum) {
    switch (a) {
    case let .Str(value):
        print(value)
    case let .Fun(value):
        print(value())
    }
}

var enumList : StringableEnum[] = [.Str("cat"), .Fun({return "dog"}), .Str("gecko")]

for element in enumList {
    printStringableEnum(element)
}
这还不错,但它要求my API的用户现在了解此枚举,并在每次调用my
printStringableEnum
函数时使用
.Str
.Fun
标记其参数。不完全是一个好的API


这可能对语言要求太高了,但有人有更好的想法吗?

我认为在这种情况下,最简单的解决方案可能是最好的。不允许直接传入字符串,而是需要闭包。将字符串转换为闭包并不困难:

let myString = "Hello"
printStringable({return  myString})
为了方便起见,您甚至可以创建一个函数将值转换为闭包:

func f<T>(value : T) -> () -> T  {
    return {return value}
}

printStringable(f("Hello"))
printStringable(f(myString))
这意味着您可以从任何受支持的类型创建枚举,如:

var stringable = StringableEnum("Hello")
stringable = StringableEnum({return "Hello"})
你可以通过这样做把绳子拿出来

stringable.value

不要忘记swift可以选择创建这样的延迟计算变量(它们不限于类/结构):


如果您不需要参数化闭包,这是解决懒惰计算/闭包问题的更简单解决方案。

编辑:不要执行以下操作。它从Beta 4开始工作,但依赖于苹果不希望公众使用的一项功能,并打算在Swift 1.0发布之前删除该功能


@auto_closure
可能适合您,但问题是您似乎无法将显式闭包传递给
@auto_closure
参数

另一种选择是将所有内容都视为闭包,并在需要时使用Swift的
\u conversion
功能将字符串隐式转换为StringClosures:

typealias StringClosure = () -> String

extension String {
    func __conversion() -> StringClosure {
        return { self } // 'return' can be omitted inside a single-expression closure
    }
}

// type inferencing automatically figures out that
// stringableList should be [StringClosure] and applies
// the conversion method to promote the string entries into
// self-returning closures
var stringableList = ["cat", { println("side-effects are bad"); return "dog"}, "gecko"]

func printStringClosure(s: StringClosure) {
    println(s())
}

for s in stringableList {
    printStringClosure(s)
}

printStringClosure("test")
printStringClosure { let a = 5*5; return "\(a)" }

你的意思是
扩展字符串:Stringable…
这里的实际目标是什么?您想为API的用户提供一种方法来传递一个延迟计算的字符串,并考虑到该字符串的值可能会在调用函数的时间和使用该值的时间之间发生变化?@drewag yes。或者你可能会因为闭包而产生副作用。在我的应用程序中,它是一个状态机,当定义状态转换时,你可以只指定下一个状态,或者指定一个返回下一个状态并可能发出值的闭包,产生副作用,等等。其中一个问题是,在发布之前,我实际上简化了我的问题。我的闭包有时带有参数,所以我有几种不同的“闭包”类型,所有这些我都希望能够接受。我希望能够学习例如
(Int,String)->String
(Int)->String
()->String
,以及原始
String
。我不想让我的用户自己去做这些事情。目前这不是一个大问题,我只是使用重载,但这意味着我定义的每个函数都需要定义n次,每个签名一次。@wxs,是的,如果你真的不想给API的用户做任何额外的工作,我认为没有办法解决额外的工作;)@wxs,好的,是的,在这种情况下,您必须像对待枚举解决方案一样使用组合。您可以通过为每种类型提供一个初始值设定项来改进枚举的界面,这样用户就不必手动指定特定的大小写。@wxs您还可以提供一种方法来轻松地从枚举中获取值。@wxs,您稍微误解了我的意思。我用我的意思更新了我的答案。有趣的是,我忘记了那些。然而,在我的实际情况中,回调并不是每次都返回相同的值,所以不能简单地用惰性参数计算。就是这样!我不知道转换,谢谢,我说得太快了。Chris Lattner(Swift的首席设计师)刚刚在开发论坛上表示,转换不是供公众使用的,他们打算在最终发布之前将其删除。这就解释了为什么我从未见过它。真遗憾……我想我不应该接受这个答案。
stringable.value
var lazy : String {
    return "Hello"
}

printStringable(lazy)
typealias StringClosure = () -> String

extension String {
    func __conversion() -> StringClosure {
        return { self } // 'return' can be omitted inside a single-expression closure
    }
}

// type inferencing automatically figures out that
// stringableList should be [StringClosure] and applies
// the conversion method to promote the string entries into
// self-returning closures
var stringableList = ["cat", { println("side-effects are bad"); return "dog"}, "gecko"]

func printStringClosure(s: StringClosure) {
    println(s())
}

for s in stringableList {
    printStringClosure(s)
}

printStringClosure("test")
printStringClosure { let a = 5*5; return "\(a)" }