12

我正在研究 Smalltalk 反射,我想知道是否有可能像在 Ruby 中那样扩展单个对象。我的意思是一个只有特定对象响应的选择器。

这是一些说明我的意思的 Ruby 代码。澄清一下:在 Ruby 中,这会为此对象打开一个虚拟类,并使用新定义对其进行扩展。这里最重要的部分是类定义没有任何变化!

o = Object.new
o.instance_eval {def foo;puts "foo";end}
o.foo #=> "foo"

#however this will fail:
m = Object.new
m.foo #=> NoMethod error

更具体地说,我的问题是这在标准 Squeak/Pharo 或其他 smalltalk 实现中是否可能,而无需添加大量结构或代码来实现这一点。换句话说,Smalltalk 中存在常规反射功能。

例如,可以添加方法、删除方法、将新代码编译到类中、更改实例变量等等,但我还没有找到扩展单个对象的方法。

Test addInstVarNamed: #var.
Test compile: 'var ^var'.
t:= Test new.
Test instVarNames.
t instVarNamed: #var put: 666. 
t var. #=> 666

如果答案是否定的,请解释原因。我不是要解决这个问题,而是要理解为什么它不在 smalltalk 中。

4

6 回答 6

11

在 Smalltalk 中具有特定于实例的行为基本上涉及更改指针类和原始调用。Bob Hinkle、Vicki Jones 和 Ralph E. Johnson 发表于 Smalltalk 报告,第 2#9 卷,1993 年 7 月至 8 月的“调试对象”文章包含所有解释。

我已经从 Bob Hinkle 于 1995 年发布的 VisualWorks 2.0 版本中移植了最初的轻量级类代码。VisualWorks 源代码包括分为三个包的代码,分别名为“ParameterizedCompiler”“Breakpoint”“Lightweight”。这种划分的原因是出于对具有单独和可重用功能的渴望。他们都在 OOP 期刊上发表了单独的文章。

我的Squeak/Pharo 端口包含一个“实例浏览器”,它基于OmniBrowser(以及此处的更多文档)框架,它允许您通过经典的 Smalltalk 浏览器 UI 浏览和修改添加轻量级行为的实例。它可以在最新的 Squeak 4.x 版本中运行,几乎不需要努力。不幸的是,Pharo 的基础设施从 <= 1.2 版本发生了很大变化,因此它可能需要一些工作才能使用最新版本。

修改了实例的实例浏览器

在 VisualWorks 中,由于 VisualWorks GUI 从 2.0 到 7.3 的深刻变化,大部分用于管理轻量级类的工具都没有包括在内。如果有人有兴趣,我可以上传大众 7.3 的包裹

VisualWorks 中的实例浏览器

测试轻量级类功能的基本脚本是:

| aDate |
aDate := Date today.
aDate becomeLightweight.
aDate dispatchingClass 
        compile: 'day ^42' 
      notifying: nil 
         ifFail: [self error].
aDate day inspect
于 2013-01-07T19:16:31.723 回答
8

在 Smalltalk 中,没有内置的方式以这种方式执行特定于实例的行为。Smalltalk 坚持每个对象属于一个类的原则,其行为和状态形状取决于类。这就是为什么您可以轻松更改类(添加 inst 变量、编译新方法等),但这意味着将行为更改为所有实例。但是,有不同的方法(根据 Smalltalk 风格)来实现特定于实例的行为,例如轻量级类,其想法是为特定实例创建一个特殊的(轻量级)类,并用自定义的类替换原始类一。因此,每个“特殊”实例都有一个特殊的类。Digitalk St 中的 AFAIK 调度机制更加灵活,可以轻松实现基于实例的行为(参见第 4 个链接)。我将在这里留下一些您可能会觉得有用的链接:

高温高压

编辑: Hernan 发布的链接(Hinkle、Jones & Johnson 的“调试对象”)是我所指的但找不到的链接。

于 2013-01-07T13:17:14.327 回答
5

Squeak Etoys 大量使用对象特定的行为和状态。这被实现为“uniclasses”。当你为一个 Etoys 对象(Player 类的实例)创建脚本时,该对象的类将被更改为“uniclass”,即 Player 的唯一子类,它可以有自己的方法(对应于 Etoys 脚本)和实例变量(对应于 Etoys 用户变量)。

其他基于 Squeak 的项目使用“匿名”单类,这些单类未在其超类中列为子类。这意味着它们几乎是不可见的,因为它们不会出现在系统浏览器中,例如(而 Etoys 风格的 uniclass 确实出现在浏览器中)。

于 2013-01-08T18:01:44.803 回答
3

一位同事一直在为 Pharo Smalltalk 开发一个新的反射 API,称为Bifrost。您可以查看Bifrost 项目的页面。

他的方法将特定于实例的适应作为其核心。一切都是通过将元对象绑定到常规对象以适应它们而发生的。较低级别的元对象可以组合成较高级别、粗粒度的元对象,这些元对象定义了合理的适应,例如,将测量对目标对象的每次调用所花费的时间的分析元对象。

于 2013-01-08T07:56:46.777 回答
2

正如@ewernli 指出的那样,Bifrost 基本上使 Smalltalk 成为以对象为中心的反射系统。所有反射更改首先针对对象,而不是具有与类的混合机制。您仍然可以在以对象为中心的反射之上进行所有传统的类反射。我认为与这种新方法相关的是我们发现的一些应用程序,它们改进了我们开发和感知实时系统的方式:

以对象为中心的调试通过专注于对象并允许开发人员保持与活动对象交互而不必在源代码级别插入条件断点,彻底改变了我们的调试方式。

人才是可组合的动态重用单元,就像特征一样,但用于对象。还有很多应用。

于 2013-01-08T09:41:01.520 回答
1

据我所知,在 Ruby 中,方法字典附加到对象上。

在 Smalltalk 中,方法字典绑定到一个Class对象,因此,在一个普通的 Smalltalk 图像中,您不能编写类似 eigenclass 的东西。

话虽如此,有几个原型库:这个问题的答案提到了很多。

于 2013-01-07T13:26:47.473 回答