https://github.com/akkartik/mu/blob/master/057immutable.cc
  1 //: Ingredients of a recipe are meant to be immutable unless they're also
  2 //: products. This layer will start enforcing this check.
  3 //:
  4 //: One hole for now: variables in surrounding spaces are implicitly mutable.
  5 //: [tag: todo]
  6 
  7 :(scenario can_modify_ingredients_that_are_also_products)
  8 # mutable container
  9 def main [
 10   local-scope
 11   p:point <- merge 34, 35
 12   p <- foo p
 13 ]
 14 def foo p:point -> p:point [
 15   local-scope
 16   load-ingredients
 17   p <- put p, x:offset, 34
 18 ]
 19 $error: 0
 20 
 21 :(scenario can_modify_ingredients_that_are_also_products_2)
 22 def main [
 23   local-scope
 24   p:&:point <- new point:type
 25   p <- foo p
 26 ]
 27 # mutable address to container
 28 def foo p:&:point -> p:&:point [
 29   local-scope
 30   load-ingredients
 31   *p <- put *p, x:offset, 34
 32 ]
 33 $error: 0
 34 
 35 :(scenario can_modify_ingredients_that_are_also_products_3)
 36 def main [
 37   local-scope
 38   p:&:@:num <- new number:type, 3
 39   p <- foo p
 40 ]
 41 # mutable address
 42 def foo p:&:@:num -> p:&:@:num [
 43   local-scope
 44   load-ingredients
 45   *p <- put-index *p, 0, 34
 46 ]
 47 $error: 0
 48 
 49 :(scenario ignore_literal_ingredients_for_immutability_checks)
 50 def main [
 51   local-scope
 52   p:&:d1 <- new d1:type
 53   q:num <- foo p
 54 ]
 55 def foo p:&:d1 -> q:num [
 56   local-scope
 57   load-ingredients
 58   x:&:d1 <- new d1:type
 59   *x <- put *x, p:offset, 34  # ignore this 'p'
 60   return 36
 61 ]
 62 container d1 [
 63   p:num
 64   q:num
 65 ]
 66 $error: 0
 67 
 68 :(scenario cannot_modify_immutable_ingredients)
 69 % Hide_errors = true;
 70 def main [
 71   local-scope
 72   x:&:num <- new number:type
 73   foo x
 74 ]
 75 # immutable address to primitive
 76 def foo x:&:num [
 77   local-scope
 78   load-ingredients
 79   *x <- copy 34
 80 ]
 81 +error: foo: cannot modify 'x' in instruction '*x <- copy 34' because it's an ingredient of recipe foo but not also a product
 82 
 83 :(scenario cannot_modify_immutable_containers)
 84 % Hide_errors = true;
 85 def main [
 86   local-scope
 87   x:point-number <- merge 34, 35, 36
 88   foo x
 89 ]
 90 # immutable container
 91 def foo x:point-number [
 92   local-scope
 93   load-ingredients
 94   # copy an element: ok
 95   y:point <- get x, xy:offset
 96   # modify the element: boom
 97   # This could be ok if y contains no addresses, but we're not going to try to be that smart.
 98   # It also makes the rules easier to reason about. If it's just an ingredient, just don't try to change it.
 99   y <- put y, x:offset, 37
100 ]
101 +error: foo: cannot modify 'y' in instruction 'y <- put y, x:offset, 37' because that would modify 'x' which is an ingredient of recipe foo but not also a product
102 
103 :(scenario can_modify_immutable_pointers)
104 def main [
105   local-scope
106   x:&:num <- new number:type
107   foo x
108 ]
109 def foo x:&:num [
110   local-scope
111   load-ingredients
112   # modify the address, not the payload
113   x <- copy null
114 ]
115 $error: 0
116 
117 :(scenario can_modify_immutable_pointers_but_not_their_payloads)
118 % Hide_errors = true;
119 def main [
120   local-scope
121   pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
<HTML>
<HEAD>
<TITLE>Using Computers for Educational Freedom</TITLE>
</HEAD>
<BODY>
<H1>Using Computers for Educational Freedom</H1>
<CITE>Brian Harvey<BR>University of California, Berkeley</CITE>

<P>[This article is adapted from a talk given by the author at the Second
Annual Computer Conference at Lesley College, May 3, 1980.]

<P>The computer is fast becoming an educational cure-all; depending on which
expert you consult, it can teach problem-solving skills, teach basic
arithmetic by making drill fun, replace the teacher, augment the teacher,
or provide experiential learning.  Given all these possibilities, it's
hard to establish priorities when setting up a computer facility.  The way
to choose what to do first, from among the many exciting possibilities, is
to start with a clear idea of your overall goals.

<P>I would like to suggest one possible goal, and consider its practical
implications.  The goal is summed up in this statement by Ted Nelson:
<EM>``The purpose of computers is human freedom.''</EM>
[From <CITE>The Computer Lib Pledge</CITE> (c) 1978 Ted Nelson.]
That's pretty vague, as it stands.  Let me say first that it doesn't mean
<EM>not</EM> to think about other goals.  It does, though, establish priorities
in buying equipment and in spending time on development of the facility.  We
have set up a computer facility at Lincoln-Sudbury Regional High School
(we bought a Digital Equipment Corporation PDP-11/70 in 1979), and
our choices will help explain what I understand by this goal.

<P>The word ``freedom'' means many different things in different contexts.  For
the purposes of this discussion, though, I want to consider two fairly
narrow components of freedom: <EM>variety</EM> and <EM>initiative</EM>.

<H2>Variety: Activities vs. Tools</H2>

<P>It makes no sense to talk about freedom for students unless they have
choices to make.  And as Jonathan Kozol points out, they have to be
<EM>significant</EM> choices--deciding between tuna fish and peanut butter
in the cafeteria doesn't count.

<P>Probably the first thing which comes to mind under the heading of variety
is a variety of activities, as in the open classroom approach.  In the
context of computer education, we can provide a variety of game programs
for student use, and a variety of suggested programming projects.  This
kind of variety is an obviously worthwhile step, but I think that the
computer allows a much more profound step toward freedom: a variety of
<EM>tools</EM>.

<P>You can go to the store and buy a ``computer game'' with a name like
Electronic Football.  The game implies one specific activity.  If it's
a good game, you may play it often.  But if you get bored with the
activity, the device is useless to you.  Alternatively, you can buy a
screwdriver.  This tool is not limited to one activity; in fact, it
doesn't suggest an activity at all.  That is, you don't say ``I think
I'll go play with my screwdriver now.''  Instead, you say ``I think I'll
fix that loose hinge now,'' and you reach for your screwdriver without
thinking about it.

<P>It may be overstating the case to say that the Electronic Football game
actually decreases its owner's freedom, but certainly a good assortment
of tools is much more conducive to free behavior.  The computer lends
itself to creating such a toolkit.  Here
are some of the tools we offer:

<UL>
<LI>The <EM>turtle</EM> is a small robot which moves on the
floor under computer control.  It also has headlights and a horn.  It
lends itself to many different
activities: it can be taught to dance; it can draw pictures with the pen
in its belly; it can be sent across the room to attack one's friend at another
terminal; using its touch sensors, it can be taught to maneuver around
obstacles or to escape from mazes.

<LI>We have a Diablo printer, a typewriter-like device which allows variable
character spacing.  With suitable software tools, it can be used to print
papers with justified margins, boldface, underlining, and automatic
hyphenation.  This tool was originally envisioned as an aid to the writing
of English or history papers.  However, students have come up with several
unanticipated uses for this tool.  Our school newspaper, the <EM>Promethean</EM>,
is using it to typeset their articles.  This activity is one in which a
group of students with no direct interest in computers is using the machine
to further what <EM>is</EM> their direct interest: putting out a
newspaper.  Another activity is the creation of computer graphics, by
printing closely spaced dots.  The first such project was done by a student who
typed in the necessary sequence of
dots and spaces by hand, with no programming involved; later projects have
become more sophisticated.

<LI>The primary means of communication with the computer are our VT-100 display
terminals.  These are not graphics terminals, in the sense that it is not
possible to draw a smooth curve on their screens.  They were intended simply
as text display terminals, and one of our most important software tools is a
display-oriented text editor which exploits their capabilities.  The chore of
entering information into the computer is made infinitely easier with good
display software, compared to the more common line-oriented hardcopy editors.
But the VT-100 also has a ``graphics character set'' which allows lines, blocks,
and a few other special symbols to be displayed in place of text.  I've been
spoiled by the powerful graphics terminals at university research centers,
and the limited graphics of the VT-100 was beneath my notice.  But several
students have used the VT-100 to program PDP-11 timesharing versions of some
of the standard personal computer video games, such as Breakout and Asteroids,
and Conway's mathematical game of Life.  Writing such a game is both more
educational and more fun than simply playing one which somebody else programmed.
But even for the other students who play these games, it is better that they
are programmed by a fellow student and not by a wizard off in a distant
castle.  The program is available for inspection, and the authors are available
for questioning.

<LI>We have five Atari 800 personal computers, which we use as graphics
terminals.  The same commands which move the robot turtles across the
floor can be used to draw pictures by moving a ``display turtle'' across
the TV screen.  A special 6502 machine language program for the Atari
processes display commands sent from the central PDP-11 system.  The
Ataris are used as terminals, rather than as independent computers, so
that students can use the powerful Logo programming language, which is
not available for the Atari itself.

<LI>The Unix
operating system we use on the PDP-11 provides literally hundreds
of software tools, small and large.  At the small end are things like a
program to print a file, going over each line twice, to get readable output
from a hardcopy terminal with a weak ribbon.  At the large end are the
programming languages, of which more later.  In the middle are the document
formatter, an automatic spelling checker with a large dictionary, the text
editor mentioned earlier, a sort program, and a utility program which extracts
from a data file all lines containing a user-specified text string.  As an
example of the importance of these tools, one of the computer center ``regulars''
two years ago was
also very interested in the newly-formed school radio station.  He wanted
to use the computer to maintain a catalog of the radio station's record
collection.  He envisioned a major programming project to develop programs to
allow typein of record titles, recording artists, and so on; to produce lists
sorted by title, by artist, or by record company; and to find out whether a
particular record, or any record by a particular artist, is in the catalog.  I
pointed out that
the existing text editor, sort program, and text-matching utility are
sufficient without additional programming.  A sequence of two or three commands
to the existing programs can be written in a minute or two.  Also, the operating
system allows such a sequence of commands to be written in a file, and given
a name, defining a new command.  This facility makes it easy for other students
to use the record catalog without knowing the details of operation of the
utility programs.

<P>The example is important because it illustrates the point that a student
with a well-equipped toolkit can accomplish tasks of practical interest, which
might otherwise seem impossible to non-wizards.  <EM>Good tools expand kids'
view of the possible.</EM>  This point ties the technical issue of a variety of
tools to the more political, or psychological, question of initiative.  The
connection will be discussed further below.
</UL>

<P>The most powerful of software tools is the programming language.  A student
who can program is truly free to use the computer in ways not anticipated by
a teacher or operating system designer.  The choice of programming language
has a profound effect on the range of problems within the student's grasp;
some languages are more powerful than others, and also some are more
conducive than others to a programming style which will make large problems
comprehensible to mere human beings.  For beginning programming students, we
use the Logo language.  This language, developed specifically as a teaching
language at MIT and at Bolt Beranek and Newman, Inc., is simple and interactive,
like BASIC, but also allows the power of list processing and recursive
procedures, like the LISP language from which many of its ideas came.  The
beginning programmer can type in a simple command for immediate execution
(PRINT 2+2) or store a sequence
of commands as a named procedure for later
use.  A complex problem can be divided naturally into sub-problems, each
solved by a sub-procedure of the main program.  A procedure can also use
<EM>itself</EM> as a sub-procedure.  The language contains provisions for
interesting problem domains like graphics (through the turtle commands
mentioned earlier) and language processing (for example, translating a
sentence into Pig Latin).

<P>Other languages we use are APL, Pascal, C, and LISP.  APL is used by the
Mathematics Department not to teach programming <EM>per se</EM>, but to provide
as a tool to students what amounts to a calculator which understands algebra.
The language hides many of the problems of control structure which are
prominent in more conventional languages, and emphasizes instead mathematical
concepts like functions, vectors, and matrices.  Pascal is quickly becoming
a very popular teaching language because it is available on many microcomputers
and is designed to foster the Structured Programming style.  I think it suffers
as an initial teaching language from the fact that it is not interactive; the
student must learn to cope with details of text editors, files, and operating
systems before writing even the simplest Pascal program.  However, it is a
marvelous <EM>second</EM> language for the student who has mastered these details,
because it calls attention to issues of data types and storage allocation which
are hidden in an interactive language with dynamic allocation, like Logo.  [1994
addendum:  I can't believe I said that!] The
C language is much like Pascal in its design, but it has the added benefit that
most of the Unix operating system software itself is written in C, so a student
who is curious about the inner workings of the software can read the actual
programs after learning C.  Finally, LISP is one of the most powerful of
languages, used widely in Computer Science research.  It provides a worthwhile
challenge to our advanced students, and has been used in one formal course on
Computational Linguistics.

<P>Finally, an important role for the teacher in all this is as a sort of human
tool; he is a consultant on ways and means, rather than an initiator of
activities for students.  I spend my time helping individual students debug
their programs, rather than lecturing to a large group.  I also encourage
students to use one another as consultants and as tutors.

<H2>Initiative: a Political Issue</H2>

<P>Educational freedom means, first of all, that students can make significant
choices from a variety of alternatives.  But if the choices are always made
from a list invented by a teacher, the freedom is of a very limited sort.  The
example of using the computer to typeset the <EM>Promethean</EM> illustrates
a very different sort of choice, in which
students meet <EM>their own
needs</EM> (the newspaper is an extracurricular activity, not a course) using
the computer as a tool.  That's what initiative means.

<P>There is a clear relationship between this notion of initiative and the
availability of a variety of tools.  The more traditional variety of
activities encourages what might be called ``passive freedom''; students are
free to choose, but not free to initiate.  In Paulo Freire's terms, students
are still <EM>objects</EM> of an education provided by their teachers.  But a
variety of tools encourages students to become the <EM>subjects</EM>--the
actors rather than the acted-upon--of their own education.

<P>Any attempt to make initiative a guiding principle in teaching will
confront two psychological barriers: first, it is hard for <EM>adults</EM> to
<EM>permit</EM> student initiative; second, it is hard for <EM>students</EM>
to <EM>accept</EM> the
burden, an unusual one in a high school, if we encourage them to take
initiative.

<P>Many of the experts who write articles or talk at conferences about the use
of computers in education give the impression that simply introducing
computers to the classroom will automatically lead to increased freedom for
learners.  The truth, I think, is that the use of computers can go either
way.  When Ted Nelson says ``The purpose of computers is human freedom,'' he
really means that that is what the purpose <EM>should be</EM>.  In practice, most
computers are better described as dedicated to human slavery!  The computers
at the IRS check up on income tax cheaters; the ones at the bank send you
bills (or your paycheck, which is more pleasant than a bill but a more
important form of economic slavery).  More sophisticated research computers
at the universities are used to study pictures of Vietnamese jungles to help
figure out where to drop the napalm.  Similarly, many
computers in schools are still used exclusively for administrative computing;
students don't get near them.  If students do use the computers, it is often
only for teacher-directed drill and practice, no matter how cleverly disguised
as a game.  Better uses of the technology are possible,
but they aren't inevitable.

<P>Consider an analogy.  Most teachers probably agree, in principle, with the
idea of educational freedom.  Students learn best through intrinsic motivation,
not through force.  What you learn under pressure doesn't last past the exam.
Everyone says these things, and yet almost all teachers continue to give
grades.  Why?  ``It's required''; ``The colleges need grades''; ``The parents
wouldn't stand for it''; ``It's the way things are.''  In short, the reasons
for grades are political.  The same political reasons make educational
freedom through computers a difficult goal.  If students are left to their
own devices to initiate projects, how do we evaluate them?  How do we know
they aren't just wasting time?  Remember, many school computers are funded
through federal grants, and the feds always insist on evaluation of the
program.  That means coopting the computer into the usual school routine
of assignments initiated and evaluated by teachers.

<P>An even more frustrating barrier is that the students themselves are not
accustomed to being without instructions from an adult.  Many students
will find valuable projects on their own, but many more will have to be
weaned away slowly from dependence on explicit assignments.  One of my early
students taught himself four different programming
languages, and learned a great deal about issues of programming
style and structure in his senior year.  He'll probably
learn less about computers in four
years of college.  But he told me every day that I'm a terrible teacher,
because I didn't <EM>make</EM> him learn anything.  I didn't stand in front of
the room and impart information, I didn't send in skip slips if he didn't
show up, and I didn't punish him when he acted obnoxious.  Well, it's not
much fun to hear all this.  It was tempting to say ``OK, if that's what you
want, sit down and shut up!''  But I doubt if the most effective classroom
manager in the world could teach this student as much in a year as he
learned on his own--he would start directing his efforts into a
power struggle.

<P>What does all this mean as a guide to action?  Well, our computer was
installed for a full year before I started working on curriculum materials
or organizing a course structure.  I spent that year collecting and
building tools, and kids spent the year learning on their own, or by
asking questions.  Two years later, we have a computer course in operation
based on self-paced curriculum units, with no grades and with many
different options in the actual course content.  And about 50 kids have
keys to the computer center, and use it evenings and weekends without
adult supervision.  The path from there to here was far from smooth,
but it's been exciting.


<P><ADDRESS>
<A HREF="index.html"><CODE>www.cs.berkeley.edu/~bh</CODE></A>
</ADDRESS>
</BODY>
</HTML>
"Identifier">break; 373 case GET: 374 case INDEX: 375 case MAYBE_CONVERT: 376 // current_ingredient_indices can only have 0 or one value 377 if (!current_ingredient_indices.empty() && !inst.products.empty()) { 378 if (is_mu_address(inst.products.at(0)) || is_mu_container(inst.products.at(0)) || is_mu_exclusive_container(inst.products.at(0))) 379 current_ingredient_and_aliases.insert(inst.products.at(0)); 380 } 381 break; 382 default: break; 383 } 384 } 385 else { 386 // defined recipe 387 set<int> contained_in_product_indices = scan_contained_in_product_indices(inst, current_ingredient_indices); 388 for (set<int>::iterator p = contained_in_product_indices.begin(); p != contained_in_product_indices.end(); ++p) { 389 if (*p < SIZE(inst.products)) 390 current_ingredient_and_aliases.insert(inst.products.at(*p)); 391 } 392 } 393 } 394 395 set<int> scan_contained_in_product_indices(const instruction& inst, set<int>& ingredient_indices) { 396 set<reagent, name_and_space_lt> selected_ingredients; 397 const recipe& callee = get(Recipe, inst.operation); 398 for (set<int>::iterator p = ingredient_indices.begin(); p != ingredient_indices.end(); ++p) { 399 if (*p >= SIZE(callee.ingredients)) continue; // optional immutable ingredient 400 selected_ingredients.insert(callee.ingredients.at(*p)); 401 } 402 set<int> result; 403 for (int i = 0; i < SIZE(callee.products); ++i) { 404 const reagent& current_product = callee.products.at(i); 405 const string_tree* contained_in_name = property(current_product, "contained-in"); 406 if (contained_in_name && selected_ingredients.find(contained_in_name->value) != selected_ingredients.end()) 407 result.insert(i); 408 } 409 return result; 410 } 411 412 bool is_mu_container(const reagent& r) { 413 return is_mu_container(r.type); 414 } 415 bool is_mu_container(const type_tree* type) { 416 if (!type) return false; 417 if (!type->atom) 418 return is_mu_container(get_base_type(type)); 419 if (type->value == 0) return false; 420 if (!contains_key(Type, type->value)) return false; // error raised elsewhere 421 type_info& info = get(Type, type->value); 422 return info.kind == CONTAINER; 423 } 424 425 bool is_mu_exclusive_container(const reagent& r) { 426 return is_mu_exclusive_container(r.type); 427 } 428 bool is_mu_exclusive_container(const type_tree* type) { 429 if (!type) return false; 430 if (!type->atom) 431 return is_mu_exclusive_container(get_base_type(type)); 432 if (type->value == 0) return false; 433 if (!contains_key(Type, type->value)) return false; // error raised elsewhere 434 type_info& info = get(Type, type->value); 435 return info.kind == EXCLUSIVE_CONTAINER; 436 } 437 438 :(before "End Types") 439 // reagent comparison -- only in the context of a single recipe 440 struct name_and_space_lt { 441 bool operator()(const reagent& a, const reagent& b) const; 442 }; 443 :(code) 444 bool name_and_space_lt::operator()(const reagent& a, const reagent& b) const { 445 int aspace = 0, bspace = 0; 446 if (has_property(a, "space")) aspace = to_integer(property(a, "space")->value); 447 if (has_property(b, "space")) bspace = to_integer(property(b, "space")->value); 448 if (aspace != bspace) return aspace < bspace; 449 return a.name < b.name; 450 } 451 452 :(scenarios transform) 453 :(scenario immutability_infects_contained_in_variables) 454 % Hide_errors = true; 455 container test-list [ 456 value:num 457 next:&:test-list 458 ] 459 def main [ 460 local-scope 461 p:&:test-list <- new test-list:type 462 foo p 463 ] 464 def foo p:&:test-list [ # p is immutable 465 local-scope 466 load-ingredients 467 p2:&:test-list <- test-next p # p2 is immutable 468 *p2 <- put *p2, value:offset, 34 469 ] 470 def test-next x:&:test-list -> y:&:test-list/contained-in:x [ 471 local-scope 472 load-ingredients 473 y <- get *x, next:offset 474 ] 475 +error: foo: cannot modify 'p2' in instruction '*p2 <- put *p2, value:offset, 34' because that would modify p which is an ingredient of recipe foo but not also a product 476 477 :(code) 478 void check_immutable_ingredient_in_instruction(const instruction& inst, const set<reagent, name_and_space_lt>& current_ingredient_and_aliases, const string& original_ingredient_name, const recipe& caller) { 479 // first check if the instruction is directly modifying something it shouldn't 480 for (int i = 0; i < SIZE(inst.products); ++i) { 481 if (has_property(inst.products.at(i), "lookup") 482 && current_ingredient_and_aliases.find(inst.products.at(i)) != current_ingredient_and_aliases.end()) { 483 string current_product_name = inst.products.at(i).name; 484 if (current_product_name == original_ingredient_name) 485 raise << maybe(caller.name) << "cannot modify '" << current_product_name << "' in instruction '" << to_original_string(inst) << "' because it's an ingredient of recipe " << caller.name << " but not also a product\n" << end(); 486 else 487 raise << maybe(caller.name) << "cannot modify '" << current_product_name << "' in instruction '" << to_original_string(inst) << "' because that would modify " << original_ingredient_name << " which is an ingredient of recipe " << caller.name << " but not also a product\n" << end(); 488 return; 489 } 490 } 491 // check if there's any indirect modification going on 492 set<int> current_ingredient_indices = ingredient_indices(inst, current_ingredient_and_aliases); 493 if (current_ingredient_indices.empty()) return; // ingredient not found in call 494 for (set<int>::iterator p = current_ingredient_indices.begin(); p != current_ingredient_indices.end(); ++p) { 495 const int current_ingredient_index = *p; 496 reagent current_ingredient = inst.ingredients.at(current_ingredient_index); 497 canonize_type(current_ingredient); 498 const string& current_ingredient_name = current_ingredient.name; 499 if (!contains_key(Recipe, inst.operation)) { 500 // primitive recipe 501 // we got here only because we got an instruction with an implicit product, and the instruction didn't explicitly spell it out 502 // put x, y:offset, z 503 // instead of 504 // x <- put x, y:offset, z 505 if (inst.operation == PUT || inst.operation == PUT_INDEX) { 506 if (current_ingredient_index == 0) { 507 if (current_ingredient_name == original_ingredient_name) 508 raise << maybe(caller.name) << "cannot modify '" << current_ingredient_name << "' in instruction '" << to_original_string(inst) << "' because it's an ingredient of recipe " << caller.name << " but not also a product\n" << end(); 509 else 510 raise << maybe(caller.name) << "cannot modify '" << current_ingredient_name << "' in instruction '" << to_original_string(inst) << "' because that would modify '" << original_ingredient_name << "' which is an ingredient of recipe " << caller.name << " but not also a product\n" << end(); 511 } 512 } 513 } 514 else { 515 // defined recipe 516 if (is_modified_in_recipe(inst.operation, current_ingredient_index, caller)) { 517 if (current_ingredient_name == original_ingredient_name) 518 raise << maybe(caller.name) << "cannot modify '" << current_ingredient_name << "' in instruction '" << to_original_string(inst) << "' because it's an ingredient of recipe " << caller.name << " but not also a product\n" << end(); 519 else 520 raise << maybe(caller.name) << "cannot modify '" << current_ingredient_name << "' in instruction '" << to_original_string(inst) << "' because that would modify '" << original_ingredient_name << "' which is an ingredient of recipe " << caller.name << " but not also a product\n" << end(); 521 } 522 } 523 } 524 } 525 526 bool is_modified_in_recipe(const recipe_ordinal r, const int ingredient_index, const recipe& caller) { 527 const recipe& callee = get(Recipe, r); 528 if (!callee.has_header) { 529 raise << maybe(caller.name) << "can't check mutability of ingredients in recipe " << callee.name << " because it uses 'next-ingredient' directly, rather than a recipe header.\n" << end(); 530 return true; 531 } 532 if (ingredient_index >= SIZE(callee.ingredients)) return false; // optional immutable ingredient 533 return is_present_in_products(callee, callee.ingredients.at(ingredient_index).name); 534 } 535 536 bool is_present_in_products(const recipe& callee, const string& ingredient_name) { 537 for (int i = 0; i < SIZE(callee.products); ++i) { 538 if (callee.products.at(i).name == ingredient_name) 539 return true; 540 } 541 return false; 542 } 543 544 set<int> ingredient_indices(const instruction& inst, const set<reagent, name_and_space_lt>& ingredient_names) { 545 set<int> result; 546 for (int i = 0; i < SIZE(inst.ingredients); ++i) { 547 if (is_literal(inst.ingredients.at(i))) continue; 548 if (ingredient_names.find(inst.ingredients.at(i)) != ingredient_names.end()) 549 result.insert(i); 550 } 551 return result; 552 } 553 554 //: Sometimes you want to pass in two addresses, one pointing inside the 555 //: other. For example, you want to delete a node from a linked list. You 556 //: can't pass both pointers back out, because if a caller tries to make both 557 //: identical then you can't tell which value will be written on the way out. 558 //: 559 //: Experimental solution: just tell Mu that one points inside the other. 560 //: This way we can return just one pointer as high up as necessary to capture 561 //: all modifications performed by a recipe. 562 //: 563 //: We'll see if we end up wanting to abuse /contained-in for other reasons. 564 565 :(scenarios transform) 566 :(scenario can_modify_contained_in_addresses) 567 container test-list [ 568 value:num 569 next:&:test-list 570 ] 571 def main [ 572 local-scope 573 p:&:test-list <- new test-list:type 574 foo p 575 ] 576 def foo p:&:test-list -> p:&:test-list [ 577 local-scope 578 load-ingredients 579 p2:&:test-list <- test-next p 580 p <- test-remove p2, p 581 ] 582 def test-next x:&:test-list -> y:&:test-list [ 583 local-scope 584 load-ingredients 585 y <- get *x, next:offset 586 ] 587 def test-remove x:&:test-list/contained-in:from, from:&:test-list -> from:&:test-list [ 588 local-scope 589 load-ingredients 590 *x <- put *x, value:offset, 34 # can modify x 591 ] 592 $error: 0 593 594 :(before "End Immutable Ingredients Special-cases") 595 if (has_property(current_ingredient, "contained-in")) { 596 const string_tree* tmp = property(current_ingredient, "contained-in"); 597 if (!tmp->atom 598 || (!is_present_in_ingredients(caller, tmp->value) 599 && !is_present_in_products(caller, tmp->value))) { 600 raise << maybe(caller.name) << "/contained-in can only point to another ingredient or product, but got '" << to_string(property(current_ingredient, "contained-in")) << "'\n" << end(); 601 } 602 continue; 603 } 604 605 :(scenario contained_in_product) 606 container test-list [ 607 value:num 608 next:&:test-list 609 ] 610 def foo x:&:test-list/contained-in:result -> result:&:test-list [ 611 local-scope 612 load-ingredients 613 result <- copy null 614 ] 615 $error: 0 616 617 :(scenario contained_in_is_mutable) 618 container test-list [ 619 value:num 620 next:&:test-list 621 ] 622 def foo x:&:test-list/contained-in:result -> result:&:test-list [ 623 local-scope 624 load-ingredients 625 result <- copy x 626 put *x, value:offset, 34 627 ] 628 $error: 0