Conversation
Edited 2 days ago

I’ve been thinking about how my ideal #Lisp would work, and I already know that I want it to have very similar semantics to #Lua. but I figured out some fun details. for example, ideally the only non-primitive data type would be Lua-style tables. which means that S-expressions would be converted by the reader into tables (not linked lists!)

so for example this S-expression:

(+ 1 2 (* 3 4))

would become the equivalent of this Lua code: (with 0-based indexes instead of Lua’s 1-based indexes because those are an affront to the machine spirits)

{
  [0] = symbol("+"),
  [1] = 1,
  [2] = 2,
  [3] = {
    [0] = symbol("*"),
    [1] = 3,
    [2] = 4,
  },
}

but of course a Lua table lets you have more keys than just numbers (it acts as a list and as a dictionary!), so I would have a syntax for that in the Lisp too:

(some-func [:some-arg some-value]
           [:another-arg another-value])
{
  [0] = symbol("some-func"),
  [keyword("some-arg")] = symbol("some-value"),
  [keyword("another-arg")] = symbol("another-value"),
}

which means that all functions get keyword arguments by default, with no hacks needed! it also provides a nice Racket-like syntax for certain forms:

(let ([foo some-value]
      [bar another-value])
  ; code goes here
  )

and IMO the biggest improvement from regular Lisp is that this language would have a special syntax for getting/setting a value from a table:

foo.bar.baz
; this ↑ gets turned into this ↓ by the reader
(. foo 'bar 'baz)
; ↑ which is the same as this ↓
(. (. foo 'bar) 'baz)

; you can also use it with functions like `set!`, `set`, `update!`, `update`, etc.
(define foo '())
(set! foo.bar '())
(set! foo.bar.baz 666)
foo ; -> '([bar ([baz 666])])
foo.bar.baz ; -> 666

(update! foo.bar.baz add1)
foo.bar.baz ; -> 667

of course I would also want to have metatables:

(define some-table '())
(set! (meta some-table)
  '([foo bar]))
(meta some-table) ; -> '([foo bar])

and metamethods:

(define some-table '())
(set! (meta some-table)
  '([:+
     (λ (self other) "wait - this isn't doing addition!")]
    [:+r
     (λ (self other) "this isn't addition either!")]))

(+ some-table '()) ; -> "wait - this isn't doing addition!"
(+ '() some-table) ; -> "this isn't addition either!"

there would be a special case where the reader turns #this into a reference to the table that it’s currently inside of:

(define some-table '())
(set! (meta some-table)
  '([:index #this]
    [foo bar]))
(. some-table 'foo) ; -> 'bar

and it would also shamelessly steal “borrow” the absolutely incredible : syntax for calling methods in Lua:

(define some-table
  (set (meta '([some-value 0]))
       '([inc-some-value
          (λ (self) (update! self.some-value add1))])))

(define inc-some-value
        some-table:inc-some-value)
; this ↑ is equivalent to this ↓ (it automatically supplies `self`)
(define inc-some-value
        (λ () (some-table.inc-some-value some-table)))

some-table.some-value ; -> 0
(some-table:inc-some-value)
some-table.some-value ; -> 1
(inc-some-value)
some-table.some-value ; -> 2

oh and importing would just execute the contents of the file you’re importing and then evaluate to the result of the last form! just like it’s inside of a (do) form:

; ./some-lib.kaslisp
(define lib '())

(set! lib.foo 12)

; this file now evaluates to the table called `lib`, because that's the last form (just like how a function works!
lib

; ./why-is-this-lib-a-number.kaslisp
; this one just evaluates to 666? that's a weird thing to do...
666

; ./main.kaslisp
(define some-lib (require "./some-lib.kaslisp"))
some-lib ; -> '([foo 12])
some-lib.foo ; -> 12

(require "./why-is-this-lib-a-number.kaslisp") ; -> 666

it just puts all the code inside of a function that takes no arguments, and then runs the function! so importing would have very simple easy-to-understand semantics

2
0
3

@kasdeya You've probably already come across Fennel, but just in case: https://fennel-lang.org

1
0
1

@benjamingeer I have and I absolutely love it - it might be my favorite Lisp ever. this idea was heavily inspired by Fennel, and by me missing a lot of its syntax and semantics when trying other Lisps

1
0
1

@kasdeya Your idea looks great, too!

1
0
1

my priorities with a language like this would be:

  1. make the language as simple and {easy to reason about} as possible. give it as few discrete rules/concepts as possible. whenever possible, reuse existing concepts (like how (require) reuses the concept of (do), which would also be used in a lot of other places in the language)
  2. make the language as flexible as possible, so that it can be molded into anything that the user wants
  3. give it nice syntax so that it’s easy to read what’s going on (but only if that doesn’t interfere with my top two priorities)
0
0
0

@benjamingeer omg thank you! I’m so glad that you think so. and thanks for telling me about Fennel - if I didn’t already know about it it would’ve made my day lol

0
0
1