使用Swift探索存在类型
作为程序员,我们花费大量时间编写程序来解决问题,自动化任务,收集数据,分析数据以及制定希望使我们的用户生活更加轻松的决策。 我们的大多数白天编程本质上都是实用的,因为世界是建立在截止日期和预算以及用户故事和冲刺之上的。
只有当孩子们开始入睡并且白天的烦恼逐渐消失在背景中时,我们才能编写夜间程序,探索程序,学习程序,存在性程序。
我因此编程! 不,这不是一个存在的类型,只是一个me脚的笑话。
临时多态性
在这样一个探索的夜晚,我想尝试Swift的即席多态性。 临时多态性听起来很有趣,但是每所小学都理解并使用它。
1 +1 = 2
1.0 + 1.0 = 2.0
加法运算符使我们可以将整数相加,将浮点数相加,将玩具熊甚至是Elsa娃娃相加。 小学孩子们知道加法不限于事物的一个特定子集。
当然,编程语言从来都不是那么直观的,通常来说,我们使用的语言很少支持即席多态。 例如,在OCaml中,我们有一个不同的运算符来添加整数和浮点数。
1 +1 = 2
1.0 +。 1.0 = 2.0
Java和.Net都不是更好,因为它们都不允许我们将相同的函数应用于不同的类型。
但是,Swift的精神是使专业人员和学龄儿童都可以访问编程,并且由于学童们了解临时的多态性,也许职业程序员也应该这样做。
协议编号{
关联类型A
静态函数Add(x:A,y:A)-> A
}
扩展名Int:Num {
静态函数Add(x:Int,y:Int)-> Int {return x + y}
}
扩展名Double:Num {
静态函数Add(x:Double,y:Double)-> Double {return x + y}
}
我们创建了一个Num协议,该协议允许我们将相同类型的实例相加在一起,我们在Int和Double类型中采用了该协议,并且由于孩子们的狂热热情,我们的专业人员终于可以编写一个可以正常工作的Add函数在Ints和Doubles上。
func Add (x:A,y:A)-> A其中AA == A {
返回A.Add(x:x,y:y)
}
令r1 = Add(x:1,y:2)
令r2 = Add(x:1.0,y:2.0)
//参数类型'String'不符合预期的类型Num
//让r3 = Add(x:“ hello”,y:“ world”)
打印(“ r1:\(r1)r2:\(r2)”)
很简单,这就是行动中的临时多态性,将函数应用于不同类型的参数。
深潜
一旦我们知道我们可以做某事,知道某事如何运作总是很有趣的。 当很多其他语言不能使用时,为什么我们可以将函数应用于不同类型的参数?
存在主义者很愚蠢,这当然引出了一个问题,到底存在主义是什么?
func Add(x:int)-> int {
//在这里在int上执行任何其他函数
返回x + x;
}
func Id (x:A)-> A {
返回A
}
func Add (x:A,y:A)-> A其中AA == A {
返回A.Add(x:x,y:y)
}
使用常规函数,我们可以访问基础类型以及与该类型关联的所有功能。
使用泛型函数,我们将失去对基础类型的所有访问权,该类型参数已被普遍量化,除返回类型或将其放入列表外,我们对类型没有太多处理。
使用将类型约束应用于类型的函数,我们仍然无法对类型执行任何操作,但是可以执行约束中指定的操作。 我们知道存在一种可以在Num中执行操作的类型,我们将此类型称为存在类型。
存在类型是基础值和我们可以对该值执行的操作的元组。
(值,运算:Num)
因此,尽管我们丢失了有关基础类型的所有信息,但是我们获得了可以对类型执行的一组操作。 违反直觉,通过限制类型,我们获得了功能。
有趣的是,我们不能通过类型本身的传统vtable,因为我们不能,我们不知道类型! 相反,我们遍历类型,并且通过约束中指定的临时操作表。
异构列表
因此,本着探索的精神,让我们看看我们可以对这些存在的类型做些什么,我们将尝试构建难以捉摸的异类类型列表。
协议显示{
关联类型A
静态函数Show(x:A)->字符串
}
扩展名Int:Show {
静态函数Show(x:Int)->字符串{return“ \(x)”}
}
扩展程序Double:Show {
静态函数Show(x:Double)->字符串{return“ \(x)”}
}
我们的Show协议允许类型将自身显示为字符串。
//协议显示只能用作一般约束
//让xs:[Show] = [1,1.0,2,2.0]
但是,我们不能将协议用作类型,而只能用作通用约束,因此即使直观上我们知道这是正确的,我们也禁止构建异构列表。
我们需要的是一种将异类值装箱为同质值的方法,同时始终保持在通用约束中捕获的功能。
Swift枚举是装箱类型的理想工具,因为它们允许枚举的情况携带相关的值并通过切换模式解构这些值。
枚举ShowBox {
个案编号(数字)
}
这是将值装箱的明显方法,但是我们已经知道不能将Num用作类型,而只能将其用作通用约束,所以让我们尝试以这种方式使用它。
枚举ShowBox {
案号(A)
}
//无法将Showbox 转换为预期的Showbox类型
让xs:[ShowBox] = [ShowBox.num(1),ShowBox.num(1.0)]
Doh,现在我们要隐藏的类型已经泄露,因为我们需要声明ShowBox 或ShowBox 的列表。 如果我们的类型泄漏出去,那么我们将被迫拥有一个同类列表。
因此,让我们采用强力方法并整理每种情况。
枚举ShowBox {
案例int(Int)
案例双(双)
}
让xs:[ShowBox] = [ShowBox.int(1),ShowBox.double(1.0)]
扩展ShowBox:Show {
静态函数show(x:ShowBox)->字符串{
切换x {
案例让.int(值):
返回Int.show(x:值)
案例让.double(value):
返回Double.show(x:值)
}
}
}
对于xs中的x {
打印(ShowBox.show(x:x))
}
这行得通,但是感觉很不对劲,因为编译器没有为我们做任何艰苦的工作。 我们必须手动管理框中允许的类型,而不是由Num约束为我们处理。
因此,让我们尝试包装Any类型。
枚举ShowBox {
案例编号(任意)
}
func SB (x:A)-> ShowBox其中,AA == A {
返回ShowBox.num(x)
}
func showSB (x:A)->字符串,其中AA == A {
返回A.show(x:x)
}
让xsa:[ShowBox] = [SB(x:1),SB(x:1.0)]
//无法以任何类型的形式调用showSB
扩展ShowBox:Show {
静态函数show(x:ShowBox)->字符串{
切换x {
大小写.num(value):
返回showSB(x:值)
}
}
}
但是,当我们解构我们的盒子时,我们失去了所有与扩展相关的操作。
我们真正需要做的是回到最初的解决方案,以下枚举将解决我们的问题。
枚举ShowBoxNum {
个案编号(数字)
}
具有关联类型的协议将需要扩展以允许在枚举中使用。 从上面的代码中我们可以看到,将我们的存在项装箱和拆箱是类型安全的,因为SB构造函数会强制所有类型符合Show协议。
因此,当我们在switch语句中解构枚举时,我们应该拥有该值以及相关的Show操作。 正如我们已经了解的那样,存在性实际上只是一个元组,对其进行解构应该可以揭示这一事实。
ew
希望这篇文章对Swift中的存在性有所启发。 存在性很重要,因为临时多态性很重要,因为小学生的直觉很重要,因为编程作为一门艺术应该会变得更容易。
主流语言或我们的时代继续以基于子类型多态性的非直观框架构建的运行时为我们的专业程序员加重负担。 很高兴看到时代可能会改变。