+
1
|
skin
|
login
|
edit
workshop
::
oxford_slides
user:anonymous
{{hide} {def BLOCK none}} ;; block | none ;; {CHRONO 15} {TITLE} {{CONTENT} {background #000 0.5} {{slide_content wait} {pre {pre {WAIT_SCREEN} '{CHURCH {{lambda {:n} {{lambda {:g :n} {:g :g :n}} {lambda {:g :n} {{{ISZERO :n} {CONS {lambda {:g :n} ONE} {lambda {:g :n} {MUL :n {:g :g {PRED :n}}}} }} :g :n}} :n}} FIVE}} -> {b 120} }} } ;; end wait {{slide_content 0} {img {@ src="data/brussels/nef.jpg" width="100%"}} {center {i La Sagrada Familia | Barcelona | Gaudi } } _h2 introduction _p The '{lambda way} project is a web application built on two engines: {pre • '{lambda tank}, a tiny wiki built as a thin overlay on top of any web browser, • '{lambda talk}, a purely functional language unifying authoring, styling and scripting in a single and coherent s-expression based syntax. } {{note.call context} {h4 but why?}} {{note.content context} {pre _ul There are hundred of wiki engines and hundred of languages! Why yet another wiki and another language nobody will ever want to use? _ul The HTML/CSS syntaxes, the DOM, Javascript, PHP, ..., and numerous frameworks, make coding web documents a painful struggle. {b John McCarthy} said: « An environment where the markup, styling and scripting is {b all s-expression} based would be nice. » _ul Hence the '{lambda way} project! } } ;; end context {{note.call presentation} {h4 this presentation ...}} {{note.content presentation} _p In this presentation we forget {b '{lambda tank}}, mainly a PHP engine managing text files on the server side, and progressively introduce {b '{lambda talk}}, a Javascript engine evaluating code in realtime on the client side. The making of '{lambda talk} is progressively described in three steps: {pre • 1) '{lambda word} built on a minimal set of {b 3 rules}, • 2) '{lambda calc} enhanced with {b numbers & operators}, • 3) '{lambda talk} on top of {b browsers' functionalities}. } } ;; end presentation {{note.call guilding_line} {h4 the guilding line ...}} {{note.content guilding_line} _p As a guilding line we give for each step a recursive algorithm computing the factorial of any natural number with all valid digits, for instance: {pre 5! = 120 50! = {{RED}304140932017133780436126081660647 68844377641568960512000000000000} // 65 valid digits } ;; _p {b Note:} Javascript returns {b 50! = 3.0414093201713376e+64} with only {b 15} valid digits. } ;; end guilding_line {center {{slide_call 1.}>>>> '{lambda word}}} } ;; end 0. {{slide_content 1.} {img {@ src="data/brussels/mies_pav.jpg" width="100%"}} {center {i Barcelona Pavilion | Mies van der Rohe {br} The Maxwell's equations of Architecture}} _h2 1. '{lambda word} {{note.call 1.1.} {h3 1.1. structure & evaluation}} {{note.content 1.1.} _p '{lambda word} is freely inspired by the {b λ-calculus}. _p A '{lambda word} expression is made of {b words}, {b abstractions} and {b applications}: {pre {b expression is [word|abstraction|application]* } {i where} 1) word is [^\s'{}]* -> word 2) abstraction is '{lambda {words} expression} -> word 3) application is '{expression expression} -> words } _ul {b 1)} a {b word} is any characters except spaces and curly braces, and is {b not evaluated} out of curly braces, _ul {b 2)} an {b abstraction} is a {b special form} (called {i a function}) {b selecting words} (called {i arguments}) in an {b expression} (called {i the body}), and is {b evaluated to a word} referencing an {b anonymous function} added to a single dictionary, _ul {b 3)} an {b application} is a {b simple form} calling an {b abstraction} to replace {b selected words} by some other {b words} (called {i values}), and is {b evaluated to words}. _p The evaluation {b stops} when all expressions have been replaced by {b words}. _p What can be done with so little? } ;; end 1.1. {{note.call 1.2.} {h3 1.2. examples}} {{note.content 1.2.} _p In order to make code easier to write and read, {b we add a second special form}: {pre {center '{def word expression}}} _p allowing to create {b constants} added to the dictionary and give {b names to anonymous functions.} {{note.call 1.2.1.} {h4 1.2.1 words}} {{note.content 1.2.1.} {pre Hello World -> Hello World } _p Out of curly braces words are not evaluated. {pre '{def HI Hello World} -> {def HI Hello World} HI, I just say '{HI} -> HI, I just say {HI} } _p Sequences of words can be given a name. } ;; end 1.2.1. {{note.call 1.2.2.} {h4 1.2.2. abstraction & application{sup (1)}}} {{note.content 1.2.2.} {pre '{lambda {o a} oh happy day!} -> _LAMB_10 } _p This abstraction defines {b o} and {b a} as words whose occurences in the expression {b oh happy day!} will be replaced by some other words, and returns the reference to an anonymous function. {pre '{{lambda {o a} oh happy day!} oOOOo aaAAaa} -> {{lambda {o a} oh happy day!} oOOOo aaAAaa} } _p Here the abstraction is {b defined and immediately called}. The abstraction is first evaluated, the application gets the awaited values {b oOOOo} and {b aaAAaa}, calls the abstraction which makes the substitution and returns the result, {b oOOOoh haaAAaappy daaAAaay!}. _p Abstractions can be given a name and then called easily several times: {pre '{def GOOD_DAY {lambda {o a} oh happy day!}} -> {def GOOD_DAY {lambda {o a} oh happy day!}} '{GOOD_DAY oOOOo aaAaa} -> oOOOoh haaAaappy daaAaay! '{GOOD_DAY ♠ ♥} -> ♠h h♥ppy d♥y! } } ;; end 1.2.2. {{note.call 1.2.3.} {h4 1.2.3. abstraction & application{sup (2)}}} {{note.content 1.2.3.} _p Most of the time expressions are nested. _p The expressions below return the first and the second word of {b Hello World}: {pre '{{lambda {z} {z {lambda {x y} x}}} {{lambda {x y z} {z x y}} Hello World}} -> {b Hello} '{{lambda {z} {z {lambda {x y} y}}} {{lambda {x y z} {z x y}} Hello World}} -> {b World} } _p Using names helps to recognize a {b data structure} and its {b accessors}: {pre '{def CONS {lambda {:x :y :z} {:z :x :y}}} -> {def CONS {lambda {:x :y :z} {:z :x :y}}} '{def CAR {lambda {:z} {:z {lambda {:x :y} :x}}}} -> {def CAR {lambda {:z} {:z {lambda {:x :y} :x}}}} '{def CDR {lambda {:z} {:z {lambda {:x :y} :y}}}} -> {def CDR {lambda {:z} {:z {lambda {:x :y} :y}}}} '{CAR {CONS Hello World}} -> Hello '{CAR {CONS Hello World}} -> World } _p The {b "unreadable" expression} below is evaluated to an anonymous function: {pre '{{lambda {n} {{lambda {g n} {g g n}} {lambda {g n} {{{{lambda {n} {n {lambda {x} {lambda {z} {z {lambda {x y} y}}}} {lambda {z} {z {lambda {x y} x}}}}} n} {{lambda {x y z} {z x y}} {lambda {g n} {lambda {f x} {f x}}} {lambda {g n} {{lambda {n m f x} {n {m f} x}} n {g g {{lambda {n} {{lambda {z} {z {lambda {x y} x}}} {{n {lambda {p} {{lambda {x y z} {z x y}} {{lambda {z} {z {lambda {x y} y}}} p} {{lambda {n f x} {f {{n f} x}}} {{lambda {z} {z {lambda {x y} y}}} p}}}}} {{lambda {x y z} {z x y}} {lambda {f x} x} {lambda {f x} x}}}}} n}}}}}} g n }} n}} {lambda {f x} {f {f {f {f {f x}}}}}}} -> _LAMB_167 } _p We will demonstrate later that this expression evaluates to the {b factorial of 5}. } ;; end 1.2.3. } ;; end 1.2. {{note.call 1.3.} {h3 1.3. implementation}} {{note.content 1.3.} _p '{lambda talk} is not implemented following the standard process, {b code -> tokens -> tree -> eval}. {b The code is a string from beginning to end}. At each keyboard input, the code is processed by a single function, {b do_eval()}, which returns words sent to the browser for the final evaluation and display. {pre var do_eval = function( code ) '{ code = pre_processing( code ); code = eval_lambdas( code ); // {lambda {args} body} code = eval_defs( code ); // {def name body} code = eval_forms( code ); // {a {b {c ...}}} code = post_processing( code ); return code }; } _p The {b eval_lambdas()} and {b eval_defs()} functions match special forms {b '{lambda {args} body}} and {b '{def name body}}, evaluate them to {b anonymous functions} and {b constants}, add them to the dictionary and replace them in the code string by words. Then the {b eval_forms()} function progressively replaces nested forms '{a {b {c ...}}} by words. {{note.call 1.3.1.} {h4 1.3.1. evaluation of nested forms}} {{note.content 1.3.1.} {img {@ src="data/brussels/turing_machine.png" width="100%"}} {pre . {{RED}[ DICTIONARY ]} [ _LAMB_1 _LAMB_2 _LAMB_3 _LAMB_4 ] [ CONS CAR CDR .. HI GOOD_DAY ... ] [ .. _LAMB_167 .. .. .. ... .. .. ] ↑ ↓ ↑ ↓ ↑ ↓ ← ← ← {{CYAN} [regexpWindow]} → → → .... .... {{CYAN} '{GOOD_DAY ♠ ♥}} .... {b {{RED}'{GOOD_DAY ooOOoo aaAAaa}}} .... Hello World .... HI, I just say {{RED}'{HI}} .... .... .... .... {{RED}'{CAR {CONS Hello World}}} .... .... .... .... .... ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ {b evaluated to:} ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ .... .... {{CYAN}♠h h♥ppy day!} .... {{RED}oOOOoh haaAaappy day!} .... Hello World .... HI, I just say {{RED}Hello World} .... .... .... .... {{RED}Hello} .... .... .... .... .... .... .... .... } _p Using a {b single regular expression} the {b eval_forms() function} loops over the code {b string}, skips the words out of curly braces, matches nested forms {b '{first rest}} {b from inside out}, and replaces them by words. {b The repeated substitutions inside the code string overcomes limitations of regular language}. It's a kind of {b Turing machine}. ;; It could be seen as a {b Turing machine} where a regexp based window runs, reads and writes on a long tape containing nested forms, according to primitive functions stored in a dictionary. _p The Javascript implementation of the {b eval_forms()} function is amazingly simple: {pre var DICT = '{}; // {{RED}initially empty} var {b eval_forms} = function( str ) '{ while (str != (str = str.replace(leaf, eval_leaf))) ; return str }; [ first ] space [ rest ] var {b leaf} = {{RED}/\'{([^\s{}]*)(?:[\s]*)([^{}]*)\}/g}; var {b eval_leaf} = function(_,f,r) '{ return (DICT.hasOwnProperty(f)) ? DICT[f].apply( null, [r] ) : '('+f+' '+r+')' }; } } ;; end 1.3.1. {{note.call 1.3.2.} {h4 1.3.2. how do lambdas work?}} {{note.content 1.3.2.} _p Lambdas use {b arguments as Regular Expressions} to successively replace occurences found in the function's body by the given values. {pre °° . ___________________________ ↑↑ ↓↓↓↓ 1: {{lambda {:o :a} :oh h:appy day!} oOOOo aaAAaa} ↑↑________________↓↓↓↓ ____________________________ ↑↑ ↓↓↓↓↓ 2: {{lambda {:a} oOOOoh h:appy day!} aaAAaa} ↑↑___________↓↓↓↓↓ 3: oOOOoh haaAAaappy day! °°} _p Note how prefixing with a colon arguments and their occurences in the body, prevented the word {b day} to be modified. Even if not always necessary, {b prefixing/escaping local variables} is higly recommended and will be done systematically. _p Lambdas have the following properties: {pre - {b lambdas are first class functions}, they can be called as functions' arguments and returned from functions - {b lambdas are pure black boxes}, -- they don't create closures and are context free, -- inner lambdas don't see outer functions' arguments, -- there are no lexical scoping, no free variables, - {b lambdas accept {{RED} de facto} partial function application}: called with a number of values lesser than its arity, a lambda {b memorizes} the given values and returns a {b new lambda} waiting for the rest. } _p Note: Currying uses partial application to return a function until all arguments are filled. } ;; end 1.3.2. {{note.call 1.3.3.} {h4 1.3.3. conclusion}} {{note.content 1.3.3.} _h6 >>> more to read later: {{note.call tracing} tracing the '{CAR {CONS Hello World}} -> Hello ...} {{note.content tracing} _p To better understand the whole evaluation process, we {b trace} the example of section 1.2.3. returning {b Hello} from {b Hello World}: {pre '{{lambda {z} {z {lambda {x y} x}}} {{lambda {x y z} {z x y}} Hello World}} } _p Nested lambdas are first matched and abstracted: {pre °° 1: match of the first lambda -- {lambda {z} {z {lambda {x y} x}}} a) evaluation of inner lambda: {lambda {x y} x} -> _LAMB_1 b) evaluation of outer lambda: {lambda {z} {z _LAMB_1}} -> _LAMB_2 2: match of the second lambda: -- {lambda {x y z} {z x y}} -> _LAMB_3 -> {_LAMB_2 {_LAMB_3 Hello World}} °°} _p Then simple forms are evaluated from inside out. {b _LAMB_3} gets two values instead of three and returns a lambda waiting for the third, {b _LAMB_4}: {pre °° 1: {_LAMB_3 Hello World} {{lambda {x y z} {z x y}} Hello World} -> {lambda {z} {z Hello World}} -> _LAMB_4 °°} _p Then: {pre °° 2: {_LAMB_2 _LAMB_4} -- {{lambda {z} {z _LAMB_1}} _LAMB_4} 3: {_LAMB_4 _LAMB_1} -- {{lambda {z} {z Hello World}} _LAMB_1}} 4: {_LAMB_1 Hello World} -- {{lambda {x y} x} Hello World} 5: Hello °°} } ;; end tracing _p To conclude this section, we remember that '{lambda word} doesn't know anything but words and it's difficult to believe that in example 1.2.3. we have computed the factorial of {b 5}. _p It's time to {b give life to words}. We leave '{lambda word} and enter in '{lambda calc}. } ;; end 1.3.3. } ;; end 1.3. {center {{slide_call 2.}>>>> '{lambda calc}}} } ;; end 1. {{slide_content 2.} {img {@ src="data/brussels/operators.jpg" width="100%"}} {center {i Code is Data, Data is Code.}} _h2 2. '{lambda calc} {{note.call 2.1.} {h3 2.1. church numbers}} {{note.content 2.1.} _p '{lambda calc} is '{lambda word} + numbers & operators. _p After {b Alonzo CHURCH}, we define a CHURCH number {b N} as a function iterating N times the application of a function {b f} on a variable {b x}. » For instance: {pre '{def ZERO {lambda {:f :x} :x}} -> {def ZERO {lambda {:f :x} :x}} '{def ONE {lambda {:f :x} {:f :x}}} -> {def ONE {lambda {:f :x} {:f :x}}} '{def TWO {lambda {:f :x} {:f {:f :x}}}} -> {def TWO {lambda {:f :x} {:f {:f :x}}}} '{def THREE {lambda {:f :x} {:f {:f {:f :x}}}}} -> {def THREE {lambda {:f :x} {:f {:f {:f :x}}}}} '{def FOUR {lambda {:f :x} {:f {:f {:f {:f :x}}}}}} -> {def FOUR {lambda {:f :x} {:f {:f {:f {:f :x}}}}}} '{def FIVE {lambda {:f :x} {:f {:f {:f {:f {:f :x}}}}}}} -> {def FIVE {lambda {:f :x} {:f {:f {:f {:f {:f :x}}}}}}} } _p Applied to a couple of any words {pre '{FIVE foo bar} -> (foo (foo (foo (foo (foo bar))))) } _p CHURCH number return {b strange things} inviting to define a function displaying them in a more familiar shape: {pre '{def CHURCH {lambda {:n} {{:n {lambda {:x} {+ :x 1}}} 0}}} -> {def CHURCH {lambda {:n} {{:n {lambda {:x} {+ :x 1}}} 0}}} '{CHURCH ZERO} -> 0 '{CHURCH ONE} -> 1 '{CHURCH TWO} -> 2 } _p Note that the {b CHURCH} function is built on a primitive function, {b '+'}, and on numbers, [{b 0, 1, ...}] which are not supposed to exist at this point. {b Consider that it's only for readability.} } ;; end 2.1. {{note.call 2.2.} {h3 2.2. operators}} {{note.content 2.2.} _p The definition of CHURCH numbers gives the basis of a set of operators. {{note.call 2.2.1.} {h4 2.2.1. a first set of operators}} {{note.content 2.2.1.} _p The set [{b SUCCessor ADDition MULtiplication POWER}] is rather easy to define: {pre '{def SUCC {lambda {:n :f :x} {:f {{:n :f} :x}}}} -> {def SUCC {lambda {:n :f :x} {:f {{:n :f} :x}}}} '{def ADD {lambda {:n :m :f :x} {{:n :f} {{:m :f} :x}}}} -> {def ADD {lambda {:n :m :f :x} {{:n :f} {{:m :f} :x}}}} '{def MUL {lambda {:n :m :f :x} {:m {:n :f} :x}}} -> {def MUL {lambda {:n :m :f :x} {:m {:n :f} :x}}} '{def POWER {lambda {:n :m :f :x} {{:m :n :f} :x}}} -> {def POWER {lambda {:n :m :f :x} {{:m :n :f} :x}}} '{CHURCH {SUCC ZERO}} -> 1 '{CHURCH {SUCC ONE}} -> 2 '{CHURCH {ADD TWO THREE}} -> 5 // 2+3 '{CHURCH {MUL TWO THREE}} -> 6 // 2*3 '{CHURCH {POWER TWO THREE}} -> 8 // 2^3 } } ;; end 2.2.1. {{note.call 2.2.2.} {h4 2.2.2. a second set of operators}} {{note.content 2.2.2.} _p Building opposite operators like [{b PREDecessor, SUBTRACTion, DIVIDE}] is not so easy, and {b Church} himself avoided them in the primitive version of λ-calculus. The answer was given by {b Stephen Cole Kleene}, the father of {b Regular Expressions}: {pre {center Church numbers can be used to iterate{br}and pairs to aggregate.}} _p Applied to the {b PRED} operator, this sequence of transformations: {pre 1: [0,0] -> [0,0+1] 2: [1,1] -> [1,1+1] 3: [2,2] -> [2,2+1] 4: [3,3] -> [3,3+1] {b 5}: [4,4] -> [{b 4},4+1] -> {b 4} // the predecessor of {b 5} } _p leads to define two functions, {b PRED.PAIR} and {b PRED}: _ul 1) {b PRED.PAIR} gets a pair {b [a,a]} and returns a pair {b [a,a+1]}, _ul 2) {b PRED} computes {b n} iterations of {b PRED.PAIR} starting on the pair {b [0,0]} and leading to the pair {b [n-1,n]} and returns the first, {b n-1}. _p Put in final form: {pre '{def PRED {def PRED.PAIR {lambda {:p} {CONS {CDR :p} {SUCC {CDR :p}}}}} {lambda {:n} {CAR {{:n PRED.PAIR} {CONS ZERO ZERO}}}}} -> {def PRED {def PRED.PAIR {lambda {:p} {CONS {CDR :p} {SUCC {CDR :p}}}}} {lambda {:n} {CAR {{:n PRED.PAIR} {CONS ZERO ZERO}}}}} '{CHURCH {PRED ZERO}} -> 0 // zero has no predecessor! '{CHURCH {PRED FIVE}} -> 4 } _h6 >>> more to read later: {{note.call iteration} other iteration processes can be built in a similar way ...} {{note.content iteration} _p A similar scheme can be used to build the {b ITER} operator with which, for instance, we can compute this expression, {b 1*2*3*4*5}. Again: {pre 1: [1,1] -> [1+1,1*1] 2: [2,1] -> [2+1,2*1] 3: [3,2] -> [3+1,3*2] 4: [4,6] -> [4+1,4*6] {b 5}: [5,24] -> [5+1,{b 5*24}] -> {b 120} // the factorial of {b 5} } _ul we define a function {b ITER.PAIR} getting a pair {b [a,b]} and returning a pair {b [a+1,a*b]}, _ul the function {b ITER} computes {b n} iterations of {b ITER.PAIR}, starting on the pair {b [1,1]} and leading to the pair {b [n,n!]} and returns the second, {b n!} {pre '{def ITER {def ITER.PAIR {lambda {:p} {CONS {SUCC {CAR :p}} {MUL {CAR :p} {CDR :p}}}}} {lambda {:n} {CDR {{:n ITER.PAIR} {CONS ONE ONE}}}}} -> {def ITER {def ITER.PAIR {lambda {:p} {CONS {SUCC {CAR :p}} {MUL {CAR :p} {CDR :p}}}}} {lambda {:n} {CDR {{:n ITER.PAIR} {CONS ONE ONE}}}}} '{CHURCH {ITER TWO}} -> 2 '{CHURCH {ITER THREE}} -> 6 '{CHURCH {ITER FOUR}} -> 24 '{CHURCH {ITER FIVE}} -> 120 } _p We just got the factorial of {b 5} using iteration! The algorithm could be easily generalized to compute other kinds of iterations. For instance: {pre '{def APPLY {def APPLY.PAIR {lambda {:op :p} {CONS {SUCC {CAR :p}} {:op {CAR :p} {CDR :p}}}}} {lambda {:op from :a to :b} {CDR {{:b {APPLY.PAIR :op}} {CONS ONE :a}}}}} -> {def APPLY {def APPLY.PAIR {lambda {:op :p} {CONS {SUCC {CAR :p}} {:op {CAR :p} {CDR :p}}}}} {lambda {:op from :a to :b} {CDR {{:b {APPLY.PAIR :op}} {CONS ONE :a}}}}} '{CHURCH {APPLY ADD from ZERO to FIVE}} -> 15 // 1+2+3+4+5 '{CHURCH {APPLY MUL from ONE to FIVE}} -> 120 // 1*2*3*4*5 } } ;; end iteration } ;; end 2.2.2. } ;; 2.2. {{note.call 2.3.} {h3 2.3. recursion}} {{note.content 2.3.} °°° {img {@ src="data/brussels/recursion.jpg" width="100%"}} {center « Iteration is Human, Recursion Divine! »} °°° {{note.call 2.3.1.} {h4 2.3.1. control structure}} {{note.content 2.3.1.} {pre {center fac(n) = (n==0) ? 1 : n*fac(n-1)}} _p This compact definition of the factorial highlights a control structure, {b ISZERO? [FIRST REST]}, reminding the {b [CONS CAR CDR]} data structure. The main difference is that a control structure {b must be evaluated lazily} - only one term must be evaluated - contrary to the data structure, made of user defined functions and {b evaluated eagerly} - all terms are evaluated. Lambdas will help to introduce a touch of {b laziness}. We "sketch" the body of the factorial: {pre fac(n) is {b '{{{ISZERO n} {CONS T F}} n}} // force } _p where: {pre - {b ISZERO} returns CAR if n == 0 else CDR - {b T} is '{lambda {n} "1"} // delay - {b F} is '{lambda {n} "n*fac(n-1)"} // delay } _p and we "sketch" its evaluation: {pre fac(n) = {b if} (n == 0) {b then} '{ISZERO 0} returns {b CAR} and fac(0) becomes {b '{{CAR {CONS T F}} 0}}, evaluated to {b '{T 0}}, evaluated to {b 1} // base case {b else} '{ISZERO n} returns {b CDR} and fac(n) becomes {b '{{CDR {CONS T F}} n}}, evaluated to {b '{F n}}, evaluated to {b n*fac(n-1)} // recursion } _p Put in final form: {pre '{def ISZERO {lambda {:n} {:n {lambda {:x} CDR} CAR}}} -> {def ISZERO {lambda {:n} {:n {lambda {:x} CDR} CAR}}} '{def FAC {lambda {:n} {{{ISZERO :n} {CONS {lambda {:n} ONE} {lambda {:n} {MUL :n {FAC {PRED :n}}}}}} :n}}} -> {def FAC {lambda {:n} {{{ISZERO :n} {CONS {lambda {:n} ONE} {lambda {:n} {MUL :n {FAC {PRED :n}}}} }} :n}}} '{CHURCH {FAC FIVE}} -> {b 120} } _p Thanks to {b lambdas & pairs}, we could define a recursive process without any boolean and any Y-combinator. But we have to demonstrate that this code is equivalent to the {b unreadable expression} shown in section 1.2.3. and the Y-combinator will help us to do so. _h6 >>> more to read later: {{note.call laziness} tracing the evaluation of FAC ...} {{note.content laziness} _ul 1) evaluation of nested lambdas: {pre °° :1 {def FAC {lambda {:n} {{{ISZERO :n} {CONS _LAMB_0 _LAMB_1} }} :n}}} :2 {def FAC _LAMB_2} :3 FAC °°} _ul 2) evaluation of simple forms: {pre °° {FAC FIVE -> {_LAMB_2 FIVE} -> {{lambda {:n} {{{ISZERO :n} {CONS _LAMB_0 _LAMB_1 }}} :n}}} FIVE} -> {{{ISZERO FIVE} {CONS _LAMB_0 _LAMB_1}} FIVE} -> {{CDR _CONS_xx} FIVE} -> {_LAMB_1 FIVE}} -> {{lambda {:n} {MUL :n {FAC {PRED :n}}}} FIVE} -> {MUL FIVE {FAC {PRED FIVE}}} -> {MUL FIVE {FAC FOUR}} .. -> {MUL FIVE {MUL FOUR {MUL THREE {MUL TWO {MUL ONE {FAC ZERO}}}}}} -> {MUL FIVE {MUL FOUR {MUL THREE {MUL TWO {MUL ONE ONE}}}}} °°} } ;; end lazyness } ;; end 2.3.1. {{note.call 2.3.2.} {h4 2.3.2. Y-combinator}} {{note.content 2.3.2.} _p First, we replace the {b recursive} function by an {b almost-recursive} one, on which we will apply an appropriate {b Y-combinator}: {pre '{def Y {lambda {:g :n} {:g :g :n}}} -> {def Y {lambda {:g :n} {:g :g :n}}} '{def ALMOST_FAC {lambda {:g :n} {{{ISZERO :n} {CONS {lambda {:g :n} ONE} {lambda {:g :n} {MUL :n {:g :g {PRED :n}}} } }} :g :n}}} -> {def ALMOST_FAC {lambda {:g :n} {{{ISZERO :n} {CONS {lambda {:g :n} ONE} {lambda {:g :n} {MUL :n {:g :g {PRED :n}}} } }} :g :n}}} '{CHURCH {Y ALMOST_FAC FIVE}} -> {b 120} } _p We mix the both: {pre '{def YFAC {lambda {:n} {{lambda {:g :n} {:g :g :n}} {lambda {:g :n} {{{ISZERO :n} {CONS {lambda {:g :n} ONE} {lambda {:g :n} {MUL :n {:g :g {PRED :n}}} } }} :g :n }} :n}}} -> {def YFAC {lambda {:n} {{lambda {:g :n} {:g :g :n}} {lambda {:g :n} {{{ISZERO :n} {CONS {lambda {:g :n} ONE} {lambda {:g :n} {MUL :n {:g :g {PRED :n}}} } }} :g :n }} :n}}} '{CHURCH {YFAC FIVE}} -> {b 120} } _p Throwing away the name, we define and immediately call the lambda on the value {b FIVE}: {pre '{CHURCH {{lambda {:n} {{lambda {:g :n} {:g :g :n}} {lambda {:g :n} {{{ISZERO :n} {CONS {lambda {:g :n} ONE} {lambda {:g :n} {MUL :n {:g :g {PRED :n}}} } }} :g :n }} :n}} FIVE}} -> {b 120} } _p And finally, replacing all constants by their lambda based values, we get the {b pure λ-calculus} expression shown in section 1.2.3.. {pre '{CHURCH {{lambda {:n} {{lambda {:g :n} {:g :g :n}} {lambda {:g :n} {{{{lambda {:n} {:n {lambda {:x} {lambda {:z} {:z {lambda {:x :y} :y}}}} {lambda {:z} {:z {lambda {:x :y} :x}}}}} :n} {{lambda {:x :y :z} {:z :x :y}} {lambda {:g :n} {lambda {:f :x} {:f :x}}} {lambda {:g :n} {{lambda {:n :m :f :x} {:n {:m :f} :x}} :n {:g :g {{lambda {:n} {{lambda {:z} {:z {lambda {:x :y} :x}}} {{:n {lambda {:p} {{lambda {:x :y :z} {:z :x :y}} {{lambda {:z} {:z {lambda {:x :y} :y}}} :p} {{lambda {:n :f :x} {:f {{:n :f} :x}}} {{lambda {:z} {:z {lambda {:x :y} :y}}} :p}}}}} {{lambda {:x :y :z} {:z :x :y}} {lambda {:f :x} :x} {lambda {:f :x} :x}}}}} :n}}}}}} :g :n }} :n}} {lambda {:f :x} {:f {:f {:f {:f {:f :x}}}}}}}} -> {b 120} } _h4 QED! } ;; end 2.3.2. {{note.call 2.3.3.} {h4 2.3.3. conclusion}} {{note.content 2.3.3.} _p Concluding this section we have demonstrated that, using nothing but the three initial rules, the factorial of any natural number can be computed, {b theoretically} without any limitation. _p But if computing the factorial of {b 5} is {b relatively} fast, computing the factorial of {b 50} would still be too long! Probably {b thousands years}. _p It's time to remember that we can use the power of modern browsers to make coding easier and code much more effective! We leave '{lambda calc} and enter in '{lambda talk}. } ;; end 2.3.3. } ;; end 2.3. {center {{slide_call 3.}>>>> '{lambda talk}}} } ;; end 2. {{slide_content 3.} {img {@ src="data/brussels/bibliotheque_virtuelle.jpg" width="100%"}} {center An infinity of words.} _h2 3. '{lambda talk} {{note.call 3.1.} {h3 3.1. the browser's environment}} {{note.content 3.1.} _p '{lambda talk} is '{lambda word} + browser's full functionalities. _p In this section: _ul 1) we replace {b Church numbers & operators} by the {b Javascript Math Object}, _ul 2) we add built-in structures, {b pairs, lists, arrays}, _ul 3) we add a new special form {b '{if bool then one else two}}, and some others. _p The set of special forms becomes {b [lambda, def, if, let, quote, macro, require, script, style]}. _p {b '{lambda talk}} is now a functional programming language - no {b set!} special form - for writing {b readable and effective} code. Beginning with recursive functions. _h6 >>> more to read later: {{note.call dictionary} {b the dictionary's content ...}} {{note.content dictionary} _p This is the current {b dictionary} containing the built-in primitive and the user functions defined in the current page: {pre {lib}} } ;; end dictionary } ;; end 3.1. {{note.call 3.2.} {h3 3.2. recursion made easy}} {{note.content 3.2.} ;; {img {@ src="data/brussels/infinity_time.jpg" width="100%"}} _p In the previous section we have seen how tricky it was to write a recursive algorithm. We had to build {b manually} a lazy behaviour. Using the third {b '{if ...}} special form and its {b built-in lazy evaluation} the way is opened to readable and efficient recursive algorithms. Writing the basic version is straightforward: {pre '{def fac {lambda {:n} {if {= :n 0} then 1 else {* :n {fac {- :n 1}}}}}} -> {def fac {lambda {:n} {if {= :n 0} then 1 else {* :n {fac {- :n 1}}}}}} '{fac 5} -> 120 '{fac 50} -> 3.0414093201713376e+64 } _p and the tail-recursive version too: {pre '{def tfac {def tfac.rec {lambda {:a :n} {if {= :n 0} then :a else {tfac.rec {* :a :n} {- :n 1}}}}} {lambda {:n} {tfac.rec 1 :n}}} -> {def tfac {def tfac.rec {lambda {:a :n} {if {= :n 0} then :a else {tfac.rec {* :a :n} {- :n 1}}}}} {lambda {:n} {tfac.rec 1 :n}}} '{tfac 5} -> 120 '{tfac 50} -> 3.0414093201713376e+64 } _p It's now possible to compute the factorial of {b 50} in a decent time, ( ~ 40ms ). But the result is a rounded value with only {b 15} valid digits. We have to overcome limitations coming with the Javascript Math Object. _h6 >>> more to read later: {{note.call Zcombi} the Y-combinator, again ...} {{note.content Zcombi} _p In {b tfac} the recursive part is called by a {b helper function} introducing the accumulator "{b :a}". '{lambda talk} doesn't know lexical scoping and this leads to some pollution of the dictionary. A new {b Z combinator} {b making recursive almost-recursive functions}, will help us to discard this helper function: {pre '{def Z {lambda {:f :a :n} {:f :f :a :n}}} -> {def Z {lambda {:f :a :n} {:f :f :a :n}}} '{def almost_fac {lambda {:f :a :n} {if {= :n 0} then :a else {:f :f {* :a :n} {- :n 1}}}}} -> {def almost_fac {lambda {:f :a :n} {if {= :n 0} then :a else {:f :f {* :a :n} {- :n 1}}}}} '{Z almost_fac 1 5} -> 120 '{Z almost_fac 1 50} -> 3.0414093201713376e+64 } _p We merge the both: {pre '{def Zfac {lambda {:n} {{lambda {:f :a :n} {:f :f :a :n}} {lambda {:f :a :n} {if {= :n 0} then :a else {:f :f {* :a :n} {- :n 1}}}} 1 :n}}} -> {def Zfac {lambda {:n} {{lambda {:f :a :n} {:f :f :a :n}} {lambda {:f :a :n} {if {= :n 0} then :a else {:f :f {* :a :n} {- :n 1}}}} 1 :n}}} '{Zfac 5} -> 120 '{Zfac 50} -> 3.0414093201713376e+64 } _p Forgetting the name we can define and call the lambda on some value: {pre '{{lambda {:n} {{lambda {:f :a :n} {:f :f :a :n}} {lambda {:f :a :n} {if {= :n 0} then :a else {:f :f {* :a :n} {- :n 1}}}} 1 :n}} 50} -> 3.0414093201713376e+64 } } ;; end Zcombi } ;; end 3.2. {{note.call 3.3.} {h3 3.3. big numbers}} {{note.content 3.3.} ;; {img {@ src="data/brussels/nombres.jpg" width="100%"}} _p The way the {b Javascript Math Object} is implemented puts the limit of natural numbers to {b 2{sup 54}}. Beyond this limit last digits are rounded to zeros. For instance the four last digits of {b 2{sup 64} = '{pow 2 64} = {pow 2 64}} should be {b 1616} instead of {b 2000}. And beyond {b {b 2{sup 69}}} natural numbers are replaced by float numbers with a maximum of 15 valid digits. In order to {b overcome this limitation} we remember that natural numbers can be defined as {b polynomials} and implemented as {b lists}: {pre {center A natural number a{sub 0}a{sub 1}...a{sub n} is the value of the polynomial Σ{sub i=0}{sup n} a{sub i}x{sup i} for some value of x, called the base. For instance: (1 2 3 4 5){sub x=10} = 1*10{sup 4}+2*10{sup 3}+3*10{sup 2}+4*10{sup 1}+5*10{sup 0} = 12345} } _p We present shortly two strategies, the first built on pure '{lambda talk} code, the second calling an external Javascript code. {{hide} {require lib_BI lib_BN lib_mathLT} } {{note.call 3.3.1.} {h4 3.3.1. the lib_BI library}} {{note.content 3.3.1.} _p We build a set of {b pure '{lambda talk}} functions working on {b natural big numbers} defined as {b lists of digits}, and stored in a wiki page, (see [[lib_BI]]): {pre BI.bigint2pol : abc...n -> '{list a b c ... n} BI.pol2bigint : '{list a b c ... n} -> abc...n BI.simplify : '{list a b c ... n} -> '{list x y z ... m} BI.pk : k * '{list a b c ... n} BI.p+ : '{list a b c ... n} + '{list x y z ... n} BI.p* : '{list a b c ... n} * '{list x y z ... n} {{note.call lib_BI} >>> see code} {{note.content lib_BI} '{def BI.bigint2pol {def BI.bigint2pol.rec {lambda {:n :p :i} {if {< :i 0} then :p else {BI.bigint2pol.rec :n {cons {charAt :i :n} :p} {- :i 1}} }}} {lambda {:n} {BI.bigint2pol.rec :n nil {- {chars :n} 1}} }} '{def BI.pol2bigint {def BI.pol2bigint.rec {lambda {:p :n} {if {equal? :p nil} then :n else {BI.pol2bigint.rec {cdr :p} {car :p}:n} }}} {lambda {:p} {let { {:q {list.reverse :p}} } {BI.pol2bigint.rec {cdr :q} {car :q}}} }} '{def BI.simplify {def BI.simplify.rec {lambda {:p :q :r} {if {and {equal? :p nil} {= :r 0}} then :q else {if {equal? :p nil} then {cons :r :q} else {BI.simplify.rec {cdr :p} {cons {+ {% {car :p} 10} :r} :q} {floor {/ {car :p} 10}} }} }}} {lambda {:p} {BI.simplify.rec {list.reverse :p} nil 0} }} '{def BI.pk {lambda {:k :p} {if {equal? :p nil} then nil else {cons {* :k {car :p}} {BI.pk :k {cdr :p} }} }}} '{def BI.p+ {lambda {:p1 :p2} {if {and {equal? :p1 nil} {equal? :p2 nil}} then nil else {if {equal? :p1 nil} then :p2 else {if {equal? :p2 nil} then :p1 else {cons {+ {car :p1} {car :p2}} {BI.p+ {cdr :p1} {cdr :p2} }}}} }}} '{def BI.p* {lambda {:p1 :p2} {if {or {equal? :p1 nil} {equal? :p2 nil}} then nil else {if {not {cons? :p1}} then {BI.pk :p1 :p2} else {BI.p+ {BI.pk {car :p1} :p2} {cons 0 {BI.p* {cdr :p1} :p2}}}} }}} } ;; end lib_BI } _p on which we write a tail-recursive version of the factorial: {pre '{def BI.tfac {def BI.tfac.rec {lambda {:n :p} {if {< :n 1} then :p else {BI.tfac.rec {- :n 1} {BI.simplify {BI.p* {BI.bigint2pol :n} :p}}}}}} {lambda {:n} {BI.pol2bigint {BI.simplify {BI.tfac.rec :n {BI.bigint2pol 1}}}}}} -> {def BI.tfac {def BI.tfac.r {lambda {:n :p} {if {< :n 1} then :p else {BI.tfac.r {- :n 1} {BI.simplify {BI.p* {BI.bigint2pol :n} :p}}}}}} {lambda {:n} {BI.pol2bigint {BI.simplify {BI.tfac.r :n {BI.bigint2pol 1}}}}}} '{BI.tfac 5} -> 120 '{BI.tfac 50} -> 304140932017133780436126081660647 68844377641568960512000000000000 } _p {b Theory becomes reality}, we can actually compute the exact value of any natural number. But it's still very slow, about {b 10 seconds} on a recent computer. Pure '{lambda talk} functions are not the best choice for {b intensive computations}. Plain Javascript built-in functions will help! } ;; end 3.3.1. {{note.call 3.3.2.} {h4 3.3.2. the lib_BN library}} {{note.content 3.3.2.} _p [[Jonas Raoni Soares Silva|http://jsfromhell.com/classes/bignumber]] has written a small and smart library, (see [[lib_BN]]), ready to be called via {b '{lambda talk} wrapping functions}. We just extend the dictionary with a Javascript function, {b BN.fac}: {pre °° LAMBDATALK.DICT['BN.fac'] = function(){ var fac = function(n) { return (n.compare(0) === 0) ? 1 : n.multiply( fac( n.subtract(1))) }; return fac( new BigNumber( arguments[0], BN_DEC ) ) }; °°} _p and call it: {pre '{BN.fac 50} -> 304140932017133780436126081660647 68844377641568960512000000000000 } _p The computation time decreases under {b 20ms} and we can even compute bigger ones in a decent time, {b 650ms}: {pre '{BN.fac 500} -> 1220136825991110068701238785423046926253574342803192842192413588385845373153881997605496447502203281863013616477148203584163378722078177200480785205159329285477907571939330603772960859086270429174547882424912726344305670173270769461062802310452644218878789465754777149863494367781037644274033827365397471386477878495438489595537537990423241061271326984327745715546309977202781014561081188373709531016356324432987029563896628911658974769572087926928871281780070265174507768410719624390394322536422605234945850129918571501248706961568141625359056693423813008856249246891564126775654481886506593847951775360894005745238940335798476363944905313062323749066445048824665075946735862074637925184200459369692981022263971952597190945217823331756934581508552332820762820023402626907898342451712006207714640979456116127629145951237229913340169552363850942885592018727433795173014586357570828355780158735432768888680120399882384702151467605445407663535984174430480128938313896881639487469658817504506926365338175055478128640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 } } ;; end 3.3.2. } ;; end 3.3. {center {{slide_call 4.}>>>> conclusion}} } ;; end 3. {{slide_content 4.} _h2 conclusion _p We have presented the evolution of '{lambda talk} from the foundations to the superstructures, following the computation of the factorial of any natural number. Much more can be done: writing math symbols, sorting lists, traversing trees, creating spreadsheets, [[PDF papers|data/lambdatalk_20170728.pdf]], vector graphics, ray-tracing, ... and {{note.call macros} {b {u {i even macros}}}}! {{note.content macros} _p Lambdatalk is closely related to Regular Expressions. Macros can be built following this syntax: {pre '{macro regular expression to lambdatalk expression} } _p For instance using this macro: {pre '{macro {FOR (\w+?) TO (\w+?) STEP (\w+?) (?:[\s]*)BEGIN(?:[\s]*)(.*?)(?:[\s]*)END(?:[\s]*)} to {map €4 {serie €1 €2 €3}}} } _p this '{lambda talk} expression {pre '{map {lambda {:i} {pow 2 :i}} {serie 1 10}} -> 2 4 8 16 32 64 128 256 512 1024 } _p could be replaced by this {i old fashion} one {pre '{FOR 1 TO 10 STEP 1 BEGIN {lambda {:i} {pow 2 :i}} END } -> 2 4 8 16 32 64 128 256 512 1024 } _p Very useful in a wiki context, '{lambda talk} comes with a small set of predefined macros allowing writing easily {b titles, paragraphs, lists and links}. For instance you can write: {pre {span {@ style="color:blue"} _{span}p A link to the [{span}[home page]{span}].} {i instead of:} '{p A link to the {a {@ href="?view=start"}home page}.} } } ;; end macros {center {show {@ src="data/brussels/spreadsheet_shot.jpg" height="150" width="1000" title="An integrated spreadsheet using lambda talk expressions." style="border:1px solid #ccc"}} {show {@ src="data/brussels/PDF_paper.jpg" height="150" width="1000" title="Academic paper, ACM US format, generated directly from the browser." style="border:1px solid #ccc"}} {show {@ src="data/brussels/pforms_coons.jpg" height="130" width="600" title="Coons patches built on 6 cubic spline curves with embedded regular polygons" style="border:1px solid #ccc"}} {show {@ src="data/brussels/turtle_20161105.jpg" height="130" width="600" title="Turtle graphics with Lindenmeier, L-system" style="border:1px solid #ccc"}} {show {@ src="data/brussels/mandel_20160104.jpg" height="130" width="600" title="Fractals, the MandelBrot set" style="border:1px solid #ccc"}} {show {@ src="data/brussels/d1.jpg" height="130" width="550" title="Integrated Ray-tracing" style="border:1px solid #ccc"}} {div {@ style="font:italic 1.9em georgia;"} i{del h}{QUOTIENT 40 ∂ψ ∂t} (x,t) = {PAREN 3 (}mc{sup 2}α{sub 0} - i{del h}c {SIGMA 40 j=1 3} α{sub j}{QUOTIENT 40 ∂ ∂x{sub j}} {PAREN 3 )} ψ(x,t) } } {{note.call last} {h4 one last thing ...}} {{note.content last} {center {img {@ src="data/brussels/browsers_icons.jpg" height="100"}} {img {@ src="data/brussels/braces.png" height="100" style="background:#888"}} } _p As a dwarf standing on their shoulders, '{lambda talk} takes benefit from the extraordinary power of modern web browsers. It does not re-invent the wheel and simply adds a {b coherent and unique language on existing tools} - HTML/CSS, the DOM, Javascript, PHP, ... _p The '{lambda way} project adds on browsers a thin overlay, '{lambda tank}, proposing a small "Interactive Development Environment" - {input {@ type="button" value="open frame editor" onclick="LAMBDATANK.toggle_display('frame_editor')"}} - without any external dependencies and thereby {b easy to download and install} on any web account provider running PHP. _p From any web browser, on any system, complex and dynamic web pages can be created, enriched, structured and tested in real time on the web. _p {b It's exactly how the current presentation was created}. ( {i [[http://lambdaway.free.fr/workshop|http://lambdaway.free.fr/workshop/]]} ) {{note.call thanks} {h5 Thank you!}} {{note.content thanks} _h6 Alain Marty _ul {b 1946} born in PERPIGNAN OCCITANIE FRANCE _ul {b 1962-1972} study engineering [ECL LYON] & architecture [UP7 PARIS] _ul {b 1973-2012} build houses as an architect _ul {b 1990-2012} teach CAD in a school of architecture MONTPELLIER _ul {b 2012-2017} explore {b LISP's Holly Wooood} } ;; end thanks } ;; end last } ;; end 4. } ;; end CONTENT {{hide} ;; the code below could be externalized in another page {def WRAP {@ style="word-wrap: break-word; white-space:pre-wrap;"}} {def CALLS {lambda {:y :x} div {@ style="position:relative; top::ypx; left::xpx; color:#000; width:100%; z-index:2"}}} {def CONTENT div {@ style="position:relative; top:0; left:0; box-shadow:0 0 8px black; z-index:1"}} {def slide_call {lambda {:id} span {@ class="call" style="width:200px; margin:0; padding:0; text-align:left; cursor:pointer;" onclick="SLIDES.show(':id')"}}} {def slide_content {lambda {:id} div {@ id=":id" class="content" style="display:{BLOCK}; z-index:2; font-family:times; color:#000; border-top:1px dashed #ccc; background:#eee; padding:10px;"}}} {def background {lambda {:col :opa} {div {@ id="background" style="display:none; z-index:-1; position:fixed; top:0; left:0; width:5000px; height:5000px; width:100vw; height:100vh; background: :col; opacity: :opa;"}}}} {def note.call {lambda {:id} span {@ style="opacity:1.0; cursor:pointer;" onmouseover="this.style.color='#f00'" onmouseout="this.style.color='#000'" onclick=" getId(':id').style.display = (getId(':id').style.display==='none')?'block':'none'; this.style.opacity = (this.style.opacity==='0.5')?'1.0':'0.5'; "}}} {def note.content {lambda {:id} div {@ id=":id" style="display:{BLOCK}; border:1px solid #ccc; padding:5px; background:#eee; color:#000; box-shadow:0 0 100px #000;" }}} {def RED span {@ style="color:#f00; font-weight:bold;"}} {def CYAN span {@ style="background:#0ff; font-weight:bold;"}} {def CHRONO {lambda {:d} {div {@ style="position:fixed; top:0px; right:0px; text-align:center; z-index:2; background:transparent;"} {input {@ id="CHRONO" type="text" value=":d" style="font:bold 2.0em courier; color:#444; text-align:center; height:40px; width:100px; border:0; margin-bottom:-20px; background:transparent;"}} {br} {input {@ type="button" value="start" onclick="CHRONO.start(this)"}} } }} {def WAIT_SCREEN {@ style="font:italic 1.4em courier; box-shadow:0 0 0; background:transparent; margin:0px 0px 30px 10px; word-wrap: break-word; white-space:pre-wrap; -webkit-transform:rotate(-5deg); transform:rotate(-5deg); "}} {def TITLE {div {@ onclick="SLIDES.hide()" style=" position:relative; top:0; left:0; z-index:100; font:bold 3.5em Optima; text-align:center; "} '{lambda {} talk}} {center SCHEME & FUNCTIONAL PROGRAMMING WORKSHOP 2017 | OXFORD UK | 2017/09/03 {br}marty.alain@free.fr | http://lambdaway.free.fr/workshop/ {br} {{CALLS 0 0} {{slide_call wait}+ |} {{slide_call 0}introduction} | {{slide_call 1.}'{lambda word}} | {{slide_call 2.}'{lambda calc}} | {{slide_call 3.}'{lambda talk}} | {{slide_call 4.}conclusion} } }} } ;; end hide {script var SLIDES = (function() { function hide() { getId('background').style.display = 'none'; var slides = document.getElementsByClassName('content'); for (var i=0; i < slides.length; i++) slides[i].style.display = 'none'; } function show(id) { hide(); getId('background').style.display = 'block'; getId(id).style.display = (getId(id).style.display === 'none')? 'block':'none'; } return {show:show, hide:hide} })(); var CHRONO = (function() { var deadline, ID = null; var update = function() { var now = new Date().getTime(), d = Math.round( (deadline - now ) / 1000 ), m = Math.floor( d / 60 ), s = d % 60; getId('CHRONO').value = m +':'+ s; if (m === 0 && s === 0) clearInterval( ID ); }; var start = function(id) { deadline = new Date().getTime() + getId('CHRONO').value *60*1000; ID = setInterval( update, 1000 ); id.disabled = true; getId('CHRONO').disabled = true; }; return { start:start } })(); } {style #title { display:block; } #frame_view { width:700px; font:normal 1.0em Times; border:0; background:#fff; box-shadow:0 0 0; } .call:hover { opacity:0.5; } .call img { box-shadow:0 0 4px #000; } pre { word-wrap: break-word; white-space:pre-wrap; font:normal 1.2em courier; background:#fff; color:#000; box-shadow:0 0 4px #000; } code { font:normal 1.2em courier; } h1, h2, h3, h4, h5, h6 { font-family:Optima;} }