+
1
|
skin
|
login
|
edit
workshop
::
foundations
user:anonymous
{img {@ src="data/mies_pav.jpg" width="100%" title="lambdaway is written with lambdaway and gives informations about lambdaway"}} _h1 lambdatalk's foundations °°° START TOC °°° {div {@ style=""}{input {@ id="startTOC" type="submit" value="TABLE OF CONTENT" onclick="TOC.build(this,'TOC','CONTENT')"}}} {div {@ id="TOC" style=" width:600px; padding:2px; background:white; border:1px solid; box-shadow:0 0 8px; font-size:0.8em;"}} °°° END TOC °°° {div {@ id="CONTENT"} _p {b You are short in time?} This concise introduction by [[Ward Cunningham|http://ward.asia.wiki.org/view/welcome-visitors/view/recent-changes/view/testing-microtalk]] is for you: {{ward_intro} _p Lambdatalk is a dialect of Lisp.{sup (more precisely of [[lambda calculus|?view=calc2talk]])} _ul 1) Expressions are written in a prefix notation using curly braces. _ul 2) Away from curly braces words are just words. {{bloc 1} 2+3 is equal to '{+ 2 3} {{bloc 2} 2+3 is equal to {+ 2 3} }} _ul 3) Functions are created with {code lambda} and named with {code def}. {{bloc 1} '{def foo {lambda {:a :b} :a+:b is equal to {+ :a :b}}} '{foo 2 3} {{bloc 2} {def foo {lambda {:a :b} :a+:b is equal to {+ :a :b}}} {foo 2 3} }}} _p {b Still here?} Take time to read the following introduction. _h3 introduction _p A {b wiki} {sup [[(***)|https://en.wikipedia.org/wiki/Wiki]]} is a web application which allows collaborative modification, extension, or deletion of its content and structure. In a typical wiki, text is written using a simplified and rather limited {i markup syntax} intended for an average user. Works have been done to build enhanced syntaxes in order to unify writing, styling and coding, for instance [[skribe]], [[Scribble|http://docs.racket-lang.org/scribble/]], [[LAML|http://people.cs.aau.dk/~normark/laml/papers/web-programming-laml.pdf]], [[LML]], [[SXML|https://en.m.wikipedia.org/wiki/SXML]], [[Curl|?view=curl]], [[Pollen|http://docs.racket-lang.org/pollen/]], ..., not to speak of [[PDF]] and [[LaTeX|?view=latex]] on the desktop side. But these tools are definitively devoted to coders and not to web designers and even less to beginners. _p My project was to build a wiki equiped with a true programming language which could be used collaboratively by an author, a web designer and a coder. The result is a small programming language, {b lambdatalk}, embedded in a tiny interactive development tool, {b lambdatank}, working as a light overlay on top of any modern web browser and easy to install on a standard web hosting. _p This is a snapshot of an editing session: {center {show {@ src="data/lambdatalk_splash.jpg" height="230" width="900" title="The lambdatank interface with code on the left and preview on the right in a real-time interaction."}}} _p As it can be seen, there are no {b B, I, U} fancy buttons for bold, italic or underline styles and other tools alike. Every enrichments are entered as special words, as commands to do something on other words. Even at the lowest level, users write code and are ready to collaborate with web designers and coders and improve their own knowledge. _p In this document we will focus on the language, {b lambdatalk}. We will introduce: _ul 1) the {u code structure}, _ul 2) the {u evaluation process}, _ul 3) the {u dictionary} of primitive Javascript functions, _ul 4) and some {u more things} highlighting design choices. _p {b Note:} Focusing on lambdatalk foundations this page forgets numerous examples given in [[introducing lambdatalk|?view=lambdatalk_slides_5]] you could have a look to. _h3 1) code structure _p In lambdatalk code is a string made of {b words} and {b forms}: _ul a {b word} is a sequence of any {b chars} except {code spaces} and curly braces {code '{}}, _ul a {b form} is an expression {code '{first rest}} where: _ul30 {code first} is a {b word}, _ul30 {code rest} is empty or a sequence of {b words} and {b forms}. _p The recursive definition of {b forms} gives code a flattened {b tree} structure. In {b lambdatalk} there are neither {code number}, nor {code boolean} nor {code string} types. Valid {b JavaScript numbers} {code 123, 1.23e10, ...} and {b booleans} {code true|false} are just plain words. And quoted {b strings} are useless, {code '{b Hello {u brave new} World}} displays {b Hello {u brave new} World}, as expected. _h3 2) evaluation process _p Lambdatalk code is evaluated on each keyboard input and progressively replaced by HTML code: _ul {b words} are not evaluated and are quickly skipped, _ul {b forms} {code '{first rest}} are divided into two classes, {b special forms} and {b simple forms}: _ul30 {b simple forms}, where {code first} belongs to a dictionary, are evaluated in a main {b loop}. For instance {code '{+ 1 {* 2 3} 4}}, in which {code +,*} are primitive functions and {code 1,2,3,4} are valid numbers, will be evaluated to {code '{+ 1 6 4}} then to {b {+ 1 {* 2 3} 4}}. _ul30 {b special forms}, where {code first} belongs to a reduced set, {code [lambda, quote, def, if, let]}, are specifically evaluated in a {b pre-processing} phase. For instance {code '{def myPI 3.1416}} in which we want to define {code myPI} as a {u future} name associated to the number {code 3.1416}, is replaced by {b {def myPI 3.1416}}. Later, the {b simple form} {code '{myPI}} will be evaluated to {b {myPI}}. _h4 2.1) simple forms evaluation _p A {b simple form} {code '{first rest}} where {code rest} doesn't contain anymore {b form} is called a {b leaf}. Lambdatalk progressively catches and evaluates {b leaves} in a loop: _ul if {code first} belongs to the dictionary, {code first} is applyed to {code rest} and {code '{first rest}} is {u replaced} by the resulting word(s), _ul else {code '{first rest}} is {u replaced} by {code (first rest)}, curly braces being simply replaced by simple parenthesis. _p For instance the following form: {pre 0: '{center √(3{sup 2}+4{sup 2}) = {sqrt {+ {* 3 3} {* 4 4}}}}} _p will be evaluated progressively in 4 steps: {pre 1: '{center √(3
2
+4'
2
) = {sqrt {+ 9 16}}} 2: '{center √(3
2
+4'
2
) = {sqrt 25}} 3: '{center √(3
2
+4'
2
) = 5} 4:
√(3
2
+4'
2
) = 5
} _p The loop stops when every {b leaves} have been {u replaced} by words. The resulting sequence of words is a valid HTML/CSS code sent to the browser's engine for a final evaluation and display. For instance, the previous expression will be displayed as: {div {@ style="text-align:center; border:1px solid grey; padding:4px 10px; font:bold 1.2em courier;"} √(3{sup 2}+4{sup 2}) = {sqrt {+ {* 3 3} {* 4 4}}}} _p More later. _h4 2.2) special forms evaluation _p In the pre-processing phase, lambdatalk catches and evaluates {b special forms} {code '{first rest}} in which {code first} belongs to this set {code [quote, lambda, def, if, let]}. _h5 2.2.1) delaying forms evaluation with "quote" _p The special form {code '{quote rest}}, (or the equivalent {i syntaxic sugar} form {code 'rest}), is {u replaced} by {code rest} unevaluated. For instance: {pre '{quote Hello World {+ 1 2} and {+ 1 {* 2 3} 4}} -> {quote Hello World {+ 1 2} and {+ 1 {* 2 3} 4}} or Hello World ''{+ 1 2} and ''{+ 1 {* 2 3} 4} -> Hello World '{+ 1 2} and '{+ 1 {* 2 3} 4} } _h5 2.2.2) creating functions with "lambda" _p The special form {code '{lambda {:args*} rest}} where {code :args*} is empty or a sequence of words, is {u replaced} by a unique word behaving as a reference to a function with {code :args*} as arguments and {code rest} as body, delaying its evaluation until function is called with related values: {code '{{lambda {:args*} body} vals*}} ; the special form {code lambda} is used to create {b anonymous functions} which are {b first citizen class and curried by default}. Lambdas are mainly used to bind in a {b form} some arguments to future values. For instance: {pre '{lambda {:a :b} :a + :b is equal to {+ :a :b}} -> {lambda {:a :b} :a + :b is equal to {+ :a :b}} // an internal reference to the form } _p Even if this function is built "diadic", it can be called with any number of values: {pre 1) zero values '{{lambda {:a :b} :a + :b is equal to {+ :a :b}}} -> {{lambda {:a :b} :a + :b is equal to {+ :a :b}}} // a function still waiting for two values 2) one value '{{lambda {:a :b} :a + :b is equal to {+ :a :b}} 3} -> {{lambda {:a :b} :a + :b is equal to {+ :a :b}} 3} // a function storing 3 and waiting for another value 3) two values once '{{lambda {:a :b} :a + :b is equal to {+ :a :b}} 3 4} -> {{lambda {:a :b} :a + :b is equal to {+ :a :b}} 3 4} 4) two values in sequence '{{{lambda {:a :b} :a + :b is equal to {+ :a :b}} 3} 4} -> {{{lambda {:a :b} :a + :b is equal to {+ :a :b}} 3} 4} 5) three values or more '{{lambda {:a :b} :a + :b is equal to {+ :a :b}} 3 4 5} -> {{lambda {:a :b} :a + :b is equal to {+ :a :b}} 3 4 5} // extra values are ignored } _p Thanks to these capabilities, lambdas will be used to create specialized functions, for instance {b derivatives of a function} and aggregate data, for instance {b complex numbers}, {b 3Dvectors}, {b pairs}, {b lists}, {b trees}, ... _p More later. _h5 2.2.3) extending the dictionary with "def" _p The special form {code '{def name rest}} is {u replaced} by the word {code name} behaving as a reference to {code rest}, which can be a sequence of words, evaluable forms and anonymous functions ; the special form {code def} is used to extend the dictionary with global {u variables} and {u functions}. For instance, we can give a name to a sentence: {pre '{def LIM Less is More (Mies van der Rohe)} -> {def LIM Less is More (Mies van der Rohe)} '{LIM} -> {LIM} } _p Sentences can be HTML tags and CSS rules: {pre '{def myStyle div {@ style = "text-align:center; color:blue; font-weight:bold; text-shadow:0 0 4px black" }} -> {def myStyle div {@ style="text-align:center; color:blue; font-weight:bold; text-shadow:0 0 4px black"}} '{{myStyle} This text is centered, blue and shadowed} -> } {{myStyle} This text is centered, blue and shadowed} _p We can give a name to an evaluable form, for instance the golden ratio {b φ}: {pre '{def φ {/ {+ 1 {sqrt 5}} 2}} -> {def φ {/ {+ 1 {sqrt 5}} 2}} '{φ} -> {φ} } _p We can give a name to an anonymous function created with {code lambda}: {pre '{def cute_add {lambda {:a :b} :a + :b is equal to {+ :a :b} }} -> {def cute_add {lambda {:a :b} :a + :b is equal to {+ :a :b} }} } _p and call it several times with different values: {pre '{cute_add 3 4} -> {cute_add 3 4} '{cute_add 1 1} -> {cute_add 1 1} } _p More later. _h5 2.2.4) building conditional control with "if" _p The special form {code '{if bool then one else two}} returns {code one} {b OR} {code two} according to the boolean value {code true|false} returned by {code bool}. The special form {code if} is used to create conditional expressions, recursive functions and loops. For instance: {pre '{if true then Hello World else Good Bye World} -> {if true then Hello World else Good Bye World} '{if false then Hello World else Good Bye World} -> {if false then Hello World else Good Bye World} } _p More useful, we can now build recursive functions: {pre '{def fac {lambda {:a :b} {if {< :b 1} then :a else {fac {* :a :b} {- :b 1}}}}} -> {def fac {lambda {:a :b} {if {< :b 1} then :a else {fac {* :a :b} {- :b 1}}}}} '{fac 1 20} -> {fac 1 20} } _p Several kinds of loop structures can be built with such tail-recursive functions. _p More later. _h5 2.2.5) some syntaxic sugar for lambdas with "let" _p The fifth {b special form} {code '{let { {:arg val}* } body}} is a {i syntaxic sugar} for the form {code '{{lambda {:args*} body} vals*}} ; {code let} is essentially used to emphasize {code :args*} as local variables. For instance, we want to compute {code f(x)=(x+1)*(x-1)} as {code f(a,b)=a*b} where {code a=x+1} and {code b=x-1}: {pre 1) using lambdas: '{{lambda {:x} {{lambda {:a :b} {* :a :b} // :a and :b are used here } {+ :x 1} // {+ :x 1} -> :a {- :x 1} // {- :x 1} -> :b }} 3} -> {{lambda {:x} {{lambda {:a :b} {* :a :b}} {+ :x 1} {- :x 1}} } 3} 2) using let: '{{lambda {:x} {let {{:a {+ :x 1}} // :a = {+ :x 1} {:b {- :x 1}}} // :b = {- :x 1} {* :a :b} // :a and :b are used here }} 3} -> {{lambda {:x} {let {{:a {+ :x 1}} {:b {- :x 1}}} {* :a :b}}} 3} } _p More later. _h5 2.2.6) more special forms _p The set of special forms is extended with: _ul {b script}: to write Javascript code in a wiki page _ul {b style}: to write CSS code in a wiki page _ul {b macro}: to write regular expressions in a wiki page _ul {b require}: to call external code written in a wiki page _p More later. _h3 3) dictionary _p The dictionary contains functions on which is based the evaluation of leaves. Currently, it contains a set of HTML/CSS tags, a set of maths operators and functions, a set of SVG functions, a set of pairs, lists and arrays functions and some others specific to lambdatank: {dl {dt 1. html tags and css rules} {dd @, div, span, a, ul, ol, li, dl, dt, dd, table, tr, td, h1, h2, h3, h4, h5, h6, p, b, i, u, pre, center, blockquote, sup, sub, del, code, br, hr, img, canvas, audio, video, iframe, input, script, style, _p {b Note}: Some HTML tags intensively used in a wiki context to build blocks, [{code h1, h2,..., h6, p, ul, ol}], can be used in an alternative easier syntax. For instance {code '{h1 TITLE}} can be replaced by {code _{span }h1 TITLE ¬}, without any opening and closing curly braces. Lambdatalk doesn't forget beginners! } {dt 2. math operators and functions} {dd >, <, >=, <=, =, not, or, and, +, *, -, /, %, abs, acos, asin, atan, atan2, ceil, cos, exp, floor, log, random, round, sin, sqrt, tan, pow, min, max, PI, E, date, serie, map, reduce,} {dt 3. svg tags} {dd svg, line, rect, circle, polyline, path, text, g, animateMotion, mpath, use, textPath,} {dt 4. sentences, pairs, lists, arrays} {dd first, rest, nth, length, equal?, empty?, chars, charAt, cons, cons?, car, cdr, cons.disp, list.new, list.disp, array.new, array?, array.disp, array.length, array.item, array.push, } {dt 5. others specific to lambdatank} {dd lib, lambdas, conses, arrays, when, mailto, note, note_start, note_end, back, drag, show, lightbox, forum, editable, lisp, lc, sheet, require.} } _p The special form {code '{def name rest}} allows the user to extend indefinitely the dictionary. For instance, the functions currently defined in this page are listed below: {dl {dt 6. user defined functions} {dd foo, myPI, LIM, myStyle, φ, cute_add, fac, add, D, log', log'', log''', six.two, six.three, six, inside?, euler, amélie, ...} } _p Still here? Congratulations! So let's listen to John Surman: {center {audio {@ controls} {source {@ src="http://marty.alain.free.fr/musique/JohnSurmanTaleOfTheAncient.mp3"}} }} _h3 4) some more things _p Here we give some more technical details intended to highlight implementation design choices. The first of them is that lambdatalk engine doesn't use the evaluation process followed by standard interpreters which are based on an {b Abstract Syntax Tree}, something like: {center {pre output = walk_tree( build_tree( tokenize_string( input )))}} _p In fact, lambdatalk has essentially to translate a tree structured code input into a tree structured code output, the {b HTML code}, which will be sent to the browser's engine for final evaluation and display. The couple lambdatank and lambdatalk is a light overlay, (~100kb), working on top of modern browsers, {i a dwarf on the shoulders of giants}. Lambdatalk quickly flights over words and uses Regular Expressions to catch and replace by words {b simple forms} in a loop, and {b special forms} in a pre-processing phase. This rather iconoclastic approach is considered as {i evil} by some coders, but it works fine in a wiki context. We give here some answers to frequently asked questions. _h5 4.1) How is caught a leaf? _p A leaf {code '{first rest}} is matched and evaluated in one line of Javascript: {center {pre while (code != (code = code.replace( pattern , apply ))) ; }} _p where {code pattern} is a single Regular Expression: {center {pre pattern = /\{([^\s{}]*)(?:[\s]*)([^{}]*)\}/g }} _p and {code apply} is a function applying {code first} to {code rest} and returning (a sequence of) words. _h5 4.2) Why using Regular Expressions? _p Because Regular Expressions are powerful and work fine in a wiki context! For instance, the code of this document is evaluated in about 30ms on a MacBook 2Ghz Intel Core i7 and in about 400ms on an old 1{sup st} generation iPad where the realtime evaluation can be locked to make editing easier. _p This is what Ward Cunningham wrote about that: « {i I was surprised that the technique worked so well in so many cases. I knew that regex are highly optimized and the cpus themselves optimize sequential access to memory which the regex must have at its core. [..] Yes, this has at its heart the repeated application of a text transformation. The fact that it is repeated application of the same transformation makes it exceptional. [..] Repeated application of Regular Expressions can perform Touring Complete computations. {u This works because the needed "state" is in the partially evaluated text itself.}} » All is said! _p And we don't forget that, thanks to the lambdatalk Regular Expressions based approach, we can simply write {code '{b Hello {i brave new} World}}, without quoting words, to get {b Hello {i brave new} World}, as in this brave old HTML! As far as I know, in the similar works related in the introduction, we must write {code '{b "Hello " {i "brave new"} " World"}} or something alike, without forgetting any space inside quotes. It's obviously not acceptable in a wiki context made of 99% text! _h5 4.3) Why using a loop and not recursion? _p Because recursion can {b and does} cause stack overflows and {code while} doesn't. It's as simple as that! _h5 4.4) How do lambdas bind values to arguments? _p Lambdas use arguments as Regular Expressions to replace their occurences found in the function's body - which is a string - by the given values: {pre 1) calling '{{lambda {:a :b} :a+:b is equal to {+ :a :b}} 3 4} 2) all occurences of :a and :b in the string ":a+:b is equal to '{+ :a :b}" are replaced by 3 and 4 in two steps: ":a+:b is equal to '{+ :a :b}".replace( /:a/g, 3 ) -> ":3+:b is equal to '{+ 3 :b}" ":3+:b is equal to '{+ 3 :b}".replace( /:b/g, 4 ) -> ":3+:4 is equal to '{+ 3 4}" 3) and ":3+:4 is equal to '{+ 3 4}" is returned for a future evaluation. } _p When the number of values is less then the function's arity, a new function is returned, storing the given values and waiting for the future values, making lambdatalk functions curried by default: {pre 1) calling '{{lambda {:a :b} :a+:b is equal to {+ :a :b}} 3} // 4 is forgotten! 2) all occurences of :a in the string ":a+:b is equal to '{+ :a :b}" are replaced by 3 ":a+:b is equal to '{+ :a :b}".replace( /:a/g, 3 ) -> "3+:b is equal to '{+ 3 :b}" 3) and a new function '{lambda {:b} 3+:b is equal to {+ 3 :b}} is returned for a future call and evaluation. } _p We can understand now why {b arguments are prefixed by a colon}. In order to avoid substitution conflicts, the string replacement process used by lambdas implies that arguments be prefixed by a distinctive char, say a colon. In the example shown this precaution obviously prevents the word {b equal} to be replaced by {b equ3l} ! _p {b Note:} Using for instance {code :a} and {code :ab} would generate an issue because their intersection, {code :a}, is not null. So for a good security arguments might be "bracketed" like this: {code :a: :ab:}. The key point is that their intersection must be null! _h5 4.5) Do lambdas create closures? _p No they don't! This weakness is partially balanced by the fact lambdas are curried by default: {pre 1) defining as usual: '{def add {lambda {:a :b} {+ :a :b}}} -> {def add {lambda {:a :b} {+ :a :b}}} 2) calling: '{add} -> {add} // return a function waiting for 2 values '{add 3} -> {add 3} // storing 3 and waiting for a second value ... '{{add 3} 4} -> {{add 3} 4} // ... which is now given '{add 3 4} -> {add 3 4} // calling with the two values awaited '{add 3 4 5} -> {add 3 4 5} // the extra value is ignored } _p As a more useful application of currying, this is how can be defined derivatives of a function {b f(x)}: {b f'(x), f''(x), f'''(x)}, ..., which are functions of {b x} and not values at a given {b x}: {pre 1) we define the derivative function: '{def D {lambda {:f :h :x} {/ {- {:f {+ :x :h}} {:f {- :x :h}} } {* 2 :h}}}} -> {def D {lambda {:f :h :x} {/ {- {:f {+ :x :h}} {:f {- :x :h}} } {* 2 :h}}}} 2) we create the 1st, 2nd and 3rd derivatives of the function log for a given value of :h and as functions of :x: '{def log' {lambda {:x} {D log 0.01 :x}} } -> {def log' {D log 0.01} } '{def log'' {lambda {:x} {D log' 0.01 :x} }} -> {def log'' {D {log'} 0.01} } '{def log''' {lambda {:x} {D log'' 0.01 :x} }} -> {def log''' {D {log''} 0.01} } 3) we can now call the 1st, 2nd and 3rd derivatives of log on a given value of x: '{log' 1} -> {{log'} 1} // ≠ 1 '{log'' 1} -> {{log''} 1} // ≠ -1 '{log''' 1} -> {{log'''} 1} // ≠ 2 } _p Agregate data can be built with lambdas, for instance {code rational numbers, complex numbers, 2D/3Dvectors, ...} Following [[SCIP|http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-14.html#%_sec_2.1.1]] (Structure and Interpretation of Computer Programs), this is how the fundamental Lisp's {code pairs} could be simply built using lambdas: {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}}}} '{cons Hello World} // store Hello and World and return a function -> {cons Hello World} // waiting for a third value given by car/cdr '{car {cons Hello World}} -> {car {cons Hello World}} '{cdr {cons Hello World}} -> {cdr {cons Hello World}} } _p Because {code pairs} are so useful to create {code lists, binary trees, ...}, they are actually built as primitives in the lambdatalk's dictionary. More to see about {b pairs} and {b lists} in [[introducing lambdatalk|../alphawiki_2/?view=lambdatalk_slides_5]] or in other pages in this wiki. _h5 4.6) Can special forms be nested? _p Yes you can! _ul {code def}s can be nested. Inner {code defs} don't return a name avoiding code pollution, but definitions are not local to the outer {code def}. So, in order to prevent names conflicts, a good practice (mimicking the OOP paradigm) is to prefix inner-defined names with the outer-defined names. For instance in the following example {code hypo.square} can be considered as a property of {code hypo}: {pre '{def hypo {def hypo.square {lambda {:x} {* :x :x}}} {lambda {:a :b} {sqrt {+ {hypo.square :a} {hypo.square :b}}}}} -> {def hypo {def hypo.square {lambda {:x} {* :x :x}}} {lambda {:a :b} {sqrt {+ {hypo.square :a} {hypo.square :b}}}}} '{hypo 3 4} -> {hypo 3 4} '{hypo 1 1} -> {hypo 1 1} } _ul {code if}s can be nested, leading to conditional sequences, for instance: {pre '{def inside? {lambda {:x :a :b} {if {equal? {+ 1 :x} NaN} then {u :x} is Not a Number else {if {< :x :a} then false else {if {< :x :b} then true else false}}}}} -> {def inside? {lambda {:x :a :b} {if {equal? {+ 1 :x} NaN} then {u :x} is Not a Number else {if {< :x :a} then false else {if {< :x :b} then true else false}}}}} '{inside? 0 1 10} -> {inside? 0 1 10} '{inside? 2 1 10} -> {inside? 2 1 10} '{inside? 11 1 10} -> {inside? 0 11 10} '{inside? foo 1 10} -> {inside? foo 1 10} } _ul {code lambda}s can be nested as we have seen in {b 2.2.5) some sugar for lambdas}. This is another example where we want to compute the area of a right angle triangle [a,b,c] given by this formula: {code area = √{span {@ style="border-top:1px solid black"} s*(s-a)*(s-b)*(s-c)}} where {code s= (a+b+c)/2}: {pre 1) using lambdas: '{{lambda {:a :b :c} {{lambda {:a :b :c :s} {sqrt {* :s {- :s :a} {- :s :b} {- :s :c}}} } :a :b :c {/ {+ :a :b :c} 2}}} 3 4 5} -> {{lambda {:a :b :c} {{lambda {:a :b :c :s} {sqrt {* :s {- :s :a} {- :s :b} {- :s :c}}} } :a :b :c {/ {+ :a :b :c} 2}}} 3 4 5} 2) using let: '{{lambda {:a :b :c} {let { {:a :a} {:b :b} {:c :c} {:s {/ {+ :a :b :c} 2} }} {sqrt {* :s {- :s :a} {- :s :b} {- :s :c}}}}} 3 4 5} -> {{lambda {:a :b :c} {let { {:a :a} {:b :b} {:c :c} {:s {/ {+ :a :b :c} 2} }} {sqrt {* :s {- :s :a} {- :s :b} {- :s :c}}}}} 3 4 5} } _ul Note that, because lambdas don't create closures, inner lambdas have no access to the outer lambdas arguments. This is why {code :a :b :c} must be duplicated in the above both versions. This workaround could be seen as a {i manual closure} ... which could be automatized in a next version of lambdatalk. Wait and see! _h5 4.7) What about iterative processes? _p Theoretically recursion can be used to build every iterative processes. But lambdatalk is nice ... and comes with three useful primitives [{code serie, map, reduce}] which can make things easier, safer and quicker. For instance, this is how can be computed a factorial without any recursion: {pre '{* {serie 1 20}} // because +,*,-,/ accept any number of arguments -> {* {serie 1 20}} } and the Euler's number {b {E}} defined as an infinite serie: {pre E = {sup 1}/{sub 0!}+{sup 1}/{sub 1!}+{sup 1}/{sub 2!}+{sup 1}/{sub 3!}+ ... {sup 1}/{sub n!} + ... for n -> ∞ '{def euler {lambda {:n} {reduce {lambda {:a :b} {+ :a :b}} {map {lambda {:n} {/ 1 {fac 1 :n}}} {serie 0 :n}}}}} -> {def euler {lambda {:n} {reduce {lambda {:a :b} {+ :a :b}} {map {lambda {:n} {/ 1 {fac 1 :n}}} {serie 0 :n}}}}} '{euler 17} -> {euler 17} } _h5 4.8) What about mutability? _p Defined values can't be modified, lambdatalk is {b purely functional} and has no {code set!} special form. Sometimes however this capability could be useful and lambdatalk comes with a {code '{lisp ...}} function allowing to call a true lisp interpreter, [[lambdalisp]], in which {code set!} has been defined. For instance we can follow the successive states of an account. Writing: {pre '{lisp (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 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 }} _h5 4.9) What would be a minimal set of special forms? {blockquote “Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary.” } {blockquote "Perfection is reached not when there is nothing to add but when there is nothing to remove." } _p We have seen that: _ul {code '} is a {i syntaxic sugar} for {code quote}, _ul {code let} is a {i syntaxic sugar} for {code lambda}, _ul {code if} is a {i syntaxic sugar} for the primitive function {code '{when bool then one else two}} when {code one} and {code two} must be quoted, for instance in a recursive function. _p And theoretically {code def} could be also forgotten even in recursive functions. For instance, this is how a factorial function could be defined and called without being named: {pre '{{lambda {:f :n} {:f :f :n}} {lambda {:f :n} {when {= :n 1} then 1 else {quote {* :n {:f :f {- :n 1}}}}}} 6} -> {{lambda {:f :n} {:f :f :n}} {lambda {:f :n} {when {= :n 1} then 1 else {quote {* :n {:f :f {- :n 1}}}}}} 6} } _p So, {i at least theoretically}, the set of special forms could be ultimately reduced to this one, [{code quote,lambda}], two operators acting on time, both delaying evaluation to future. Funny, isnt'it? _h5 4.10) Why is lambdatalk easy to use by web designers? _p This is how a global sequences of CSS rules can be defined and used: {pre '{def amélie {img {@ id="amelie" src="data/amelie_poulain.jpg" height="150" title="Amélie Poulain" style="box-shadow:0 0 8px black; border:1px solid white; -webkit-transform:rotate(-5deg); -moz-transform:rotate(-5deg); transform:rotate(-5deg);"}}} -> {def amélie {img {@ id="amelie" src="data/amelie_poulain.jpg" height="150" title="Amélie Poulain" style="box-shadow:0 0 8px black; border:1px solid white; -webkit-transform:rotate(-5deg); -moz-transform:rotate(-5deg); transform:rotate(-5deg);"}}} '{amélie} -> {amélie} } _p We can see that the {code '{@ ...}} form breaks the previously claimed syntax unicity, there are no clean {b forms} in it and a lot of {i ugly} quoted strings used by HTML attributes and CSS rules! It's a matter of {b design choice}, avoiding any pollution of the dictionary with a myriad of CSS rules and giving web designers an access to {b standard HTML attributes and CSS rules} they are familiar with. And any coder will be able to hide behind names all those {i ugly} things. _h5 4.11) What about styles? _p CSS rules applied to the entire wiki are externalized as CSS files in the {b skins} folder and can be selected in the lambdatank's top-left menu. The current skin can be analyzed in the file [[skins/newone/CSS.css|skins/newone/CSS.css]]. Local CSS rules can be applied to a page using the {code '{style ...}} form. For instance in this page, a few tags have been overloaded like this: {pre '{style °° h1 {color:white; text-shadow:0 0 8px black;} h3, h4, h5, h6 {color:#822} code {color:#000; background:#fff; font:normal 0.9em courier;} pre {color:#000; font:normal 0.9em courier;} b {color:#822;} blockquote {background:#fff; font-style:normal} °°} } _h5 4.12) What about scripts? _p The primitive {code '{input ...}} can easily embed short scripts via any onEvent attribute. For instance: {pre '{input {@ id="smart_hello" type = "text" placeholder = "Please, enter your name" onkeyup = "getId('yourName').innerHTML = 'Hello ' + getId('smart_hello').value + ' !'" }} '{h1 {@ id="yourName"}} } _p displays this text field in which you can enter your name: {p {input {@ id="smart_hello" type = "text" placeholder = "Please, enter your name" onkeyup = "getId('yourName').innerHTML = 'Hello ' + getId('smart_hello').value + ' !'" }}} {div {@ id="yourName" style="font:bold 4em georgia; color:blue"}} _p The primitive {code '{script ...}} can be used to insert more complex scripts. For instance: {pre '{div {@ id="output" style="..."}time: } '{input {@ type="submit" value="start" onclick="start()"}} '{input {@ type="submit" value="stop" onclick="stop()"}} '{script function start() { document.chrono = window.setInterval( function() { getId('output').innerHTML = 'time: ' + LAMBDATALK.eval_forms('{date}'); }, 1000 );} function stop() { window.clearInterval( document.chrono ) } } } _p displays a digital watch: {div {@ id="output" style="font:bold 1.5em courier; color:red;"}time: } {input {@ type="submit" value="start" onclick="start()"}} {input {@ type="submit" value="stop" onclick="stop()"}} {script °° function start() { document.chrono = window.setInterval( function () { getId('output').innerHTML = 'time: ' + LAMBDATALK.eval_forms('{date}'); }, 1000 ); } function stop() { window.clearInterval( document.chrono ); } °°} _p And scripts can be externalized in Javascript files to add functionalities, for instance a spreadsheet, a ray-tracer, a 3D shapes modeler, a true Lisp interpreter. _p More later. _h5 4.13) What about libraries? _p Sets of user variables and functions can be stored in wiki pages and called elsewhere via a {code (require library)} call. Thus, the primitive dictionary can be extended as necessary with coherent modules carefully built to avoid name conflicts. For instance, knowing that the page {code amelie_lib} contains definitions of {code [picture, name, exact_born]}, writing: {pre 1) call definitions written in amelie_lib: (require lib_amelie) 2) and use them: {quote {picture}{br} {name} is born the {nth 1 {exact_born}}{sup th} month of {nth 0 {exact_born}}, and so, today, is {age exact_born date}.} } _p will display: {blockquote {require lib_amelie} {picture}{br} {name} is born the {nth 1 {exact_born}}{sup th} month of {nth 0 {exact_born}}, and so, today, is {age exact_born date}. } _p More later. _h5 4.14) What about macros? _p Macros in lambdatalk bring the power of regular expressions directly in the language, have a look at this page [[macros]]. _h5 4.15) Where does lambdatalk come from? _p Not exactly from Lisp, I would say from [[lambda_calculus|?view=calc2talk]]. _h5 4.16) Still here? _p If you want to know a little more, for instance about displaying math formulas {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) } _p about fancy curves like these: {div {@ style="position:relative; top:0; left:0; background:#ffe; border:1px solid black; height:300px; box-shadow:0 0 8px;"} {dot {{def Q0 200 30}} 20 black cyan} {dot {{def Q1 500 30}} 20 black cyan} {dot {{def Q2 30 200}} 20 black cyan} {dot {{def Q3 350 270}} 20 black cyan} {map {lambda {:t} {dot {bez_cubic Q0 Q1 Q2 Q3 :t} 5 black red}} {serie -0.2 1.2 0.02}} {map {lambda {:t} {dot {bez_cubic Q0 Q2 Q3 Q1 :t} 5 black white}} {serie 0.1 0.9 0.02}} } _p or about more complex things like these ones: {center {show {@ src="http://epsilonwiki.free.fr/alphawiki_2/data/pforms_coons.jpg" height="150" width="500" title="Live Pascalian Forms in a wiki page"}} {show {@ src="http://epsilonwiki.free.fr/lambdaway_2.0/data/lambdaray_20130329/d1.jpg" height="150" width="500" title="Live Ray Tracing in a wiki page"}} {show {@ src="http://epsilonwiki.free.fr/alphawiki_2/data/lambdasheet/lambdasheet_1.jpg" height="150" width="500" title="A spreadsheet coming with lambdatalk as embedded language"}} } _p have a look in the other pages of this wiki. And feel free to ask questions in page [[forum]]. _h3 conclusion _p {b lambdatalk} is a small but complete programming language offering tools which can be used, in any modern web browser and on any device (from desktop PCs to smartphones) at three levels of increasing complexity: _ol with a handful of basic commands any {b author} can easily create minimally structured documents made of words and pictures, _ol any {b web designer} can find a full set of HTML tags and CSS rules to enrich them, _ol and any {b coder} can fit specific needs and make pages compositing more easy by extending the language's built-in functionalities. _p Everybody sharing a clear, unique and coherent notation allowing to produce rich and dynamical documents in a true collaborative work. Commenting this work, somebody wrote this: « Reminds me of {b John McCarthy}'s lament at the W3C's choice of SGML as the basis for HTML: "{i An environment where the markup, styling and scripting is all s-expression based would be nice.}" » _p This was the goal of the {b lambdaway} project. _p Alain Marty, 2015/10/26 (partially updated on 2017/09/10) } {div {@ style="display:none"} {def ward_intro div {@ style="box-shadow:0 0 8px black; margin:10px 0; padding:20px; font:normal 1.0em verdana;"}} {def bloc {lambda {:col} div {@ style="{if {= :col 1} then background:#eee; font-family:courier; else background:#ddd; font-family:verdana;} white-space:pre-wrap; padding:10px; margin:5px 0;"} {div {@ style="font-size:0.6em; text-align:right; color:red; margin:-10px 0px -20px -8px;"} {if {= :col 1} then code else view}}}} {def bez_cubic {def bc.interp {lambda {:a0 :a1 :a2 :a3 :t :u} {round {+ {* :a0 :u :u :u 1} {* :a1 :u :u :t 3} {* :a2 :u :t :t 3} {* :a3 :t :t :t 1}}}}} {lambda {:p0 :p1 :p2 :p3 :t} {bc.interp {nth 0 {:p0}} {nth 0 {:p1}} {nth 0 {:p2}} {nth 0 {:p3}} :t {- 1 :t}} {bc.interp {nth 1 {:p0}} {nth 1 {:p1}} {nth 1 {:p2}} {nth 1 {:p3}} :t {- 1 :t}} }} {def dot {lambda {:x :y :r :bord :back} {span {@ style="position:absolute; left:{- :x {/ :r 2}}px; top:{- :y {/ :r 2}}px; width::rpx; height::rpx; border-radius::rpx; border:1px solid :bord; background::back;"}}}} {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 paren {lambda {:s :p} {span {@ style="font:normal :sem arial; vertical-align:-0.15em;"}:p}}} } {style °° h1 {color:white; text-shadow:0 0 8px black;} h3, h4, h5, h6 {color:#622} code {color:#000; background:#fff; font:normal 0.9em courier;} pre {color:#000; font:normal 0.9em courier;} b {color:#622;} blockquote {background:#fff; font-style:normal} °°}