CSC447
Concepts of Programming Languages
Scope
Instructor: James Riely
Scope
-
Discussed in lecture on closures
Scope is not just for Variables
-
Applies to identifiers for
-
normal variables
-
function parameters
-
function type parameters
-
function/method names
-
class names
-
and more
Forward Declarations
-
C requires forward declarations for mutual recursion
-
Most other modern languages do not
char f (int x);
char g (int x) {
return f (x) + f (x);
}
char f (int x) {
if (x > 0) {
return g (x >> 8);
} else {
return 1;
}
}
Shadowing - Java
public class C {
static void f () {
int x = 1;
{
int y = x + 1;
{
int x = y + 1;
System.out.println ("x = " + x);
}
}
}
}
$ javac C.java
C.java:7: error: variable x is already defined in method f()
int x = y + 1;
^
1 error
Shadowing - Java
-
Fields have different treatment
public class C {
static int x = 1;
static void f () {
int y = x + 1;
{
int x = y + 1;
System.out.println ("x = " + x);
}
}
public static void main (String[] args) {
f ();
}
}
$ javac C.java
$ java C
x = 3
Shadowing C
-
C is less strict than Java (on shadowing)
int main () {
int x = 1;
{
int y = x + 1;
{
int x = y + 1;
printf ("x = %d\n", x);
}
}
}
$ gcc -o scope scope.c
$ ./scope
x = 3
Shadowing Scala
-
Scala is less strict than Java (on shadowing)
-
Indentation is significant here
object C2:
def main(args: Array[String]): Unit =
var x = 1
{
var y = x + 1
{
var x = y + 1
println ("x = " + x)
}
}
$ scala C2
x = 3
Shadowing Scala
-
Shadowing occurs in the REPL naturally
scala> val x = 1
x: Int = 1
scala> def f (a:Int) = x + a
f: (a: Int)Int
scala> f (10)
res0: Int = 11
scala> val x = 2
x: Int = 2
scala> x
res1: Int = 2
scala> f (10)
res2: Int = 11
Shadowing Scala
-
REPL behavior corresponds to
{
val x = 1;
def f (a:Int) = x + a
f (10)
{
val x = 2;
x
f (10)
...
}
}
C Initialization
-
Which
x
in the initializer, x + 1
| int main (void) { |
| int x = 10; |
| { |
| int x = x + 1; |
| printf ("x = %08x\n", x); |
| } |
| return 0; |
| } |
$ gcc -o scope scope.c
$ gcc -Wall -o scope scope.c
scope.c: In function ‘main’:
scope.c:5:7: warning: unused variable ‘x’ [-Wunused-variable]
scope.c:7:9: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
$ ./scope
x = 00000001
Java Initialization
-
Java requires that all variables be initialized before use.
| class C { |
| public static void main (String[] args) { |
| int x = 1 + x; |
| System.out.printf ("x = %08x\n", x); |
| } |
| } |
x.java:3: error: variable x might not have been initialized
int x = 1 + x
^
1 error
Scala Initialization
-
Scala fields are set to 0 before the initialization code is run
-
Recursion is allowed when initializing fields
scala> val x:Int = 1 + x
x: Int = 1
scala> :javap -p -c -filter -
Compiled from "<console>"
public class {
private final int x;
public int x();
0: aload_0
1: getfield #22 // Field x:I
4: ireturn
public ();
...
10: invokevirtual #28 // Method x:()I
13: iconst_1
14: iadd
15: putfield #22 // Field x:I
...
}
Local variables versus fields
-
Recursion disallowed for local variables
-
vals declared in the REPL are fields
val x = "dog"
def f() = { val x:Int = x + 1; x }
^
x is a forward reference extending over the definition of x
object o { val x:Int = x + 1 }
o.x
// defined object o
val res3: Int = 1
Recursive values in scala
-
Recursive values can be useful
val f : Int=>Int = (x) => if x<=0 then 1 else x * f(x-1)
f(5)
val f: Int => Int = Lambda/0x000000c801541bf0@731e0bff
val res0: Int = 120
Scala Initialization
-
Try with a list
-
Null pointer exception happening on print, since
null != Nil
scala> val xs:List[Int] = 1 :: xs
java.lang.NullPointerException
... 28 elided
Scala Initialization
-
Try to code these as our own objects
scala> case class S(head:Int, tail:S)
defined class S
scala> val ss:S = S(1, ss)
ss: S = S(1,null)
-
Need to delay evaluation of tail
scala> case class T(head:Int, tail:()=>T)
defined class T
scala> val ts:T = T(1, ()=>ts)
ts: T = T(1,$$Lambda$1324/2038353966@4d500865)
scala> ts.tail().tail().head
res14: Int = 1
Scala LazyList
-
LazyList do this for us!
-
#::
does not immediately evaluate its arguments
scala> val ones:LazyList[Int] = 1 #:: ones
val ones: LazyList[Int] = LazyList(<not computed>)
scala> ones.take (5)
res0: LazyList[Int] = LazyList(<not computed>)
scala> ones.take (5).toList
res1: List[Int] = List(1, 1, 1, 1, 1)
Scala LazyLists
-
Lazy evaluation of list elements
scala> def f (x:Int) : LazyList[Int] = { println (s"f(${x})"); x } #:: f(x+1)
f: (x: Int)LazyList[Int]
scala> val xs:LazyList[Int] = f(10)
xs: LazyList[Int] = <not computed>
scala> xs.take(4).toList
f(10)
f(11)
f(12)
f(13)
res12: List[Int] = List(10, 11, 12, 13)
scala> xs.take(4).toList
res13: List[Int] = List(10, 11, 12, 13)
scala> xs.take(6).toList
f(14)
f(15)
res14: List[Int] = List(10, 11, 12, 13, 14, 15)
Recursive values in scala
-
Recursive values can be useful
import scala.math.BigInt
val fibs: LazyList[BigInt] =
BigInt(0) #:: BigInt(1) #::
fibs
.zip(fibs.tail)
.map { (a,b) =>
println(s"Adding ${a} and ${b}")
a + b
}
println(fibs.take(5).toList)
println(fibs.take(8).toList)
Adding 0 and 1
Adding 1 and 1
Adding 1 and 2
List(0, 1, 1, 2, 3)
Adding 2 and 3
Adding 3 and 5
Adding 5 and 8
List(0, 1, 1, 2, 3, 5, 8, 13)
val fibs: LazyList[BigInt] = LazyList(0, 1, 1, 2, 3, 5, 8, 13, <not computed>)
Recursive values in scala
-
Recursive values can be useful
import scala.math.BigInt
val fibs: LazyList[BigInt] =
BigInt(0) #:: BigInt(1) #:: {
for
(a,b) <- fibs.zip(fibs.tail)
yield
println(s"Adding ${a} and ${b}")
a + b
}
println(fibs.take(5).toList)
println(fibs.take(8).toList)
Adding 0 and 1
Adding 1 and 1
Adding 1 and 2
List(0, 1, 1, 2, 3)
Adding 2 and 3
Adding 3 and 5
Adding 5 and 8
List(0, 1, 1, 2, 3, 5, 8, 13)
val fibs: LazyList[BigInt] = LazyList(0, 1, 1, 2, 3, 5, 8, 13, <not computed>)