Settings

Theme

Copper: A statically-typed, loose syntax programming language

github.com

30 points by nercht12 5 years ago · 31 comments

Reader

throwaway_pdp09 5 years ago

(Edit: this comes across a bit aggressive, not meant so).

What benefits does copper bring over other langs?

Also loose is a bad move. Loose semantics proved a mistake (PL1), loose syntax brings nothing to the party. Semicolons were used in pascal because wirth understood parsing, and they are there to help the parser resync after a syntax error is detected.

From experience if you make semicolons optional as in sql, it helps not at all, AND they are (said by microsoft to) become mandatory in future releases of TSQL, AND I and at least some other users of SQL want them mandatory.

I also don't understand why a non-typical syntax is used. Not at all saying it's wrong but it seems to differ from other syntactic conventions only to be different rather than better (I may be wrong!). Why "gte(a: 100)"? Syntax matters to a degree, why diverge?

  • nercht12OP 5 years ago

    An article on the blog explains the parse tree.

    https://copperlang.wordpress.com/2016/11/18/printsyntax/

    Edit: The design of the parse tree is such that there is no need for things like statement termination, parameter separation, among other things. By its simplicity, the code focuses on what really matters. Complex syntax is mentally taxing and distracts the programmer from focusing his energy on problem solving.

    • throwaway_pdp09 5 years ago

      > Complex syntax is mentally taxing and distracts the programmer from focusing his energy on problem solving.

      With respect I don't see it that way (nor know of any studies to back that up qualitatively). While complex syntaxes do exist and they are horrible (I pray you never have to use XSLT) dropping semicolons and commas do not make a syntax noticeably simpler. But they may lay unexpected traps.

      See how your users find it though.

      • nercht12OP 5 years ago

        The dropping of semicolons and commas isn't what makes it simple, I'll agree. I was referring to the simplicity of the parse tree as a whole. It's easier to figure out what a Copper statement does because there are very few types of statements. Yes, there are a couple of traps [1]. Try reading this example [2] and see how you like it.

        [1] When passing either an object or data to a function, the parameter is stored as a function. e.g. if f=[p]{}, then f(a) and f(5) have p=a and p=5 respectively. If a=5, then the results are identical. The trap is that if a={ret({ret(5)})}, which is a wrapping function, and you call "a" before passing it to "f", then you get the nested function {ret(5)} instead of the wrapping function {ret({ret(5)})}. Having worked with Copper in practice, it's not too hard to spot the error, but it is one of those things that will catch beginners off guard.

        [2] https://github.com/chronologicaldot/CupricBridge/blob/master...

        • tom_mellior 5 years ago

          That "trap" looks weird. Does this mean that a's type changes somehow if you call it before passing it to f? That is indeed surprising for a statically typed language. Could you write out the types of the variants (wrapped vs. non-wrapped)?

          • nercht12OP 5 years ago

            a's type didn't change. The trap was that the wrapping function returned its nested function instead of itself. You can do the same thing in other languages, it's just easier to slip into code in Copper. Let me illustrate with pseudo C code:

            [code]

            class F {

            int mydata;

            F( int a ) : mydata(a) {}

            F* operator( int p ) { a=p; return new Function(0); }

            };

            myF = new F(10);

            doSomething( &myFunc );

            doSomething( myFunc() );

            [/code]

            This is a basic formula for how things appear "under the hood" in the VM. Notice that doSomething() accepts F*, but in the first case, the F instance passed has a different mydata value. In Copper, the above code corresponds to:

            [code]

            doSomething( myF )

            doSomething( myF: ) or doSomething( myF() )

            [/code]

  • skohan 5 years ago

    I agree about looseness in general, but I disagree about semicolons. After having gotten used to programming without them, they always feel like a tedious burden which adds no value when I'm working with a language where they're required again.

  • kroltan 5 years ago

    > Why "gte(a: 100)"? Syntax matters to a degree, why diverge?

    Not sure what about it are you criticizing?

    Local variable access using :?

    No commas between parameters?

    Operators using function call syntax?

    • throwaway_pdp09 5 years ago

      Not criticising, asking.

      > Local variable access using :? / No commas between parameters? / Operators using function call syntax?

      Well, yes, yes and yes.

      • kroltan 5 years ago

        Didn't mean to imply, my bad.

        Commas and function-call operators is actually pretty common in Lisp-likes, though they usually still use the symbol instead of some shorthand or initialism, and the opening parens comes before the function name:

            (= (> 4 1) true)
        
        I'm not a fan of Copper's local variable syntax though, as the uncle comment by tom_mellior articulated, looks too much like association.
    • tom_mellior 5 years ago

      Not the parent, but I personally don't like prefix syntax for such common operators. As for the colons combined with the absence of commas, this suggests keyword syntax to me. I.e., as if the gte function took one argument named a. Like this Python call:

          gte(a=100)
      • nercht12OP 5 years ago

        Actually, it can take multiple. gte(a: b: c: d:) is equivalent to (in C) a >= b && a >= c && a >= d. By making operators like functions, you can group like-operations and simplify code. Admittedly, it's not as readable for someone accustomed to seeing C style.

        Last note: gte(a=100) would produce an error in Copper because a=100 is an assignment statement that returns "a" (a function), and gte( function ) means nothing.

        Edit: I see you're referring to Python, but I figured I'd keep the note of comparison.

        • kroltan 5 years ago

          That's... surprising, I could easily expect

              gte(a: b: c: d:)
          
          to possibly mean

              a >= b && b >= c && c >= d
          
          I think it's just not as readable precisely because of the ambiguity.
tom_mellior 5 years ago

I wonder what "loose syntax" is supposed to mean here. No semicolons at the ends of lines? OK. Optional commas between function arguments? Hum. But at the same time this seems to require colons after... what exactly? Variable read accesses in argument lists? Unintuitive and not exactly "loose".

  • uryga 5 years ago

    > But at the same time this seems to require colons after... what exactly?

    not 100% sure but it kind of looks like copper's Whole Deal™ are "object-functions" (closures?) and you use colons to get an object function's return value. something like this

      adder = [a b]{ ret(this.a + this.b) }
      x = adder(3 5)
      print(x.a x.b x:) # 3 5 8
    
    (i looked at the docs for like 10 minutes, could be wrong)
    • nercht12OP 5 years ago

      You are correct. "object-function" is basically like in Javascript: It's an object (having members) and an executable body. The colons are a shorthand for function call. ie myfunc()

      In Copper, variables only store functions. This separates routine from data so you never end up with null pointer errors like in languages that have Any Types or pointers. Functions can return data, so you end up having function calls everywhere. a=5 is basically a={ret(5)}

      In your above example the correct first line would be: adder = [a b] { ret(+(a: b:)) }

      Parameters to a function are those that are not assigned data, whereas members are: add = [Param, Member=10) { ret(+(Param: this.Member:)) }

      Now you can probably see what's wrong with your third line.

      • uryga 5 years ago

        yeah, i think i get it now. but if variables can only store functions, how would you write something like this in Copper?

          x = foo()
          bar(x.a, x.b)
        • nercht12OP 5 years ago

          x = foo()

          bar(x.a: x.b:)

          Functions can return data. So say, a=5, then to get the value of 5 out, all you have to do is call the function a.

          Edit: I'm assuming foo() here returns an object with members "a" and "b". An example of such a function-object would be:

          foo = [] { ret( [a=5, b=10] ) }

          • uryga 5 years ago

            right, but doesn't that contradict this:

            > "in Copper, variables only store functions"

            because here, `x` clearly stores an object... is this about the whole "object-function" thing where Copper doesn't really distinguish the two?

            (btw i'm sure this is explained in the docs... but maybe this'll help folks like me who often just read the comments)

            • nercht12OP 5 years ago

              Copper does not distinguish between function and object. An object-function has two parts: the member part and the executable body. In C++, it's analogous to:

              class FunctionObject {

              FunctionObject* members[];

              void* operator() { /* executable body */ }

              };

  • skohan 5 years ago

    Isn't the trailing colon thing also in Ruby? I thought I remembered seeing something like this elsewhere where it also did not make much sense

    • nercht12OP 5 years ago

      The colon is a shorthand for (). Incidentally, it also makes for lots of little smiley faces everywhere.

  • nercht12OP 5 years ago

    No semicolons are ever required. It is loose syntax in that most anything, including this paragraph, is valid and readable code

    • tom_mellior 5 years ago

      That's still not very helpful to me. You might want to explain it a bit more in the README.

      For example, taking this line from the first example:

          this.peek = +(this.a: this.b:)
      
      If I replace this by:

          this.peek = +(this.a: this.b:
      
      (note the dropped closing parenthesis) and leave everything else as is, that will not be a syntax error?

      I know that Forth is "loose syntax" in that Forth code is just a sequence of white-space separated words, so your comment and mine are both syntactically valid Forth, but without meaningful semantics. But Forth does not use parenthesized function calls the way Copper does.

      • nercht12OP 5 years ago

        I see what you're getting at. Yes, a ) is needed... eventually. I think the key with "loose" is that it's forgiving. You won't encounter many syntax errors in average programming because the syntax has few rules. In that case, "very simple" would be better said than "loose".

wool_gather 5 years ago

Might have been better to link to the docs (https://chronologicaldot.github.io/CopperLang/), where the "simplicity" part of the pitch is made clearer, as well as the inspiration from JS and Lisp.

Reading through them, it seems like an interesting language concept/experiment.

mdaniel 5 years ago

GitHub doesn't recognize this enough to summarize it, and that last paragraph seems weird to me; is this a customized license of one that I might recognize? https://github.com/chronologicaldot/CopperLang/blob/master/l...

  • dcuthbertson 5 years ago

    The license seems self-contradictory. On one hand it says "THIS SOFTWARE MAY BE USED, MODIFIED, REDISTRIBUTED, DECOMPILED, DISASSEMBLED, REVERSE-ENGINEERED AND INCORPORATED INTO OTHER WORKS FREELY"

    So, it can be used and incorporated freely, but it also says "YOU ARE NOT GRANTED LEGAL AUTHORITY OVER THE USAGE, PROTECTION, AND DISTRIBUTION OF THIS SOFTWARE." so you don't have legal authority over its usage. IANAL, but that seems to limit one's own authority over one's own work based on this software. It's very confusing.

    • Rochus 5 years ago

      What you intend essentially corresponds to the two-clause BSD or the MIT license. Here is a good summary: https://opensource.stackexchange.com/questions/217/what-are-... Your statement "you are not granted legal authority ..." essentially means that you are the copyright owner and don't want to give away your copyright, but only give a license to others as explicitly stated; that's essentially the default in all jurisdictions I'm aware of, so you don't have to repeat it in your license. But if you're in US you might want to register your copyright.

    • nercht12OP 5 years ago

      The guy who wrote it isn't a lawyer either.

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection