[Scheme] Values are always pointers to objects

Values

You can define a variable in Scheme using a define:

(define my-variable 5)

This tells Scheme to allocate space for my-variable, and initialize that storage with the value 5.In Scheme, you always give a variable an initial value, so there's no such thing as an uninitialized variable or an unininitialized variable error.

Scheme values are always pointers to objects, so when we use the literal 5, Scheme interprets that as meaning a pointer to the object 5. Numbers are objects you can have pointers to, just like any other kind of data structure. (Actually, most Scheme implementations use a couple of tricks to avoid pointer overheads on numbers, but that doesn't show up at the language level. You don't have to be aware of it.)

After the above definition, we can draw the resulting situation like this:

    +-------+ 
foo |   *---+--->5 
    +-------+

In the picture, the box represents the fact that Scheme has allocated storage for a variable. The name foo beside the box means that we've given that storage the name foo. The arrow says that the value in the box is a pointer to the integer object 5. (Don't worry about how the integer object is actually represented. It doesn't really matter.)

Pointer semantics

As I said earlier, all values are conceptually pointers to objects on a heap, and you don't ever have to explicitly free memory.

By "object," I don't necessarily mean object in the object-oriented sense. I just mean data objects like Pascal records or C structs, which can be referenced via pointers and may (or may not) hold state information.

Conceptually, all Scheme objects are allocated on the heap, and referred to via pointers. This actually makes life simple, because you don't have to worry about whether you should dereference a pointer when you want to use a value--you always do. Since pointer dereferencing is uniform, procedures always dereference a pointer to a value when they really use the value, and you never have to explicitly force the dereferencing.

For example, the predefined Scheme procedure + takes two pointers to numbers, and automatically dereferences both pointers before doing the addition. It returns a pointer to the number that's the result of the addition. So when we evaluate the expression (+ 2 3) to add two to three, we are taking a pointer to the integer 2 and a pointer to integer 3, and passing those as arguments to the procedure +. + returns a pointer to the integer 5.

When you think about it, it doesn't make any sense to change the value of an integer, in a mathematical sense. For example, what would it mean to change the integer 6's value to be 7? It wouldn't mean anything sensible, for sure. 6 is a unique, abstract mathematical object that doesn't have any state that can be changed---6 is 6, and behaves like 6, forever.

What's going on in conventional programming languages is not really changing the value of an integer-- it's replacing one (copy of an) integer value with (a copy of) another. That's because most programming languages have both pointer semantics (for pointer variables) and value semantics (for nonpointer variables, like integers). You make multiple copies of values, and then clobber the copies when you perform an assignment.

In Scheme, we don't need to clobber the value of an integer, because we get the effect we want by replacing pointers with other pointers. An integer in Scheme is a unique entity, just as it is in mathematics. We don't have multiple copies of a particular number, just multiple references to it. (Actually, Scheme's treatment of numbers is not quite this simple and pretty, for efficiency reasons I'll explain later, but it's close.)

As we'll see later, an implementation is free to optimize away these pointers if it doesn't affect the programmer's view of things--but when you're trying to understand a program, you should always think of values as pointers to objects.

It is sometimes said that languages like Scheme (and Lisp, Smalltalk, Eiffel, and Java) "don't have pointers." It's at least as reasonable to say that the opposite is true--everything's a pointer. What they don't have is a distinction between pointers and nonpointers that you have to worry about.

The Implementation and Optimization

You might think that making every value a pointer to an object would be expensive, because you'd have to have space for all of the pointers as well as the things they point to, and you'd have to use extra instructions to access things via pointers.

Everything's a pointer at the language level--i.e., from the programmer's point of view--but a Scheme system doesn't actually have to represent things the way they appear at the languages level.

Most Scheme implementations optimize away a lot of pointers. For example, it's inefficient to actually represent integer values as pointers to integer objects on the heap. Scheme implementations therefore use tricks to represent integers without really using pointers. (Again, keep in mind that this is just an implementation trick that's hidden from the programmer. Integer values have the semantics of pointers, even if they're represented differently from other things.)

Rather than putting integer values on the heap, and then passing around pointers to them, most implementations put the actual integer bit pattern directly into variables--after all, a reasonable-sized integer will fit in a machine word.

A short value (like a normal integer) stored directly into a variable is called an immediate value, in contrast to pointers which are used to refer to objects indirectly.

The problem with putting integers or other short values into variables is that Scheme has to tell them apart from each other, and from pointers which might have the same bit patterns. The solution to this is tagging. The value in each variable actually has a few bits devoted to a type tag which says what kind of thing it is--e.g., whether it's a pointer or not. The use of a few bits for a tag slightly reduces the amount of storage available for the actual value, but as we'll see next, that usually isn't a problem.

It might seem that storing integer bit patterns directly in variables would break the abstraction that Scheme is supposed to present--the illusion that all values are pointers to objects on the heap. That's not so, though, because the language enforces restrictions that keep programmers from seeing the difference.

Objects on the Heap

Most Scheme objects only have fields that are general-purpose value cells---any field can hold any Scheme value, whether it's a tagged immediate value or a tagged pointer to another heap-allocated object. (Of course, conceptually they're all pointers, so the type of a field is just "pointer to anything.")

So, for example, a pair (also known in Lisp terminology as a "cons cell") is a heap-allocated object with two fields. Either field can hold any kind of value, such as a number, a text character, a boolean, or a pointer to another heap object.

The first field of a pair is called the car field, and the second field is called the cdr field. These are among the dumbest names for anything in all of computer science. (They are just a historical artifact of the first Lisp implementation and the machine it ran on.)

Pairs can be created using the procedure cons. For example, to create a pair with the number 22 as the value of its car field, and the number 15 as the value of its cdr field, you can write the procedure call (cons 22 15).

The fields of a pair are like variable bindings, in that they can hold any kind of Scheme value. Both bindings and fields are called value cells---i.e., they're places you can put any kind of value.

In most implementations, each heap-allocated object has a hidden "header" field that you, as a Scheme programmer, are not supposed to know about. This extra field holds type information, saying exactly what kind of heap allocated object it is. So, laid out in memory, the pair looks something like this:

      +-----------+ 
header| <PAIR-ID> | 
      +===========+ 
   car|     *-----+----->22
      +-----------+ 
   cdr|     *-----+----->15 
      +-----------+

In this case, the car field of the pair (cons cell) holds the integer 22, and the cdr field holds the integer 15. The values stored in the fields of the pair are drawn as arrows, because they are pointers to the numbers 22 and 15.

(The actual representation of these values might be a 30-bit binary number with a two-bit tag field used to distinguish integers from real pointers, but you don't have to worry about that.)

Suppose we have a top-level variable binding for the variable foo, and its value is a pointer to the above pair. We would draw that situation something like this:

                             +---------+ 
    +---------+        header| <PAIR>  | 
foo |    *----+------------->+=========+ 
    +---------+           car|    *----+---->22 
                             +---------+ 
                          cdr|    *----+---->15 
                             +---------+

Most other objects in Scheme are represented similarly. For example, a vector (one-dimensional array) is typically represented as a linear array of value cells, which can hold any kind of value.

Even objects that aren't actually represented like this can be thought of this way, since conceptually, everything's on the heap and referred to via a pointer.


Reference

An Introduction to Scheme and its Implementation

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • **2014真题Directions:Read the following text. Choose the be...
    又是夜半惊坐起阅读 9,355评论 0 23
  • 晚上爸问我联系她怎么样了,说话有没有更好一些。我随口一说,反正就是联系咯,哪有什么好的。然后他就挂了电话。 再打给...
    扶路阅读 218评论 0 0
  • 我叫梅茜,我拼命写字的理由是,当你看见狗狗的时候,希望你能想起我,觉得他是你的好朋友,微笑着拍拍他的脑袋。希望这些...
    秀秀xiu阅读 451评论 1 2
  • 有时候事情殷勤过头了,反而会带来不想要的结果。 本来顶简单的一件事,确认好会议日期就好了,照原计划返回即可。可是不...
    深夜芝士阅读 258评论 0 0
  • 失眠越发严重。怪不了它,实在是拖延症所致。 睡前习惯性的会刷一下充满代购的朋友圈,点开一下图,想想搭配,然后关上;...
    夏天的甜橙阅读 204评论 0 0