Re: Lark - A surface syntax for Common Lisp

"David McClain (as dbm at refined-audiometrics dot com)" <[email protected]>
Newsgroups gmane.lisp.lispworks.general
Message-ID <[email protected]>
Heh… Those were supposed to be closing parens… but being dyslexic, I constantly get those things confused. Just all the more awful to have a multiplicity of closings.



> On Feb 14, 2026, at 17:39, David McClain <[email protected]> wrote:
> 
> Wow! I really admire your tenacity. 
> 
> But honestly, I cannot imagine anything more awful than trying to remember how to close off compound expressions. Was that {([ or {[( ? 
> 
> I had to write Rust for a year, and faced that dilemma constantly. Why would I ever wish to have this same handicap in my Lisp?
> 
> 
> 
>> On Feb 14, 2026, at 12:39, Gerry Weaver (as gerryw at compvia dot com) <[email protected]> wrote:
>> 
>> Hello All,
>> 
>> I've been working on this thing for a while. I'm getting close to the 
>> point where I'm going to call it good for now. I'm mainly working on the 
>> stdlib stuff (ie; http server, http client, websockets, etc.). It isn't 
>> meant to replace Lisp. It is intended to be an ergonomic extension. 
>> There is a lark command that is similar to that provided by the go 
>> language go command and provides most of the same functionality. There 
>> is also a syntax file for Sublime Text. I'm very interested in any 
>> thoughts or suggestions y'all might have. If enough folks are 
>> interested, I will try to put it on github at some point.
>> 
>> Here is a rough description of the syntax. It is still evolving a 
>> little, but mostly stable.
>> 
>> Thanks,
>> 
>> -G
>> 
>> 
>> 
>> -- Lark is a readable surface syntax for Common Lisp.
>> -- Everything here compiles to standard CL s-expressions.
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- COMMENTS
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- Single line comment (double dash)
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- VARIABLES
>> -- ═══════════════════════════════════════════════════════════
>> 
>> let x = 42                     -- lexical binding (let)
>> let* y = x + 1                 -- sequential binding (let*)
>> let name = "Lark"              -- strings
>> let pi = 3.14159               -- floats
>> let ratio = 2/3                -- rationals
>> let hex = 0xFF                 -- hex literals
>> let nothing = nil              -- nil
>> let yes = true                 -- boolean true (t)
>> let no = false                 -- boolean false (nil)
>> let ch = #\A                   -- character literal
>> let kw = :hello                -- keyword symbol
>> 
>> -- Assignment (mutation)
>> x := 100                       -- setf
>> 
>> -- Destructuring multiple return values
>> let (q, r) = floor(17, 5)     -- multiple-value-bind
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- FUNCTIONS
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- Expression form (single expression body)
>> fn square(x) = x * x
>> 
>> -- Block form (multiple statements)
>> fn greet(name)
>>    let msg = concat("Hello, ", name, "!")
>>    print(msg)
>>    msg
>> end
>> 
>> -- With docstring
>> fn factorial(n)
>>    "Compute n! recursively."
>>    if n <= 1 then 1 else n * factorial(n - 1) end
>> end
>> 
>> -- Optional parameters
>> fn connect(host, port = 8080) = print("%s:%d", host, port)
>> 
>> -- Keyword parameters
>> fn makeUser(name:, age: 0, role: "guest")
>>    hash(name: name, age: age, role: role)
>> end
>> -- Call with keywords: makeUser(name: "Gerry", age: 30)
>> 
>> -- Rest parameters
>> fn logAll(...args) = forEach(args, |a| print(a))
>> 
>> -- CL lambda-list keywords also work
>> fn withRest(a, b, &rest others) = list(a, b, others)
>> 
>> -- Return values
>> fn safeDivide(a, b)
>>    if b == 0 then return nil end
>>    a / b
>> end
>> 
>> -- Multiple return values
>> fn divmod(a, b)
>>    return (floor(a, b))    -- returns quotient and remainder
>> end
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- CLOSURES (LAMBDAS)
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- Single expression
>> let double = |x| x * 2
>> 
>> -- Multi-statement
>> let counter = |start| do
>>    let n = start
>>    n := n + 1
>>    n
>> end
>> 
>> -- No parameters
>> let greetWorld = || print("Hello, World!")
>> 
>> -- Function references (like CL #'name)
>> let f = &square                -- #'square
>> map([1, 2, 3], &square)       -- (mapcar #'square '(1 2 3))
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- DATA STRUCTURES
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- Lists
>> let nums = [1, 2, 3, 4, 5]
>> let empty = []
>> 
>> -- Vectors
>> let vec = #(10, 20, 30)
>> 
>> -- Hash tables (dictionaries)
>> let config = {host: "localhost", port: 8080, debug: true}
>> let empty = {}
>> 
>> -- Access
>> config["host"]                  -- lark-ref (string key)
>> nums[0]                         -- lark-ref (index)
>> 
>> -- Dot access (method-style)
>> config.host                     -- not valid for hashes, use []
>> nums.length                     -- (length nums)
>> 
>> -- Quoted symbols and lists
>> let sym = 'hello               -- (quote hello)
>> let quoted = '(1, 2, 3)        -- (quote (1 2 3))
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- OPERATORS
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- Arithmetic
>> 1 + 2                           -- addition
>> 10 - 3                          -- subtraction
>> 4 * 5                           -- multiplication
>> 10 / 3                          -- division (CL exact: 10/3)
>> 17 mod 5                        -- modulo
>> 17 rem 5                        -- remainder
>> -x                              -- negation
>> 
>> -- Comparison
>> x == y                          -- equal
>> x != y                          -- not equal
>> x === y                         -- eq (identity)
>> x !== y                         -- not eq
>> x < y                           -- less than
>> x > y                           -- greater than
>> x <= y                          -- less or equal
>> x >= y                          -- greater or equal
>> 
>> -- Logical
>> x and y                         -- short-circuit and
>> x or y                          -- short-circuit or
>> not x                           -- negation
>> 
>> -- Pipeline (threading)
>> "hello world" |> upper() |> split(" ")
>> -- → (split (upper "hello world") " ")
>> 
>> -- Data flows left to right:
>> [1, 2, 3, 4, 5]
>>    |> filter(|x| x > 2)
>>    |> map(|x| x * 10)
>>    |> reduce(0, |acc, x| acc + x)
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- STRINGS
>> -- ═══════════════════════════════════════════════════════════
>> 
>> let s = "hello"
>> let interp = "x is ${x} and 2+2 is ${2 + 2}"   -- string interpolation
>> let raw = "line1\nline2\ttab"                     -- escape sequences
>> 
>> -- Triple-quoted strings (no escaping needed)
>> let html = """
>> <html>
>>  <body><h1>Hello</h1></body>
>> </html>
>> """
>> 
>> let json = """
>> {"host": "localhost", "port": 8080, "debug": true}
>> """
>> 
>> let regex = """\d+\.\d+"""              -- backslashes are literal
>> 
>> -- Triple-quoted with interpolation
>> let page = """
>> <html>
>>  <title>${title}</title>
>>  <body>${content}</body>
>> </html>
>> """
>> 
>> -- String operations
>> split("a,b,c", ",")            -- ["a", "b", "c"]
>> join(["a", "b"], "-")          -- "a-b"
>> trim("  hi  ")                 -- "hi"
>> upper("hello")                 -- "HELLO"
>> lower("HELLO")                 -- "hello"
>> replace("hello", "l", "r")    -- "herlo" (first)
>> replaceAll("hello", "l", "r") -- "herro" (all)
>> startsWith?("hello", "he")    -- true
>> endsWith?("hello", "lo")      -- true
>> contains?("hello", "ell")     -- true (works on strings, lists, hashes)
>> indexOf("hello", "ll")        -- 2
>> substr("hello", 1, 3)         -- "el"
>> concat("a", "b", "c")         -- "abc"
>> repeat("ha", 3)               -- "hahaha"
>> lines("a\nb\nc")              -- ["a", "b", "c"]
>> words("hello world")          -- ["hello", "world"]
>> chars("abc")                   -- [#\a, #\b, #\c]
>> padLeft("42", 5)               -- "   42"
>> padRight("hi", 5)              -- "hi   "
>> len("hello")                   -- 5
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- COLLECTIONS
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- Functional operations
>> map([1, 2, 3], |x| x * 2)             -- [2, 4, 6]
>> filter([1, 2, 3, 4], |x| x > 2)       -- [3, 4]
>> reject([1, 2, 3, 4], |x| x > 2)       -- [1, 2]
>> reduce([1, 2, 3], 0, |a, b| a + b)    -- 6
>> find([1, 2, 3], |x| x > 1)            -- 2
>> any?([1, 2, 3], |x| x > 2)            -- true
>> all?([1, 2, 3], |x| x > 0)            -- true
>> sort([3, 1, 2], |a, b| a < b)         -- [1, 2, 3]
>> forEach([1, 2, 3], |x| print(x))      -- side effects
>> 
>> -- Container operations
>> len([1, 2, 3])                          -- 3
>> size({a: 1, b: 2})                      -- 2
>> keys({a: 1, b: 2})                      -- ["a", "b"]
>> values({a: 1, b: 2})                    -- [1, 2]
>> has?({a: 1}, "a")                       -- true
>> pairs({a: 1, b: 2})                     -- [("a" . 1), ("b" . 2)]
>> merge({a: 1}, {b: 2})                   -- {a: 1, b: 2}
>> contains?([1, 2, 3], 2)                 -- true
>> flatten([[1, 2], [3, [4]]])             -- [1, 2, 3, 4]
>> unique([1, 2, 2, 3])                    -- [1, 2, 3]
>> 
>> -- Mutation
>> let lst = [1, 2, 3]
>> push!(lst, 4)                           -- lst is now (4 1 2 3)
>> pop!(lst)                               -- returns 4
>> reverse!(lst)                           -- in-place reverse
>> let h = {a: 1, b: 2}
>> delete!(h, "a")                         -- remove key
>> 
>> -- Slicing
>> take([1, 2, 3, 4], 2)                  -- [1, 2]
>> drop([1, 2, 3, 4], 2)                  -- [3, 4]
>> zip([1, 2], ["a", "b"])                 -- [(1 "a"), (2 "b")]
>> range(5)                                -- [0, 1, 2, 3, 4]
>> range(1, 5)                             -- [1, 2, 3, 4]
>> range(0, 10, 2)                         -- [0, 2, 4, 6, 8]
>> enumerate(["a", "b"])                   -- [(0 . "a"), (1 . "b")]
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- CONTROL FLOW
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- If/elseif/else (expression — returns value)
>> let label = if x > 100 then "big"
>>            elseif x > 10 then "medium"
>>            else "small"
>>            end
>> 
>> -- If/then (statement)
>> if ready then
>>    process()
>> end
>> 
>> -- Match (pattern matching)
>> fn describe(val)
>>    match val with
>>    | 0           -> "zero"
>>    | 1           -> "one"
>>    | n when n < 0 -> "negative"
>>    | _           -> "other"
>>    end
>> end
>> 
>> -- Type matching
>> fn handle(msg)
>>    match msg with
>>    | _: String   -> print("string: %s", msg)
>>    | _: Integer  -> print("integer: %d", msg)
>>    | _           -> print("unknown")
>>    end
>> end
>> 
>> -- Named bindings in match
>> match result with
>> | x when x > 0 -> print("positive: %d", x)
>> | x             -> print("non-positive: %d", x)
>> end
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- LOOPS
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- For-in (iteration)
>> for item in [1, 2, 3] do
>>    print(item)
>> end
>> 
>> -- For-range (numeric)
>> for i = 0, 10 do
>>    print(i)
>> end
>> 
>> -- For-range with step
>> for i = 0, 100, 5 do
>>    print(i)
>> end
>> 
>> -- While loop
>> let n = 10
>> while n > 0 do
>>    print(n)
>>    n := n - 1
>> end
>> 
>> -- Loop (CL loop pass-through)
>> loop
>>    for i from 1 to 10
>>    when i mod 2 == 0
>>    collect i
>> end
>> 
>> -- For-collect (loop comprehension)
>> for x in range(10) when x > 5 collect x * 2
>> 
>> -- For-sum
>> for x in [1, 2, 3, 4] sum x * x
>> 
>> -- List comprehension (bracket syntax)
>> [x * 2 for x in range(10)]
>> [x * 2 for x in range(10) if x > 5]
>> 
>> -- Comprehension with destructuring
>> [concat(k, "=", v) for (k, v) in pairs(config)]
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- ERROR HANDLING
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- Try/on/ensure (→ handler-case + unwind-protect)
>> try
>>    riskyOperation()
>> on Error e do
>>    print("caught: %s", e)
>> on TypeError e do
>>    print("type error: %s", e)
>> ensure
>>    cleanup()
>> end
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- LOCAL FUNCTIONS
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- Where block (→ labels, mutually recursive)
>> where
>>    fn isEven(n) = n == 0 or isOdd(n - 1)
>>    fn isOdd(n) = n != 0 and isEven(n - 1)
>> in
>>    isEven(42)
>> end
>> 
>> -- Flet block (→ flet, non-recursive)
>> flet
>>    fn double(x) = x * 2
>>    fn triple(x) = x * 3
>> in
>>    double(5) + triple(5)
>> end
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- CLASSES AND METHODS
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- Class definition (→ defclass)
>> class Point(x: 0, y: 0)
>> 
>> -- Inheritance
>> class Point3D(z: 0) extends Point
>> 
>> -- Struct definition (→ defstruct: faster, typed, no CLOS overhead)
>> struct PacketHeader(version: 0, length: 0, type: :tcp)
>> 
>> -- Struct with inheritance (single only)
>> struct ExtHeader(extra: nil) extends PacketHeader
>> 
>> -- Struct constructor: makeStructName(slot: value, ...)
>> -- let pkt = makePacketHeader(version: 1, type: :udp)
>> 
>> -- Struct predicate: structName?(x)
>> -- packetHeader?(pkt)  → true
>> 
>> -- Slot access works the same as class: pkt.version, pkt.type
>> 
>> -- Methods (typed params → defmethod)
>> fn describe(p: Point) = format(nil, "(%d, %d)", p.x, p.y)
>> 
>> -- Generic declaration
>> generic area(shape)
>> 
>> -- Method qualifiers via annotations
>> @before
>> fn validate(p: Point)
>>    print("about to use point")
>> end
>> 
>> @after
>> fn logUsage(p: Point)
>>    print("point was used")
>> end
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- MACROS
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- Macro definition
>> macro unless(test, &body body)
>>    `(if (not ,test) (progn ,@body))
>> end
>> 
>> -- Backtick templates work for quasiquoting
>> macro myIf(test, consequent, alternate)
>>    `(cond (,test ,consequent) (t ,alternate))
>> end
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- PACKAGES
>> -- ═══════════════════════════════════════════════════════════
>> 
>> package MyLib
>>    use: [:cl]
>>    export: [:myFunc, :myVar]
>> end
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- ANNOTATIONS
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- @inline — declare function inline
>> @inline
>> fn fastAdd(a, b) = a + b
>> 
>> -- @const — compile-time constant
>> @const let maxSize = 1024
>> 
>> -- @type, @optimize, @ignore — CL declarations
>> -- Used inside function bodies for type hints
>> -- @type fixnum x
>> -- @optimize (speed 3) (safety 0)
>> -- Or use s-expression escape for complex declarations:
>> @(declaim (optimize (speed 3) (safety 0)))
>> 
>> -- @when/@unless — conditional compilation (feature flags)
>> @when(:sbcl)
>> fn sbclOnly() = print("SBCL specific")
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- TYPE DEFINITIONS
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- Type aliases (→ deftype)
>> type Byte = @(unsigned-byte 8)
>> type Index = @(integer 0 #.most-positive-fixnum)
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- PRINTING AND FORMAT
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- print: adds newline
>> print("hello")                  -- simple
>> print("x = %d", x)             -- printf-style: %s %d %f %x %o %b %e %g
>> print(x, y, z)                 -- multiple values (space-separated)
>> 
>> -- printn: no newline
>> printn("waiting...")
>> 
>> -- format: CL format with printf syntax
>> let s = format(nil, "%.2f", 3.14159)  -- returns string
>> format(t, "output: %s\n", result)     -- prints to stdout
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- INTEROP WITH COMMON LISP
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- S-expression escape: @(raw CL code)
>> @(defvar *global* 42)
>> @(declaim (optimize (speed 3)))
>> 
>> -- Backtick quasiquote
>> let form = `(+ 1 2 3)
>> 
>> -- All CL functions available directly
>> let result = mapcar(&car, '((1, 2), (3, 4)))
>> 
>> -- Name translation: camelCase → kebab-case
>> -- hashTable → hash-table
>> -- processOrders → process-orders
>> -- empty? → emptyp (? → p)
>> -- reverse! → nreverse (! → f)
>> -- HTMLParser → html-parser
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- FFI (FOREIGN FUNCTION INTERFACE)
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- C library bindings (requires CFFI)
>> -- clib "libm"
>> --     fn sqrt(x: double) -> double
>> --     fn pow(base: double, exp: double) -> double
>> -- end
>> 
>> -- C struct definitions
>> -- cstruct Timeval
>> --     sec: long
>> --     usec: long
>> -- end
>> 
>> -- FFI types: int, uint, int8-64, uint8-64, float, double,
>> --            ptr, cstr, void, bool, size, ssize
>> 
>> -- FFI operations:
>> -- alloc(type), cfree(ptr), nullPtr(), nullPtr?(p)
>> -- sizeOf(type), memRef(ptr, type), memSet(ptr, type, val)
>> -- cStr(s), fromCStr(ptr), slot(ptr, type, name)
>> -- ptrInc(ptr, offset), ptrAddress(ptr), makePtr(addr)
>> 
>> 
>> -- ═══════════════════════════════════════════════════════════
>> -- SCRIPTING / EMBEDDING API
>> -- ═══════════════════════════════════════════════════════════
>> 
>> -- Create an isolated scripting environment
>> -- let env = larkEnv("my-scripts")
>> 
>> -- Execute Lark code in the environment
>> -- larkRun(env, "let x = 42")
>> -- larkRunFile(env, "scripts/handlers.lk")
>> 
>> -- Exchange data between host and scripts
>> -- let val = larkGet(env, "x")
>> -- larkSet(env, "config", myConfig)
>> 
>> -- Expose host functions to scripts
>> -- larkExpose(env, "notify", |msg| sendSlack(msg))
>> 
>> -- Call script functions from host
>> -- larkCall(env, "onEvent", eventData)
>> 
>> -- Hot reload
>> -- larkReload(env)
>> 
>> -- Quick one-shot (no environment)
>> -- larkEval("2 + 2")
>> -- larkEvalFile("script.lk")
>> 
>> -- Cleanup
>> -- larkDestroy(env)
>> 
>> 
>> 
>> _______________________________________________
>> Lisp Hug - the mailing list for LispWorks users
>> [email protected]
>> http://www.lispworks.com/support/lisp-hug.html
> 


_______________________________________________
Lisp Hug - the mailing list for LispWorks users
[email protected]
http://www.lispworks.com/support/lisp-hug.html
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.