Functional Languages
      Interpretation
        in Smalltalk

Alexandre Bergel
Lero & DSG, Trinity College Dublin,
Irela...
Goal

     Studying the implementation of Scheme-like languages in an
 •
     object-oriented environment.
     Dynamic ve...
Outline

 1.    Essence of Scheme
 2.    SmallScheme overview
 3.    Modeling environment
 4.    Reading and parsing Schem...
Essence of Scheme

 1. Idea behind Scheme
 2. Examples
 3. Evaluation




Alexandre Bergel         4
Essence of Scheme

    “Scheme demonstrates that a very small number of rules
    for forming expression, with no restrict...
Manipulate parenthesized expressions

    Example of Scheme expression:
         10                    => 10
         ‘aSy...
Closure application

    In such expression:
         (expr expr1 expr2 ... exprN)



    1 - expr is first evaluated into ...
Special form (1/3)

    Exceptions to the previous rule are special forms.

   Condition of a if statement needs to be eva...
Special form (2/3)

    The body of a function is evaluated only when the function
    is applied
         (define (foo x)...
Special form (3/3)

    The body of an anonymous function is evaluated only when
    applied
         ((lambda (x) (* x x)...
Manipulate parenthesized expressions

    Sequence of evaluation
         (fac 2)




Alexandre Bergel             11
Manipulate parenthesized expressions

    Sequence of evaluation
         (fac 2)
         ((lambda (n) (if (= n 0)
      ...
Manipulate parenthesized expressions

    Sequence of evaluation
         (fac 2)
         ((lambda (n) (if (= n 0)
      ...
Manipulate parenthesized expressions

    Sequence of evaluation
         (fac 2)
         ((lambda (n) (if (= n 0)
      ...
SmallScheme overview




Alexandre Bergel        15
What is SmallScheme

     SmallScheme is an interpreter of a subset of Scheme in
 •
     Squeak.




Alexandre Bergel     ...
How to use SmallScheme

    In a programmatic way, Scheme expression can be
    evaluated:

         Scheme evalString: ‘(...
Writing Scheme in Smalltalk with SmallScheme

    Available on www.squeaksource.com

         MCHttpRepository
           ...
Class Hierarchy

                              SchemeObject
                               eval: anEnv


Boolean          ...
Modeling environment

 1. Set of bindings
 2. Hierarchy of environments
 3. Primitive definitions




Alexandre Bergel     ...
Environment


                        (let ((x 5) (y 7))
                          (let ((a 1) (b 2))
                    ...
Modeling Environments (1/3)

    Each environment has a set of bindings and a parent:

         Object subclass: #SchemeEn...
Modeling Environment (2/3)

    Creating a new environment:

         SchemeEnvironment class>>createFromEnv: anEnv
      ...
Modeling Environment (3/3)

    The root of the environment contains the primitives:
         SchemeEnvironment>>initializ...
Reading Scheme (R5RS, Section 1.2, 2, 7)

 1. Lexical analysis
 2. Parsing Scheme
 3. SmallScheme uses a minimal grammar

...
Lexical Analysis

    Defined by the class SchemeLexer

    Public methods are:
         SchemeLexer   class>>analyseString...
Parsing Scheme

    Defined by the class SchemeParser

    Public methods are:
         SchemeParser class>>lexer: aLexer
 ...
Grammar of SmallScheme

    Very minimal grammar
         expr =
           <number>        |
           <symbol>        |...
Example: the quote case

                   ‘x = (quote x) =
                                      quote x
    SchemeParse...
Primitive expression types (R5RS, Section 4.1)

 1.    Variable reference
 2.    Literal expression (quote)
 3.    Procedu...
Variable Reference

    Class SchemeSymbol:

         SchemeObject subclass: #SchemeSymbol
           instanceVariableName...
Literal expression (quote)

    Class SchemePrimitiveQuote:

         SchemePrimitive subclass: #SchemePrimitiveQuote

   ...
Procedure calls

    Associated with the lambda keyword
    SchemePrimitive subclass:
                #SchemePrimitiveMake...
Procedure calls

    SchemePrimitive subclass: #SchemePrimitiveClosure
      instanceVariableNames: 'arguments body env'

...
Procedure calls

    SchemePrimitiveClosure>>
       apply: listOfArguments withEnv: anEnvironment
       | env b valuesPr...
Creation of a new environment

    SchemePrimitiveClosure>>
       apply: listOfArguments withEnv: anEnvironment
       | ...
Binding variables in the new environment

    SchemePrimitiveClosure>>
       apply: listOfArguments withEnv: anEnvironmen...
Evaluating the body of the closure

    SchemePrimitiveClosure>>
       apply: listOfArguments withEnv: anEnvironment
    ...
And returning the last value...

    SchemePrimitiveClosure>>
       apply: listOfArguments withEnv: anEnvironment
       ...
Conditionals

    (if (= 2 3) ‘hello ‘world)
         SchemeObject subclass: #SchemePrimitiveIf

         SchemePrimitiveI...
Conditionals explained

    (if (= 2 3) ‘hello ‘world)
         SchemeObject subclass: #SchemePrimitiveIf

         Scheme...
Dynamic and static scoping




Alexandre Bergel        42
Dynamic vs static scoping

     Static scoping: variable references are bound to the
 •
     environment in which those va...
Static scoping: variable are statically bound

    The result is 15

         (let ((a 10))
           (let ((f (lambda (x...
Dynamic scoping: variable are dynamically bound

    The result is 5

         (let ((a 10))
           (let ((f (lambda (...
Static scoping with SmallScheme

    SchemePrimitiveClosure>>
       apply: listOfArguments withEnv: anEnvironment
       ...
Dynamic scoping with SmallScheme

    SchemePrimitiveFunction>>
       apply: listOfArguments withEnv: anEnvironment
     ...
Further notes

 1.    Symbiosis
 2.    Tail recursion
 3.    Grammar
 4.    Useful links




Alexandre Bergel        48
Symbiosis between Smalltalk and Scheme

    Few class extensions:

         Object>>asSmalltalkObject
           ^ self

 ...
Tail Recursion (R5RS, Section 3.5)

     But... We did not talk about tail recursion...
 •


     “Tail-recursion allows t...
Tail Recursion

    Intuitively there is a stack consumption in:
         (define (fac n)
           (if (= n 0)
         ...
Minimal or large grammar?

     SmallScheme uses a very minimal grammar.
 •


     The fact that we use a small grammar:
 ...
Useful links

    SmallScheme:
         http://www.squeaksource.com/Scheme

    Smacc (also available on SqueakMap):
     ...
Upcoming SlideShare
Loading in …5
×

2006 Small Scheme

2,604 views

Published on

Building a Scheme interpreter in Smalltalk. Lectured given at the Engineering school of Annecy (France) in 2006

Published in: Economy & Finance, Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
2,604
On SlideShare
0
From Embeds
0
Number of Embeds
113
Actions
Shares
0
Downloads
28
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

2006 Small Scheme

  1. 1. Functional Languages Interpretation in Smalltalk Alexandre Bergel Lero & DSG, Trinity College Dublin, Ireland Alexandre.Bergel@cs.tcd.ie www.cs.tcd.ie/Alexandre.Bergel November 2006
  2. 2. Goal Studying the implementation of Scheme-like languages in an • object-oriented environment. Dynamic versus static scoping • Alexandre Bergel 2
  3. 3. Outline 1. Essence of Scheme 2. SmallScheme overview 3. Modeling environment 4. Reading and parsing Scheme 5. Primitive expression types 6. Dynamic and static scoping 7. Further notes... Alexandre Bergel 3
  4. 4. Essence of Scheme 1. Idea behind Scheme 2. Examples 3. Evaluation Alexandre Bergel 4
  5. 5. Essence of Scheme “Scheme demonstrates that a very small number of rules for forming expression, with no restriction on how they are composed, suffice to form a practical and efficient programming language that is flexible enough to support most of the major programming paradigms in use today.” R5RS, page 2 The first description of Scheme was written in 1975 Alexandre Bergel 5
  6. 6. Manipulate parenthesized expressions Example of Scheme expression: 10 => 10 ‘aSymbol => aSymbol (+ 1 2) => 3 (if cond else then) (let ((a 10) (b 15) (+ a (* 2 b))) => 40 (define (fac n) (if (= n 0) 1 (* n (fac (- n 1))))) (fac 10) => 3628800 Alexandre Bergel 6
  7. 7. Closure application In such expression: (expr expr1 expr2 ... exprN) 1 - expr is first evaluated into f 2 - expr1 ... exprN are evaluated (most of the time sequentially). Let’s assume results are v1 ... vN 3 - v1 ... vN are applied to f Alexandre Bergel 7
  8. 8. Special form (1/3) Exceptions to the previous rule are special forms. Condition of a if statement needs to be evaluated before other arguments. (if (= x 0) ‘error (/ 1 x)) (= x 0) is evaluated first, then, according to this value, ‘error or (/ 1 x) is evaluated. Alexandre Bergel 8
  9. 9. Special form (2/3) The body of a function is evaluated only when the function is applied (define (foo x) (+ x (* 2 x))) It binds (lambda (x) (+ x (* 2 x))) to foo Alexandre Bergel 9
  10. 10. Special form (3/3) The body of an anonymous function is evaluated only when applied ((lambda (x) (* x x)) 2) Alexandre Bergel 10
  11. 11. Manipulate parenthesized expressions Sequence of evaluation (fac 2) Alexandre Bergel 11
  12. 12. Manipulate parenthesized expressions Sequence of evaluation (fac 2) ((lambda (n) (if (= n 0) 1 (* n (fac (- n 1))))) 2) Alexandre Bergel 12
  13. 13. Manipulate parenthesized expressions Sequence of evaluation (fac 2) ((lambda (n) (if (= n 0) 1 (* n (fac (- n 1))))) 2) (if (= 2 0) 1 (* 2 (fac (- 2 1)))) Alexandre Bergel 13
  14. 14. Manipulate parenthesized expressions Sequence of evaluation (fac 2) ((lambda (n) (if (= n 0) 1 (* n (fac (- n 1))))) 2) (if (= 2 0) 1 (* 2 (fac (- 2 1)))) (* 2 (fac (- 2 1))) (* 2 (fac (- 2 1))) (* 2 (fac (- 2 1))) (* 2 ((lambda (n) (if (= n 0) ...)) (- 2 1))) ... Alexandre Bergel 14
  15. 15. SmallScheme overview Alexandre Bergel 15
  16. 16. What is SmallScheme SmallScheme is an interpreter of a subset of Scheme in • Squeak. Alexandre Bergel 16
  17. 17. How to use SmallScheme In a programmatic way, Scheme expression can be evaluated: Scheme evalString: ‘(+ 2 3)’ => 5 Scheme evalString: ‘(define (fac n) (if (= n 0) 1 (* n (fac (- n 1))))) (fac 10)’ => 3628800 Alexandre Bergel 17
  18. 18. Writing Scheme in Smalltalk with SmallScheme Available on www.squeaksource.com MCHttpRepository location: 'http://www.squeaksource.com/Scheme' user: '' password: '' Alexandre Bergel 18
  19. 19. Class Hierarchy SchemeObject eval: anEnv Boolean Number Pair String Symbol Primitive shouldArgsBeEvaluated apply: args withEnv: anEnv Add Begin Car If Closure Cons MakeClosure ... Alexandre Bergel 19
  20. 20. Modeling environment 1. Set of bindings 2. Hierarchy of environments 3. Primitive definitions Alexandre Bergel 20
  21. 21. Environment (let ((x 5) (y 7)) (let ((a 1) (b 2)) ...)) + -> ... current - -> ... a -> 1 x -> 5 environment if -> ... b -> 2 y -> 7 ... An environment has a parent and a set of bindings Alexandre Bergel 21
  22. 22. Modeling Environments (1/3) Each environment has a set of bindings and a parent: Object subclass: #SchemeEnvironment instanceVariableNames: 'dictionary parent' SchemeEnvironment>>get: aSymbol ^ dictionary at: aSymbol asSmalltalkObject ifAbsent: [parent get: aSymbol] SchemeEnvironment>>at: aSymbol put: aValue dictionary at: aSymbol asSmalltalkObject put: aValue Alexandre Bergel 22
  23. 23. Modeling Environment (2/3) Creating a new environment: SchemeEnvironment class>>createFromEnv: anEnv ^ self new withEnvironment: anEnv. SchemeEnvironment>>withEnvironment: anEnv parent := anEnv. dictionary := Dictionary new. parent isNil ifTrue: [self initializePrimitives]. The environment root should be aware of different primitives. Alexandre Bergel 23
  24. 24. Modeling Environment (3/3) The root of the environment contains the primitives: SchemeEnvironment>>initializePrimitives {{'if' . SchemePrimitiveIf} . {'begin' . SchemePrimitiveBegin} . {'loadfile' . SchemePrimitiveLoadfile} . {'display' . SchemePrimitiveDisplay} . {'newline' . SchemePrimitiveNewline} . {'set!' . SchemePrimitiveSet} . {'let' . SchemePrimitiveLet} . {'>' . SchemePrimitiveGreater} . {'<' . SchemePrimitiveLesser} . {'+' . SchemePrimitiveAdd} . {'/' . SchemePrimitiveDiv} . {'*' . SchemePrimitiveMul} . {'-' . SchemePrimitiveSub} . {'=' . SchemePrimitiveEqual} . {'define' . SchemePrimitiveDefine} . {'quote' . SchemePrimitiveQuote} . {'lambda' . SchemePrimitiveMakeClosure} . {'null?' . SchemePrimitiveNull} . {'car' . SchemePrimitiveCar} . {'cdr' . SchemePrimitiveCdr} . {'cons' . SchemePrimitiveCons}} do: [:pair| self at: pair first asSymbol put: pair second new] Alexandre Bergel 24
  25. 25. Reading Scheme (R5RS, Section 1.2, 2, 7) 1. Lexical analysis 2. Parsing Scheme 3. SmallScheme uses a minimal grammar Alexandre Bergel 25
  26. 26. Lexical Analysis Defined by the class SchemeLexer Public methods are: SchemeLexer class>>analyseString: aString SchemeLexer >>next SchemeLexer >>getToken SchemeLexer >>getType The type of a token could either be a boolean, (, ), ‘(), an integer, a string, or a symbol. Alexandre Bergel 26
  27. 27. Parsing Scheme Defined by the class SchemeParser Public methods are: SchemeParser class>>lexer: aLexer SchemeParser >>next The next method returns a scheme object Alexandre Bergel 27
  28. 28. Grammar of SmallScheme Very minimal grammar expr = <number> | <symbol> | <string> | <nil> | ( expr+ ) | ‘ expr Primitive types are not part of the grammar. This gives more flexibility regarding the definition of those types. This is a major difference between SmallScheme and R5RS. Alexandre Bergel 28
  29. 29. Example: the quote case ‘x = (quote x) = quote x SchemeParser>>next |l| l := lexer next. ... lexer getType == SchemeLexer quoteID ifTrue: [ ^ SchemePair car: (‘quote’ asSchemeSymbol) cdr: (SchemePair car: self next cdr: nil)] Alexandre Bergel 29
  30. 30. Primitive expression types (R5RS, Section 4.1) 1. Variable reference 2. Literal expression (quote) 3. Procedure calls 4. Procedures (lambda) 5. Conditionals Alexandre Bergel 30
  31. 31. Variable Reference Class SchemeSymbol: SchemeObject subclass: #SchemeSymbol instanceVariableNames: 'value' SchemeSymbol>>eval: anEnvironment ^anEnvironment get: value Scheme evalString: ‘(define x 5) x’ => 5 Alexandre Bergel 31
  32. 32. Literal expression (quote) Class SchemePrimitiveQuote: SchemePrimitive subclass: #SchemePrimitiveQuote SchemePrimitiveQuote>> apply: listOfArguments withEnv: anEnvironment “arguments are not evaluated” ^listOfArguments car SchemePrimitiveQuote>>initialize self doNotEvaluateArguments Scheme evalString: '(quote (+ 1 2)' => (+ 1 2) Alexandre Bergel 32
  33. 33. Procedure calls Associated with the lambda keyword SchemePrimitive subclass: #SchemePrimitiveMakeClosure SchemePrimitiveMakeClosure>> apply: listOfArguments withEnv: anEnvironment |args body| args := listOfArguments car. body := listOfArguments cdr. ^ SchemePrimitiveClosure createWithArgs: args body: body env: anEnvironment 33 Alexandre Bergel
  34. 34. Procedure calls SchemePrimitive subclass: #SchemePrimitiveClosure instanceVariableNames: 'arguments body env' When we execute the following: Scheme evalString: ‘((lambda (x y) (+ x y)) 2 3)’ SchemePrimitiveClosure>> apply: listOfArguments withEnv: anEnvironment ... listOfArguments is a collection containing 2 and 3 Alexandre Bergel 34
  35. 35. Procedure calls SchemePrimitiveClosure>> apply: listOfArguments withEnv: anEnvironment | env b valuesProvided argNames | valuesProvided := listOfArguments. argNames := arguments. env := SchemeEnvironment createFromEnvironment: environment. [valuesProvided isNil] whileFalse: [ env at: argNames car put: valuesProvided car. argNames := argNames cdr. valuesProvided := valuesProvided cdr. ]. b := body. [b cdr isNil] whileFalse: [ b car eval: env. b := b cdr]. ^ b car eval: env Alexandre Bergel 35
  36. 36. Creation of a new environment SchemePrimitiveClosure>> apply: listOfArguments withEnv: anEnvironment | env b valuesProvided argNames | valuesProvided := listOfArguments. argNames := arguments. env := SchemeEnvironment createFromEnvironment: environment. [valuesProvided isNil] whileFalse: [ env at: argNames car put: valuesProvided car. argNames := argNames cdr. valuesProvided := valuesProvided cdr. ]. b := body. [b cdr isNil] whileFalse: [ b car eval: env. b := b cdr]. ^ b car eval: env Alexandre Bergel 36
  37. 37. Binding variables in the new environment SchemePrimitiveClosure>> apply: listOfArguments withEnv: anEnvironment | env b valuesProvided argNames | valuesProvided := listOfArguments. argNames := arguments. env := SchemeEnvironment createFromEnvironment: environment. [valuesProvided isNil] whileFalse: [ env at: argNames car put: valuesProvided car. argNames := argNames cdr. valuesProvided := valuesProvided cdr. ]. b := body. [b cdr isNil] whileFalse: [ b car eval: env. b := b cdr]. ^ b car eval: env Alexandre Bergel 37
  38. 38. Evaluating the body of the closure SchemePrimitiveClosure>> apply: listOfArguments withEnv: anEnvironment | env b valuesProvided argNames | valuesProvided := listOfArguments. argNames := arguments. env := SchemeEnvironment createFromEnvironment: environment. [valuesProvided isNil] whileFalse: [ env at: argNames car put: valuesProvided car. argNames := argNames cdr. valuesProvided := valuesProvided cdr. ]. b := body. [b cdr isNil] whileFalse: [ b car eval: env. b := b cdr]. ^ b car eval: env Alexandre Bergel 38
  39. 39. And returning the last value... SchemePrimitiveClosure>> apply: listOfArguments withEnv: anEnvironment | env b valuesProvided argNames | valuesProvided := listOfArguments. argNames := arguments. env := SchemeEnvironment createFromEnvironment: environment. [valuesProvided isNil] whileFalse: [ env at: argNames car put: valuesProvided car. argNames := argNames cdr. valuesProvided := valuesProvided cdr. ]. b := body. [b cdr isNil] whileFalse: [ b car eval: env. b := b cdr]. ^ b car eval: env Alexandre Bergel 39
  40. 40. Conditionals (if (= 2 3) ‘hello ‘world) SchemeObject subclass: #SchemePrimitiveIf SchemePrimitiveIf>>initialize self doNotEvaluateArguments SchemeSymbol>>apply: listOfArguments withEnv: anEnvironment | cond then else | cond := listOfArguments car. then := listOfArguments cdr car. listOfArguments cdr cdr ifNotNil: [else := listOfArguments cdr cdr car]. cond := cond eval: anEnvironment. cond isTrue ifTrue: [^ then eval: anEnvironment]. else notNil ifTrue: [^ else eval: anEnvironment]. ^ Scheme undefined Alexandre Bergel 40
  41. 41. Conditionals explained (if (= 2 3) ‘hello ‘world) SchemeObject subclass: #SchemePrimitiveIf SchemePrimitiveIf>>initialize self doNotEvaluateArguments SchemeSymbol>>apply: listOfArguments withEnv: anEnvironment | cond then else | cond := listOfArguments car. then := listOfArguments cdr car. listOfArguments cdr cdr ifNotNil: [else := listOfArguments cdr cdr car]. cond := cond eval: anEnvironment. cond isTrue ifTrue: [^ then eval: anEnvironment]. else notNil ifTrue: [^ else eval: anEnvironment]. ^ Scheme undefined Alexandre Bergel 41
  42. 42. Dynamic and static scoping Alexandre Bergel 42
  43. 43. Dynamic vs static scoping Static scoping: variable references are bound to the • environment in which those variables are defined. Dynamic scoping: variables are looked up dynamically, • according to the current environment Alexandre Bergel 43
  44. 44. Static scoping: variable are statically bound The result is 15 (let ((a 10)) (let ((f (lambda (x) (+ a x)))) (let ((a 0)) (f 5)))) Alexandre Bergel 44
  45. 45. Dynamic scoping: variable are dynamically bound The result is 5 (let ((a 10)) (let ((f (lambda (x) (+ a x)))) (let ((a 0)) (f 5)))) Alexandre Bergel 45
  46. 46. Static scoping with SmallScheme SchemePrimitiveClosure>> apply: listOfArguments withEnv: anEnvironment | env b valuesProvided argNames | valuesProvided := listOfArguments. argNames := arguments. env := SchemeEnvironment createFromEnvironment: environment. ... The variable environment is bound to the environment in which the closure was created. And not to the current environment. Alexandre Bergel 46
  47. 47. Dynamic scoping with SmallScheme SchemePrimitiveFunction>> apply: listOfArguments withEnv: anEnvironment | env b valuesProvided argNames | valuesProvided := listOfArguments. argNames := arguments. env := SchemeEnvironment createFromEnvironment: anEnvironment. ... The variable anEnvironment is bound to the current environment. Which is not the environment in which the closure was created. Alexandre Bergel 47
  48. 48. Further notes 1. Symbiosis 2. Tail recursion 3. Grammar 4. Useful links Alexandre Bergel 48
  49. 49. Symbiosis between Smalltalk and Scheme Few class extensions: Object>>asSmalltalkObject ^ self SchemeObject>>asSmalltalkObject self subclassResponsibility SchemeSymbol>>asSmalltalkObject ^ self value ... Alexandre Bergel 49
  50. 50. Tail Recursion (R5RS, Section 3.5) But... We did not talk about tail recursion... • “Tail-recursion allows the execution of an iterative • computation in constant space, even if the iterative computation is described by a syntactically recursive procedure.” Easy to implement in SmallScheme • Alexandre Bergel 50
  51. 51. Tail Recursion Intuitively there is a stack consumption in: (define (fac n) (if (= n 0) 1 (* n (fac (- n 1))))) But there is no stack consumption in: (define (fac n) (fac2 n 1)) (define (fac2 n acc) (if (= n 0) acc (fac2 (- n 1) (* acc n)))) Alexandre Bergel 51
  52. 52. Minimal or large grammar? SmallScheme uses a very minimal grammar. • The fact that we use a small grammar: • – new primitives can be added without having to modify the grammar – primitive names can be easily changed However we need: • – evaluation of arguments need to be controlled (evaluateArguments var in primitive) – need to have a MakeClosure primitive Alexandre Bergel 52
  53. 53. Useful links SmallScheme: http://www.squeaksource.com/Scheme Smacc (also available on SqueakMap): http://www.refactory.com/Software/SmaCC Squeak: http://www.squeak.org Everything you wish to know about Scheme: http://www.schemers.org Especially the R5RS: http://www.schemers.org/Documents/Standards/R5RS/ Alexandre Bergel 53

×