Giter Site home page Giter Site logo

Comments (8)

munificent avatar munificent commented on July 28, 2024

An anonymous function, however, has its arguments surrounded by pipes.

Right. This is because using parentheses before the curly body would be ambiguous given the other syntax for passing a function to a method that also takes parameters:

someMethod (foo) { ... }

Here, is foo the first argument to someMethod, or the parameter for the function being passed to it?

When passing an anonymous function as a parameter to another function, it doesn't need to be instantiated. But when storing a function in a variable, it does.

The same amount of allocation happens either way, actually. The new Fn {} just returns the function you pass to it, so don't read too much into that new there. In both cases, you need to create an object, but only one. :)

var someFn = {
  IO.print("Hi!")
}

I really like the syntax you suggest here. I actually did pretty much this for the language Wren is based on. However, there are two things that get in the way:

  1. Wren has a "block statement" similar to C, Java, etc. Like:
var a = "outer"
{
  var a = "inner"
  var b = "second"
  IO.print(a) // inner
}

IO.print(a) // outer
IO.print(b) // ERROR: b is not in scope
  1. Sometime soon, Wren will have maps (think hashes/hashtables/objects in JS) and those will also use curly bodies:
{
  "key": "value"
}

That ends up with a lot of corners of the grammar fighting for those curlies and it gets ambiguous very easily.

Calling a method with a function(s) as an argument, just like any other argument.

Yes, we could definitely do that if there was a dedicated function syntax. Wren actually used to have that (see: d146018). It looked like:

var someFn = fn() {
  IO.print("Hi!")
}

Where fn was a reserved word. In practice, though, most of the time you use functions, you are immediately passing them a method, like:

someList.map(fn() {
  ...
})

All of that ( () { }) punctuation really bothers me, which is why I added the syntactic sugar for creating a function directly by passing it to a method:

someList.map {
  ...
}

But, then, once I did that, I realized I didn't need the fn() { ... } syntax at all anymore. I could just make some built-in method that would return the block you pass to it in the rare cases where you don't need to immediately pass it to a method. The obvious name for this built-in method was... new Fn. :)

That let me tear out the old fn syntax (55c2c6f) and simplify the language, which is always a good thing.

I later realized Ruby does the exact same thing.

The only part that bugs me is having two syntaxes for parameters lists, (a, b, c) for methods and |a, b, c| for functions. I'd like to make those consistent, but haven't been able to come up with a good way to do it that plays nice with:

someList.method(args, for, method) {
  ...
}

from wren.

JAForbes avatar JAForbes commented on July 28, 2024

Thanks @munificent!

It is really interesting to hear the reasons things end up the way they do, and then to hear the ruby probably did the same thing for the same reason is just weird.

I can see how quickly the various uses of { ... } would introduce complexity. And again, I've never written a language so I don't know how hard it is to resolve that complexity.

But shouldn't the presence of the var <varname> = { ... } resolve the ambiguity?

var fn = {
  IO.print('I am a function')
}

var block = { 
  // unless you can have a named block the above shouldn't be ambiguous
}

And doesn't the : in the map resolve ambiguity too?

The only part that bugs me is having two syntaxes for parameters lists

Perhaps the argument list could always live in side the braces

var fn = new Fn { ( a,b )
  IO.print(a+b)
}

class Blondie {
  callMe { (fn)
    // Call it...
  }
}

That is a little different, but may resolve the ambiguity of

someList.method(args, for, method) {
  ...
}

someList.method(args, for, method) { (arg, def, for, anon, func )
  ...
}

That may just introduce more ambiguity in other places though ...

Maybe I'll try and implement my own language before asking anymore questions 😄

from wren.

munificent avatar munificent commented on July 28, 2024

But shouldn't the presence of the var = { ... } resolve the ambiguity?

Yup, but that means you could only define a function as the right-hand side of a variable declaration, which would be an annoying limitation. I think users expect to be able to write a function anywhere an expression is allowed.

And doesn't the : in the map resolve ambiguity too?

In general, yes. It gets a bit tricky with empty maps, blocks and functions. You basically have to just decide which one {} represents.

What gets nasty is if you allow arbitrary expressions as map keys, like:

{
  "some" + "long" + "map" + "key": "ok, finally a value"
}

If the parser doesn't know if it's parsing a map or a function until it sees that :, it may have to parse arbitrarily far ahead before it finds one. This isn't intractable, but it requires unbounded lookahead, which tends to make parsers nastier and can cause slowdowns if you aren't careful.

Perhaps the argument list could always live in side the braces

That's definitely an option. I don't know about you, but it looks a bit funny to me?

from wren.

JAForbes avatar JAForbes commented on July 28, 2024

In general, yes. It gets a bit tricky with empty maps, blocks and functions. You basically have to just decide which one {} represents.

I hadn't thought of that problem. And I am not sure how you are going to handle empty blocks/maps now that you mention it.

I don't know about you, but it looks a bit funny to me?

Honestly, it does seem a little off, but, for me, a large component of beauty is utility.

This isn't intractable, but it requires unbounded lookahead, which tends to make parsers nastier and can cause slowdowns if you aren't careful.

Ah I see! That is exactly the kind of answer I was looking for. Thanks for taking the time.

from wren.

munificent avatar munificent commented on July 28, 2024

And I am not sure how you are going to handle empty blocks/maps now that you mention it.

I believe it will work out. A {} by itself will be an empty map. A {} that appears where a function argument can appear will be parsed as an empty function. There's no use for empty blocks, so they just won't be possible.

from wren.

JAForbes avatar JAForbes commented on July 28, 2024

A {} that appears where a function argument can appear will be parsed as an empty function

Which explains why your passed functions are outside of the arg parens (another thing I wondered). Otherwise you wouldn't know if the argument was a function or a map literal!

The only part that bugs me is having two syntaxes for parameters lists, (a, b, c) for methods and |a, b, c| for functions.

Just reflecting on this again. Could argument definitions always be in pipes, and argument passing always be in parens?

That way you can keep your parens outside the body without having ambiguity.

class Blondie {
  someFunc |a,b,c, fn| {

  }
}

...

blondie.someFunc(a,b,c) | c,d | {
  IO.print(c,d)
}

from wren.

munificent avatar munificent commented on July 28, 2024

Could argument definitions always be in pipes, and argument passing always be in parens?

That's definitely possibly, but I think the unfamiliarity tax would be too high. :(

blondie.someFunc(a,b,c) | c,d | {
  IO.print(c,d)
}

Putting the | c, d | before the { is ambiguous with the infix bitwise | operator. Consider:

foo.bar | b | {}

Is that passing a one-argument function to bar, or calling the bitwise | on foo.bar, b, and an empty map? Syntax design is hard! :)

from wren.

JAForbes avatar JAForbes commented on July 28, 2024

Syntax design is hard! :)

Indeed :)

from wren.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.