yaml / yamlscript

Programming in YAML
MIT License
362 stars 31 forks source link

Implementing LISP without extra syntax #110

Closed colltoaction closed 2 months ago

colltoaction commented 4 months ago

I'm quite interested in this project and how it was designed. I see there is a mix of clojure-style blocks of strings with structure given by sequences and mappings.

This made me think, is it possible to let go of some of this syntax? If lists are native in YAML, it should be possible to build a LISP using just that. Like [say, Hello].

I have more ideas on how this extends to mappings, so I wanted to know your thoughts before this post becomes too long.

Thanks and great project!

ingydotnet commented 4 months ago

Greetings! I would have replied sooner but I only noticed this now. Most of the issues are just to-do task I've made myself.

There's certainly a lot I can say about YS design decisions. I'm not sure what you've read about YS, since I haven't written proper user docs yet. Maybe you read the Advent blog posts https://yamlscript.org/posts/advent-2023/index/

YS has 3 modes which is talked about here: https://yamlscript.org/posts/advent-2023/dec-06/ In a YS document each node must be in either "code mode" or "data mode". "data mode" is pretty much plain old YAML. "code mode" is used to express clojure expressions.

While it may seem appropriate to do a lisp in regular YAML as you say, it doesn't work out well in practice.

I initially shaped the syntax about a year ago by writing clojure code as yaml, and seeing what I liked and what I didn't.

At this point, when in code mode, block sequences and flow collections are not allowed at all. If you see:

- foo
- [ a, b ]
- { x: y }

in a valid YS program, you know you are in data mode because none of those forms are allowed in code mode. And that's the reason I forbid them. To make the language more easily understandable when reading YS code embedded into YAML files.

What is allowed in code mode is block mappings and scalars. Plain (unquoted) scalars are "code" and, quoted, literal, and folded scalars are strings. Double-quoted and literal scalars support interpolation.

Here's a contrived sample program that tries to show several things about YS syntax. It show the program compiled to clojure, and the program being run with different arguments.

$ cat greet.ys 
!yamlscript/v0

default =: 'world'

defn main(name='world' n=5):
  each [i (0 .. dec(n))]:
    i =: i + 1
    say: "$i) Hello, $(name.clojure::string/upper-case().subs(0 4))!!"
$ ys -c greet.ys 
(def default "world")
(defn
 main
 ([name n]
  (each
   [i (rng 0 (dec n))]
   (let
    [i (_+ i 1)]
    (say
     (str
      i
      ") Hello, "
      (__ name '(clojure.string/upper-case) '(subs 0 4))
      "!!")))))
 ([name] (main name 5))
 ([] (main "world" 5)))
(+++ (apply main ARGS))
$ ys greet.ys
1) Hello, WORL!!
2) Hello, WORL!!
3) Hello, WORL!!
4) Hello, WORL!!
5) Hello, WORL!!
$ ys greet.ys Martin 3
1) Hello, MART!!
2) Hello, MART!!
3) Hello, MART!!
$ 

Give it a close look. Then think about how you might do that better in a plain YAML Lisp. :)

I'll stop here for now. If you want to talk more about this, we can do it here, or you can find me at:

ingydotnet commented 2 months ago

Closing this for now. Feel free to continue chatting with me on matrix :)