Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

With Statement #620

Closed
danielribeiro opened this issue Aug 16, 2010 · 17 comments
Closed

With Statement #620

danielribeiro opened this issue Aug 16, 2010 · 17 comments
Labels

Comments

@danielribeiro
Copy link

It would be nice to have with statement on coffescript. Actually, instance eval would be nicer, but with stament is close enough for most dsls.

@hen-x
Copy link

hen-x commented Aug 16, 2010

The with statement is excluded from Coffeescript for reasons best expressed by Douglas Crockford, and reflected by most of the JS geekdom. It buys barely more clarity than you can already achieve with a single-letter temporary variable, yet the costs entail semantic ambiguity, potential global leaks (which are thankfully impossible in current Coffeescript), and the defeat of compiler optimizations.

If you really don't want to use a temporary variable, you can always cook up something like this instead:

_with = (object, block) -> block.call object

_with document, ->
    img = @createElement 'img'
    img.src = url
    @body.appendChild img

@danielribeiro
Copy link
Author

We have several dsls in javascript, and some of them are incredibly more readable in with statement. For instance:

    with(dsl()) {
        div('class', 'menu_list',
            ul(li(alink('/user_config', 'User Configuration')),
                li(alink('/create_user', 'create user')),
                li(alink('/logout')),
                li(alink('/login'))))

        }

Instead of your old fashioned overly verbose, full of noise:

This example is from a port of Ruby's Erector to JS, which we are building, and plan to make open source shortly. We were even considering to switch to coffescript, but the lack of instance_eval and with statement in the language make it a deal breaker.

@satyr
Copy link
Collaborator

satyr commented Aug 16, 2010

`with(dsl()){`
...
`}`

@danielribeiro
Copy link
Author

It is an ok workaround. We'll keep coffesscript on our radar. We'll see if we can change the current nested function style to a Method chaining one.

It is important to note that nested function from inherited methods in javascript is quite horrible, as it is in python, because of the obligatory this/self keyword (respectively), which the with keyword removes in js. This is a point where javascript would be actually worse than java in dsls expressiveness, if weren't for 'with(this)'. And I am not even comparing with real pro-dsl languages, such as Scala and Ruby.

@weepy
Copy link

weepy commented Aug 16, 2010

you might want to check out Kaffeine - it's got a similar goal to CoffeeScript, but it's closer to Javascript and doesn't disallow any syntax. Additionally it's got a using plugin that will bring in an objects to the local scope - something that might be useful for your DSLs'. http://github.com/weepy/kaffeine

@danielribeiro
Copy link
Author

Very nice to hear. Definitely considering it.

@carlsmith
Copy link
Contributor

carlsmith commented Oct 25, 2018

Sorry to reopen such an old thread, but I wanted to propose something slightly different.

It would make sense in CoffeeScript specifically to support a with <expression> then <block> grammar that actually just wraps the block in a function and binds the expression to this:

with Math
  result = @floor @max x, y

Binding the value of the expression to this avoids the ambiguity contra that got the JavaScript version of with banned in Strict Mode.

This fix wouldn't make much sense for JavaScript (Math.floor becomes this.floor), but in CoffeeScript, it would work really well:

with canvas.getContext "2d"
  @fillStyle = "rgb(200, 0, 0)"
  @fillRect 10, 10, 50, 50
  @fillStyle = "rgba(200, 0, 200, 0.5)"
  @fillRect 30, 30, 50, 50

@GeoffreyBooth
Copy link
Collaborator

@carlsmith Can you add what the intended JavaScript output would be?

@carlsmith
Copy link
Contributor

Hey, @GeoffreyBooth. Sure.

Assuming foo is assigned to {bar: true}, then this CoffeeScript...

with foo then console.log @bar

...would compile to something like...

!function() {
  console.log(this.bar);
}.bind(foo)();

... and would log true to the console.

@vendethiel
Copy link
Collaborator

Isn't do (o = foo) -> console.log o.bar good enough?

@mrmowgli
Copy link
Contributor

mrmowgli commented Oct 25, 2018 via email

@jashkenas
Copy link
Owner

We shouldn't re-use with for this (fairly odd) pattern. Writing it this way is just fine:

(->
  @fillStyle = "rgb(200, 0, 0)"
  @fillRect 10, 10, 50, 50
  @fillStyle = "rgba(200, 0, 200, 0.5)"
  @fillRect 30, 30, 50, 50
).call canvas.getContext "2d"

@carlsmith
Copy link
Contributor

carlsmith commented Oct 25, 2018

Isn't do (o = foo) -> console.log o.bar good enough?

Not really. That code just assigns the longer name to a shorter name that is automatically cleaned up at the end of the block, which wouldn't be applicable to the original example, as you would just use a shorter name to begin with. It's examples like this that I think with would help with:

const context = canvas.getContext("2d");
context.fillStyle = "rgb(200, 0, 0)";
context.fillRect(10, 10, 50, 50);
context.fillStyle = "rgba(200, 0, 200, 0.5)";
context.fillRect(30, 30, 50, 50);

Most developers would use an abbreviation, so would probably do this:

const ctx = canvas.getContext("2d");
ctx.fillStyle = "rgb(200, 0, 0)";
ctx.fillRect(10, 10, 50, 50);
...

In these cases, there's no way to apply your suggestion, except to just use an even shorter abbreviation:

const c = canvas.getContext("2d");
c.fillStyle = "rgb(200, 0, 0)";
c.fillRect(10, 10, 50, 50);
...

It would be nice to get rid of the temporary variable altogether:

with canvas.getContext "2d"
  @fillStyle = "rgb(200, 0, 0)"
  @fillRect 10, 10, 50, 50
...

@carlsmith
Copy link
Contributor

carlsmith commented Oct 25, 2018

@jashkenas' example would work the same way, so yeah, I'd like to use with to sugar this (though it seems he doesn't):

(->
  @fillStyle = "rgb(200, 0, 0)"
  @fillRect 10, 10, 50, 50
  @fillStyle = "rgba(200, 0, 200, 0.5)"
  @fillRect 30, 30, 50, 50
).call canvas.getContext "2d"

I don't think the pattern is "odd". It's really just doing what with did in JavaScript, but using a feature unique to CoffeeScript to fix the problem with the original approach.

@GeoffreyBooth
Copy link
Collaborator

I’m concerned by the overloading of @; you’re creating a new function scope without necessarily being explicit about it, i.e. users would need to know that with is compiling into a function, despite there being no -> or =>. And what if you wanted to access the enclosing scope’s this? We’d need a fat arrow variant of with.

Most libraries solve this problem through chaining:

xAxis = d3.scaleBand()
  .rangeRound [0, width]
  .padding 0.15, 0.15
  .domain data.periodNames

I know the vanilla DOM methods don’t chain, and that’s what you appear to be working with; and maybe you don’t want to use jQuery, but there’s probably a simpler library out there that wraps those methods into chainable variants.

@carlsmith
Copy link
Contributor

Yeah, on balance, it seems best to just use one of the workarounds. The (-> block).call context approach works pretty well as is, and you can wrap it in a nice API based on method chaining easily enough.

Thanks for considering this.

@Inve1951
Copy link
Contributor

Inve1951 commented Nov 5, 2018

If anyone really ever needs it:

_with = (context, args..., block) -> Reflect.apply block, context, args

_with {a: 1, b: 2}, ->
  @a * @b

_with canvas.getContext("2d"), this, (self) ->
  for {rect, color, bActive} in self.regions when bActive
    @fillStyle = color
    @fillRect rect...
  this

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants