COP 4020 Lecture -*- Outline -*- * Extensions for a Practical Language (2.6-2.8) Q: Why not just program in the kernel language? Too verbose ------------------------------------------ PICTURE OF SUGARS AND THE KERNEL |---------------------------------| | sugared language | | | | | | | | |------------| | | | kernel | | | | | | | | | | | |------------| | | | | | | | | | | | |---------------------------------| ------------------------------------------ ** Syntactic conventions (2.6.1) ------------------------------------------ SYNTACTIC SUGARS FOR VALUES, DECLARATIONS, AND PATTERNS Let E1, ..., En be expressions, lit, f1, ..., fn be literals, X1, ..., Xn be identifiers lit(f1:E1, ..., fn: En) % sugared ==> local X1 = E1 in % desugared ... local Xn = En in lit(f1:X1, ..., fn:Xn) end ... end local X = E in S end ==> R = (local X = E1 in E2 end) ==> local X in X=E1 R=E2 end local X1 ... Xn in S end ==> local X1 in ... local Xn in S end ... end {P E} ==> local X in X=E {P X} end e.g., {{CAdd 3} 5} ==> local = in end ==> where e.g., local H|T = [7 99] in R=(H+1)|T end ==> local H T X0 in X0 = [7 99] X0 = '|'(1:H 2:T) local HP1 = H+1 in R='|'(1:HP1 2:T) end end e.g., X0 = [7 99] ==> local Seven=7 NN=99 Nil=nil in local Tail='|'(1:NN 2:Nil) in X0 = '|'(1:Seven 2:Tail) end end ------------------------------------------ ... local X in X=E S end ... local X1 ... Xn X0 in X0 = X0 = end Q: Any conditions on the new identifiers in the local = ... one? the ones declared are the (free) identifiers occurring in the pattern Q: How does this get back to kernel syntax? Q: Why do they preserve meaning? ------------------------------------------ PATTERNS IN FUNCTION DEFINITIONS In general: proc {P ... 1 ... n ...} S end ==> proc {P ... X1 ... Xn ...} case X1 of 1 then ... case Xn of n then S end ... end end where X1 ... Xn are fresh Example fun {First A#_} A end ==> ------------------------------------------ ... fun {First X} case X of A#_ then A end end Q: How would you call First? Something like {First a#3} Q: How could you use this to define a Tail function for lists? fun {Tail _|T} T end Q: What sugars would help with if statements? elseif, inStatement andthen, orelse ------------------------------------------ IF-RELATED SUGARS ------------------------------------------ Q: How could you desugar the short-circuiting andthen and orelse? E1 andthen E2 ==> if E1 then E2 else false end E1 orelse E2 ==> if E1 then true else E2 end Q: What sugars help with case statements? [], andthen, nested patterns, omitting else ------------------------------------------ CASE-RELATED SUGARS ------------------------------------------ case X of P1 then S1 end ==> case X of P1 then S1 else raise raise error(kernel(noElse ...) ...) end end case X of P1 then S1 [] P2 then S2 ... [] Pn then Sn else S0 end ==> case X of P1 then S1 else case X of P2 then S2 else ... case X of Pn then Sn else S0 end ... end end ------------------------------------------ Q: How would you desugar the use of "andthen" in case clauses? case Y of L(F1:X1 ... Fn:Xn) andthen E(X1...Xn) then S1 else S2 end ==> ------------------------------------------ ... case Y of L(F1:X1 ... Fn:Xn) then if E(X1...Xn) then S1 else S2 end else S2 end ------------------------------------------ Q: How would you desugar the use of constants in case clauses? case Y of L(F1:E1 ... Fn:En) then S1 else S2 end ==> ------------------------------------------ case Y of L(F1:X1 ... Fn:Xn) andthen (X1==E1 andthen ... andthen Xn==En) then S1 else S2 end where the Xi are fresh Q: What are these like in C, C++, and Java? NOT switch! ------------------------------------------ NESTING MARKERS Use '$' to turn a statement into an expression Examples: R = {Obj get($)} ==> {Obj get(R)} ------------------------------------------ General rule (from "The Oz Notation") R = {P E1 ... Ej($) ... En} ==> {P E1 ... Ej(R) ... En} Q: What's the general rule for this? There can only be one $ in value-creation statement, and that is used as the result variable (identifier). The same applies, in essence to proc and fun statements (but in reverse): Q: How would you translate proc {P X} X=Y end ? P = proc {$ X} X=Y end ** Expressions and Functions (2.6.2) ------------------------------------------ DESUGARING FUNCTION STATMENTS AND CALLS Desugaring rule for fun statements fun {F X1 ... Xn} S E end ==> Desugaring calls to functions Z = {F Y1 ... Yn} ==> ------------------------------------------ ... proc {F X1 ... Xn R} S R=E end where R is fresh ... {F Y1 ... Yn Z} ------------------------------------------ EXPRESSIONS AND FUNCTIONS How to translate into kernel syntax: fun {Add1 X} X+3 end R = {Add1 3} R = {Add1 3 * 4} R = {Add1 {Add1 3 * 4}} ------------------------------------------ Q: Can you use these rules in C or Java? No, because you don't have call by reference, so you can't fill in the result identifier with a value. But you can do analogous transformations in C++... ** Interactive Interface (2.6.3) Q: How can you think of declare in terms of kernel syntax? like local, but it's open ended The key distinction is that it manipulates a single environement for the interactive interface (a global environment), which is not tied to a deliminted syntactic unit. ------------------------------------------ DECLARE EXAMPLE declare X X=1 % Feed the above lines first {Browse 'X='#X} declare X X=3 {Browse 'X='#X} % Vs. local declare Y Y=1 local Y in Y=2 {Browse 'Y='#Y} end {Browse 'Y='#Y} % Static scoping still declare X X=1 fun {F Z} X+Z end declare X X=3 % What does this do? {Browse '{F 4}='#{F 4}} ------------------------------------------ See Declare.oz use feed region in the above Sugars (don't need to emphasize) declare X = ... ==> declare X X = ... (Could you write a rule in the TTS for Oz for declare?) Need a new system, since the interactive loop manipulates its environment. TTS for interactive State = (Statement)* x Environment x Store T = { (nil, E, s) | (nil, E, s) in State } input[[S]] = ([S], {}, {}) output[[(nil, E, s)]] = s Transitions ( -i-> ) (declare X | Rest, E, s) -i-> (Rest, E', s') where E' = E + {X --> x} and x#s' = alloc(s) (S1 S2| Rest, E, s) -i-> (S1 | S2 | Rest, E, s) (S | Rest, E, s) -i-> (Rest, E, s') where S is not a declare or sequence statement and ([(S,E)],s) -->* (nil,s') ** Exceptions (2.7) Q: Why do we need exception handling? Why not just check return codes? error prone, non-local (non-modular) why error prone? because you have to remember to check for errors each time why non-modular? because can't just stop the program when something goes wrong (have to give other parts of the program a chance to clean up) ------------------------------------------ EXCEPTION HANDLING EXAMPLE proc {Assert B Msg} if B then skip else raise 'Assertion failed: ' # Msg end end end try {Assert false oops} {Browse skipped} catch S#M then {Browse S#M} end {Browse hi} ------------------------------------------ See exception-ex.oz Q: What happens in the above? Q: What languages have exception handling? C++, Java, C#, but not older versions of C ------------------------------------------ TRANSITIONS FOR EXCEPTIONS [try] ((try S1 catch X then S2 end, E) | Rest, s) --> ((S1, E) | (catch X then S2 end, E) | Rest, s) [raise] ((raise X end, E) | Rest, s) --> ((Sc, Ec') | Rest', s) where (catch Y then Sc end, Ec) | Rest' = findHandler(Rest) and Ec' = Ec + {Y -->E(X)} [raise-error] ((raise X end, E) | Rest, s) --> "Uncaught exception" where nil = findHandler(Rest) [catch] ((catch X then S2 end, E) | Rest, s) --> (Rest, s) ------------------------------------------ Q: Does the identifier in a raise have to be determined? no! Q: How would you define findHandler? look for the first catch statement in Rest (Note: no pattern matching, as catches always match everything in the kernel syntax) Q: What's that statement catch X then S2 end added in [try]? It's a semantics only statement, used in the semantics. This is a common "trick" used in semantics Q: Why is its semantics to do nothing? Because it is only activated during a raise, if we find it normally, then it should be ignored ------------------------------------------ SUGARS try S1 finally S2 end ==> try S1 catch X then S2 raise X end end S2 try S1 catch X then S2 finally S3 end ==> try try S1 catch X then S2 end finally S3 end ------------------------------------------ Q: Do these sugars give the same semantics as in Java? yes Q: How could we use pattern matching with try? yes Q: How would you desugar that? into a case that raises the exception if it doesn't match (p. 95) Q: What kind of data should be used for exceptions to help matching? records ** Functional Languages (2.8.1) *** foundational calculus (skip) ------------------------------------------ LAMBDA CALCULUS E ::= X | \X . E | E E | (E) parsing rules: scope of \X extends as far as possible application associates to the left example: \x . y z z means (\x . ((y z) z)) In Oz this is E ::= X | fun {$ X} E end | {E E} | (E) example: fun {$ X} {{Y Z} Z} end ------------------------------------------ This syntax is used in many papers... Q: In what way is this more primitive than the kernel language? Q: How would you write a TTS for the lambda calculus? *** functional programming on complete values The kernel allows partial values, and procedural syntax Q: How can we restrict the model to only work with complete values? - always bind identifier to a value when declared, i.e., only allow local X = V in S end local X={F Y1 ... Yn} in S end - use only function, not procedure syntax and translate nested calls before creation of data structure Then don't need undetermined locations in the store. Get a (strict) functional model, as in Scheme or SML. ** Entailment and disentailment (2.8.2.4) ------------------------------------------ ENTAILMENT X == Y means X and Y are structurally equal blocks if some nodes are different but one is undetermined What happens when we do: declare R1 R2 X R1 = pig(weight:100) R2 = pig(weight:X) {Browse R1 == R2} declare R1 R2 X R1 = pig(weight:X) R2 = pig(weight:X) {Browse R1 == R2} declare R1 R2 X R1 = horse(weight:X) R2 = pig(weight:X) {Browse R1 == R2} ------------------------------------------ See entailment.oz Q: What answers do these give? ** Static vs. Dynamic Typing (2.8.3) (skip, already covered) ------------------------------------------ STATIC VS. DYNAMIC TYPING def: A *type error* is def: A language has a *static type system* iff def: A *dynamic type system* catches type errors ------------------------------------------ ... an attempt to apply an operation to data outside its domain (as defined by the language) Q: What kind of typing does Java have? static (casts are NOT type errors) Q: Can a language have no type errors? Sure, BCPL does. Q: Is calling a number as a procedure a type error? yes Q: What are other examples? wrong number of arguments, adding ints and floats Q: What about accessing an array outside its bounds? not a type error (it's kind of expected :-) Q: What about casting an object to a type it doesn't have in Java? Java doesn't think of it that way ... type errors are discovered before runtime. ... at runtime (in general). Q: Which is more general? Q: Which makes separate compilation easier to implement? Q: Which is better for exploratory programming? Q: Which is better for safety critical systems? Q: Which has faster compiled code? Q: Which does Oz have? There is a variant of Oz, called Alice, that is statically typed. Q: Is it possible to blend static and dynamic typing? Yes