spy16 / sabre

Sabre is highly customisable, embeddable LISP engine for Go. :computer:
GNU General Public License v3.0
28 stars 5 forks source link

Runtime & Better Contracts #26

Open spy16 opened 4 years ago

spy16 commented 4 years ago

This PR has the new structure and types as per the discussions on #25 . New contracts are nested within a sub-package sabre for the time being (Just to make it easy to move.)..

Highlights:

Opening this incomplete PR to get feedbacks early on. (Expect lot of random commits here. I will be rewriting commit history to make sense before merge)

spy16 commented 4 years ago

Hi, I have been super busy and haven't been able to do all the planned changes yet. But, I am slowly working on this. One decision I need to take now is about the Special Forms.

I am considering to bake the special-forms handling into the Runtime itself. It will remain the responsibility of Runtime to handle the special form during Eval. This has the following advantages:

  1. Special forms are special evaluation rules and it makes sense that they are defined with the Runtime.
  2. Adding custom special forms becomes possible but only when extreme customisation is needed (In cases where a new Runtime would be required anyway)

For the built-in basic runtime implementation provided in the runtime package, the factory function will be runtime.New(flags int32, parent Runtime) Runtime.. (flags can control what built in features should be enabled). And the built-in special forms will be:

  1. (fn name? [args] expr*) - and the multi-arity variant.
  2. (def symbol expr doc?)
  3. (cond test1 expr1 ...) --> No if-then-else form since cond can be used for it.
  4. (do expr*)
  5. (let [bindings] expr*)
  6. (error expr) --> need to think about this bit more and also the handling form.

@lthibault Let me know your thoughts. And also, if i have missed any essential special form.

lthibault commented 4 years ago

LGTM. I agree with both points regarding the integration of special forms into Runtime.

Regarding runtime.New, any particular reason you're leaning towards int32 flags? I'm guessing some of the bits will be reserved, and others will allow for customization. If that's the case, I'm a bit nervous about running out of space - 32 options isn't a lot.

Would it make sense to use functional options, as in the repl package? I quite like the idea of writing:

// signature: func New(parent runtime.Runtime, opt ...runtime.Option) runtime.Runtime
runtime.New(nil, runtime.WithDefault, somelib.WithFoo)

I think this is a nicer API for users as well.

spy16 commented 4 years ago

Yea it is. I didn't really go with flags. :) I mentioned it here but then decided probably not a good idea.

lthibault commented 4 years ago

@spy16 Just wondering: does anything else still need implementing, or can we begin the formal code-review/merge process?

lthibault commented 4 years ago

@spy16 can we get rid of the strings.TrimSpace in repl.WithBanner? It's interfering with my template, which specifies a newline at the end.

Right now my banner looks like this:

$ go run cmd/ww/main.go
Wetware v0.0.0, go1.14
Copyright 2020 The Wetware Project
ww » 

But I want it to look like this:

$ go run cmd/ww/main.go
Wetware v0.0.0, go1.14
Copyright 2020 The Wetware Project

ww » 
lthibault commented 4 years ago

@spy16 I have some spare cycles beginning next week -- is there anything I can do to help move this forward? This is becoming a bit of a blocking issue on my end, so I'm happy to pick up some of the slack. 🙂

spy16 commented 4 years ago

Hey @lthibault, Very sorry for the delay here. I ran into couple of issues while working on this refactor.

  1. Analyse turned out to be a really complicated thing to do when we use the interface for everything approach.. (For example, if a map is encountered, Analyse needs to check each key and value and expand it if necessary and re-construct a map with the new set of key-values. But since how to construct a map is allowed to vary, Analyse cannot construct a map easily)..
  2. Since Eval rules are left to the Value type, it is hard to control things from the Runtime. For example, if you want to be able to support stack-traces, Runtime needs to "know" what is being invoked and maintain a stack frame for it. This will require List to be able to know how stack is implemented in the Runtime causing a cyclic relation..

I was experimenting with an approach very similar to Clojure and the Joker (#18) and i think i was close to solving both the issues. But my father had a heart attack (He is fine now) and I had to take a break from work and this.

I plan on looking at this again this weekend but unsure about timelines. Here's what Clojure does:

Joker being Clojure in Go, replicates a similar design. I have some ideas on a similar approach.

Also, I saw your PR in which you said the design of atoms had to change for your use-case. I would definitely like to hear about the requirements.

Finally, If delay here is blocking you, it might make sense to fork this repo or even copy and merge it into your project I think. I know this is not ideal and it's not how i wanted it as well. But considering my situation and possibility that there might be further delay, I don't want this to become a bottleneck.

lthibault commented 4 years ago

@spy16 I'm terribly sorry to hear about your father! I'm glad he's alright now, although you and your family must still be reeling from the shock. By all means take your time ... this can wait.

In the meantime, thanks for your response -- I'm all the more grateful for your insights given the situation. I'm going to proceed as follows for the time being:

Let me know when you're back in action and we can compare notes. I'll try to move some things around on my end so that I can be available for implementation work when you return.

P.S.: I'm more than happy to fill you in on the requirements for Wetware. Let's have that discussion over at https://github.com/spy16/sabre/pull/27, at your convenience.