Crowdsourcing the syntax

May 30, 2011 § 18 Comments

Feedback from Opa testers suggests that we can improve the syntax and make it easier for developers new to Opa to read and write code. We have spent some time both inside the Opa team and with the testers designing two possible revisions to the syntax. Feedback on both possible revisions, as well as alternative ideas, are welcome.

A few days ago, we announced the Opa platform, and I’m happy to announce that things are going very well. We have received numerous applications for the closed preview – we now have onboard people from Mozilla, Google and Twitter, to quote but a few, from many startups, and even from famous defense contractors – and I’d like to start this post by thanking all the applicants. It’s really great to have you guys & gals and your feedback. We are still accepting applications, by the way.

Speaking of feedback, we got plenty of it, too, on just about everything Opa, much of it on the syntax. This focus on syntax is only fair, as syntax is both the first thing a new developer sees of a language and something that they have to live with daily. And feedback on the syntax indicates rather clearly that our syntax, while being extremely concise, was perceived as too exotic by many developers.

Well, we aim to please, so we have spent some time with our testers working on possible syntax revisions, and we have converged on two possible syntaxes. In this post, I will walk you through syntax changes. Please keep in mind that we are very much interested in feedback, so do not hesitate to contact us, either by leaving comments on this blog, by IRC, or at feedback@opalang.org .

Important note: that we will continue supporting the previous syntax for some time and we will provide tools to automatically convert from the previous syntax to the revised syntax.

Let me walk you through syntax changes.

Edit

  • Fixed typoes.
  • Removed most comments from revised versions, they were redundant.

Hello, web

Original syntax

start() = <>Hello, web!</>
server = one_page_server("Hello", start)

or, equivalently,

server = one_page_server("Hello", -> <>Hello, web!</>)

This application involves the following operations:

  • define some HTML content – note that this is actually a data structure, not inline HTML;
  • put this content in a either a function called start (first version) or an anonymous function (second version);
  • call function one_page_server to build a server;
  • use this server as our main server.

Revised syntax, candidate 1

/**
 * The function defining our user interface.
 */
start() {
  <>Hello, web!</> //HTML-like content.
  //As the last value of the function, this is the result.
}

/**
 * Create and start a server delivering user interface [start]
 */
start_server(one_page_server("Hello", start))

Fork me on github

or, equivalently

/**
 * The function defining our user interface.
 */
start = -> <>Hello, web!</> //HTML-like content.
  //Using the syntax for anonymous functions

/**
 * Create and start a server delivering user interface [start]
 */
start_server(one_page_server("Hello", start))

Fork me on github

or, equivalently

start_server(one_page_server("Hello", -> <>Hello, web!</> ));

Fork me on github

Rationale of the redesign

  • JS-style {} around function bodies indicate clearly where a function starts and where a function ends, which makes it easier for people who do not know the language to make sense of source code;
  • an explicit call to function start_server makes it more discoverable and intelligible that you can define several servers in one application.

Not redesigned

  • the HTML-like for user interface – feedback indicates that developers understand it immediately;
  • anonymous functions can still be written with -> – this syntax is both lightweight and readable;
  • the fact that the last value of a function is its result – now that we have the curly braces, it’s clear, and it fits much better with our programming paradigm than a return that would immediately stop the flow of the function and would not interact too nicely with our concurrency model;
  • the syntax of comments – it works as it is.

Revised syntax, candidate 2

/**
 * The function defining our user interface.
 */
def start():
  <>Hello, web!</> //HTML-like content.
  //As the last value of the function, this is the result.

/**
 * Create and start a server delivering user interface [start]
 */
server:
   one_page_server("Hello", start)

Fork me on github

or, equivalently

server:
   one_page_server("Hello", def(): <>Hello, web!</>)

Fork me on github

Redesign and rationale

  • Python-style meaningful indents force readable pagination;
  • in the second version, Python-inspired anonymous “def” makes it easier to spot anonymous functions and their arguments – note that this is not quite Python “lambda“, as there is no semantic difference between what an anonymous function can do and what a named function can do ;
  • Keyword server: is clearer than declaration server = .

Not redesigned

as above

Distributed key-value store

Original syntax

/**
 * Add a path called [/storage] to the schema of our
 * graph database.
 *
 * This path is used to store one value with type
 * [stringmap(option(string))]. A [stringmap] is a dictionary.
 * An [option(string)] is an optional [string],
 * i.e. a value that may either be a string or omitted.
 *
 * This path therefore stores an association from [string]
 * (the key) to either a [string] (the value) or nothing
 * (no value).
 */
db /storage: stringmap(option(string))

This extract adds a path to the database schema and provides the type of the value stored at this path. Note that Opa offers a graph database. Each path contains exactly one value. To store several values at one path, we actually store a container, which integrates nicely into the graph. Here, Opa will detect that what we are storing is essentially a table, and will automatically optimize storage to take advantage of this information.

/**
 * Handle requests.
 *
 * @param request The uri of the request. The URI is converted
 * to a key in [/storage], the method determines what should be
 * done, and in the case of [{post}] requests, the body is used
 * to set the value in the db
 *
 * @return If the request is rejected, [{method_not_allowed}].
 * If the request is a successful [{get}], a "text/plain"
 * resource with the value previously stored. If the request
 * is a [{get}] to an unknown key, a [{wrong_address}].
 * Otherwise, a [{success}].
 */
dispatch(request) =
(
  key = List.to_string(request.uri.path)
  match request.method with
   | {some = {get}}    ->
     match /storage[key] with
       | {none}        -> Resource.raw_status({wrong_address})
       | {some = value}-> Resource.raw_response(value,
               "text/plain", {success})
     end
   | {some = {post}}   ->
         do /storage[key] <- request.body
         Resource.raw_status({success})
   | {some = {delete}} ->
         do Db.remove(@/storage[key])
         Resource.raw_status({success})
   | _ -> Resource.raw_status({method_not_allowed})
  end
)

This extract  inspects the HTTP method of the request to decide what to do with the request – this is called “pattern-matching”. First case handles GET and performs further matching on the database to determine whether the key is already associated to a value.

/**
 * Main entry point: launching the server.
 */
server = Server.simple_request_dispatch(dispatch)

Finally, this extract launches the server.

Revised syntax, candidate 1

Fork me on github

db {
  option<string> /storage[string];
}

or, equivalently,

db option<string> /storage[string];

Redesigns and rationale

  • The type of the value appears before the value – this is more understandable by developers used to C-style syntax.
  • Syntactic sugar makes it clear that the path is indexed by strings – this syntax matches the syntax used to place requests or to update the value.
  • Allowing braces around schema declaration is a good visual clue.
  • We now use <> for generics syntax – again, this matches the syntax of C++, Java, C# and statically typed JS extensions.

Not redesigned

  • Keyword db – we need a keyword to make it clear that we are talking about the database.
dispatch(request) {
  key = List.to_string(request.uri.path);
  match(request.method) {
    case {some: {get}}:
        match(/storage[key]) {
           case {none}:  Resource.raw_status({wrong_address});
           case {some: value}: Resource.raw_response(value,
              "text/plain", {success});
        }
    case {some: {post}}: {
         /storage[key] <- request.body;
         Resource.raw_status({success})
    }
    case {some: {delete}}: {
         Db.remove(@/storage[key]);
         Resource.raw_status({success});
    }
    case *: Resource.raw_status({method_not_allowed});
  }
}

Redesigns and rationale

  • Pattern-matching syntax  becomes  match(…) { case case_1: …; case case_2: …; … } – this syntax resembles that of switch(), and is therefore more understandable by developers who are not accustomed to pattern-matching. Note that pattern-matching is both more powerful than switch and has a different branching mechanism, so reusing keywords switch and default would have mislead developers.
  • Records now use : instead of =, as in JavaScript – now that we use curly braces, this is necessary to ensure that there is a visual difference between blocks and structures.

Not redesigned

  • Operator <- for updating a database path – we want developers to be aware that this operation is very different from =, which serves to define new values.
  • The syntax for paths – it’s simple, concise and it’s an immediate cue that we are dealing with a persistent value.
start_server(Server.simple_request_dispatch(dispatch))

No additional redesigns or rationales.

Revised syntax, candidate 2

Fork me on github

db:
  /storage[string] as option(string)

Redesigns and rationale

  • Again, Python-style meaningful indents force readable pagination;
  • syntactic sugar makes it clear that the path is indexed by strings – this syntax matches the syntax used to place requests or to update the value;
  • keyword as (inspired by Boo) replaces : (which is used pervasively in Python syntax);
  • python-style keyword db: is more visible than db.

Not redesigned

  • We still use parentheses for generic types – no need to clutter the syntax with Java-like Foo<Bar>
def dispatch(request):
  key = List.to_string(request.uri.path)
  match request.method:
    case {some = {get}}:
       match /storage[key]:
          case {none}:        Resource.raw_status({wrong_address})
          case {some: value}: Resource.raw_response(value,
                "text/plain", {success});
    case {some = {post}}:
         /storage[key] <- request.body
         Resource.raw_status({success})
    case {some = {delete}}:
         Db.remove(@/storage[key])
         Resource.raw_status({success})
    case *:
         Resource.raw_status({method_not_allowed})

Redesigns and rationale

  • Pattern-matching syntax  becomes  match: and case …: … .

Not redesigned

As above

server:
   Server.simple_request_dispatch(dispatch)

No further redesign.

Web chat

Original syntax

/**
 * {1 Network infrastructure}
 */

/**
 * The type of messages sent by a client to the chatroom
 */
type message = {author: string /**Arbitrary, untrusted, name*/
              ; text: string  /**Content entered by the user*/}

/**
 * A structure for routing and broadcasting values of type
 * [message].
 *
 * Clients can send values to be broadcasted or register
 * callbacks to be informed of the broadcast. Note that
 * this routing can work cross-client and cross-server.
 *
 * For distribution purposes, this network will be
 * registered to the network as "mushroom".
 */
room = Network.cloud("mushroom"): Network.network(message)

In this extract, we define a type and the distribution infrastructure to broadcast value changes between servers or between clients and servers. Needless to say, these two lines hide some very powerful core concepts of Opa.

/**
 * Update the user interface in reaction to reception
 * of a message.
 *
 * This function is meant to be registered with [room]
 * as a callback. Its sole role is to display the new message
 * in [#conversation].
 *
 * @param x The message received from the chatroom
 */
user_update(x) =
(
  line = <div>
     <div>{x.author}:</div>
     <div>{x.text}</div>
  </div>
  do Dom.transform([#conversation +<- line ])
  Dom.scroll_to_bottom(#conversation)
)

/**
 * Broadcast text to the [room].
 *
 * Read the contents of [#entry], clear these contents and send
 * the message to [room].
 *
 * @param author The name of the author. Will be included in the
 * message broadcasted.
 */
broadcast(author) =
  do Network.broadcast({author=author
     text=Dom.get_value(#entry)}, room)
  Dom.clear_value(#entry)

/**
 * Build the user interface for a client.
 *
 * Pick a random author name which will be used throughout
 * the chat.
 *
 * @return The user interface, ready to be sent by the server to
 * the client on connection.
 */
start() =
(
    author = Random.string(8)
    <div id=#conversation
     onready={_ -> Network.add_callback(user_update, room)}></div>
    <input id=#entry  onnewline={_ -> broadcast(author)}/>
    <div onclick={_ -> broadcast(author)}>Send!</div>
)

In this extract, we define the user interface and connect it to the aforementioned distribution mechanism. Again, we describe the user interface as a datastructure in a HTML-like syntax.

/**
 * Main entry point.
 *
 * Construct an application called "Chat" (users
 * will see the name in the title bar), embedding
 * statically the contents of directory "resources",
 * using the global stylesheet "resources/css.css"
 * and the user interface defined in [start].
 */
server = Server.one_page_bundle("Chat",
    [@static_resource_directory("resources")],
    ["resources/css.css"], start)

Finally, as usual, we define our main entry point, with our user interface and a bundle of resources.

Revised syntax, candidate 1

Fork me on github

type message = {author: string /**Arbitrary, untrusted, name*/
               ,text:   string} /**Content entered by the user*/

Network.network<message> room = Network.cloud("mushroom")

user_update(x) {
  line = <div>
     <div>{x.author}:</div>
     <div>{x.text}</div>
  </div>;
  Dom.transform([#conversation +<- line ]);//Note: If we want to change the syntax of actions, now is the right time
  Dom.scroll_to_bottom(#conversation)
}

broadcast(author){
  Network.broadcast({author=author,
    text:Dom.get_value(#entry)}, room);
  Dom.clear_value(#entry)
}

start() {
  author = Random.string(8);
  <div id=#conversation
    onready={ * -> Network.add_callback(user_update, room) }></div>
  <input id=#entry  onnewline={ * -> broadcast(author) }/>
  <div onclick={ * -> broadcast(author) }>Send!</div>
}

start_server(Server.one_page_bundle("Chat",
   [@static_resource_directory("resources")],
   ["resources/css.css"], start))

Revised syntax, candidate 2

Fork me on github

type message:
   author as string //Arbitrary, untrusted, name
   text   as string   //Content entered by the user

room = Network.cloud("mushroom") as Network.network(message)

Redesign and rationale

  • We introduce a Python-ish/Boo-ish syntax for defining types.
  • Again, we use as instead of : for type annotations.
def user_update(x):
  line = <div>
    <div>{x.author}:</div>
    <div>{x.text}</div>
  </div>
  Dom.transform([#conversation +<- line ])//Note: If we want to change the syntax of actions, now is the right time
  Dom.scroll_to_bottom(#conversation)

def broadcast(author):
   message = new:
      author: author
      text:   Dom.get_value(#entry)
   Network.broadcast(message, room)
   Dom.clear_value(#entry)

Redesign and rationale

  • We introduce a new keyword new: to define immediate records – we find this both clearer than the Python syntax for defining objects as dictionaries, and more suited to both our paradigm and our automated analysis.
def start():
   author = Random.string(8)
   html:
    <div id=#conversation onready={def *: Network.add_callback(user_update, room)}></div>
    <input id=$entry onnewline={def *: broadcast(author)}/>
    <div onclick={def *: broadcast(author)}>Send!</div>

Redesign and rationale

  • We introduce keyword html: to introduce a block of HTML-like notations. A similar keyword xml: will be used when producing XML documents with a XML-like notation.
server:
  Server.one_page_bundle("Chat",
    [@static_resource_directory("resources")],
    ["resources/css.css"], start)

No additional change here.

What now?

At this stage, we have not switched syntax yet and we have the following options:

  • keep our current syntax;
  • adopt revised syntax 1, or a variant thereof – so, start coding a conversion tool, the new syntax itself, and start updating the documentation;
  • adopt revised syntax 2, or a variant thereof – so, start coding a conversion tool, the new syntax itself, and start updating the documentation;

Yes, I mention variants, because I am certain that many among you will have interesting ideas. So please feel free to express yourselves.

You can provide feedback:

  • on this blog;
  • by e-mail, at feedback@opalang.org;
  • on IRC.

Remember, we are still accepting applications for the preview.

Tagged: , , , , , , , , , , , ,

§ 18 Responses to Crowdsourcing the syntax

  • Nagoya says:

    Candidate 2: you might as well make it a framework for Ruby/Python.

    Candidate 1 is much better without the braces. The anonymous function syntax

    
        start = ->
           hello
    

    is much nicer. It resembles CoffeeScript.

    • yoric says:

      Candidate 1 is much better without the braces. The anonymous function syntax

      start = ->
      hello

      is much nicer.

      Thanks for the feedback.

      Candidate 2: you might as well make it a framework for Ruby/Python.

      Well, take my word when I say that our work doesn’t stop at syntax. We didn’t make it a framework for Ruby/Python because that would simply not have been possible. Otoh, many people enjoy Python-like syntaxes, so adopting a Python-like syntax does make sense.

  • Enzo90910 says:

    I am not ready to vote for either syntax, but I’d say that choosing one or the other to define a function has probably a lot of more or less logical repercussions on other parts of the syntax. Therefore it would probably be better to compare two wholly different program demonstrating all the possible syntax choices at once rather than making several very specific choices; the end result of these several choices is at risk of not being very consistent.

    • yoric says:

      Indeed, the Python-ish syntax is definitely incompatible with the JS-ish one. However, we’ve been trying very hard to keep each consistent.

      Therefore it would probably be better to compare two wholly different program demonstrating all the possible syntax choices at once rather than making several very specific choices; the end result of these several choices is at risk of not being very consistent.

      Are you suggesting that choosing a syntax (without altering semantics) will deeply change the structure of the program? That’s possible, but I wouldn’t think so.

  • AltGr says:

    I really like variant 1… _except_ for the “case” keyword, which IMHO makes pattern-matching too syntax-heavy. A different syntax could be understood almost as quickly (esp. if you have a “match” above and proper indentation), and would emphasize the fact that it is a different, more generic pattern (and that it should be used more often than a switch)

    Also, in that syntax, we end up with curly braces delimiting:
    * records (in types and exprs)
    * code blocks (function body, match cases)
    * specific constructs (body of a match)
    * anonymous functions
    * escapes within strings/xhtml
    * top-level constructs (db…)

    that may be a bit much, can we live with it ?

    • yoric says:

      A different syntax could be understood almost as quickly (esp. if you have a “match” above and proper indentation), and would emphasize the fact that it is a different, more generic pattern (and that it should be used more often than a switch)

      What would you suggest?

      Also, in that syntax, we end up with curly braces delimiting:
      […]
      that may be a bit much, can we live with it ?

      That’s a lot, indeed. I believe that we can work with it, but do you have any better suggestion?

      • AltGr says:

        What would you suggest?
        just nothing would be alright ; we could use ‘=>’ instead of ‘:’ for more visibility, but we lose in similarity with ‘switch’ (Perl and Ada use ‘=>’, so it’s not completely new). Also, this removes a conflict with type annotations.

        match (request.method) {
        {some: {get}} =>
        match (/storage[key]) {
        {none} => Resource.raw_status({wrong_address});
        {some: value} => Resource.raw_response(value, "text/plain", {success});
        }
        {some: {post}} => {
        /storage[key] {
        Db.remove(@/storage[key]);
        Resource.raw_status({success});
        }
        * => Resource.raw_status({method_not_allowed});
        }

        here the {} around blocks with only one element are optional, is it specific to pattern-matching ?

        […speaking about curly braves everywhere…]
        any better suggestion?

        hmm, not sure, but I would prefer if we could distinguish at least records and code blocks more easily ; just an idea, maybe changing the syntax for field names instead of the braces would give something ? After all it’s a different namespace.

        message = {
        .author: author;
        .text: Dom.get_value(#entry);
        }

      • yoric says:

        message = {
        .author: author;
        .text: Dom.get_value(#entry);
        }

        Interesting idea, I like it. Anybody else?

  • gasche says:

    Hello yoric,

    I had a look at the OPA doc when it became available. I’ve had a look again when you announced the imminent open-sourcing (congrats !). I have read it with interested, but I thought I would wait until it is finally released to dive deeper. I have noticed some syntactic hurdles however, and this post prompted me to react.

    I also think the OPA syntax has some annoyances. Interestingly, none of the things you’re currently commenting on is one of them.
    I think the problem in the OPA syntax are the places where *ambiguities* and *fragilities* crept in. In your documentation there is a lot of warnings “beware of this error people often make because this looks like it means this, but it means that instead”. Having noticed the problems and reported them in the documentation is good. Having those problems is bad. I don’t remember most warnings, but on the top of my head there was the dangerous interaction between the optional end of match..end and an enclosing begin..end.

    Have a syntax that is nice is good. Having a syntax that is reliable is better. I think that before trying to please anyone with a syntax that looks more like “the syntax of … you’re used to”, you should try to fix those ambiguities and fragilities. This means making more delimiters mandatory (eg. making “end” mandatory in a matching, like in Coq, or using Python-like meaningful indentation to get an “invisible” mandatory closing delimiter), or changing some keywords to avoid conflicts. The goal being to suppress the cause of all those warnings you have in your documentation.

    Regarding your current unrelated post. I didn’t know you were still open to syntax changes. I assumed you would not. I’m not sure this is a good idea. If you’re confident the change will bring important benefits to the language, better to break things now that later. But “syntax crowdsourcing” is certainly not the way to go; by-commitee syntax design just doesn’t work, and is one of the less-productive activity. Poking the web for advice to hear about experience of other people with regard to major vicious syntax failures, however, can be helpful. Maybe that’s what you try to do here.

    Finally coming to the content of your proposal. In one sentence : “people are not used to our OCaml syntax, let’s switch to either C/Java/.. or Python syntax instead”. Being an enthusiastic OCaml fan, I don’t like the sound of it so much, but you may be right in that choosing a more widely known syntax may help adoption (and the OCaml syntax has its flaws, but it’s clearly not what this is about). You should keep in mind however that reflecting “political” choices in your syntax (adopting the syntax of this or this group to attract them) may have wider consequences to your user base, that may come to expect this of this because of your choice. Bad programmers often do not make a difference between syntax and semantics. Lowering the transition cost on syntax is good, unless it make people think that there also is no transition cost on the semantic, which is wrong. Of your two proposal, I think that syntactically the Python choice is the best one (see also this blog post by Chris Okasaki on how mandatory indentation helps beginners). But do you really want your audience to expect that your language is “like Python”?

    Having a reliable syntax is objectively good. Having a politically attractive syntax is a double-edged sword.

    Finally, some small details:
    1) mixing brackets for both indentation and records looks like a terrible idea; when you hear yourself saying that “we can make the difference because of the :”, you should know that something is wrong.
    2) using “case” for matching looks like a bad idea (it will make the keystroke cost of using pattern-matching for simple functions much higher); “|” is very good for that purpose and is quite standard among languages that actually have pattern matching (not Python). “|” can also express nested union patterns, which “case” cannot.
    3) I don’t like (hint, this is subjective) the idea of putting types before path in the database description. First, SQL use field then type. Second, your previous description read like a mapping “at path /…., you store type …”, which is much more natural that considering the path somehow like a variable. “as” is ok, but why do you need a keyword here anyway?

    • blopblop says:

      Well, even if the problem of the ambiguities (and the fragility) of the syntax is not explicitely mentioned, the fact is that the syntax proposed contain much more (mandatory) delimiters than the current one. Either of these syntaxes seem way less ambiguous than the current one.

      • yoric says:

        Indeed, that’s one of the main ideas behind both redesigns.

      • gasche says:

        In this case, have you considered the ML dialects with meaningful-indentation syntax, such as Ocaml+TWT, Haskell¹, or F#?

        ¹: ML is a bit more than a ML dialect, and the changes to the traditional ML syntax are more important than just the indentation (but I suppose some of the current OPA syntax, such as the keyword-less binding, is inspired from Haskell), but I’m speaking here specifically of the layout rules.

        For meaningful-indentation syntax there is a compromise to be made between naturalness and convenience of the result in all specific cases, and simplicity and generality of the syntax rules. Having written meaningful-indentation syntaxes for OCaml in the past, I know this is hard, but I think this is also easier for a new language or if you’re not afraid of breaking habits. The F# result is decent, but the rules (while relatively well documented) are lots of ad-hoc exceptions and not very comfortable to work with from a designer p.o.v. I think the Haskell designers have done a rather good job of choosing not-too-complex rules with nice to use results, but this is facilitated by the semantics of the language (lazy evaluation means you can be very un-careful in the choice of your “where”-blocks place for example).

        The Python syntax is overall very good. This is something that the Python design community has done very well. One important idea for meaningful-indentation syntax is also the avaibility of a non-indented fallback syntax. Haskell has “{ … }” for example which are available as an alternative. This is very good for meta-programming (tools that output Haskell syntax), and it also enforces a reasonably sane semantics for the indentation : having to explain the effect of the meaningful-indentation syntax as a translation from a meaningful-indentation syntax to a whitespace-independent by only adding brackets is a good conceptual framework.

    • yoric says:

      Thanks for this detailed feedback!

      Have a syntax that is nice is good. Having a syntax that is reliable is better. I think that before trying to please anyone with a syntax that looks more like “the syntax of … you’re used to”, you should try to fix those ambiguities and fragilities.

      The main objective of this redesign is to remove anything that is too subtle and that may be perceived as ambiguous. Chiefly, this is about making blocks clearly visible, for which we have essentially two choices: meaningful indents or block delimiters. The rest of the redesign is just pushing this remark to all its consequences.

      Lowering the transition cost on syntax is good, unless it make people think that there also is no transition cost on the semantic, which is wrong.

      Indeed. This is one of the reasons why we have a keyword match and not a keyword switch.

      1) mixing brackets for both indentation and records looks like a terrible idea; when you hear yourself saying that “we can make the difference because of the :”, you should know that something is wrong.

      Fair enough. What do you think of AltGr’s suggestion?

      2) using “case” for matching looks like a bad idea (it will make the keystroke cost of using pattern-matching for simple functions much higher); “|” is very good for that purpose and is quite standard among languages that actually have pattern matching (not Python). “|” can also express nested union patterns, which “case” cannot.

      I disagree with this one. Yes, case is slightly more costly for your keyboard, but it is also more visible in the source code. Conversely, using | for two very different things is generally not a good idea.

      3) I don’t like (hint, this is subjective) the idea of putting types before path in the database description. First, SQL use field then type. Second, your previous description read like a mapping “at path /…., you store type …”, which is much more natural that considering the path somehow like a variable. “as” is ok, but why do you need a keyword here anyway?

      The keyword is not strictly necessary, but is, once again, a visual clue. As for putting type before or after path, I have no preference.

  • Bron says:

    I am new to programming so also to my opinion going towards “a syntax that looks more like “the syntax of … you’re used to”” is not the way. It needs to be good and logic. So I understand that replacing of = with : etc. For me as a starter I would also appreciate simplicity which I think ruby syntax has. And I don’t mean to encourage anybody to try to mimic it. Rather to keep it minimal. This can be a quality which could help future generations of programmers to decide which technology to dig in at the advantage of yours.

    • yoric says:

      In fact, the current Opa syntax is quite minimal. Feedback from testers indicated that it was actually too minimal, i.e. that it relied on clues that were often too subtle for new developers. This is what prompted the general redesign. Now, making it look similar to JS or Python is all about simplifying the approach to the language: if developers have both to learn a new syntax and a new manner of developing, that’s making the approach much harder than just learning the new manner of developing.

  • Bron says:

    Well my voice is not of an expert so it is just about my general feelings. Consistency is also a great value. Don’t let it grow complex beyond reasonable amounts of minimalism. 😉

  • phil tomson says:

    I generally like candidate 1 better than the current syntax, but I would not like to see ‘case’ used in pattern matches – it’s too verbose. Go with with more of an ML’ish pattern match:
    match foo with
    | bar -> option1
    | baz -> option2
    | _ -> default

    If you’re using “|” for something else, then change it (if it’s being used for logical ‘or’ you could change to || or perhaps just ‘or’.)

    Also, I would echo the suggestion to take a look at CoffeeScript for inspiration – that would be more of an indentation-based syntax.

    Get rid of the ‘do’ syntax: either braces {} or indentation would be preferable… even something which uses semicolons would be preferable.

    • Just throwing in another vote for something in the same syntactic vein as coffeescript. It’s been a long time since I’ve come across a language with a syntax that can only be described as joyful to work with. Of course it would need a teaspoon or two of sugar to support pattern matching, object persistence, typing, and client/server slicing magic. I know this discussion is from a while ago, however I just discovered Opa yesterday and would like to throw my 2c in anyway.

      CS style:
      // assuming server is now a function (like start_server example)
      server one_page_server “Hello”, -> Hello, web!
      // same as: server(one_page_server(“Hello”, -> Hello, web! ))

      // database storage. if db acted like a regular function taking two params..
      db /storage, stringmap option string
      // same as: db(/storage, stringmap(option(string)))

      // pattern matching CSified.. is the | operator required?
      dispatch = (request) ->
      key = List.to_string request.uri.path
      match request.method with
      {some = {get}} ->
      match /storage[key] with
      {none} ->
      Resource.raw_status {wrong_address}
      {some = value} ->
      Resource.raw_response value, “text/plain”, {success}
      {some = {post}} ->
      do /storage[key]
      do Db.remove @/storage[key]
      Resource.raw_status {success}
      _ -> Resource.raw_status {method_not_allowed}

      // just to see what what chat example might look like… I can only speak for myself, but I’m really enjoying jumping onto the paren-free bandwagon.

      type message:
      author: string,
      text: string

      // above and below may have been oversimplified.
      room = Network.cloud “mushroom”, message

      user_update = (msg) ->
      // inline xhtml will be duper handy.
      line =
      {msg.author}:
      {msg.text}
      ; // Is the trailing ; needed to end xhtmls?
      // I don’t mind Dom.transform([#conversation +
      Network.broadcast {
      author: author,
      text: Dom.get_value #entry
      }, room
      Dom.clear_value #entry

      start = ->
      author = Random.string 8
      // what exactly does the * in ” * -> /*fndef*/ ” really do?
      Network.add_callback user_update, room }>
      broadcast author }/>
      broadcast author }>Send!

      server Server.one_page_bundle “Chat”,
      [@static_resource_directory “resources” ],
      [“resources/css.css”],
      start

      /////
      Anyways, in general I really enjoy what I’m seeing as I read through your developer manual. I have to agree that some aspects of the syntax need a bit of housecleaning, however in general it seems to be succinct and consice, which is nice. If there were no changes made from it’s current implementation, I would likely still be quite a happy coder.

Leave a comment

What’s this?

You are currently reading Crowdsourcing the syntax at Il y a du thé renversé au bord de la table.

meta