Page Not Found | LIPS Scheme
-
+
diff --git a/assets/js/e1902777.57d1fa3f.js b/assets/js/e1902777.57d1fa3f.js
deleted file mode 100644
index 48c1ce9e..00000000
--- a/assets/js/e1902777.57d1fa3f.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(self.webpackChunknew_docs=self.webpackChunknew_docs||[]).push([[5609],{5264:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>o,contentTitle:()=>c,default:()=>h,frontMatter:()=>i,metadata:()=>t,toc:()=>l});var r=s(5893),a=s(1151);const i={sidebar_position:1,description:"Core LIPS features added on top of Scheme, related to JavaScript"},c="Core features",t={id:"lips/intro",title:"Core features",description:"Core LIPS features added on top of Scheme, related to JavaScript",source:"@site/docs/lips/intro.md",sourceDirName:"lips",slug:"/lips/intro",permalink:"/docs/lips/intro",draft:!1,unlisted:!1,editUrl:"https://github.com/jcubic/lips/tree/master/docs/docs/lips/intro.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1,description:"Core LIPS features added on top of Scheme, related to JavaScript"},sidebar:"tutorialSidebar",previous:{title:"LIPS introduction",permalink:"/docs/category/lips-introduction"},next:{title:"Reflection",permalink:"/docs/lips/reflection"}},o={},l=[{value:"Special constants",id:"special-constants",level:2},{value:"Numerical tower",id:"numerical-tower",level:2},{value:"Print procedure",id:"print-procedure",level:2},{value:"Emoji",id:"emoji",level:2},{value:"Macros",id:"macros",level:2},{value:"Gensyms",id:"gensyms",level:3},{value:"Single argument eval",id:"single-argument-eval",level:2},{value:"Procedures",id:"procedures",level:2},{value:"length property",id:"length-property",level:3},{value:"Doc strings",id:"doc-strings",level:2},{value:"Typechecking",id:"typechecking",level:2},{value:"Integration with JavaScript",id:"integration-with-javascript",level:2},{value:"Dot notation",id:"dot-notation",level:3},{value:"Mutating object properties",id:"mutating-object-properties",level:3},{value:"Boxing",id:"boxing",level:3},{value:"Procedures",id:"procedures-1",level:3},{value:"Procedure arity",id:"procedure-arity",level:4},{value:"Helper macros and functions",id:"helper-macros-and-functions",level:3},{value:"Legacy macros and functions",id:"legacy-macros-and-functions",level:4},{value:"Scheme functions",id:"scheme-functions",level:3},{value:"JavaScript functions",id:"javascript-functions",level:3},{value:"Callbacks",id:"callbacks",level:3},{value:"Regular Expressions",id:"regular-expressions",level:3},{value:"Vectors",id:"vectors",level:3},{value:"Object literals",id:"object-literals",level:3},{value:"Automagic async/await",id:"automagic-asyncawait",level:3},{value:"Promise quotation",id:"promise-quotation",level:3},{value:"Promises vs delay expression",id:"promises-vs-delay-expression",level:3},{value:"Exceptions",id:"exceptions",level:3},{value:"JavaScript Generars and iterators",id:"javascript-generars-and-iterators",level:3},{value:"Classes",id:"classes",level:3},{value:"Node.js",id:"nodejs",level:3},{value:"Binary compiler",id:"binary-compiler",level:2}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.a)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.h1,{id:"core-features",children:"Core features"}),"\n",(0,r.jsx)(n.h2,{id:"special-constants",children:"Special constants"}),"\n",(0,r.jsxs)(n.p,{children:["LIPS define ",(0,r.jsx)(n.code,{children:"#null"})," and ",(0,r.jsx)(n.code,{children:"#void"})," as Parser constants so they can be used inside quoted expressions:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(let ((lst '(#null #void)))\n (write (symbol? (car lst)))\n (newline)\n (write (symbol? (cadr lst)))\n (newline))\n;; ==> #f\n;; ==> #f\n"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"NOTE"})," ",(0,r.jsx)(n.code,{children:"#null"})," is the same as JavaScript ",(0,r.jsx)(n.code,{children:"null"})," value and it's a false value. Similar to\n",(0,r.jsx)(n.a,{href:"https://www.gnu.org/software/kawa/index.html",children:"Kawa Scheme"})," that use ",(0,r.jsx)(n.code,{children:"#!null"}),"."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.code,{children:"#void"})," constants is the same as result of ",(0,r.jsx)(n.code,{children:"(value)"})," or ",(0,r.jsx)(n.code,{children:"(if #f #f)"}),". In Scheme it's unspecified value,\nbut in LIPS it's JavaScript undefined. ",(0,r.jsx)(n.code,{children:"#void"})," is not false value."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(eq? (if #f #f) (values))\n;; ==> #t\n"})}),"\n",(0,r.jsx)(n.h2,{id:"numerical-tower",children:"Numerical tower"}),"\n",(0,r.jsx)(n.p,{children:"LIPS support full numerical tower (not yet 100% unit tested):"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"integers - using BitInt"}),"\n",(0,r.jsx)(n.li,{children:"floats - using JavaScript numbers"}),"\n",(0,r.jsx)(n.li,{children:"rationals"}),"\n",(0,r.jsx)(n.li,{children:"complex numbers (that can use integers, floats, or rationals)"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"print-procedure",children:"Print procedure"}),"\n",(0,r.jsxs)(n.p,{children:["LIPS define helper ",(0,r.jsx)(n.code,{children:"print"})," procedure that display all its arguments with newline after each element."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(print 1 2 3)\n;; ==> 1\n;; ==> 2\n;; ==> 3\n"})}),"\n",(0,r.jsx)(n.h2,{id:"emoji",children:"Emoji"}),"\n",(0,r.jsx)(n.p,{children:"LIPS fully supports all Unicode characters, including emoji:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(define smiley #\\\ud83d\ude00)\n(define poo #\\\ud83d\udca9)\n(write (string-append (string smiley) " " (string poo)))\n;; ==> "\ud83d\ude00 \ud83d\udca9"\n'})}),"\n",(0,r.jsx)(n.p,{children:"You can also use them as part of symbols (e.g. as variables name):"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(define (\u23cf\ufe0f)\n (print "ejecting"))\n(\u23cf\ufe0f)\n;; ==> ejecting\n'})}),"\n",(0,r.jsx)(n.h2,{id:"macros",children:"Macros"}),"\n",(0,r.jsxs)(n.p,{children:["LIPS define both Lisp macros and Scheme hygienic macros (",(0,r.jsx)(n.code,{children:"syntax-rules"}),")."]}),"\n",(0,r.jsx)(n.p,{children:"It also implements:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"https://srfi.schemers.org/srfi-46/",children:"SRFI-46"})," which allows changing the ellipsis symbol for nested syntax-rules."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"https://srfi.schemers.org/srfi-139/",children:"SRFI-139"})," which allows defining\n",(0,r.jsx)(n.a,{href:"/docs/scheme-intro/macros#anaphoric-hygienic-macros",children:"anaphoric syntax-rules macros"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"https://srfi.schemers.org/srfi-147/",children:"SRFI-147"})," which allows defining a new syntax-rules macros to define syntax-rules macros."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"gensyms",children:"Gensyms"}),"\n",(0,r.jsxs)(n.p,{children:["With lisp macros you can use ",(0,r.jsx)(n.a,{href:"/docs/scheme-intro/macros#gensyms",children:"gensyms"}),", they are special Scheme\nsymbols that use JavaScript symbols behind the scene, so they are proven to be unique. Additionally\nyou can use named gensym if you pass string as first argument:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(gensym)\n;; ==> #:g5\n(gensym "sym")\n;; ==> #:sym\n'})}),"\n",(0,r.jsx)(n.h2,{id:"single-argument-eval",children:"Single argument eval"}),"\n",(0,r.jsxs)(n.p,{children:["Eval in LIPS don't require second argument to ",(0,r.jsx)(n.code,{children:"eval"}),". The environment is optional and default\nit's a result of calling ",(0,r.jsx)(n.code,{children:"(interaction-environment)"}),"."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(define x 10)\n(eval '(+ x x))\n;; ==> 20\n"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(define x 10)\n(let ((x 20))\n (eval '(- x)))\n;; ==> -10\n"})}),"\n",(0,r.jsx)(n.p,{children:"But you can also use the second arguments:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(define x 10)\n(let ((x 20))\n (eval '(- x) (current-environment)))\n;; ==> -20\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Read more about ",(0,r.jsx)(n.a,{href:"/docs/lips/environments",children:"LIPS environments"}),"."]}),"\n",(0,r.jsx)(n.h2,{id:"procedures",children:"Procedures"}),"\n",(0,r.jsxs)(n.p,{children:["Procedures in LIPS have access additional objects ",(0,r.jsx)(n.code,{children:"arguments"}),", but the have nothing to do with JavaScript.\narguments is an array/vector with calling arguments and it have an object callee which points to the same\nprocedure. So you can create recursive functions with anonymous lambda:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"((lambda (n)\n (if (<= n 0)\n 1\n (* n (arguments.callee (- n 1))))) 10)\n;; ==> 3628800\n"})}),"\n",(0,r.jsx)(n.p,{children:"This is classic factorial function written as lambda without the name."}),"\n",(0,r.jsx)(n.h3,{id:"length-property",children:"length property"}),"\n",(0,r.jsxs)(n.p,{children:["LIPS functions similarly to JavaScript functions also have ",(0,r.jsx)(n.code,{children:"length"})," property that indicate how many\narguments a function accepts. If function get more or less argumenets it's not an error like in Scheme. More arguments are ignored,\nand if less arguments are passed they are ",(0,r.jsx)(n.code,{children:"undefined"})," (",(0,r.jsx)(n.code,{children:"#void"}),")."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(define (sum a b c)\n (+ a b c))\n\n(print sum.length)\n;; ==> 3\n"})}),"\n",(0,r.jsx)(n.p,{children:"It return number of number arguments the rest (dot notation) arguments are ignored."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(define (sum a b . rest)\n (apply + a b rest))\n\n(print sum.length)\n;; ==> 3\n(sum 1 2 3 4 5 6)\n;; ==> 21\n"})}),"\n",(0,r.jsx)(n.h2,{id:"doc-strings",children:"Doc strings"}),"\n",(0,r.jsx)(n.p,{children:"Procedures, macros, and variables can have doc strings."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(define (factorial n)\n "(factorial n)\n\n Calculate factorial of a given number"\n (if (<= n 0)\n 1\n (* n (factorial (- n 1)))))\n'})}),"\n",(0,r.jsxs)(n.p,{children:["You can access doc string with ",(0,r.jsx)(n.code,{children:"help"})," procedure or with ",(0,r.jsx)(n.code,{children:"__doc__"})," property."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(write factorial.__doc__)\n"(factorial n)\n\nCalculate factorial of a given number"\n'})}),"\n",(0,r.jsxs)(n.p,{children:["If you define variable or hygienic macro with doc string, the string is hidden (you can access it with ",(0,r.jsx)(n.code,{children:"__doc__"}),"),\nso help is the only way to access it:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(define-syntax q\n (syntax-rules ()\n ((_ x) \'x))\n "(q expression)\n\n Macro quote the expression")\n\n(write q.__doc__)\n;; ==> #void\n(help q)\n;; ==> (q expression)\n;; ==>\n;; ==> Macro quote the expression\n'})}),"\n",(0,r.jsx)(n.h2,{id:"typechecking",children:"Typechecking"}),"\n",(0,r.jsx)(n.p,{children:"LIPS do typechecking for all scheme procedures."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(+ "hello" 10)\n;; ==> Expecting number got string\n'})}),"\n",(0,r.jsx)(n.p,{children:"You can incorporate typechecking in your own code:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(let ((x 10))\n (typecheck "let" x "string" 0))\n;; ==> Expecting string got number in expression `let` (argument 0)\n(let ((x "string"))\n (typecheck "let" x "number"))\n;; ==> Expecting number got string in expression `let`\n'})}),"\n",(0,r.jsx)(n.p,{children:"There is also another function to check type of number:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(let ((i 10+10i))\n (typecheck-number "let" i "bigint"))\n;; ==> Expecting bigint got complex in expression `let`\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"NOTE"}),": In LIPS all integers are BigInts."]}),"\n",(0,r.jsxs)(n.p,{children:["The last typecking function is ",(0,r.jsx)(n.code,{children:"typecheck-args"})," that check if all arguments are of same type."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(let ((number \'(1 10 1/2 10+10i)))\n (typecheck-args "number" "let" number))\n;; ==> #void\n(let ((number \'(1 10 1/2 "string")))\n (typecheck-args "number" "let" number))\n;; ==> Expecting number got string in expression `let` (argument 4)\n'})}),"\n",(0,r.jsx)(n.h2,{id:"integration-with-javascript",children:"Integration with JavaScript"}),"\n",(0,r.jsx)(n.h3,{id:"dot-notation",children:"Dot notation"}),"\n",(0,r.jsx)(n.p,{children:"LIPS allow accessing JavaScript objects with dot notation:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"document.querySelector\n;; ==> #\n"})}),"\n",(0,r.jsx)(n.h3,{id:"mutating-object-properties",children:"Mutating object properties"}),"\n",(0,r.jsxs)(n.p,{children:["You can use dot notation with ",(0,r.jsx)(n.code,{children:"set!"})," to change the value:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(set! self.foo 10)\nself.foo\n"})}),"\n",(0,r.jsxs)(n.p,{children:["top level ",(0,r.jsx)(n.code,{children:"self"})," always points to a global object ",(0,r.jsx)(n.code,{children:"window"})," in browser or ",(0,r.jsx)(n.code,{children:"global"})," in Node."]}),"\n",(0,r.jsxs)(n.p,{children:["There is also older API that still work, which is ",(0,r.jsx)(n.code,{children:"set-obj!"})," but with dot notation you don't\nneed it anymore:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(set-obj! self 'foo 10)\n(display self.foo)\n;; ==> 10\n"})}),"\n",(0,r.jsx)(n.p,{children:"In both platforms you can access global JavaScript objects like normal variables:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(set! self.greet "hello, LIPS")\n(write greet)\n;; ==> "hello, LIPS"\n'})}),"\n",(0,r.jsx)(n.h3,{id:"boxing",children:"Boxing"}),"\n",(0,r.jsx)(n.p,{children:"LIPS have its own representation for numbers, strings and characters. And when\ninteracting with JavaScript the values may get boxed or unboxed automagically."}),"\n",(0,r.jsxs)(n.p,{children:["You should not confuse boxing with boxes (",(0,r.jsx)(n.a,{href:"https://srfi.schemers.org/srfi-111/",children:"SRFI-111"})," and\n",(0,r.jsx)(n.a,{href:"https://srfi.schemers.org/srfi-195",children:"SRFI-195"}),"). LIPS boxes are part of implementation of Scheme\ndata types. And SRFI boxes are containers written in Scheme. Name boxing came from JavaScript, when\nprimitive values are wrapped in objects when you try to use them in object context (like accessing\na property)."]}),"\n",(0,r.jsx)(n.p,{children:"You need to be careful with some of the JavaScript native methods, since they can unbox the value when you don't\nwhen them to be unboxed."}),"\n",(0,r.jsxs)(n.p,{children:["Example is ",(0,r.jsx)(n.code,{children:"Array::push"})," using with native LIPS types:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(let ((v (vector)))\n (v.push 1/2)\n (print v))\n;; ==> #(0.5)\n"})}),"\n",(0,r.jsx)(n.p,{children:"As you can see the rational number got unboxed and converted into JavaScript float numbers.\nUnboxing always can make you loose some information because LIPS types needs to be converted into native JavaScript\ndata types. And JavaScript doesn't have a notion of rationals, there are only floating point numbers, and big ints."}),"\n",(0,r.jsx)(n.h3,{id:"procedures-1",children:"Procedures"}),"\n",(0,r.jsx)(n.p,{children:"LIPS Scheme procedures are JavaScript functions, so you can call them from JavaScript."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(set! self.greet (lambda () "hello, LIPS"))\n'})}),"\n",(0,r.jsx)(n.p,{children:"You can call this function from JavaScript"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"console.log(greet());\n// ==> {__string__: 'hello, LIPS'}\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Note that the value was not automagically unboxed because we are no longer in LIPS Scheme code and LIPS can't access native\nJavaScript. So to get the real a string you need to call ",(0,r.jsx)(n.code,{children:"valueoOf()"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"console.log(greet().valueOf());\n// ==> hello, LIPS\n"})}),"\n",(0,r.jsx)(n.h4,{id:"procedure-arity",children:"Procedure arity"}),"\n",(0,r.jsx)(n.p,{children:"LIPS don't check the number of argumnents when calling a procedure:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(let ((test (lambda (a b c)\n (print a b c))))\n (test 10))\n;; ==> 10\n;; ==> #void\n;; ==> #void\n"})}),"\n",(0,r.jsxs)(n.p,{children:["The same as with JavaScript if you don't pass an argument it will be undefined. But you still have full compatible with Scheme and use ",(0,r.jsx)(n.a,{href:"/docs/scheme-intro/core#variable-number-of-arguments",children:"arguments with variable artity"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(let ((test (lambda (first . rest)\n (apply print first rest))))\n (test 1)\n (test 2 3 4))\n;; ==> 1\n;; ==> 2\n;; ==> 3\n;; ==> 4\n"})}),"\n",(0,r.jsx)(n.h3,{id:"helper-macros-and-functions",children:"Helper macros and functions"}),"\n",(0,r.jsxs)(n.p,{children:["The most useful macro in LIPS (for interacting with JavaScript) is ",(0,r.jsx)(n.code,{children:"--\x3e"})," it acts like a chain of\nmethod calls in JavaScript"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(--\x3e "this is string" (split " ") (reverse) (join " "))\n;; ==> "string is this"\n'})}),"\n",(0,r.jsx)(n.p,{children:"You can chain methods that return arrays or string and call a method of them. The above expression\nis the same as JavaScript:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"\"this is string\".split(' ').reverse().join(' ');\n"})}),"\n",(0,r.jsx)(n.p,{children:"With --\x3e you can also gab property of a function:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(--\x3e #/x/ (test.call #/foo/ "foo"))\n;; ==> #t\n(let ((test-bar (--\x3e #/x/ (test.bind #/bar/i))))\n (test-bar "BAR"))\n;; ==> #t\n'})}),"\n",(0,r.jsx)(n.p,{children:"You can also return a function:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(define test (--\x3e #/x/ test))\n(test.call #/foo/ "foo")\n;; ==> #t\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Read more about ",(0,r.jsx)(n.a,{href:"https://tinyurl.com/ykvb836s",children:"function::bind"})," and\n",(0,r.jsx)(n.a,{href:"https://tinyurl.com/yc6j7fdh",children:"function::call"})," on ",(0,r.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/",children:"MDN"}),"."]}),"\n",(0,r.jsx)(n.h4,{id:"legacy-macros-and-functions",children:"Legacy macros and functions"}),"\n",(0,r.jsx)(n.p,{children:"There are two legacy macros that are still part of LIPS, but you don't need\nthem most of the time."}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"."})," - dot function was a first way to interact with JavaScript, it allowed to\nget property from an object:"]}),"\n"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(. document 'querySelector)\n"})}),"\n",(0,r.jsxs)(n.p,{children:["This returned function querySelector from document object in browser. Note that dot a function can only appear\nas first element of the list (it's handled in special way by the parser). In any other place dot is a pair separator,\nsee documentation about ",(0,r.jsx)(n.a,{href:"/docs/scheme-intro/data-types#pairs",children:"Pairs in Scheme"}),"."]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:".."})," - this is a macro is that simplify usage of ",(0,r.jsx)(n.code,{children:"."})," procedure:"]}),"\n"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(.. document.querySelector)\n"})}),"\n",(0,r.jsxs)(n.p,{children:["You still sometimes may want to use this instead of ",(0,r.jsx)(n.code,{children:"--\x3e"})," when you want to get\nproperty from an object returned by expression."]}),"\n",(0,r.jsx)(n.p,{children:"In the old version of LIPS, you have to execute code like this:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'((. document \'querySelector) "body")\n((.. document.querySelector) "body")\n'})}),"\n",(0,r.jsx)(n.p,{children:"The first expression return a Native JavaScript procedure that is then executed."}),"\n",(0,r.jsx)(n.p,{children:"This is equivalent of:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(document.querySelector "body")\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"NOTE"})," the only time when you still need ",(0,r.jsx)(n.code,{children:"."})," function is when you want to get the property of\nobject returned by expression."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(let ((style (. (document.querySelector "body") \'style)))\n (set! style.background "red"))\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Here we get a ",(0,r.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style",children:"style object"}),"\nfrom ",(0,r.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement",children:"the DOM node"})," without sorting the\nreference to the DOM node."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"NOTE"})," because dot notation in symbols is not special syntax you can use code like this:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(let ((x #(1 2 3)))\n (print x.0)\n (print x.1)\n (print x.2))\n;; ==> 1\n;; ==> 2\n;; ==> 3\n"})}),"\n",(0,r.jsx)(n.h3,{id:"scheme-functions",children:"Scheme functions"}),"\n",(0,r.jsx)(n.p,{children:"Scheme functions (lambda's) are JavaScript functions, so you can call them from JavaScript."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(set! window.foo (lambda () (alert "hello")))\n'})}),"\n",(0,r.jsx)(n.p,{children:"If you define function like this, in browser REPL, you can call it from JavaScript\n(e.g. browser developer console)."}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"TODO"})," Screenshot"]}),"\n",(0,r.jsx)(n.h3,{id:"javascript-functions",children:"JavaScript functions"}),"\n",(0,r.jsx)(n.p,{children:"You can call JavaScript functions from Scheme, the same as you call Scheme procedures:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(document.querySelector "body")\n;; ==> #\n'})}),"\n",(0,r.jsxs)(n.p,{children:["In both browser and Node.js you can execute ",(0,r.jsx)(n.code,{children:"console.log"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(console.log "hello, LIPS")\n;; ==> hello, LIPS\n'})}),"\n",(0,r.jsx)(n.h3,{id:"callbacks",children:"Callbacks"}),"\n",(0,r.jsx)(n.p,{children:"You can use Scheme functions as callbacks to JavaScript functions:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(--\x3e #("1" "2" "3") (map string->number))\n;; ==> #(1 +nan.0 +nan.0)\n'})}),"\n",(0,r.jsx)(n.p,{children:"This is classic issue with functions that accept more than one argument. You have samilar issue\nin JavaScript:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:'["1", "2", "3"].map(parseInt)\n// ==> [1, NaN, NaN]\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"NOTE"}),": the value are different becaseu in Shceme i"]}),"\n",(0,r.jsx)(n.p,{children:"To fix the issue you can\ndefine lambda with single argument:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(--\x3e #("1" "2" "3") (map (lambda (str) (string->number str))))\n;; ==> #(1 2 3)\n'})}),"\n",(0,r.jsxs)(n.p,{children:["You can also use one of functional helpers insprired by ",(0,r.jsx)(n.a,{href:"https://ramdajs.com/",children:"Ramda"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(--\x3e #("1" "2" "3") (map (unary string->number)))\n;; ==> #(1 2 3)\n'})}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.code,{children:"unary"})," ",(0,r.jsx)(n.a,{href:"/docs/scheme-intro/core#higher-order-functions",children:"higher-order procedure"})," accept a single\nprocedure and return new procedure that accept only one argument."]}),"\n",(0,r.jsxs)(n.p,{children:["To read more check ",(0,r.jsx)(n.a,{href:"/docs/lips/functional-helpers",children:"Functional helpers"}),"."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"WARNING"})," be careful when using scheme callback functions inside JavaScript.\nSince some code may be ",(0,r.jsx)(n.code,{children:"async"})," and your code may break."]}),"\n",(0,r.jsx)(n.p,{children:"Example of procedures that are not wise to use are:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"Array::forEach"})," - this function accepts a callaback but because it doesn't return\nanything, LIPS can't automatically await the response, and your code may execute out of order."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"String::replace"})," - this function can accept optional callback and if ",(0,r.jsx)(n.code,{children:"lambda"})," is async\nyou will end up with ",(0,r.jsx)(n.code,{children:"[object Promise]"})," in output string. Any macro or function can return\na promise in LIPS, and if any of the expression inside a function return a Promise, the whole\nfunction return a Promise and become async. Here is example code that demonstrate the problem:"]}),"\n"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(--\x3e "foo bar" (replace "foo" (lambda () (Promise.resolve "xxx"))))\n"[object Promise] bar"\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Instead of ",(0,r.jsx)(n.code,{children:"Array::replace"})," you should use LIPS Scheme ",(0,r.jsx)(n.code,{children:"replace"})," procedure that works with async ",(0,r.jsx)(n.code,{children:"lambda"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(replace #/[a-z]+/g (lambda ()\n (Promise.resolve "lips"))\n "foo bar")\n;; ==> "lips lips"\n'})}),"\n",(0,r.jsx)(n.h3,{id:"regular-expressions",children:"Regular Expressions"}),"\n",(0,r.jsxs)(n.p,{children:["LIPS define regular expressions it uses native JavaScript regular expressions.\nAt first, the syntax looked like in JavaScript. It was problematic for the parser\nso you were not able to put space after ",(0,r.jsx)(n.code,{children:"/"})," to distinguish from divide procedure.\nLater, the syntax was renamed into form that start with hash ",(0,r.jsx)(n.code,{children:"#/[0-9]/"}),". The same\nsyntax is used by ",(0,r.jsx)(n.a,{href:"https://practical-scheme.net/gauche/man/gauche-refe/Regular-expressions.html",children:"Gauche"})," implementation. But LIPS supports more flags (same as JavaScript)."]}),"\n",(0,r.jsx)(n.h3,{id:"vectors",children:"Vectors"}),"\n",(0,r.jsxs)(n.p,{children:["In LIPS Scheme vectors are JavaScript arrays. So you can execute methods on them with ",(0,r.jsx)(n.code,{children:"--\x3e"})," macro:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(--\x3e #("one" "two" "three") (join ":"))\n;; ==> "one:two:three"\n'})}),"\n",(0,r.jsx)(n.h3,{id:"object-literals",children:"Object literals"}),"\n",(0,r.jsxs)(n.p,{children:["In LIPS you can define object literals with ",(0,r.jsx)(n.code,{children:"&"}),"\n",(0,r.jsx)(n.a,{href:"/docs/lips/extension#syntax-extensions",children:"syntax extension"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(define obj &(:name "Jack" :age 22))\n(write obj)\n;; ==> &(:name "Jack" :age 22)\n(console.log obj)\n;; ==> { name: \'Jack\', age: 22 }\n'})}),"\n",(0,r.jsx)(n.p,{children:"You can nest object literals and mix them with different object:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(define obj &(:name "Jack" :hobbies #("swimming" "programming")))\n(write obj.hobbies)\n;; ==> #("swimming" "programming")\n(console.log obj)\n;; ==> { name: \'Jack\', hobbies: [ \'swiming\', \'programming\' ] }\n'})}),"\n",(0,r.jsx)(n.p,{children:"Object similar to Scheme vectors, are immutable, and everything inside is quoted automatically:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(define obj &(:name Jack))\n(write obj)\n;; ==> &(:name "Jack")\n'})}),"\n",(0,r.jsx)(n.p,{children:"But to make it possible to share objects with JavaScript, native LIPS values are automatically unboxed.\nSo instead of symbol representation you get a JavaScript string."}),"\n",(0,r.jsx)(n.p,{children:"You can also use quasiquote with object literals:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(define jack (let ((name "Jack")\n (age 22))\n `&(:name ,name :age ,age)))\n(write jack)\n;; ==> &(:name "Jack" :age 22)\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"NOTE"}),": because of the construction of ",(0,r.jsx)(n.a,{href:"/docs/lips/extension#syntax-extensions",children:"syntax extensions"})," and\n",(0,r.jsx)(n.a,{href:"/docs/scheme-intro/data-types#quasiquote",children:"quasiquote"}),", you can't splice a list inside object literals:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(let ((args (list \':foo "lorem" \':bar "ipsum")))\n `&(,@args))\n;; ==> pair (unquote-splicing args) is not a symbol!\n'})}),"\n",(0,r.jsxs)(n.p,{children:["The reason why this work like this is because, syntax extensions (",(0,r.jsx)(n.code,{children:"&"}),") runs at parse time and LIPS macros are runtime.\nThis may change in the future when ",(0,r.jsx)(n.a,{href:"https://github.com/jcubic/lips/issues/169",children:"expansion time will be implemented"}),"."]}),"\n",(0,r.jsxs)(n.p,{children:["Objects also have longhand form with ",(0,r.jsx)(n.code,{children:"object"})," macro:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(let ((obj (object :name \'Jack)))\n (write obj))\n;; ==> &(:name "Jack")\n'})}),"\n",(0,r.jsx)(n.p,{children:"But note that object macro is async (return a Promise) so it may be problematic when used it\nwith native JavaScript code."}),"\n",(0,r.jsxs)(n.p,{children:["Using long form ",(0,r.jsx)(n.code,{children:"(object)"})," syntax you can use splicing with help of ",(0,r.jsx)(n.code,{children:"eval"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(let ((args \'(:foo "lorem" :bar "ipsum")))\n (eval `(object ,@args)))\n;; ==> &(:foo "lorem" :bar "ipsum")\n'})}),"\n",(0,r.jsx)(n.p,{children:"The same you can use macros that will return LIPS Scheme code:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:'(define-macro (create-object . args)\n `(object ,@args))\n\n(create-object :foo "lorem" :bar "ipsum")\n;; ==> &(:foo "lorem" :bar "ipsum")\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"NOTE"}),": this example macro works the same ",(0,r.jsx)(n.code,{children:"object"})," is it's not that useful, but you can create\nmore complex code where you will be able to generate object literals with splicing."]}),"\n",(0,r.jsx)(n.p,{children:"Object literal also have shorthad notation:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(let ((obj &(:x :y)))\n (write obj))\n;; ==> &(:x #void :y #void)\n"})}),"\n",(0,r.jsx)(n.p,{children:"It creates two writtable slots, the rest of the props are read only:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-scheme",children:"(let ((obj &(:x :y)))\n (set! obj.x 10)\n (set! obj.y 20)\n (write obj))\n;; ==> &(:x 10 :y 20)\n\n(let ((obj &(:x :y)))\n (set! obj.z 20)\n (write obj))\n;; ==> Cannot add property z, object is not extensible\n\n(let ((obj &(:x :y :z 10)))\n (set! obj.z 20)\n (write obj))\n;; ==> Cannot assign to read only property 'z' of object '#