[comp.lang.functional] Help needed with behaviour of SML

surendar@ENUXHA.EAS.ASU.EDU (Surendar Chandra) (04/27/91)

Hi,
	This is my first posting on this group. I do not even know if this
question is appropriate in this group. Anyway, I am currently implementing
a subset of the STandard ML as part of my course project and find the
behavior of SML quite confusing in the following case.

> val a = 1;
> a ;
| 1 : int ;
> fun b c = c * a ;
> b 2 ;
| 2 : int ;
> val a = 3;
> b 2 ;
| 2 : int ;
^^^^^^^^^^ Why does it do it like this.. Why doesn't it look up at the current
value of the variable 'a'. Why does it store the current value of 'a' in the
function during compilation itself? WHat is the logic behind this? I could
not lay my hands on a good reference for SML and most of the books don't seem
to talk about this at all.

Thanks in advance for any help,
surendar 

============================================================================
Surendar Chandra				602-894-2614
surendar@enuxva.eas.asu.edu 

tmb@ai.mit.edu (Thomas M. Breuel) (04/27/91)

In article <9104262016.AA28063@enuxha.eas.asu.edu> surendar@ENUXHA.EAS.ASU.EDU (Surendar Chandra) writes:
	   This is my first posting on this group. I do not even know if this
   question is appropriate in this group. Anyway, I am currently implementing
   a subset of the STandard ML as part of my course project and find the
   behavior of SML quite confusing in the following case.

   > val a = 1;
   > a ;
   | 1 : int ;
   > fun b c = c * a ;
   > b 2 ;
   | 2 : int ;
   > val a = 3;
   > b 2 ;
   | 2 : int ;
   ^^^^^^^^^^ Why does it do it like this.. Why doesn't it look up at the current
   value of the variable 'a'. Why does it store the current value of 'a' in the
   function during compilation itself? WHat is the logic behind this? I could
   not lay my hands on a good reference for SML and most of the books don't seem
   to talk about this at all.

There are several reasons. Here are some simple ones:


One is that otherwise it would be difficult to preserve type
correctness:

> val a = 1;
> fun f x = x * a;
> val a = "abc";   (* ??? *)

Not allowing you to define the variable a to be of a different type
would make program development a hassle (you couldn't ever change your
mind about the type that you gave some global variable). But if you
do allow the user to change the type "destructively", none of the
original type checking is still valid.

Another reason is that allowing bindings to change would inhibit
lots of compiler optimizations.

If you want a variable that can be modified, use ref:

> val a = ref 1;
> fun f x = x * !a;
> a := 3;

					Thomas.

bennet@ogicse.ogi.edu (Bennet Vance) (04/27/91)

In article <TMB.91Apr26222738@volterra.ai.mit.edu> tmb@ai.mit.edu (Thomas M. Breuel) writes:
>In article <9104262016.AA28063@enuxha.eas.asu.edu> surendar@ENUXHA.EAS.ASU.EDU (Surendar Chandra) writes:
>   ...[I] find the behavior of SML quite confusing in the following case.
>
>   > val a = 1;
>   > a ;
>   | 1 : int ;
>   > fun b c = c * a ;
>   > b 2 ;
>   | 2 : int ;
>   > val a = 3;
>   > b 2 ;
>   | 2 : int ;
>   ^^^^^^^^^^ Why does it do it like this.. Why doesn't it look up at the current
>   value of the variable 'a'...?
>
>There are several reasons. Here are some simple ones...


Here is another one, or another way to think about it:

At "val a = 3", you are not changing the binding of "a" from 1 to 3.
Rather, you are introducing a new variable "a" that happens to have
the same name as a previously defined variable.  Just as when you
introduce in an inner scope a variable that hides a variable in an
outer scope, here also you do not make the previous variable binding
go away, but you do make it invisible.  The only difference is that in
an interactive SML session, you will never come out of the new scope,
and the old "a" will never be seen again--except when you invoke
functions like "b" that refer to it.



Bennet

mfx@sphinx.opal (Markus Freericks) (04/27/91)

In article <9104262016.AA28063@enuxha.eas.asu.edu> surendar@ENUXHA.EAS.ASU.EDU (Surendar Chandra) writes:
> > val a = 1;
> > a ;
> | 1 : int ;
> > fun b c = c * a ;
> > b 2 ;
> | 2 : int ;
> > val a = 3;
> > b 2 ;
> | 2 : int ;
> ^^^^^^^^ Why does it do it like this.. Why doesn't it look up at the current
> value of the variable 'a'. Why does it store the current value of 'a' in the
> function during compilation itself? WHat is the logic behind this? I could
> not lay my hands on a good reference for SML and most of the books don't seem
> to talk about this at all.

ML is a 'real', i.e. 'single-assignment' language. Once a variable is defined,
it cannot change its value. So, when you define a=1, then a=3, the second
time you create a new variable hiding the old one. So, ML not being
dynamically scoped, the function b has as its closure the environment at
the time of its creation, i.e. seeing only the 'old' a.
You need not think that that value of a is compiled into the function b
(although this could be done); its simply a consequence of not being able
to alter something once its created.

Hope that helps,

Markus



--
Markus Freericks	phone: +49-30-4034110	
Oranienburger Str. 142	email: mfx@opal.cs.tu-berlin.de	
1000 Berlin 26 FRG	or mfx%tubopal@DB0TUI11.BITNET	

sfk@otter.hpl.hp.com (Steve Knight) (04/30/91)

surendar asks about the top-level environment in SML:
> Why doesn't it look up at the current
> value of the variable 'a'. Why does it store the current value of 'a' in the
> function during compilation itself? WHat is the logic behind this? 

Folks coming from other interactive environments are usually surprised by this
unfortunate behaviour.  Roughly speaking, being able to redefine existing
top-level identifiers allows the possibility of introducing type-errors in
previously type-checked functions.  

There are other arguments which are based on the uniformity of top-level and
all other levels.  I regard these as spurious.

Steve

kers@hplb.hpl.hp.com (Chris Dollin) (05/02/91)

Someone writes:

   >   ...[I] find the behavior of SML quite confusing in the following case.
   >
   >   > val a = 1;
   >   > a ;
   >   | 1 : int ;
   >   > fun b c = c * a ;
   >   > b 2 ;
   >   | 2 : int ;
   >   > val a = 3;
   >   > b 2 ;
   >   | 2 : int ;
   >   ^^^^^^^^^^ Why does it do it like this.. Why doesn't it look up at the current
   >   value of the variable 'a'...?
   >

Here's another one. SML is an interactive incremental development language.
This means that when you find an error in a function definition, all you need
to do is to redefine the function and ....

Funny, this useful feature of interactive langauges seems to have gone wrong.
I'll just have to load all the code I've compiled since then. I wonder what
modules that covers?

The ML definition is fine for batch use, but for incremental development it's a
pigs trotter.

[*Why* is ML like this? Because it's a functional language, ie, it has no
assignment, so values don't change. Apart, of course, from arrays, and
references. Pah.]

--

Regards, Kers.      | "You're better off  not dreaming of  the things to come;
Caravan:            | Dreams  are always ending  far too soon."

rockwell@socrates.umd.edu (Raul Rockwell) (05/05/91)

Shail Aditya Gupta writes:
> ML's interactive script can be viewed ...  with each statement being
> defined in the lexical scope of all the previous ones. If you view
> it this way ... it is logical and reasonable that the second
> definition of "a" shadows the first, but the first had been "closed
> over" in the lexical closure environment of the function "b", which
> can never be destroyed, only further shadowed...

Chris Dollin writes:
> The ML definition is fine for batch use, but for incremental
> development it's a pigs trotter.

> [*Why* is ML like this? Because it's a functional language, ie, it
> has no assignment, so values don't change. Apart, of course, from
> arrays, and references. Pah.]

triple pah!

(1) dynamic shadowing is just another name for changing values

(2) you shouldn't be able to re-assign values in a constant array (can
    you??)

(3) if you want to change a value referred to from a function, you
    probably ought to use a reference of some sort :-)

Raul Rockwell

Disclaimer: I'm a J person, not an ML person.  I speak with a mild
    ignorance of the mechanics of ML.

wg@opal.cs.tu-berlin.de (Wolfgang Grieskamp) (05/05/91)

kers@hplb.hpl.hp.com (Chris Dollin) writes:

 >Here's another one. SML is an interactive incremental development language.
 >This means that when you find an error in a function definition, all you need
 >to do is to redefine the function and ....

 >Funny, this useful feature of interactive langauges seems to have gone wrong.
 >I'll just have to load all the code I've compiled since then. 

Exactly.

Or is there someone working professional with SML who takes advantage of
the interactive system? Personal (relatively little) experiences with the
SML environment is that one soon writes batch files to work around it.

This is irritating, since the design of the language is highly 
based on this "interactive" window dressing. 

It seems in principal that the functional and incremental development 
paradigms do no coincide well. But a meta language level which 
manages the version problem may close the gap.

--
Wolfgang Grieskamp 
wg@opal.cs.tu-berlin.de tub!tubopal!wg wg%opal@DB0TUI11.BITNET

tmb@ai.mit.edu (Thomas M. Breuel) (05/06/91)

   [lots of articles complaining about the fact that you can't
   change top-level bindings in ML]

For example, wg@opal.cs.tu-berlin.de writes:
   Or is there someone working professional with SML who takes advantage of
   the interactive system? Personal (relatively little) experiences with the
   SML environment is that one soon writes batch files to work around it.

I use SML interactively: source files in one (Emacs) window, SML in
another window. I usually have one source file in which I write and
debug new functions, and, eventually, they get wrapped up into a new
module. Working this way, I usually only have to recompile the
function I'm currently debugging (C-c C-c). If I have to fix older
functions, I simply recompile the whole file (C-c C-u), or recompile
them in the right order. This is not very different from CommonLisp
development.

I like the behavior of the SML top-level. I think any attempt at
allowing the user to change the binding, or, worse yet, type of an
already existing binding, would introduce lots of complications and
make the top-level much harder to understand. It would also keep the
compiler from making lots of optimizations that it currently can make.

If recompilation speed really turns out to be an issue, an
implementation of SML could, within the current language semantics,
record dependencies during the first compilation and avoid
recompilation of unchanged and independent values even if you just
told it "compile everything".

Better compilers, not worse semantics, are the answer to this issue.

nico@cs.ruu.nl (Nico Verwer) (05/06/91)

In <KERS.91May2084808@cdollin.hpl.hp.com> kers@hplb.hpl.hp.com (Chris Dollin) writes:

>   >   Why does it do it like this.. Why doesn't it look up at the current
>   >   value of the variable 'a'...?
>
>Funny, this useful feature of interactive langauges seems to have gone wrong.
>I'll just have to load all the code I've compiled since then. I wonder what
>modules that covers?
>The ML definition is fine for batch use, but for incremental development it's a
>pigs trotter.

No, it is very fortunate that ML does NOT have "dynamic scope rules". One
point has been made before, i.e. type safety.
But even if the system would enforce some form of type safety, then still the
ML way of top level definitions is preferable, and I wished TeX would work
this way!

To illustrate this, I shall not give an example in TeX, but in functional
language style.
Suppose we have a functional language with a "standard prelude", which groups
several useful functions, like sin, cos, tan, etcetera.
I am writing a 3-D space invader game, using a function library which maps 3-D
objects onto the 2-D display. This library uses functions like sin and cos.
Somewhere in this program I define functions
sin (for "show invader number") and
cos (for "crash orbital ship").
My program will not work, because the math library will now use these
functions instead of the well-known trigonometric functions. Probably, in this
case I will soon find the error, but similar errors can be very hard to find.

I think this `feature' is one of the worst of TeX. It won't allow me to define
my own \ifx or \def, because the old definitions will be superseded by these.
(Try \renewcommand{\def}{...} in LaTeX. It generates an error which is not
easy to understand for the LaTeX novice.)
-- 
Nico Verwer                                       | nico@cs.ruu.nl
Dept. of Computer Science, University of Utrecht  | phone: +31 30 533921
p.o. box 80.089, 3508 TB Utrecht, The Netherlands | fax:   +31 30 513791

db@cs.ed.ac.uk (Dave Berry) (05/06/91)

In article <KERS.91May2084808@cdollin.hpl.hp.com> kers@hplb.hpl.hp.com (Chris Dollin) writes:
>
>SML is an interactive incremental development language.  This means that
>when you find an error in a function definition, all you need to do is to
>redefine the function and ....

ML was developed as a language for manipulating values interactively.
The original ML let you manipulate theorems.  Later versions let you
manipulate any values, such as windows or trees.  ALthough ML also lets
you test code incrementally, I've always felt that this was a secondary
goal (I may be wrong, of course; I can't read the designers' minds).

Code development in ML is a big improvement over a batch language, even
with a raw environment.  You can test any function interactively, and you
can redefine leaf functions easily.  Redefining non-leaf functions isn't
as convenient if you're just presented with the raw language, but it's
fairly easy to write decent programming environments to do everything
automatically.  Even a make system is a great help.  A good environment
would just recompile and re-link the changed module, without recompiling
all intermediate modules (there is at least one firm developing such an
environment).

If I were designing an ML-like language, I would probably ban redeclaration
altogether.  I'd add a renaming scheme to the module system to take care
of name clashes, and force people to use references if they wanted to
change the value bound to a variable.  (I'd also add automatic dereferencing
to prevent this becoming cumbersome).

Dave.
--
 Dave Berry, LFCS, Edinburgh Uni.      db%lfcs.ed.ac.uk@nsfnet-relay.ac.uk

  "So they gave him a general anaesthetic and cleaned him with Swarfega."

rjm@vulcan.anu.edu.au (Robert J. McArthur) (05/08/91)

In article <10144@skye.cs.ed.ac.uk> db@lfcs.ed.ac.uk (Dave Berry) writes:
>
>Code development in ML is a big improvement over a batch language, even
>with a raw environment.  You can test any function interactively, and you
>can redefine leaf functions easily.  Redefining non-leaf functions isn't
>as convenient if you're just presented with the raw language, but it's
>fairly easy to write decent programming environments to do everything
>automatically.  Even a make system is a great help.  A good environment
>would just recompile and re-link the changed module, without recompiling
>all intermediate modules (there is at least one firm developing such an
>environment).
>
Are there any "nice" environments for ML development?  Public domain?
X based?

Robert
-- 
Robert McArthur			Centre for Resource and Environment Studies
	     				     Australian National University
ACSNet                   rjm@arp.anu.oz.au	       ACT  Australia  2601
Pegasus|PeaceNet|EcoNet  peg:robert	     		      (06) 249 4760

tmb@ai.mit.edu (Thomas M. Breuel) (05/08/91)

In article <1991May8.003016.5333@newshost.anu.edu.au> rjm@vulcan.anu.edu.au (Robert J. McArthur) writes:
   Are there any "nice" environments for ML development?  Public domain?
   X based?

The GNU Emacs/SML interface that comes with the SML of NJ distribution is
quite nice.

					Thomas.

db@cs.ed.ac.uk (Dave Berry) (05/08/91)

In article <1991May8.003016.5333@newshost.anu.edu.au> rjm@vulcan.anu.edu.au (Robert J. McArthur) writes:
>
>Are there any "nice" environments for ML development?  Public domain?
>X based?

I know of two make systems.  Poly/ML has a built-in make system.  Nick
Rothwell has written a portable make system, which is included in the new
release of the Edinburgh SML library, and will be included with the next
distributions of the three major compilers.

Poplog ML supports X (via calls to Pop-11, I think).  Poly/ML currently
supports Suntools; the next release will support X.  They don't have
window based programming environments, just facilities to maniuplate windows
from SML.  SML/NJ, or rather CML (concurrent ML), has a window system in
development, but I don't know when it will be released.  There's another
commercial system being developed which will have a sophisticated
environment, but that's not available yet.

SML/NJ is free (though not public domain).  Both Poly/ML and Poplog are
commercial products with substantial discounts to academics.  Each system
has various advantages and disadvantages beyond those mentioned here.

Dave.
--
 Dave Berry, LFCS, Edinburgh Uni.      db%lfcs.ed.ac.uk@nsfnet-relay.ac.uk

  "So they gave him a general anaesthetic and cleaned him with Swarfega."

tmb@ai.mit.edu (Thomas M. Breuel) (05/09/91)

      Are there any "nice" environments for ML development?  Public domain?
      X based?

   The GNU Emacs/SML interface that comes with the SML of NJ distribution is
   quite nice.

I forgot to mention: I'm using a hacked up version of that emacs interface
that supports comint mode (command history), and has better support of
process typeout. If you want to try it out, I can send you a copy.

						Thomas.

george@hart.Princeton.EDU (Lal George) (05/09/91)

In article <KERS.91May2084808@cdollin.hpl.hp.com> kers@hplb.hpl.hp.com (Chris Dollin) writes:
>Here's another one. SML is an interactive incremental development language.
>This means that when you find an error in a function definition, all you need
>to do is to redefine the function and ....
>
>Funny, this useful feature of interactive langauges seems to have gone wrong.
>I'll just have to load all the code I've compiled since then. I wonder what
>modules that covers?

When a module is modified, all the modules that depend on 
the changes need not be recompiled, if the program is 
built up using a set of functor applications.
This should be the preferred organization when the
program becomes large.

-- Lal

kers@hplb.hpl.hp.com (Chris Dollin) (05/10/91)

Following my message that rubbishes MLs ``incremental'' compilation, a couple
of responses that I want to address ...

Nico Verwer says:

   No, it is very fortunate that ML does NOT have "dynamic scope rules". One
   point has been made before, i.e. type safety.
   But even if the system would enforce some form of type safety ...

It's not dynamic scope (which is to do with rebinding *within new scopes*.
Clearly if the new definition does not ascribe the same type as the old one,
the inconsistency must be resolved some way; for example, the definition could
be disallowed (yuk) or a new variable could be created (and the old one
assigned a value such as "\x. error 'obsolete function called", and maybe all
using functions tagged as obsolete).

Clearly redefining "sin" (Nico's later example) would be a Bad Thing. This just
means that we need some way of annotating those names no longer available for
redefinition - no big deal.

Someone suggested that we needed ``better tools, not worse semantics'', to
solve the problem of rebuilding after a change on which lots of things depend.
Well, I can see the point in that, but it's a matter of opinion as to whether
the semantics is ``worse''; and solving the problem by throwing CPU cycles at
it seems to me to be the wrong approach. First, I'll bet that the time taken
will become appreciable quite quickly; second, those are *my* CPU cycles you're
using; I might want to spend them on something else at the same time (maybe
I've got another system building in the background; perhaps I'm also waiting
for a chess program to deliver it's next move; maybe I have a fancy version of
xfish I'd not like slowed down; etc).
--

Regards, Kers.      | "You're better off  not dreaming of  the things to come;
Caravan:            | Dreams  are always ending  far too soon."

tmb@ai.mit.edu (Thomas M. Breuel) (05/14/91)

In article <KERS.91May10092912@cdollin.hpl.hp.com> kers@hplb.hpl.hp.com (Chris Dollin) writes:
   Clearly redefining "sin" (Nico's later example) would be a Bad Thing. This just
   means that we need some way of annotating those names no longer available for
   redefinition - no big deal.

   Someone suggested that we needed ``better tools, not worse semantics'', to
   solve the problem of rebuilding after a change on which lots of things depend.
   Well, I can see the point in that, but it's a matter of opinion as to whether
   the semantics is ``worse''; and solving the problem by throwing CPU cycles at
   it seems to me to be the wrong approach.

"Better tools" does not mean "throwing CPU cycles at the problem".

You are proposing that I can say something like:

      redefine fun f x = ... ;

But I think this is a bad idea. I don't know about you, but I have a
hard time keeping track of the implications of redefinitions like
this.

Ultimately, what I expect of a good SML development environment is
that I edit some value "x" deep inside some structure, tell the system
that I'm through editing my sources, switch to the SML toplevel, and
find that the internal state of the SML system reflects the new state
of my sources, in particular the new value of "x". In order for this
to happen quickly enough, the system will have to generate commands
like "redefine" internally, but I never want to see them.

The "make" systems that float around are a start. Their major
shortcomings is that they don't infer dependencies automatically
(dependencies should be inferred, stored, and updated as I first load
and then modify my sources).  Also, they only work at a file level,
not a definition level.

It may also be possible to hack something up analogous to "make" that
relies on file modification dates, signatures for blocks of text
inside files, recording of information during load, etc., so that even
without a tight integration between editor and SML toplevel I may get
reasonable performance on recompilation. That is, when I tell the
system something like 'use "file.sml";', the system could use
knowledge of the previous contents of "file.sml" (and the contents and
modification dates of files "use"d by "file.sml") to infer what
"redefine" commands need to be issued in what sequence and come close
to minimal recompilation.

Again, I think making something like "redefine" visible to the user is
a bad idea. I certainly don't want to have to bother with it, and
implementors would just use it as an excuse not to have to come up with
decent development environments.

					Thomas.

nico@cs.ruu.nl (Nico Verwer) (05/16/91)

In <KERS.91May10092912@cdollin.hpl.hp.com> kers@hplb.hpl.hp.com (Chris Dollin) writes:

>Clearly if the new definition does not ascribe the same type as the old one,
>the inconsistency must be resolved some way; for example, the definition could
>be disallowed (yuk) or a new variable could be created (and the old one
>assigned a value such as "\x. error 'obsolete function called", and maybe all
>using functions tagged as obsolete).

Disallowing the definition is obviously not a good idea. Discarding the old 
binding is better, but static scoping (like ML does) improves on this, by not 
binding the old definition to "\x. error", but hiding it.

>Clearly redefining "sin" (Nico's later example) would be a Bad Thing. This just
>means that we need some way of annotating those names no longer available for
>redefinition - no big deal.

But sometimes I really want to redefine a definition. One of the features
of teX (which supports dynamic scoping through macro substitution) which
drives me crazy is that I sometimes cannot define a command by
\newcommand{\name}{body}
because it already exists --- it is annotated as "no longer available for
definition".
I then have to use \renewcommand{\name}{body}
but that is potentially dangerous. 
Suppose I am a novice LaTeX user, writing a paper on scoping in ML, and I
want to define a command \let for typesetting programs which use 
let ... = ... in ... ;
LaTeX complains when the I say \newcommand{\let}{...}.
Now I know much about ML (remember this is only an example!) but little 
about TeX (he I don't even know about the difference between TeX/LaTeX).
I therefore say \renewcommand{\let}{...}, with disastrous results.
The problem is that TeX uses its own \let, which I don't need, and that I
want to use my own \let, and I don't want to give it another name.
Marking the old \let is just as bad as \renewcommand-ing it.
The only solution here really is static scoping!

There is a discussion about scoping in TeX on comp.text.tex at the moment.
If only TeX would scope like ML...
-- 
Nico Verwer                                       | nico@cs.ruu.nl
Dept. of Computer Science, University of Utrecht  | phone: +31 30 533921
p.o. box 80.089, 3508 TB Utrecht, The Netherlands | fax:   +31 30 513791