+
1
|
skin
|
login
|
edit
{λ way}
::
brussels_slides
user:anonymous
{{hide} {def BLOCK none}} ;; block | none {TITLE} {{CALLS 120 -210} {{slide_call 0}{b introduction}} {{slide_call 1}expressions} {{slide_call 2}'{lambda word}} {{slide_call 3}names} {{slide_call 4}numbers} {{slide_call 5}operators} {{slide_call 6}iteration} {{slide_call 7}'{lambda talk}} {{slide_call 8}recursion} {{slide_call 9}big numbers} {{slide_call 10}de Casteljau} {{slide_call 11}spreadsheet} {{slide_call 12}mathML} {{slide_call 13}scripts} {{slide_call 14}macros} {{slide_call 15}{b conclusion}} } {{CONTENT} {background #222 0.5} {{slide_content 0} {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 introduction _p The '{lambda way} project is a web application built on two engines: {b _ul 1) '{lambda tank}, a tiny wiki built as a thin overlay on top of any web browser, _ul 2) '{lambda talk}, a purely functional language unifying writing, styling and scripting in a single and coherent Lisp-like syntax. } _p « {i But there are hundred of wiki engines and hundred of languages! Why yet another wiki and another language nobody will ever want to use?} » {b Let's talk about it!} _h4 {note_start short short} ... {note_end {@ id="short"} {div {@ style="box-shadow:0 0 8px #000; padding:5px;"} _h4 the '{lambda way} project, quickly: _ul {b 1) '{lambda tank}} is a tiny {b CMS}, a wiki coming with an {input {@ type="button" value="editor frame" onclick="javascript:LAMBDATANK.toggle_display('frame_editor')"}} in which {b '{lambda talk} code} is written, evaluated in real time and displayed in the {b wiki page}. _ul {b 2)} Away from curly braces '{} {b words are just words}. _ul {b 3)} Expressions are written in a {b prefix notation}. {pre 2+3 is equal to '{b {+ 2 3}} -> 2+3 is equal to {b {+ 2 3}} } _p {i -> Remember that {b b} is the HTML tag for {b bold}!} _ul {b 4)} Functions are created with {b lambda} and named with {b def}. {pre '{def SMART_ADD {lambda {:a :b} :a+:b is equal to {b {+ :a :b}}}} -> {def SMART_ADD {lambda {:a :b} :a+:b is equal to {b {+ :a :b}} }} '{SMART_ADD 2 3} -> {SMART_ADD 2 3} }}} _h4 {note_start long long} ... {note_end {@ id="long"} {div {@ style="box-shadow:0 0 8px #000; margin:5px; padding:5px;"} _p In this presentation we forget {b '{lambda tank}}, mainly a {b PHP engine} managing text files on the server side, and we progressively introduce '{lambda talk}, a {b JS engine} evaluating code on the client side. We begin with a minimal set of three rules, then add constants and numbers, and finally call the browsers' functionalities. _p As a guilding line, we present how can be computed the factorial of any natural number, regardless of its size and with a total precision, for instance: {pre {b 5! = 120 50! = 304140932017133780436126081660647 68844377641568960512000000000000 }}}}} {{slide_content 1} {img {@ src="data/brussels/poles.jpg" width="100%"}} {center {i La Sagrada Familia | Barcelona | Gaudi} } _h2 1) expressions _p '{lambda talk} is built on three rules freely inspired by the {b lambda calculus}. An expression is defined recursively as follows: {center {pre {b expression is [word|abstraction|application]* }}} _p where {pre 1) word is [^\s'{}]* 2) abstraction is '{lambda {word*} expression} {sup {sup (more on word later..)}} 3) application is '{abstraction expression} } _p An expression is made of {b words}, {b abstractions} and {b applications} where 1) a {b word} is any character except spaces "\s" and curly braces "'{}", 2) an {b abstraction} is the "process" (called a {i function}) selecting a sequence of {b words} (called {i arguments}) in an {b expression} (called {i body}), 3) an {b application} is the "process" calling an {b abstraction} to replace selected {b words} by some other {b words} (called {i values}). _p The evaluation follows these rules: {pre 1) a word is not evaluated, 2) an abstraction is evaluated to a single word, as a reference stored in a global dictionary, 3) an application is evaluated to a sequence of words, 4) abstractions, called {b special forms}, are evaluated before applications, called {b forms}. 5) the evaluation stops when all expressions have been progressively reduced, from leaves to the root. } _p For instance: _h4 1.1. words {pre Hello World -> Hello World } _p Words are ignored. _h4 1.2. abstraction {pre '{lambda {o a} oh happy day!} -> {lambda {o a} oh happy day!} } _p The abstraction defines {b o} and {b a} as characters whose occurences in the expression {b oh happy day!} will be replaced by some future values, and returns the reference to an anonymous function. _h4 1.3. application {sup (1)} {pre '{{lambda {o a} oh happy day!} oOOOo aaAAaa} -> {{lambda {o a} oh happy day!} oOOOo aaAAaa} } _p The abstraction is 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!}. _h4 1.4. application {sup (2)} {pre '{{lambda {z} {z {lambda {x y} x}}} {{lambda {x y z} {z x y}} Hello World}} -> Hello '{{lambda {z} {z {lambda {x y} y}}} {{lambda {x y z} {z x y}} Hello World}} -> World } _p These expressions returns respectively the first and the second word of {b Hello World}. It reminds something known, isnt'it? _h4 1.5. application {sup (3)} {pre {WRAP} '{{lambda {n} {{lambda {z} {z {lambda {x y} y}}} {{n {lambda {z} {{lambda {x y z} {z x y}} {{lambda {n f x} {f {{n f} x}}} {{lambda {z} {z {lambda {x y} x}}} z}} {{lambda {n z f} {z {n f}}} {{lambda {z} {z {lambda {x y} x}}} z} {{lambda {z} {z {lambda {x y} y}}} z}}}}} {{lambda {x y z} {z x y}} {lambda {f x} {f x}} {lambda {f x} {f x}}}}}} {lambda {f x} {f {f {f {f {f {f x}}}}}}}} -> lambda_84 } _p At this point, we can only say that this unreadble expression is evaluated to an anonymous function. What is behind will be revealed a little later... } {{slide_content 2} {img {@ src="data/brussels/engrenages.jpg" width="100%"}} {center {i {quote /\{([^\s{}]*)(?:[\s]*)([^{}]*)\}/g} {br} ... and now you have two problems ... }} _h2 2) '{lambda word} _p In this section we present the {b minimal subset of '{lambda talk}} which is necessary and sufficient to implement the definition given in the previous section, {b expression := word | abstraction | application }. We will call {b '{lambda word}} this subset ... which knows nothing of numbers, Math operators, HTML, CSS ... {b nothing but words!} _h4 {note_start eval 2.1) evaluation ...} {note_end {@ id="eval"} {pre °° var LAMBDAWORD = (function() { var eval = function(str) { str = pre_processing(str); str = eval_lambdas(str); // special form str = ... // future special forms str = eval_forms(str); // simple form str = post_processing(str); return str; }; return {eval:eval} })(); °°} } _p Working on the client side the '{lambda word} evaluator is a Javascript {b IIFE} (Immediately Invoked Function Expression), {b LAMBDAWORD}, returning the public function {b eval()}. This function is called at every keyboard entry, doesn't build any Abstract Syntaxic Tree and progressively replaces the string code by its evaluation. _h4 {note_start eval_forms 2.2) application ...} {note_end {@ id="eval_forms"} {pre °° var eval_forms = function( str ) { while (str != (str = str.replace( leaf, eval_leaf ))) ; // does nothing! return str }; '{( first ) space ( rest )} var leaf = /\{([^\s{}]*)(?:[\s]*)([^{}]*)\}/g; var eval_leaf = function(_,f,r) { if (DICT.hasOwnProperty(f)) return DICT[f].apply(null,[r]); else return '('+f+' '+r+')'; }; var DICT = {}; // initially empty °°} } _p In a {b single loop}, using a {b single regular expression}, simple forms are {b recursively evaluated} from the leaves {b '{first rest} to the root} and replaced by words. The evaluator stops when they are reduced to a valid {b HTML code} sent to the browser's engine for the final evaluation and display. Using a {b regular expression} based window, the evaluator literally {b loops over the code string}, skips the words and progressively replaces {b in situ} forms by words. _p {b The repeated substitutions inside the code string overcomes limitations of regular language}. A kind of Turing machine... _h4 {note_start eval_lambdas 2.3) abstraction ...} {note_end {@ id="eval_lambdas"} _p The special form {b '{lambda {arg*} body}} is evaluated {b before} simple forms, matched and evaluated in a similar way, according to specific regexp pattern, and returns the reference of an anonymous function added to the dictionary. {pre °° var eval_lambdas = function(str) { while ( str !== ( str = form_replace(str,'{lambda', eval_lambda))); return str }; var form_replace = function(str, sym, func, flag) { sym += ' '; var s = catch_form( sym, str ); return (s==='none')? str:str.replace(sym+s+'}',func(s,flag)) }; var catch_form = function( symbol, str ) { var start = str.indexOf( symbol ); if (start == -1) return 'none'; var d0, d1, d2; if (symbol === "'{") { d0 = 1; d1 = 1; d2 = 1; } else if (symbol === "{") { d0 = 0; d1 = 0; d2 = 1; } else { d0 = 0; d1 = symbol.length; d2 = 0; } var nb = 1, index = start+d0; while(nb > 0) { index++; if ( str.charAt(index) == '{' ) nb++; else if ( str.charAt(index) == '}' ) nb--; } return str.substring( start+d1, index+d2 ) }; var eval_lambda = function(s){ s = eval_lambdas( s ); // nested lambdas var index = s.indexOf('}'), args = supertrim(s.substring(1, index)).split(' '), body = s.substring(index+2).trim(), name = 'lambda_' + g_lambda_num++, reg_args = []; for (var i=0; i < args.length; i++) reg_args[i] = RegExp( args[i], 'g'); DICT[name] = function() { var vals = supertrim(arguments[0]).split(' '); return function(bod) { if (vals.length < args.length) { for (var i=0; i < vals.length; i++) bod = bod.replace(reg_args[i],vals[i]); var _args=args.slice(vals.length).join(' '); bod = '{' + _args + '} ' + bod; bod = eval_lambda(bod); // -> a lambda } else { // -> a form for (var i=0; i < args.length; i++) bod = bod.replace(reg_args[i],vals[i]); } return bod; }(body); }; return name; }; °°} } _ul {b 1) lambdas} are {b first class} functions, _ul {b 2) lambdas} accept {b partial function application}, when called with a number of values lesser than their arity, they memorize the given values and return a lambda waiting for the rest, _ul {b 3) lambdas} don't create closures, inner lambdas have no access to outer lambdas' arguments, there is no lexical scoping, no environment, no free variables, _ul {b 4) lambdas} are pure black boxes, as are mathematical functions. _p Anyway, this {b iconoclastic behaviour} allows building user defined {b agregate data}, {b conses, lists, Btrees, Rational/Complex/Big numbers, 2D/3D vectors, ...}, even if for a matter of efficiency they are usually implemented as primitives in the underlying language. _h4 to sum up _p This minimal implementation mainly built on three functions {b eval(), eval_lambdas(), eval_forms()} and on {b an empty dictionary}, is sufficient to make {b '{lambda word}} a {b programmable programming language}. _p Introducing {b names} will add a little bit of humanity to this language. } {{slide_content 3} {img {@ src="data/brussels/definition.jpg" width="100%"}} {center {i Giving names to anonymous.}} _h2 3) names _p Introducing the special form {b '{def word expression}} we can populate some {b dictionary} with {b constants} and give {b names} to lambdas and make code more readable. Let's rewrite with names the previous examples: _h4 3.1) first example {pre '{def HI Hello World} -> {def HI Hello World} '{HI} -> {HI} } _p Note that {b HI} must be bracketed between curly braces to be evaluated. Remember that, in a spreadsheet, we must write {b =PI()} to get the value associated to {b PI}. _h4 3.2) second/third examples {pre '{def GOOD_DAY {lambda {:o :a} :oh h:appy day!}} -> {def GOOD_DAY {lambda {:o :a} :oh h:appy day!}} '{GOOD_DAY oOOOo aaAaa} -> oOOOoh haaAaappy day! '{GOOD_DAY ♠ ♥} -> ♠h h♥ppy day! } _p Note that, contrary to the previous example 1.3), {b arguments} and {b their occurences} in the function's body have been prefixed with a {b colon}. Doing that {b prevents unintentional substitutions} in the function's body, for instance the word {b day} hasn't been replaced by {b daaAaay} or {b d♥y}. {b Escaping/marking arguments}, for instance prefixing them with a colon "{b :}", is highly recommended if not always mandatory. {b We will do it systematically} and we add this constraint to the definition of {b abstraction}: {pre - arguments' functions must at least be tagged by some {i escaping} character, for instance {b :arg}, or for a better security, bracketted between two, for instance {b :arg:} } _h4 3.3) fourth example: {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 '{CDR {CONS Hello World}} -> World } _p In fact we just have built and used {b pairs}. _h4 3.4) fifth example: {pre {WRAP} '{def BAR {{lambda {:n} {{lambda {:z} {:z {lambda {:x :y} :y}}} {{:n {lambda {:z} {{lambda {:x :y :z} {:z :x :y}} {{lambda {:n :f :x} {:f {{:n :f} :x}}} {{lambda {:z} {:z {lambda {:x :y} :x}}} :z}} {{lambda {:n :z :f} {:z {:n :f}}} {{lambda {:z} {:z {lambda {:x :y} :x}}} :z} {{lambda {:z} {:z {lambda {:x :y} :y}}} :z}}}}} {{lambda {:x :y :z} {:z :x :y}} {lambda {:f :x} {:f :x}} {lambda {:f :x} {:f :x}}}}}}} } -> BAR '{BAR {lambda {:f :x} {:f {:f {:f {:f {:f x}}}}}}} -> lambda_163 } _p We notice that {b BAR} is applied to an anonymous function applying {b 5 times} {b :f} to {b :x}. We will show later that the resulting word is a reference to an anonymous function associated to {b 5!}. _p Writing the same expression whith {b :f} applied {b 50 times} to {b :x} would lead to an anonymous function associated to {b 50!} ... provided we had thousands years before us! _p At this point '{lambda word} knows {b nothing but text substitution}. Surprisingly, this is enough to introduce {b numbers} without calling the Javascript Math object. } {{slide_content 4} {img {@ src="data/brussels/nombres.jpg" width="100%"}} {center {i Numbers are alive.}} _h2 4) numbers _p We define the so-called {b Church numbers}: {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, we get strange things: {pre '{ZERO . .} -> . '{ONE . .} -> (. .) '{TWO . .} -> (. (. .)) '{THREE . .} -> (. (. (. .))) } _p We define the function {b CHURCH} which translates Church numbers 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}}} } _p Note that the {b CHURCH} function is built on a primitive function, {b '+'} and on the concept of numbers, which are not supposed to exist at this point. Consider that it's only for readability. {pre '{CHURCH ZERO} -> 0 '{CHURCH ONE} -> 1 '{CHURCH TWO} -> 2 } } {{slide_content 5} {img {@ src="data/brussels/operators.jpg" width="100%"}} {center {i ¡ data is code and code is data !}} _h2 5) operators _p Based on Church numbers, which are {b iterators by themselves}, we can easily define a first set of operators: {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} {:m {:n :f}}}} -> {def MUL {lambda {:n :m :f} {:m {:n :f}}}} '{def POWER {lambda {:n :m} {:m :n}}} -> {def POWER {lambda {:n :m} {:m :n}}} } _p and test: {pre '{CHURCH {SUCC ZERO}} -> 1 '{CHURCH {SUCC ONE}} -> 2 '{CHURCH {SUCC THREE}} -> 3 '{CHURCH {ADD TWO THREE}} -> 5 // 2+3 '{CHURCH {MUL TWO THREE}} -> 6 // 2*3 '{CHURCH {POWER THREE TWO}} -> 9 // 3^2 } _p Even if it is a little more tricky, we could go on and build next operators {b PRED, SUBTRACT, DIV, TRUE, FALSE, OR, AND, IFTHENELSE, ...} and even the {b Y-combinator}, which helps almost-recursive functions to be recursive. _p But in this presentation we only want to compute {b n! = 1*2*3*...*n} and, amazingly, we already have all the tools in hand to do it. } {{slide_content 6} {img {@ src="data/brussels/iteration.jpg" width="100%"}} {center {i To Iterate is Human ...}} _h2 6) iteration _p We already have all what is needed to evaluate complex expressions like {b n! = 1*2*3*...*n}. Church numbers can be used to {b iterate} and pairs to {b aggregate}. This is how: _ul we define a function {code ITER.PAIR} getting a pair {code [a,b]} and returning a pair {code [a+1,a*b]}, _ul the function {code ITER} computes {code n} iterations of {code ITER.PAIR}, starting on the pair {code [1,1]} and leading to the pair {code [n,n!]} and returns the second, {code 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 Forgetting user defined names, {b [ONE, FIVE, MUL, SUCC, CONS, CAR, CDR, ITER]}, we can write the last line {b '{CHURCH {ITER FIVE}}} using exclusively lambdas, {b nothing but abstractions and applications}: {pre {WRAP} '{CHURCH {{lambda {:n} {{lambda {:p} {:p {lambda {:x :y} :y}}} {{:n {lambda {:p} {{lambda {:a :b :m} {{:m :a} :b}} {{lambda {:n :f :x} {:f {{:n :f} :x}}} {{lambda {:p} {:p {lambda {:x :y} :x}}} :p}} {{lambda {:n :m :f} {:m {:n :f}}} {{lambda {:p} {:p {lambda {:x :y} :x}}} :p} {{lambda {:p} {:p {lambda {:x :y} :y}}} :p}}}}} {{lambda {:a :b :m} {{:m :a} :b}} {lambda {:f :x} {:f :x}} {lambda {:f :x} {:f :x}}}}}} {lambda {:f :x} {:f {:f {:f {:f {:f :x}}}}}}} } -> 120 } _p It's exactly the fifth example of section 1) whose evaluation is now clearly displayed as the factorial of {b 5}: {b 120}. _p {b QED!} _p It's time to remember that we can use the power of modern browsers to make things easier! {blockquote _p {b Updated on 2017/04/18}: Just before leaving '{lambda calc}, you might have a look [[here|?view=eureka3]] where adding a handful of constants, {b [TRUE, FALSE, IF, ZERO, ISZERO, SUCC, PRED, MUL, Y, IFTHENELSE]} and a little bit of lazyness can introduce recursion in '{lambda calc}. } } {{slide_content 7} {img {@ src="data/brussels/bibliotheque_virtuelle.jpg" width="100%"}} {center An infinity of words.} _h2 7) '{lambda talk} _p In order to bring a bit of humanity and make coding much more easy: _ul 1) the set of special forms is extended to {b [lambda, def, if, let, quote|', macro, require]}. Note that there is no {b set!} special form, '{lambda talk} is {b purely functional}. _ul 2) the {b dictionary} is populated with {b primitive functions} built on the browser's foundations, Javascript's numbers, Math operators and functions, HTML tags, CSS rules, and some others. This is the current content of the '{lambda talk} dictionary: {blockquote {lib} } _p where can be seen after the constant {b turtle} the user defined constants specifically built for this page. We are going now to illustrate some of them. } {{slide_content 8} {img {@ src="data/brussels/infinity_time.jpg" width="100%"}} {center ... to Recurse, Divine - L. Peter Deutsch} _h2 8) Recursion {br}and the Y-Combinator _p Until now {b 5!} was computed as a product {b 1*2*3*4*5}. With the special form {b '{if bool then one else two}} and its {b lazy evaluation} the way is opened to recursive algorithms. It's now possible to write the factorial function following its mathematical definition: {pre '{def FAC {lambda {:n} {if {< :n 0} then {b n must be positive!} else {if {= :n 0} then 1 else {* :n {FAC {- :n 1}}}}}}} -> {def FAC {lambda {:n} {if {< :n 0} then {b n must be positive!} else {if {= :n 0} then 1 else {* :n {FAC {- :n 1}}}}}}} '{FAC -1} -> {b n must be positive!} '{FAC 0} -> 1 '{FAC 5} -> 120 '{FAC 50} -> 3.0414093201713376e+64 } _p Let's write the tail-recursive version: {pre '{def TFAC {def TFAC.rec {lambda {:a :n} {if {< :n 0} then {b n must be positive!} else {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 {b n must be positive!} else {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 The recursive part is called by a {i helper function} introducing the accumulator "{b :a}". '{lambda talk} doesn't know lexical scoping and this leads to some pollution of the dictionary. The {code Y combinator} mentionned above, {i making recursive an almost-recursive function}, will help us to discard this helper function. _p The {code Y combinator} and the almost-recursive function can be defined and used like this: {pre '{def Y {lambda {:f :a :n} {:f :f :a :n}}} -> {def Y {lambda {:f :a :n} {:f :f :a :n}}} '{def ALMOST_FAC {lambda {:f :a :n} {if {< :n 0} then {b n must be positive!} else {if {= :n 0} then :a else {:f :f {* :a :n} {- :n 1}}}}}} -> {def ALMOST_FAC {lambda {:f :a :n} {if {< :n 0} then {b the number must be positive!} else {if {= :n 0} then :a else {:f :f {* :a :n} {- :n 1}}}}}} '{Y ALMOST_FAC 1 5} -> 120 } _p Because the {code Y combinator} can be applied to any other almost-recursive function (sharing the same signature, for instance fibonacci) we have reduced the pollution but we can do better. Instead of applying the Y combinator to the almost recursive function we can define a function merging the both: {pre {WRAP} '{def YFAC {lambda {:n} {{lambda {:f :a :n} {:f :f :a :n}} {lambda {:f :a :n} {if {< :n 0} then {b n must be positive!} else {if {= :n 0} then :a else {:f :f {* :a :n} {- :n 1}}}}} 1 :n}}} -> {def YFAC {lambda {:n} {{lambda {:f :a :n} {:f :f :a :n}} {lambda {:f :a :n} {if {< :n 0} then {b the number must be positive!} else {if {= :n 0} then :a else {:f :f {* :a :n} {- :n 1}}}}} 1 :n}}} '{YFAC 5} -> 120 '{YFAC 50} -> 3.0414093201713376e+64 '{map YFAC {serie 0 20}} -> 1 1 2 6 24 120 720 5040 40320 362880 3628800 39916800 479001600 6227020800 87178291200 1307674368000 20922789888000 355687428096000 6402373705728000 121645100408832000 2432902008176640000 } _p The last point to fix is that {code '{FAC 50}}, {code '{TFAC 50}} and {code '{YFAC 50}} return a rounded value {b 3.0414093201713376e+64} which is obvioulsly not the exact value. We must go a little further and build some tools capable of processing big numbers. } {{slide_content 9} ;; {img {@ src="data/brussels/bignumbers_2.jpg" width="100%"}} {img {@ src="data/brussels/planck.jpg" width="100%"}} {center {i E = {sup h}/{sub 2π}ν with h = 0.000000000000000000000000000000000662607004 J⋅s}} _h2 9) big numbers _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} and are rounded to {b 2000}. And beyond {b {b 2{sup 69}}} natural numbers are replaced by float numbers with a maximum of 15 valid digits. _p In order to overcome this limitation let's come back to the definition of a natural number: {b A natural number a{sub 0}a{sub 1}...a{sub n} is the value of a polynomial Σ{sub i=0}{sup n}a{sub i}x{sup i} for some value of x, called the base}. _p For instance {b 12345 = 1*10{sup 4}+2*10{sup 3}+3*10{sup 2}+4*10{sup 1}+5*10{sup 0}}. _p We can follow three strategies. _h4 {note_start BN_one 9.1 the BI_library} ... {note_end {@ id="BN_one"} _p We define and use a factorial function working on natural numbers of any size with an exact precision: {pre {WRAP} '{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}} }}}} -> {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 50} -> 304140932017133780436126081660647 68844377641568960512000000000000 } _p This function calls a set of '{lambda talk} functions exclusively built on numbers defined as lists of digits. {note_start BI See code...} {note_end {@ id="BI"} {pre '{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.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.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.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.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 {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}}}} }}} -> {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}}}} }}} }} _p With nothing but the set of special forms {b [lambda, def, if]}, the {b cons} and {b lists} structures and a few Math operators we have computed the exact value of {b 50!}. _p {b QED!} } _h4 {note_start BN_two 9.2 a small script} ... {note_end {@ id="BN_two"} _p In this presentation we are interested in computing {b n! = 1*2*3*...*n} which needs nothing but the {b *} operator acting on natural positive numbers. So we need nothing but a unique function {b long_mult} we define like this: {pre DICT['long_mult'] = function () '{ var args = supertrim(arguments[0]).split(' '); var n1 = args[0], n2 = args[1]; var a1 = n1.split("").reverse(); var a2 = n2.split("").reverse(); var a3 = []; for (var i1 = 0; i1 < a1.length; i1++) { for (var i2 = 0; i2 < a2.length; i2++) { var id = i1 + i2; a3[id] = a1[i1] * a2[i2] + (id >= a3.length)? 0 : a3[id]; if ( a3[id] > 9 ) { a3[id+1] = Math.floor(a3[id]/10) + (id+1 >= a3.length)? 0 : a3[id+1]; a3[id] -= Math.floor(a3[id]/10)*10; } } } return a3.reverse().join(""); }; } _p and define the factorial of a positive natural number like this: {pre {WRAP} '{def LONG_FAC {lambda {:n} {if {< :n 1} then 1 else {long_mult :n {LONG_FAC {- :n 1}}}}}} -> {def LONG_FAC {lambda {:n} {if {< :n 1} then 1 else {long_mult :n {LONG_FAC {- :n 1}}}}}} '{LONG_FAC 5} -> 120 '{LONG_FAC 50} -> 304140932017133780436126081660647 68844377641568960512000000000000 } _p It's rather quick, intense computation being done in the underlying language. } _h4 {note_start BN_three 9.3 the BN_library} ... {note_end {@ id="BN_three"} _p [[Jonas Raoni Soares Silva|http://jsfromhell.com/classes/bignumber]] as written a small (150 lines) and smart library, {b BigNumber}, ready to be called via '{lambda talk} wrapping functions, everything being stored in another wiki page, [[lib_bignum]]. Calling this code via a {b require} function, we just write this: {require lib_bignum} {pre {WRAP} '{BN.fac 50} -> 304140932017133780436126081660647 68844377641568960512000000000000 } _p Obviously the best choice! } _p With nothing but three rules working on text substitutions it was theoretically possible to compute {b 5!} and even {b 50!}. Adding names/constants and Church numbers results became readable. Using the Javascript's Math Object, things became easy and adding 150 lines of Javascript opened an effective window in the world of big numbers. _p We will now leave big numbers and show other applications of '{lambda talk}, built on the powerful functionalities of the browsers' engines. } {{slide_content 10} {img {@ src="data/brussels/2.jpg" width="100%"}} {center {i Forgets the shape, looks for control points.}} _h2 10) de Casteljau _p The animated picture below illustrates the {b de Casteljau} recursive algorithm which allows drawing Bezier curves of any degree, i.e controlled by any number of points in any multidimensionnal space. {img {@ src="data/brussels/pL4.gif" width="100%"}} _p '{lambda talk} can call the set of SVG functions implemented in web browsers. For instance the following colored {span {@ style="color:red;"}lambda}: {center {{svg.frame 250px 200px} {svg.dot {{def q0 {cons 50 10}}}} {svg.dot {{def q1 {cons 100 10}}}} {svg.dot {{def q2 {cons 200 160}}}} {svg.dot {{def q3 {cons 50 190}}}} {svg.dot {{def q4 {cons 200 190}}}} {polyline {@ points="{castel.build {list.new {q0} {q1} {q2} {q3} {q4} {q2}} -0.1 0.9 {pow 2 -5}}" stroke="#f00" fill="transparent" stroke-width="3"}} {polyline {@ points="{castel.build {list.new {q2} {q1} {q3}} 0.3 0.98 {pow 2 -5}}" stroke="#0f0" fill="transparent" stroke-width="5"}} }} _p comes from this code: {pre '{{svg.frame 250px 200px} {svg.dot {{def q0 {cons 50 10}}}} {svg.dot {{def q1 {cons 100 10}}}} {svg.dot {{def q2 {cons 200 160}}}} {svg.dot {{def q3 {cons 50 190}}}} {svg.dot {{def q4 {cons 200 190}}}} {polyline {@ points=" {castel.build {list {q0} {q1} {q2} {q3} {q4} {q2}} -0.1 0.9 {pow 2 -5}}" stroke="#f00" fill="transparent" stroke-width="3"}} {polyline {@ points=" {castel.build {list {q2} {q1} {q3}} 0.3 0.98 {pow 2 -5}}" stroke="#0f0" fill="transparent" stroke-width="5"}} } } _p On 2D points defined as {b pairs} and polylines as {b lists} we build the following set of user functions: {pre '{def castel.interpol {lambda {:p0 :p1 :t} {cons {+ {* {car :p0} {- 1 :t}} {* {car :p1} :t}} {+ {* {cdr :p0} {- 1 :t}} {* {cdr :p1} :t}} }}} -> {def castel.interpol {lambda {:p0 :p1 :t} {cons {+ {* {car :p0} {- 1 :t}} {* {car :p1} :t}} {+ {* {cdr :p0} {- 1 :t}} {* {cdr :p1} :t}} }}} '{def castel.sub {lambda {:l :t} {if {equal? {cdr :l} nil} then nil else {cons {castel.interpol {car :l} {car {cdr :l}} :t} {castel.sub {cdr :l} :t}} }}} -> {def castel.sub {lambda {:l :t} {if {equal? {cdr :l} nil} then nil else {cons {castel.interpol {car :l} {car {cdr :l}} :t} {castel.sub {cdr :l} :t}} }}} '{def castel.point {lambda {:l :t} {if {equal? {cdr :l} nil} then {car {car :l}} {cdr {car :l}} else {castel.point {castel.sub :l :t} :t} }}} -> {def castel.point {lambda {:l :t} {if {equal? {cdr :l} nil} then {car {car :l}} {cdr {car :l}} else {castel.point {castel.sub :l :t} :t} }}} '{def castel.build {lambda {:l :a :b :d} {map {castel.point :l} {serie :a :b :d}} }} -> {def castel.build {lambda {:l :a :b :d} {map {castel.point :l} {serie :a :b :d}}}} '{def svg.poly {lambda {:l} {if {equal? :l nil} then else {car {car :l}} {cdr {car :l}} {svg.poly {cdr :l}} }}} -> {def svg.poly {lambda {:l} {if {equal? :l nil} then else {car {car :l}} {cdr {car :l}} {svg.poly {cdr :l}}}}} '{def svg.dot {lambda {:p} {circle {@ cx="{car :p}" cy="{cdr :p}" r="5" stroke="black" stroke-width="3" fill="rgba(255,0,0,0.5)"}} }} -> {def svg.dot {lambda {:p} {circle {@ cx="{car :p}" cy="{cdr :p}" r="5" stroke="black" stroke-width="3" fill="rgba(255,0,0,0.5)"}} }} '{def svg.frame {lambda {:w :h} svg {@ width=":w" height=":h" style="border:1px solid #888; box-shadow:0 0 8px;"}}} -> {def svg.frame {lambda {:w :h} svg {@ width=":w" height=":h" style="border:1px solid #888; box-shadow:0 0 8px;"}}} } } {{slide_content 11} {img {@ src="data/brussels/spreadsheet.jpg" width="100%"}} {center {i A spreadsheet is a good illustration of functional languages, Simon Peyton-Jones}} _h2 11) spreadsheet _p A spreadsheet is an interactive computer application for organization, analysis and storage of data in tabular form. The basic idea is that each cell contains the input - {b words and any '{lambda talk} expression} - and displays the output. Writing: {pre '{spreadsheet 4 5}} _p displays a table of 5 rows and 4 columns of editable cells: {require lib_spreadsheet} {center {@ style="white-space:normal;"} {spreadsheet 4 5} } _p Two specific functions are added for linking cells: _ul {code '{LC i j}} returns the value of the cell {code L{sub i}C{sub j}} as an {b absolute} reference, _ul {b '{IJ i j}} returns the value of the cell {b L{sub [i]}C{sub [j]}} as a {b relative reference}. For instance writing {b '{IJ -1 -1}} in {code L2C2} will return the value of {b L1C1}. _p Datas are stored in the browser's {b localStorage}. Let's test: click on the {b [local storage]} button, copy the code in the frame below, paste it in the local storage frame, click on the {b editor -> storage} button, confirm the action and analyze the spreadsheet's cells. {pre {WRAP} °° ["{b NAME}","{b QUANT}","{b UNIT PRICE}","{b PRICE}","Item 1","10","2.1","{* {IJ 0 -2} {IJ 0 -1}}","Item 2","20","3.2","{* {IJ 0 -2} {IJ 0 -1}}","Item 3","30","4.3","{* {IJ 0 -2} {IJ 0 -1}}","","","{b TOTAL PRICE}","{+ {IJ -3 0} {IJ -2 0} {IJ -1 0}}","4"] °°} _p The '{lambda talk} and javascript functions on which is built the spreadsheet are written in another wiki page, {code lib_spreadsheet}, and called via a {b require} command. } {{slide_content 12} {div {@ style="font:italic 1.5em georgia; text-align:center;"} i{del h}{QUOTIENT 30 ∂ψ ∂t} (x,t) = {PAREN 3 (}mc{sup 2}α{sub 0} - i{del h}c {SIGMA 30 j=1 3} α{sub j}{QUOTIENT 30 ∂ ∂x{sub j}} {PAREN 3 )} ψ(x,t) } {center {i The Dirac equation in the form originally proposed by Dirac}} _h2 12) mathML _p '{lambda talk} forgets the {b MathML} markup set which is not implemented in Google Chrome. A set of functions can be defined to render Math Symbols. For instance the above {b Dirac equation} is not a picture but the result of the code below: {pre °° i{del h}{QUOTIENT 30 ∂ψ ∂t}(x,t) = {PAREN 3 (} mc{sup 2}α{sub 0} - i{del h}c {SIGMA 30 j=1 3} α{sub j} {QUOTIENT 30 ∂ ∂x{sub j}} {PAREN 3 )} ψ(x,t) °°} _p The code is built on standard HTML and CSS rules. {note_start mathML See code...} {note_end {@ id="mathML"} {pre '{def QUOTIENT {lambda {:s :num :denom} {table {@ style="width::spx; display:inline-block; vertical-align:middle; text-align:center;"} {tr {td {@ style="border:0 solid; border-bottom:1px solid;"}:num}} {tr {td {@ style="border:0 solid;"}:denom}} }}} -> {def QUOTIENT {lambda {:s :num :denom} {table {@ style="width::spx; display:inline-block; vertical-align:middle; text-align:center;"} {tr {td {@ style="border:0 solid; border-bottom:1px solid;"}:num}} {tr {td {@ style="border:0 solid;"}:denom}} }}} '{def SIGMA {lambda {:s :one :two} {table {@ style="width::spx; display:inline-block; vertical-align:middle; text-align:center;"} {tr {td {@ style="border:0 solid;"}:two}} {tr {td {@ style="border:0 solid; font-size:2em; line-height:0.7em;"}Σ}} {tr {td {@ style="border:0 solid;"}:one}} }}} -> {def SIGMA {lambda {:s :one :two} {table {@ style="width::spx; display:inline-block; vertical-align:middle; text-align:center;"} {tr {td {@ style="border:0 solid;"}:two}} {tr {td {@ style="border:0 solid; font-size:2em; line-height:0.7em;"}Σ}} {tr {td {@ style="border:0 solid;"}:one}} }}} '{def PAREN {lambda {:s :p} {span {@ style="font:normal :sem arial; vertical-align:-0.15em;"}:p}}} -> {def PAREN {lambda {:s :p} {span {@ style="font:normal :sem arial; vertical-align:-0.15em;"}:p}}} } } } {{slide_content 13} {center {show {@ src="data/brussels/d1.jpg" height="140" width="500" title="Ray tracing"}} {show {@ src="data/brussels/pforms_coons.jpg" height="140" width="500" title="Coons patches built on 6 cubic spline curves with embedded regular polygons"}} {show {@ src="data/brussels/mandel_20160104.jpg" height="140" width="500" title="The MandelBrot set"}} {show {@ src="data/brussels/turtle_20161105.jpg" height="140" width="500" title="Turtle graphics with Lindenmeier, L-system"}} } _h2 13) scripts _p '{lambda talk} can be interfaced with some Javascript code written in any wiki page via a {code '{script ...}} form, allowing the exploration of intensive computing. For instance {b ray-tracing, curved shapes modeling, fractal and turtle graphics drawing}. _h5 And what about a "true" Lisp? _p Everything in the LISP world began with the initial code of John McCarthy on which worked a lot of good people : Gerald_Jay_Sussman, Paul Graham, and others. {b lambdalisp} is a translation of the Peter Norvig's LISP Python engine, trying to save its smart and clear coding style ! _p Writing: {pre '{lisp (def fact (lambda (n) (if (= n 1) 1 (* n (fact (- n 1)))))) (fact 6) (def make-account (lambda (balance) (lambda (amt) (begin (set! balance (+ balance amt)) balance)))) (def a1 (make-account 100)) (a1 -20) (a1 -20) (a1 -20) (a1 -20) (a1 -20) (a1 -20) }} _p displays: {pre {lisp (def fact (lambda (n) (if (= n 1) 1 (* n (fact (-- n))) ))) \n (fact 6) \n\n (def make-account (lambda (balance) (lambda (amt) (begin (set! balance (+ balance amt)) balance )))) \n (def a1 (make-account 100)) \n (a1 -20)\n (a1 -20)\n (a1 -20)\n (a1 -20)\n (a1 -20)\n (a1 -20)\n }} _p Yes, in Lambdalisp the {b '{set!}} special form exists! } {{slide_content 14} {img {@ src="data/brussels/escher_hands.jpg" width="100%"}} {center {i Use with caution! }} _h2 14) macros _p '{lambda talk} macros bring the power of regular expressions directly in the language. For instance: _h4 {note_start defun 14.1 def or defun} ... {note_end {@ id="defun"} _p As a first application, the expression {code '{def name {lambda {args} body}}} could be replaced by the {b syntaxic sugar} {code '{defun {name args} body}} {pre '{macro {defun {(\w*?) ([^{}]*?)}(?:[\s]*?)(.*)} to {def €1 {lambda {€2} €3}}} '{defun {mul :x :y} {* :x :y}} -> mul '{mul 3 4} -> 12 } } _h4 {note_start variadic 14.2 make it variadic} ... {note_end {@ id="variadic"} _p As a second example, '{lambda talk} comes with some variadic primitives, for instance [{code +,-,*,/,list}]. At first sight, user functions can't be defined variadic, for instance: {pre '{* 1 2 3 4 5} -> 120 // * is variadic '{mul 1 2 3 4 5} -> 2 // 3, 4, 5 are ignored } _p In order to make {code mul} variadic we glue values in a list and use a user defined helper function: {pre '{def variadic {lambda {:f :args} {if {equal? {cdr :args} nil} then {car :args} else {:f {car :args} {variadic :f {cdr :args}}}}}} -> variadic '{variadic mul {list 1 2 3 4 5}} -> 120 } _p But it's ugly and doesn't follow a standard call. We can do better using a macro: {pre 1) defining: '{macro {mul* (.*?)} to {variadic mul {list €1}}} 2) using: '{mul* 1 2 3 4 5} -> 120 } _p Now {code mul*} is a variadic function which can be used as any other primitive or user function, except that it's not a first class function, as in most Lisps. } _h4 {note_start hplink 14.3 titles, paragraphs & links} ... {note_end {@ id="hplink"} As a last example, '{lambda talk} comes with a predefined small set of macros allowing writing without curly braces titles, paragraphs, list items, links: {pre _{span}h1 TITLE ¬ stands for: '{h1 TITLE} _{span}p Some paragraph ... ¬ stands for: '{p Some paragraph ...} [{span}[PIXAR|http://www.pixar.com/]{span}] stands for: '{a {@ href="http://www.pixar.com/"}PIXAR} [{span}[sandbox]{span}] stands for: '{a {@ href="?view=sandbox"}sandbox} } _p These simplified alternatives, avoiding curly braces as much as possible, are fully used in the current document. } } {{slide_content 15} _h2 conclusion {img {@ src="data/brussels/2CV_boule_et_bill.jpg" width=100%" title="lambdatalk rolling in a 2CV lambdatank on ze lambdaway (drawing by Jean Roba & Laurent Verron in Spirou."}} {center {i A dwarf on the shoulders of giants}} _p '{lambda talk} takes benefit from the extraordinary power of modern web browsers, simply adding a coherent and unique notation {b without re-inventing the wheel}, just using existing tools, HTML/CSS, the DOM and Javascript. _p The '{lambda way} project is a thin overlay built upon any modern browser, proposing a small interactive development environment and a coherent language {i without any external dependencies} and thereby {b easy to download and install} on a web account provider running PHP. _p From any web browser on any system, complex web pages can be created, enriched, structured and tested in real time on the web. It's exactly how the current presentation was created. _h6 Thank you! ;; {p {show {@ src="data/brussels/MrMagoo.png" height="80" width="500" title="Thank you!"}}} _h4 {note_start bio bio} {note_end {@ id="bio"} _ul {b 1946} born in Perpignan, France _ul {b 1964-1971} study engineering [ECL] and architecture [UP7] _ul {b 1973-2012} build houses as an architect _ul {b 1990-2012} teach CAD in a school of architecture _ul {b 2012-2017} explore Lisp's Holly World °°° {lightbox {@ height="400" width="1000" thumb="50"} (http://epsilonwiki.free.fr/epsilonwiki/data/galerie/ali3.JPG 1946 born in Perpignan, France) (http://epsilonwiki.free.fr/epsilonwiki/data/galerie/paris-1970.jpg 1964-1971 study engineering [ECL] and architecture [UP7]) (http://marty.alain.free.fr/confs/data/modulor_3/nyls/nyls_1.jpg 1973-2012 build houses as an architect) (http://epsilonwiki.free.fr/epsilonwiki/data/galerie/wikimarty.jpg 1990-2012 teach CAD in a school of architecture) (http://epsilonwiki.free.fr/epsilonwiki/data/ali.jpg 2012-2017 explore Lisp's Holly World) ;; (http://epsilonwiki.free.fr/epsilonwiki/data/alain_moine.jpg 2017-... who knows?) } °°° } {hr} {script °° function HMS( w ) { var d = new Date(), h = d.getHours(), m = d.getMinutes(), s = d.getSeconds(); getId('hours').style.width = (w/24*h) + 'px'; getId('hours').innerHTML = h; getId('minutes').style.width = (w/60*m) + 'px'; getId('minutes').innerHTML = m; getId('seconds').style.width = (w/60*s) + 'px'; getId('seconds').innerHTML = s; } setInterval( HMS, 1000, 550 ); °°} {div {@ style="display:none"} {def HMS {lambda {:hms :back :col} {div {@ id=":hms" style="width:0px; height:5px; text-align:right; padding-right:5px; background::back; color::col; line-height:0.4em; text-shadow:0 0 8px #000; font-weight:bold; "}}}} } {div {@ style="box-shadow:0 0 8px #000; background:#444; border:1px solid #fff"} {HMS hours #f00 #fff} {HMS minutes #0f0 #fff} {HMS seconds #00f #fff} } } } ;; end CONTENT {{hide} {def TITLE {div {@ onclick="SLIDES.hide()" style=" position:relative; top:0; left:0; z-index:100; font:bold 3.5em georgia; text-align:center; "} on the '{lambda way}} {center 10{sup th} European Lisp Symposium | Brussels,Belgium | 2017/04/3-4 | Alain Marty {br} http://epsilonwiki.free.fr/lambdaway/ | marty.alain@free.fr} } {def WRAP {@ style="word-wrap: break-word; white-space:pre-wrap;"}} {def CALLS {lambda {:y :x} div {@ style="position:absolute; top::ypx; left::xpx; 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} div {@ class="call" style="width:200px; margin:0; padding:0; text-align:right; cursor:pointer;" onclick="SLIDES.show(':id')"}}} {def slide_content {lambda {:id} div {@ id=":id" class="content" style="display:{BLOCK}; z-index:2; font-family:courier new; border-top:1px dashed #ccc; background:#fff; 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;"}}}} } {style °° #title { display:block; } #frame_view { width:700px; font-size:1.0em; border:0; background:#fff; box-shadow:0 0 0; } .call:hover { opacity:0.5; } .call img { box-shadow:0 0 4px #000; } pre { font:normal 1.2em courier; box-shadow:0 0 8px #000; background:#ffc; } °°} {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} })(); °°}