Re: Re: declaring integers
- To: mathgroup at smc.vnet.net
- Subject: [mg13125] Re: [mg13063] Re: declaring integers
- From: Joe Oswald <jaoswald at fas.harvard.edu>
- Date: Tue, 7 Jul 1998 03:44:28 -0400
- Sender: owner-wri-mathgroup at wolfram.com
On Sun, 5 Jul 1998, David Withoff wrote: > I didn't understand your discussion here, and the parts that I think I > understand don't seem to be correct. Perhaps if I describe my concerns > you can offer some clarifications. > > > When one types "a + b" in most languages (e.g. Fortran, C, Lisp "(+ a > > b)"), one is referring to a mechanical procedure where some item, named > > a, will be added to another item, named b. The nature of that procedure > > will depend on whether a and b are small-sized integers or > > floating-point numbers, or rational numbers, or large-sized integers. > > It also doesn't make much sense if a is a string and b is a number. > > I.e., it matters what "type" the "variables" are. > > That is what happens in Mathematica too. If you enter a+b, that invokes > a mechanical procedure that will depend on what a and b are. Well, no. If a and b don't have defined values, nothing gets added. The pattern is simply returned: In[1] := a + b Out[1] = a + b I.e. nothing is really "added". You might say that this is what addition "means" for things which are just symbols, but this misses my point, which is that no "machine instruction for addition" is invoked. Mathematica did some computation, but basically all to determine that *nothing* should be done. In Scheme, without any definitions (+ a b) --> error a not defined I.e. what happens is that Scheme *evaluates* all three subforms +, a, and b. Well, + evaluates to a built-in procedure <do addition>, but the evaluation of a fails because a doesn't have a value, so Scheme has to stop. >> In Mathematica, one actually is specifying a "pattern" Plus[a,b], > Perhaps I have misunderstood this too, but this doesn't seem right to > ... > The expression a+b isn't a pattern unless it is used in one of those > positions. OK. I was a bit cavalier in my use of the word pattern, which is why I enclosed it in quotes. But the fact remains that the computation above was simply a matter of pattern-matching, and didn't involve any addition. > > One particularly notable result of this pattern-matching behavior is > > that cited in the reference section of Wolfram's book, regarding > > Apply: > > Apply[Plus, g[a,b]] --> a + b > I'm not understanding your concern here either. Although the Apply > function in Mathematica and, for example, the "apply" function in Lisp > or Scheme, aren't identical, they are certainly similar, and the > differences don't represent anything fundamental. For example, Here I disagree. I believe the difference is something fundamental, and is essential to what "apply" is used for in Scheme. > (apply + '(2 2)) > > in Scheme is equivalent to > > Apply[Plus, {2, 2}] > > in Mathematica. Yes, but one would never use quote in the argument to apply! (I assume that you mean a forward quote ', and not a backquote, which as far as I know isn't used for Scheme, but in Common Lisp, refers to a more elaborate quoting that, in your example, gives the same thing.) This is a toy example, of course, because one would write (+ 2 2), just as in Mathematica one would write Plus[2,2] or 2+2 so you might say our disagreement is over superficial things. However, (apply + (2 2)) --> error 2 is not a function in (2 2) would be a closer simulation of Apply[Plus, {2, 2}], and you see the comparison starting to break down. > One of the differences is that the Apply function in Mathematica replaces > the head of the expression (the "car" in Scheme), while the "apply" > function in Scheme includes the "car" as one of the arguments. That is, > (apply + `(g a b)) becomes (+ g a b) while in Mathematica > Apply[Plus, g[a, b]] becomes Plus[a, b]. Additional arguments are > also treated differently, but not in a way that reflects fundamental > differences between the languages. There are fundamental differences. 1) There is no concept of "head" in Scheme!! In Mathematica, the concept of "head" is necessary for pattern matching to be used in the evaluation *all* expressions. "car" is a function which acts on *cons cells* NOT on code. The fact that one can transform cons cells to code by using "read" and "eval" does not make code and cons cells the same. The first element of a Scheme form (f a b) typed into the read-eval-pring loop is evaluated differently, but does not "stick around" as a head. It is replaced by a procedure, defined as the value corresponding to the symbol f, or returns an error if f is not defined. Furthermore, (apply + '(g a b)) --> error g is of type symbol in (+ g a b) is not a useful thing to write in Scheme, because "g" "a" and "b" are not things that can be added, since they are *symbols.* ...[I introduced an example using Apply]... > > Again, unless I have misunderstood something, I don't think this is true. > > While it is correct that (g a b) in (apply + (g a b)) will refer to > a procedure g that will be evaluated before applying +, the same > is true of Mathematica. That is, the g[a, b] in Apply[Plus, g[a, b]] > refers to a procedure g that will be evaluated in Mathematica before > Plus is applied. This evaluation order is used by all of the programming > languages that you mentioned, and is described for Mathematica in the > section 2.5 and appendix A.4 of the reference manual. But if g is not yet defined, Mathematica keeps it as a symbol forming the head of an expression. In Scheme, g is a symbol which is evaluated to, hopefully, return an internal procedure object. If not, you are asking the computer to **do** "g" where "g" is something it doesn't know how to do yet!! Notice that Mathematica doesn't assume you have asked it to *do* anything. g[a,b] will simply give you g[a,b], if g, a, and b has no patterns defined. > Also, since (g a b) might be quoted, as in (apply + `(g a b)), it isn't > entirely true that this procedure will always be evaluated in Scheme. > The purpose of quoting in Scheme is to inhibit evaluation. g is a symbol. '(g a b) is a textual representation of a list of three symbols, g, a, and b. Adding symbols, which is what you are doing in this example, is not defined in Scheme. You seem to me to be confusing the textual representation of code in Scheme with the code that it represents. In an unquoted form which has the *textual representation* (g a b), the symbol g is evaluated to an internal function object, a is evaluated to return some number, and b is evaluated to return some number, and then the internal function *named* g is applied to the arguments *named* by a and b. It is very important to make and keep the distinction between the name and the thing named. This is fundamental in Scheme, as well as philosophy. A similar > effect can be achieved in Mathematica using Unevaluated. That is, > Apply[Plus, Unevaluated[g[a, b]]] will apply Plus to the arguments a and > b even if g[a, b] might otherwise evaluate to something other than itself. > > > I think the desire to "declare variable types" arises from an > > underlying desire to program Mathematica in a procedural or functional > > way. However, Mathematica is, at its heart, a pattern-matching system. > > Whether that is better or worse than a functional or procedural style > > depends on both the particular problem you are trying to solve, and on > > your particular aesthetic approach to problem solving. > > Actually, Mathematica at its heart is a functional language, much like > Lisp or Scheme. Pattern matching is the mechanism that is used for > locating procedures, but it isn't used in any non-trivial way in the > examples that you mentioned. Your comments seem to be about evaluation > order rather than about pattern matching, but evaluation order is > essentially the same in Mathematica as it is in most other languages, > and the connection between these things and variable typing escapes me. If you compare Mathematica's evaluation order to that of Scheme, one find that Mathematica "keeps evaluating until nothing changes." [Or at least tries to, but makes some heuristic guesses to help performance], whereas in Scheme, there is no similar notion. Also, most function definitions in Mathematica are defined using patterns. E.g. g[a_,b_] := a Minus[a,b] *is* a pattern, so pattern matching is used even when you just want functional code. (define g (lambda (a b) (* a (- a b)))) doesn't invoke a pattern matcher when you write (g 3 5) in Scheme. Here is a demonstration of what I found confusing, comparing Scheme and Lisp. ; example in Scheme (define g (lambda (first second) (list first second))) (define a 5) (define b 3) (apply - (g a b)) --> 2 ; because (g a b) returns a list (5 3) (define g-reversed (lambda (first second) (list second first))) (apply - (g-reversed a b)) --> -2 ; because g-reversed returns a list (3 5) (* In Mathematica *) a = 5; b = 3; Apply[Minus, g[a,b]] (* oops, haven't defined g yet *) --> 2 (* well, I don't notice *) Apply[Minus, g-reversed[a,b]] --> 2 (* Hey, what's going on??? g and g-reversed give the same answer. OHHHH!!! I don't have a definition for g-reversed yet. *) g-reversed[first_,second_] := List[second,first]; Apply[Minus, g-reversed[a,b]] --> -2. Of course, these are toy examples, but if the Apply hadn't been typed at the interaction level, but was hidden behind some other code, you might have a very hard time figuring out that you had forgotten to define g or g-reversed, when your code didn't work as you expected!!! It is interesting that the example given in the reference for Apply is Apply[Plus, g[a,b]] --> a + b When this is *completely* contrary to how "apply" is used in Scheme. In fact, you *cannot* write this in Scheme. And the reason is far deeper than "Scheme apply discards the head, while Mathematica apply keeps the head". If g isn't defined, you aren't specifying a computation with g[a,b], but Mathematica doesn't mind. That is an advantage if you want to do symbolic mathematics. In Scheme, however, you are writing functions to do computations, not create symbolic patterns. I think this represents the essential difference between Mathematica and a functional language like Scheme: 1) The Apply routine was defined to superficially resemble Scheme "apply" but the semantics in the example don't exist in Scheme. What apply does in Scheme involves *INTERNAL PROCEDURE OBJECTS* whereas in Mathematica, it does surgery on the internal list form of the things it is passed. By the time Scheme's apply gets to work, the external textual representation of a list does not exist any more. 2) The distinction between the internal representation of a thing and the external representation of a thing is very different in Lisp/Scheme and in Mathematica. That is essentially what I mean when I say that Mathematica is always using "patterns". The representation of g[a,b] is very literally something that is labelled with a head g and has a two element tail containing a and b. In Scheme (g a b) *doesn't* represent any particular internal representation. It describes a *computation*. --Joe Oswald (Aside) Incidentally, many people with only a superficial knowledge of Scheme or Lisp miss the point of (2). They get the idea that "a program in Lisp/Scheme is a list of atoms." In fact, Lisp code is written on the page in the same way that lists of atoms would be written on the page; to some extent, this is superficial. In C, a C program is written as a text file. The compiler takes that text file and changes it to executable code. But the text and the code are different things! One could write a different looking text file in Pascal or in Scheme that could result in the same code. Of course, the resemblance of Lisp code and Lisp data types makes it easy for Lisp programs to write other Lisp programs. C programs that write other C programs can exist, but they are seen as obscure. In Lisp, they are seen as quite ordinary. --Joe