Julia 是怎么火起来的?( 三 )


这里的方法本身并不存在 , 而是与所使用的原料相关联的 。 现在 , 添加新的原料很容易 。 如果我们的菜谱书的修订版需要一个鱼的菜谱 , 我们只需编写处理鱼的程序 , 并将这些信息与鱼打包成一个整洁的包就可以了 。
Julia 是怎么火起来的?文章插图
我们可以在书中添加原料 , 而不必更改任何现有的菜谱 , 这些菜谱仍然完全可用 。
但是 , 如果我想在菜谱书中加入一种新的技术呢?如果修订后的版本是为了向你展示你可以用搅拌机做些什么呢?
如果不改变我们已经完成的工作 , 就不能添加新技术 , 因为现在的方法是包装在原料里面的 。 为了添加一种新的方法 , 我们需要处理之前整洁的包 , 并修改我们已经编写过的方法 。
这两种组织菜谱的方式类似于两种计算机语言 。 围绕过程组织的菜谱书就像是用函数式语言编写的 , 而围绕原料组织的菜谱书就像是面向对象语言编写的 。 在一种情况下 , 你不得不重写现有过程来添加新的成分;而在另一种情况下 , 不重写现有工作就无法添加新过程 , 这就是表达式问题 。 再回顾一下计算机语言设计中的术语:在函数式语言中 , 你可以添加新的函数 , 而不必接触现有的函数;但添加新的数据类型则意味着要重写现有的函数 。 对于面向对象语言 , 你可以随意添加新的数据类型 , 但如果你希望添加新的函数 , 则需要重新设计现有的对象 。
表达式问题意味着 , 使用这两种传统范式中的任何一种 , 都存在障碍 , 无法将一个包(菜谱书)扩展到新领域:以新的方式组合现有包的障碍甚至更多 。 在重用和组合现有代码时 , 一个包能否进行扩展而不改变已有的内容是至关重要的 。 这使得库作者可以用一种通用方式编写代码 , 而不必为每个应用程序维护特定版本 。 它允许最终用户扩展现有的模块 , 而无需引入新的错误 , 这些错误在修改时将不可避免地出现;而且也不需要维护每个库的私有版本 。
多分派 , 也可以通过比喻来解释显然 , 如果有一种方法能够组织菜谱书 , 让它可以自由扩展以处理新的原料 , 以及通过向菜谱书中添加新的方法 , 而不改变已经编写好的内容 , 那么这将是一个巨大的优势 。 与其严格按照方法或成分来组织这本书 , 也许还有一种更通用、更灵活的方式 。 代表这种新方式的图片可能看起来如下图所示 。
Julia 是怎么火起来的?文章插图
上图意在表明方法与原料之间的自由关联;一种方法不从属于另一种方法 。 用不同颜色重复出现的齿轮应该表明 , 我们通常会创建现有函数的变体 , 以对不同的成分集进行操作 , 而不是不相关的函数的随机集合 。
例如 , 我们假定的可扩展的菜谱书可能包含炸鸡制作的过程 。 如果我们想要通过增加炸鱼程序来扩充这本菜谱书 , 就不需要从头开始 。 毕竟 , 我们的想法是扩充这本菜谱书 , 而不是撰写一本新的菜谱书 。 我们可以编写炸鱼程序 , 指导读者继续进行与炸鸡制作相同的操作 , 但要使用稍微高一点的温度 , 并在 10 分钟后出锅 , 而不是 30 分钟 。
在这个思想实验中 , 还可以通过想象掌握(现在)三种组织菜谱书的方法 , 来确定每种情况下目录看起来可能是什么样的 。 “函数式”版本的目录看起来可能如下所示:

  • 第一章 煎炸
* 鸡肉
* 鱼
  • 第二章 蒸煮
* 鸡肉
* 甜菜
请注意 , 我们是如何通过添加新章节来添加新方法的 , 而添加新原料就意味着更改现有章节 。
“面向对象”版本的部分目录看起来可能如下所示:
  • 第一章 鸡肉
* 煎炸
* 蒸煮
  • 甜菜
* 蒸煮
* 炒
现在 , 我们可以通过添加新的章节来添加新的成分 , 但添加新的成分意味着要去修改已经完成的章节 。
我们的第三种方法 , 也就是我们最大限度地可扩展的菜谱书 , 其目录可能如下图所示:
  • 第一章 炸鸡
  • 第二章 蒸鸡
  • 第三章 煮甜菜
  • 第四章 炒甜菜
显然 , 适用于任何成分或成分集的任何程序都可以作为新章节添加 , 而不必修改现有章节 。 你可以自由添加新的方法 , 也可以添加新的成分 。
与前两个版本相比 , 这个最终版本可能看起来没有组织性 。 但在实际的元件库中 , 方法和成分之间的关系将是库结构的一部分 。 在我们的菜谱书比喻中 , 鸡肉和鱼肉可能都是肉的子集 , 草莓和樱桃可能被归类为红色水果的子集 , 煎炸和炒可能被视为更通用方法的变体 , 等等 。