ckknight / gorillascript

GorillaScript is a compile-to-JavaScript language designed to empower the user while attempting to prevent some common errors.
MIT License
300 stars 34 forks source link

Idea: built-in support for a "box" type #132

Open ckknight opened 11 years ago

ckknight commented 11 years ago

The idea is to provide an API for using and manipulating a standardized "box" interface that holds a value without being that value outright.

Although there would be a syntax for generating one of these boxes on-the-fly, the basic interface would be expected to be as such:

interface Box<T> = {
  // value can be a normal property or a getter, but is never expected to be set manually
  value as T
  // whenever the box's value is manually being changed, the set method will be called.
  // it is expected to return the new value of the Box
  // if not provided, then the Box is essentially seen as read-only.
  [optional] set(new-value as T) as T ->
}

Essentially, any JavaScript object can function as a read-only Box, as long as .value can be accessed.

The way it could be used in GorillaScript is with the following syntax:

let x = box! 0 // possible syntax of how to create a read-write box with a starting value
f(&x) // same as f(x.value)
&x := 1 // same as `x.set(1)`
&x += 1 // same as `x.set(x.value + 1)`
let o = { y: box! 10 }
&o.y ^= 2 // same as `let tmp = o.y; tmp.set(Math.pow(tmp.value, 2))`

This functionality could also be expanded upon by extra methods on one's Box, such as something like .on-change(func), then the following capability could open up:

let x = box! 1
let y = box! 2
let sum = formula! &x + &y
assert &sum == 3
&x += 1
assert &sum == 4
sum.on-change #(value)
  update-some-dom-element value

This essentially allows reactive code at the language level.

This would be doable by making formula! walk the AST tree looking for uses of the & prefix and declaring a dependency on that box using the on-change API (or something similar).

Some things I'm not sure about:

This would also pretty much invalidate the need for #60

ckknight commented 11 years ago

Now that I'm thinking about it, it would kind of make some sense to have the value be a method instead, like get().

Providing that functionality, one could also test for a Box-like object by doing is-function! o.get.

It would also allow for formulas to mark themselves as "dirty" and only update when needed.

ckknight commented 11 years ago

Also, in the following example:

let alpha = box! true
let bravo = box! "hello"
let charlie = box! "world"
let formula = formula!
  if &alpha
    &bravo
  else
    &charlie
assert &formula == "hello"
&charlie := "friend" // this should not recalculate `formula`
assert &formula == "hello"
&alpha := false // this would mark `formula` as dirty
assert &formula == "friend"

That could place a dependency on alpha, which would then evaluate to bravo or charlie, only updating itself if the one actually being needed were updated, ignoring any results of the other unless alpha changed.

It should not do any recalculations if a particular Box is not referenced but whose value changes.

ckknight commented 11 years ago

Also, in the event that a method rather than a normal property is used for the Box interface, another macro could be useful:

let box = lazy! factorial(6)
assert &box == 720 // only at this point is factorial(6) calculated
assert &box == 720 // uses the cached version, no recalculation.
joneshf commented 11 years ago

This seems a bit like lenses, unless I don't understand fully what you're suggesting. Which provide get and set functions over immutable structures. I don't have enough experience using them to pass worthwhile judgement on the idea, but I say go for it.

andreyvit commented 11 years ago

Hm, do I sense monads here? :-)

andreyvit commented 11 years ago

For the record, I would love a better abstraction for promises / async / lazy computation. It's just that it's a huge area and requires a lot of thinking; you can't expect an immediate useful feedback in here. If you let this idea cook for several months, maybe we can come up with something good.

andreyvit commented 11 years ago

On a completely different note, you should probably use TypeScript notation when describing interfaces; I think it's pretty awesome:

interface Box<T> {
  // value can be a normal property or a getter, but is never expected to be set manually
  value: T;
  // whenever the box's value is manually being changed, the set method will be called.
  // it is expected to return the new value of the Box
  // if not provided, then the Box is essentially seen as read-only.
  set?(newValue: T): T;
}

Btw I've seen you mention reactive stuff on IRC; I've spent a lot of time building a reactive library for LiveReload 3 (which I ended up ditching, because it was requiring exponential effort to make it handle everything I needed it to handle). Would love to discuss language support for reactive stuff.

goldfeld commented 11 years ago

I think this is really neat and would make a worthwhile addition, though I agree with @andreyvit that it might be useful to think this through the grander scheme of async and even promises. How compatible could nesting of such constructs as formula! and async and promises be made to be? On another note, have you looked into Elm, @ckknight? It has a lot of FRP built in and could provide ideas.