User Tools

Site Tools


at:tutorial:basic

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
at:tutorial:basic [2007/04/17 16:51]
tvcutsem
at:tutorial:basic [2020/02/09 22:05] (current)
elisag
Line 1: Line 1:
-<note> 
-**This Tutorial is still under heavy construction!!** 
-</note> 
 ====== Functional and Imperative Programming ====== ====== Functional and Imperative Programming ======
    
Line 19: Line 16:
 </code> </code>
  
-Variable definitions can include an initialization expression that immediately initializes the variable. Variable assignment is performed by means of the well-known ":=operator. AmbientTalk supports assignment to multiple variables as a single assignment expression. For this to work, the number of variable names on the left hand side of ":=" must match the number of expressions on the right hand side of ":=". A typical application of this is to swap the values of two variables more easily:+Variable definitions can include an initialization expression that immediately initializes the variable. Variable assignment is performed by means of the well-known '':='' operator (''='' is used for mathematical comparison). AmbientTalk supports assignment to multiple variables as a single assignment expression. For this to work, the number of variable names on the left hand side of ":=" must match the number of expressions on the right hand side of ":=". A typical application of this is to swap the values of two variables more easily:
  
 <code> <code>
Line 34: Line 31:
 </code> </code>
  
-<note>+<note important>
 When using the '':='' assignment operator, beware of the following syntactic annoyance: the expression ''a := 1'' denotes an assignment to the variable ''a'', while ''a:= 1'' is misunderstood by the parser as ''a: = 1'', which is the invocation of a keyworded message named ''a:''. Keyworded message sends will be explained later on in this chapter. Hence, as a general rule, don't forget to always put a space between the variable name and the '':='' operator. When using the '':='' assignment operator, beware of the following syntactic annoyance: the expression ''a := 1'' denotes an assignment to the variable ''a'', while ''a:= 1'' is misunderstood by the parser as ''a: = 1'', which is the invocation of a keyworded message named ''a:''. Keyworded message sends will be explained later on in this chapter. Hence, as a general rule, don't forget to always put a space between the variable name and the '':='' operator.
 </note> </note>
Line 69: Line 66:
 >>[1, [1, 2, ["a", "e", "i", "o", "u"], 4, 5], "ambientTalk"] >>[1, [1, 2, ["a", "e", "i", "o", "u"], 4, 5], "ambientTalk"]
 </code> </code>
 +
  
 ==== Table Splicing ==== ==== Table Splicing ====
  
-AmbientTalk provides the operator @ to splice tables into surrounding table expressions+AmbientTalk provides the //splice operator// ''@'' to splice tables into surrounding table expressions:
 <code> <code>
 >[1,@[2,3],4] >[1,@[2,3],4]
Line 80: Line 78:
 </code> </code>
  
-The splicing operator can be also used for matching table elements as shown below.+The splicing operator can be also used in the left-hand side of an assignment or definition to separate the head of a table with its rest elementsas shown below.
 <code> <code>
 >def [first, @rest] := [1,2,3,4] >def [first, @rest] := [1,2,3,4]
Line 87: Line 85:
 >>[2, 3, 4] >>[2, 3, 4]
 </code> </code>
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +==== Multidimensional Tables ====
 +
 +As mentioned before, there is no special constructor for definition of multidimensional tables, a table entry can contain another table. In what follows we have a closer look to manipulations with multidimensional tables.  Consider a multidimensional table which is extensionally defined as follows:
 +
 +<code>
 +def a := [[1,0,0], [0,1,0], [0,0,1]];
 +>>[[1, 0, 0], [0, 1, 0], [0, 0, 1]]
 +>a[1][2]
 +>>0
 +>a[1]
 +>>[1, 0, 0]
 +>(a[1])[2] := 3;
 +>>3
 +>a
 +>>[[1, 3, 0], [0, 1, 0], [0, 0, 1]]
 +</code>
 +
 +An implicit definition of the same table can be expressed as follows:
 +
 +<code>
 +def i := 0;
 +def aux[3] {0}; 
 +def b[3] { i := i + 1; aux := [0,0,0]; aux[i] :=1; aux};
 +>>[[1, 0, 0], [0, 1, 0], [0, 0, 1]]
 +</code>
 +
 +You can find later in this chapter a helper function for creating matrices  [[:at:tutorial:basic#optional_parameters|here]].
 +
 + 
  
 ===== Functions ===== ===== Functions =====
    
-As variables and tables, functions are defined with the keyword **def** in the form of: +Analogous to variables and tables, functions are defined with the keyword **def** in the form of: 
 <code> <code>
 def functionname( <arglist> ) { <body> } def functionname( <arglist> ) { <body> }
 </code> </code>
-The argument list is just a list of local variables which are always evaluated one by one from left to right. A basic function looks like this:+The argument list is just a list of local variables which are always evaluated one by one from left to right. Hence, AmbientTalk employs //applicative-order// function calls, like Scheme. A basic ''square'' function looks like this:
 <code> <code>
 >def square (x) { x*x } >def square (x) { x*x }
Line 101: Line 141:
 >>25 >>25
 </code> </code>
-This example also illustrates how functions are called. Calls to functions without parameters must also include the parenthesis as shown below.+This example also illustrates the //canonical// function calling syntax. Calls to functions without parameters must also include the parentheses as shown below.
 <code> <code>
->def f(){nil}+>def f() { nil }
 >><closure:f> >><closure:f>
 >f() >f()
 >>nil >>nil
 </code> </code>
-The return value of a function is the result of the last statement executed. Functions must always return a value - i.e. they cannot be abstract. The example also illustrates how to create dumb function that doesn't do anything but returning the //nil// object. +The return value of a function is the result of the last executed statement. Functions always return a valuebut a function can always opt to return the //nil// object. 
  
-Functions have access to the enclosing environment of its definition as shown in the following example.  +<note> 
 +A function definition is a statement. The body of a function can contain a list of statements, each separated by '';''. A syntax error often made in AmbientTalk is to write: 
 +<code> 
 +def funA() { 
 +  // do something useful 
 +
 +def funB() { 
 +  // do something else 
 +
 +</code> 
 +The parser will complain saying that ''def'' was an unexpected token. The reason is that the function definition statements should be separated by means of '';''. In languages like C and Java, the ''}'' token need not be followed by a semicolon, hence the confusion. 
 +</note> 
 + 
 +Functions in AmbientTalk are //lexically scoped//, which means that free variables are looked up in the enclosing environment of the function definition. This is illustrated in the following example:
 <code> <code>
 >def counter := 0 >def counter := 0
Line 124: Line 177:
 >def fac(n) {  >def fac(n) { 
   def inner(n, result) {    def inner(n, result) { 
-    if: (n =0) then: { result } else: { inner( n-1, n * result)  }+    if: (n = 0) then: { result } else: { inner( n-1, n * result)  }
   };    }; 
   inner(n,1)   inner(n,1)
Line 133: Line 186:
 </code> </code>
  
-This example also illustrates how a function can be made private by means of lexical scope. Variables and functions defined locally to functions are only visible in the scope of the function where there were defined. Note that local //inner// function is only visible inside the //fac// function and its nested scopes. Thus, calling //fac.inner(2,3)// will return a lookup failure error.+This example also illustrates how a function can be made "privateby means of lexical scoping rules. Variables and functions defined locally to functions are only visible in the scope of the function where there were defined. Note that the local ''inner'' function is only visible inside the ''fac'' function and its nested scopes.
    
 +
 ==== Variable-Length Argument Functions ==== ==== Variable-Length Argument Functions ====
  
-You can create functions that take an arbitrary number of arguments by means of the splicing operator **@** which splices the table containing the parameters into the argument list.+You can create functions that take an arbitrary number of arguments (also known as a variable arity or polyadic function) by means of the splicing operator ''@'' which collects the actual arguments into a table:
 <code> <code>
->def sum(@args)+>def sum(@args) {
   def total := 0;    def total := 0; 
   foreach: { |el|  total := total + el } in: args;    foreach: { |el|  total := total + el } in: args; 
-  total}+  total 
 +};
 >><closure:sum> >><closure:sum>
 >sum(1,2,3) >sum(1,2,3)
Line 161: Line 216:
 </code> </code>
  
-In that case, the //sum// function still accepts an arbitrary number of arguments as long as two arguments are supplied. //a// and //b// are considered as mandatory arguments of the argument list. +In that case, the //sum// function still accepts an arbitrary number of arguments as long as two arguments are supplied. //a// and //b// are considered as mandatory arguments of the argument list.
  
-A function can also declare optional arguments as shown below. Optional arguments can be omitted in a function call. Internally, the default value provided in their definition is passed as the argument to the function. +The splice operator can also be used to transform a table into an argument list for a function, for example: 
 +<code> 
 +def args := [3,4,5]; 
 +> sum(1,2, @args); 
 +>> 15 
 +</code> 
 + 
 +One way to think about this is that the splice operator splices the ''args'' table into the table of actual arguments. The "rest" arguments do not necessarily need to be the last parameters, for example: 
 +<code> 
 +> sum(1,2,@args,6); 
 +>> 21 
 +</code> 
 + 
 + 
 + 
 + 
 + 
 + 
 +==== Optional Parameters ==== 
 + 
 +A function can also declare optional arguments as shown below. Optional arguments can be omitted in a function call. If this is the case, the default expression provided in their definition is evaluated and passed as argument to the function instead.
 <code> <code>
 >def incr( number, step := 1){ number + step} >def incr( number, step := 1){ number + step}
Line 171: Line 246:
 >incr(3,3) >incr(3,3)
 >>6 >>6
 +</code>
 +
 +As is customary in languages with the above optional arguments, AmbientTalk requires mandatory parameters to be defined //before// optional parameters, which should in turn be defined //before// a variable-argument parameter, if any.
 +
 +Let us show how to use optional arguments to define an auxilary function that creates matrices:
 +
 +<code>
 +def makeMatrix(n, m := n, init := { |i,j| 0}){
 +  def [i,j] := [0,0];
 +  def makeCol(i,j) {
 +     def col[m] { j := j + 1; init(i,j) }
 +  };
 +  def matrix[n] { i := i + 1; makeCol(i,j)}
 +};
 +>def c := makeMatrix(3);
 +>>[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
 +>c[1] := [1,2,3]
 +>>[1, 2, 3]
 +>c
 +>>[[1, 2, 3], [0, 0, 0], [0, 0, 0]]
 +>def d := makeMatrix(4,4, 
 +  {|i,j| if: (i == j) then: {1} else: {0}});
 +>> [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]
 </code> </code>
  
Line 187: Line 285:
 >def [get, set] := makeCell(42); >def [get, set] := makeCell(42);
 >>[<closure:getter>, <closure:setter>] >>[<closure:getter>, <closure:setter>]
 +>get();
 +>>42
 </code> </code>
  
-This example also illustrates how a function can make public some of its local fields or functions by returning them as its return value. The get and set could be then passed as arguments to other functions such as //trustedFunction(get,set)// and  //distrustedFunction(get)//+This example also illustrates how a function can make public some of its local fields or functions by returning them as its return value. The ''get'' and ''set'' operations can then be passed separately throughout the application, e.g. an application module that has read-only access to ''val'' only receives the ''get'' closure.
  
 ===== Blocks ===== ===== Blocks =====
  
-In AmbientTalk, blocks are merely syntactic sugar for anonymous closures (aka lambdas).  Blocks are creating using the {} braces in the form of:+In AmbientTalk, blocks are merely syntactic sugar for the creation of anonymous closures (also known as //lambdas//).  Blocks are creating using curly braces in the form of:
 <code> <code>
 { |<parlist>| <body> } { |<parlist>| <body> }
 </code> </code>
-If the block do not require any parameter, the |<parlist>| can be omitted.  Consider a basic block to sum two numbers:+ 
 +If the block does not require any parameter, the |<parlist>| can be omitted. Consider a basic block to sum two numbers:
 <code> <code>
->{| a, b| a+ b} (3,2)+>{|a, b| a+ b}(3,2)
 >>5 >>5
 </code> </code>
 Note that the argument list passed to the block can define the different types of arguments previously explained. Note that the argument list passed to the block can define the different types of arguments previously explained.
 <code> <code>
->{|a, b, @rest| +>def sum := {|a, b, @rest| 
    def total := a + b;     def total := a + b; 
    foreach: { |el| total := total + el} in: rest; total     foreach: { |el| total := total + el} in: rest; total 
- }(1,2,3)+ }
 +>><closure:lambda>  
 +>sum(1,2,3)
 >>6 >>6
 </code> </code>
Line 213: Line 316:
 This example also illustrates that blocks are also used to iterate over enumerations, such as in //foreach: {} in: table//. This example also illustrates that blocks are also used to iterate over enumerations, such as in //foreach: {} in: table//.
  
-AmbientTalk doesn’t support function assigment. Howeverone can assign blocks to variables. In order to call the block the name of the variable must be usedIf the block defined parameters, these are required to the call as argument listWhat follows is an example of such manipulation:+<note> 
 +AmbientTalk borrows its block syntax from languages like Smalltalk and Selfwhere the role of the curly braces is played by square brackets, i.e. ''[ args | body ]''. In AmbientTalk, the ''<parlist>'' is only used to denote parameters to the block, not also for temporary variables as in Smalltalk. 
 +</note> 
 + 
 + 
 + 
 +===== Keywords ===== 
 + 
 +AmbientTalk supports keyworded messages, as in Smalltalk and Self. We have already seen some examples of keyword messages in the previous sections such as the ''foreach:in:'' call. Here is how to define a simple ''map:onto:'' function that takes a closure as input and applies the closure to each element of a table: 
 <code> <code>
->def square := { |x| x * x } +>def map: clo onto: tbl { 
->><closure:lambda> +  def i := 0; 
->square(3+  def mapped[tbl.length] 
->>9+    := i+1; 
 +    clotbl[ i ] ) 
 +  }; 
 +} 
 +>> <closure:map:onto:>
 </code> </code>
  
-===== Keywords =====+It can be invoked as follows: 
 +<code> 
 +>map: square onto: [1,2,3] 
 +>>[1,4,9] 
 +</code>
  
-AmbientTalk supports keyword messages. We have already seen some examples of keyword messages in the previous sections such as the foreach structure. In AmbientTalk keywords are transformed by the parser into functions in the form:+In AmbientTalk keyworded functions and function calls are actually syntactic sugar. They are transformed by the parser into their canonical equivalent. Hence:
 <code> <code>
 def foo: arg1 bar: arg2 {...} def foo: arg1 bar: arg2 {...}
-def foo:bar:(arg1,arg2){..}+</code> 
 +is transformed into: 
 +<code> 
 +def foo:bar:(arg1,arg2){...}
 </code> </code>
 +
 +It is also possible to invoke keyworded functions using the canonical function application syntax:
 +
 +<code>
 +foo:bar:(1,2)
 +</code>
 +
 +<note important>
 +Be careful when nesting calls to keyworded functions: a call of the form ''foo: foo: 1 bar: 2 bar: 3'' is parsed as ''foo: (foo: 1 bar: 2 bar: 3)'', not as ''foo: (foo: 1 bar: 2) bar: 3''. It is recommended to always explicitly parenthesize nested keyworded function calls.
 +</note>
  
 ===== Native Data Types ===== ===== Native Data Types =====
 +
 +The basic data types in AmbientTalk are numbers (i.e. integers), fractions (i.e. double precision floating point numbers), text (i.e. strings), tables (i.e. arrays) and booleans. In fact, instances of these data types are nothing but objects and as such, they respond to a variety of native methods. Objects will be the subject of the next chapter of the tutorial. This section explains the basic data types and includes some examples how to manipulate them. The complete list of methods can be found in the language reference.
  
  
-The basic types in AmbientTalk are numbers, fractions, text, tables and booleans. In fact, these data types are nothing but objects and as such, they respond to a variety of native methods. Objects will be the subject of the next chapter of the tutorial. This section explains the basic data types and includes some examples how to manipulate them. The complete list of methods can be found in the language reference. 
  
 ==== Numerical data types ==== ==== Numerical data types ====
  
-AmbientTalk supports numbers and fractions which represent what other languages call integers and floating point numbers, respectively.  +AmbientTalk supports numbers and fractions which represent what other languages call integers and double precision floating point numbers, respectively.
  
-Note that since numerical types are objects in AmbientTalk, the traditional operators +,-,*,/, >, <, <=, >=, =, !=  are nothing but syntactic sugar for method invocations. Therefore,  //1+1// is internally translated into //1.+(1)//. Unary operators are just applications, e.g. //-5// is internally translated into //-(5)//. What follows are some basic examples of manipulations with numeric types:+Note that since numerical types are objects in AmbientTalk, the traditional operators %%+,-,*,/, >, <, <=, >=, =, !=%%  are nothing but syntactic sugar for method invocations. Therefore,  ''1+1'' is internally translated into ''1.+(1)''. Unary operators are just applications, e.g. ''-5'' is internally translated into ''-(5)''. What follows are some basic examples of manipulations with numeric types:
 <code> <code>
 >1.inc() >1.inc()
 >>2 >>2
->-1.abs() 
->>1 
 >1.cos() >1.cos()
 >>0.5403023058681398 >>0.5403023058681398
Line 257: Line 389:
 >>2 >>2
 </code> </code>
 +
 +Beware of the precedence rules for function application versus method invocation, which may lead to unexpected results, e.g.:
 +<code>
 +>-1.abs()
 +>>-1
 +</code>
 +This code is interpreted as ''-(1.abs())'', hence the result.
  
 Numbers also support some useful iterator methods such as: Numbers also support some useful iterator methods such as:
 <code> <code>
->6.to: 0 step: 2 do: { |i| system.println(i) }+>1.to: 5 do: { |i| system.println(i)} 
 +
 +
 +
 +
 +
 +>1.to: 5 step: 2 do: { |i| system.println(i)} 
 +
 +
 +
 +>6.downTo: 0 step: 2 do: { |i| system.println(i) }
 6 6
 4 4
 2 2
 +0
 >>nil  >>nil 
 >3.doTimes: { |i| system.println(i) } >3.doTimes: { |i| system.println(i) }
Line 298: Line 448:
 >>true >>true
 </code> </code>
 +
  
 ==== Tables ==== ==== Tables ====
    
  
-We have already introduce how to define tables. Let us now focus on how to manipulate them with the native methods provided by the table object.+We have already introduced how to define tables. Let us now focus on how to manipulate them with the native methods provided by the table object.
 <code> <code>
 >[1,2,3].filter: {|e| e != 2 } >[1,2,3].filter: {|e| e != 2 }
Line 343: Line 494:
 ==== Booleans ==== ==== Booleans ====
    
- +As any native type, booleans are objects so, they respond to keyword messages such as:
-AmbientTalk supports infix operators for booleans as &, | and !. As any native type, booleans are objects so, they respond to keyword messages such as:+
 <code> <code>
 <booleanexpr>.ifTrue: { ...}  <booleanexpr>.ifTrue: { ...} 
Line 352: Line 502:
 </code> </code>
  
-**=** and **!=** are the infix operators for equality and inequality. **true** and **false** are the boolean constant objects. What follows is some basic examples of boolean manipulation:+''='' and ''!='' are the infix operators for equality and inequality. The prefix operator ''!'' represents logical negation. ''true'' and ''false'' are the prototypical boolean singleton objects. What follows is some basic examples of boolean manipulation:
 <code> <code>
 >(0 < 1).ifTrue: { 0 }  >(0 < 1).ifTrue: { 0 } 
Line 359: Line 509:
 >>1 >>1
 > def [i, j] := [1,3] > def [i, j] := [1,3]
->>>[1, 3]+>>[1, 3]
 >{i < j}.whileTrue: { system.println(i); i := i + 1 } >{i < j}.whileTrue: { system.println(i); i := i + 1 }
 1 1
Line 366: Line 516:
 </code> </code>
  
-Boolean infix operators such as & and | are not shortcut. Thus, both arguments will be evaluated. For lazy evaluation, you should use the natives methods. For example, false.and: { 1/0 } will return false without executing the second argument.+Compound boolean expressions can be created by means of a boolean's ''and:'' and ''or:'' methodswhich both take a zero-argument closure as argument. For example, ''false.and: { 1/0 }'' will return ''false''. The block is not applied because a logical //and// with ''false'' always fails.
  
-===== Control Flow Structures =====+===== Control Flow Constructs =====
  
-Control flow structures are defined in the lexical root of AmbientTalk. The lexical root is an object containing globally visible native methods. We have already seen in the previous sections examples of usage of the foreach and if/then structures. The complete list of traditional control flow structures defined in AmbientTalk is shown below:+Control flow constructs are defined in the "lexical root". The lexical root is an object containing globally visible native methods (i.e. it is the top-level environment). We have already seen in the previous sections examples of use of the foreach and ''if:then:'' control structures. list of traditional control flow structures defined in AmbientTalk is shown below:
 <code> <code>
-if: booleanCondition then: { consequent } +if: booleanCondition then: consequentClosure 
-if: booleanCondition then: { consequent } else: { alternative } +if: booleanCondition then: consequentClosure else: alternativeClosure 
-while: { condition } do: body } +while: conditionClosure do: body 
-foreach: { |v| body } in: table ] +foreach: iteratorClosure in: table 
-do: { body } if: condition +do: bodyClosure if: condition 
-do: { body } unless: condition+do: bodyClosure unless: condition
 </code> </code>
-An example of usage for some of these structures is shown below in the definition of the sort function.+ 
 +<note warn> 
 +Note that ''conditionClosure'' in the ''while:do:'' construct denotes a //closure// that should return a boolean value. It needs to be a closure because the code is evaluated repeatedly until the closure returns false. ''bodyClosure'', ''consequentClosure'', ''alternativeClosure'' all denote zero-argument closures. As a general rule, all code that needs to be delayed or executed repeatedly must be wrapped in a closure. 
 +</note> 
 + 
 +The above definitions in the lexical root of AmbientTalk are simply convenience functions for the methods defined on booleans and closures. For example, an if-statement can also be encoded as a message send, as in Smalltalk: ''boolean.ifTrue: {...} ifFalse: {...}''
 + 
 +An example of usage for some of the above structures is shown below in the definition of the sort function.
 <code> <code>
 >def sort(table, cmp := { |e1,e2| e1 < e2 }) { >def sort(table, cmp := { |e1,e2| e1 < e2 }) {
- def quickSort(table, low, high) { +  def quickSort(table, low, high) { 
-     def left := low; +    def left := low; 
-     def right := high; +    def right := high; 
-     def pivot := table[(left+right) /- 2]; +    def pivot := table[(left+right) /- 2]; 
-     def save := nil; +    def save := nil; 
-        while: { left <= right } do: { +    while: { left <= right } do: { 
-     while: { cmp(table[left], pivot) } do: {  +      while: { cmp(table[left], pivot) } do: {  
-                left := left + 1  +        left := left + 1  
-            }; +      }; 
-     while: { cmp(pivot, table[right]) } do: {  +      while: { cmp(pivot, table[right]) } do: {  
-                right := right - 1  +        right := right - 1  
-            }; +      }; 
-     if: (left <= right) then: { +      if: (left <= right) then: { 
-     // swap elements +        // swap elements 
-     save := table[left]; +        save := table[left]; 
- table[left] := table[right]; +        table[left] := table[right]; 
- table[right] := save; +        table[right] := save; 
- left := left + 1; +        left := left + 1; 
- right := right - 1; +        right := right - 1; 
-     }; +      }; 
-    }; +    }; 
-    if: (low<right) then: { quickSort(table,low,right) }; +    if: (low<right) then: { quickSort(table,low,right) }; 
-    if: (high>left) then: { quickSort(table,left,high) }; +    if: (high>left) then: { quickSort(table,left,high) }; 
-    table; +    table; 
-   }; +  }; 
-   quickSort(table, 1, table.getLength()); +  quickSort(table, 1, table.length); 
- };+};
 >><closure:sort> >><closure:sort>
 >sort([2,37,6,4,5,8]) >sort([2,37,6,4,5,8])
 >>[2, 4, 5, 6, 8, 37] >>[2, 4, 5, 6, 8, 37]
 </code> </code>
 +
 +AmbientTalk has no ''return'' statement. To achieve a similar jump in the control flow, see the section on [[:at:tutorial:modular#escaping_continuations|escaping continuations]].
at/tutorial/basic.1176821477.txt.gz · Last modified: 2007/04/17 17:04 (external edit)