ozra / onyx-lang

The Onyx Programming Language
Other
97 stars 5 forks source link

Loose idea: yet a function call syntax variant #60

Closed ozra closed 8 years ago

ozra commented 8 years ago

Currently you can call a function / method in two ways:

my-func(x, y) -> say "got {x} and {y}"

my-func(47, "hey")  -- with parentheses
my-func 47, "hey"   -- with an initial space - "juxtaposition style"

I'm thinking about yet one that I think would work without conflicts with the rest of the syntax (as said, still a loose idea) - using indent:

my-func
   47
   "hey"

Now, why on earth why? It would be of benefit for DSL'ish coding. With the indent style, the following arguments will follow literal-array / literal-hash rules: You can use new-lines as separator besides comma.

Let's take a practical example using "temel", half-stolen from https://github.com/f/temel:

-- Register own tags first:
tag web-app

get "/" ~>
   html
      head
         title "My page yo"
      body {id: "main"}
         web-app "Hello World!"
         h2 "Funky shit"
end

(Note: I don't know temal syntax/API, so above is slightly ad-libed regarding title/h2, but the essence holds)

How's that for writing a web app? Rocks the socks of anything else.

How about possible conflicts? Let's try to pre-empt.

if foo
   47

Above, if foo is not a local variable, it is a call, and then the indentation will apply to the function, not the if. This can be confusing. But if your functions are named properly: it will not be.

The big downside - is that the compiler cannot assume which signature overloads will be available for the function named foo, so it will always treat the indent as "beginning of args" for the function. This means that if foo is in fact an arg-less function, and the above was intended as if foo is truthy then 47 - you'd have to be explicit where you didn't have to before:

if foo:
   47

or

if foo then
   47

or... (any other variation of block starter [reminder to crystalers: block as in block of code])

In these the explicit block starter makes it clear that the indented block belongs to the if.

Good? Confusing? Gimme your input!

stugol commented 8 years ago

So, you'd have to use a : whenever you passed a function call to something like an if or a match?

a = 1
b ->

if a
   say "ok"

if b
   say "compile error"

if b:
   say "ok"

Presumably you can't use the indent syntax when using an if? Or can you?

b(n) ->

if b
   4
      say "woot"      -- ?

I like the idea in principle - it would make dsls a lot easier to write - but I anticipate problems.

b(n) ->

if b
   say "how good will the compile error be for this?"

On a side note: I don't need commas when splitting arrays and hashes over multiple lines? Awesome.

stugol commented 8 years ago

I think we need a compromise: Special syntax when defining a function, to specifically enable this indent-based usage. Think about it. Most of the time, people won't be using functions in this manner; but having to remember to put : after ifs and matches and such is going to be frustrating; not to mention visually unappealing. So instead, make it the exception:

normal-fn(n) -> n
dsl-fn(a = 0, b = 0)& -> a + b            -- & means "can be used as a dsl function"

if normal-fn
   say "perfectly fine"

if dsl-fn:
   say "needs the colon here"

if dsl-fn
   4, 5
      say "doesn't need it here"
ozra commented 8 years ago

On a side note: I don't need commas when splitting arrays and hashes over multiple lines? Awesome.

That's right, I wanted "lists" to be like natural lists, inspired by livescript/coffeescript/YAML, feels much more intuitive :-) (see #9, ctrl-f search for "List" and "Map" respectively to go directly to the details on the syntax)

Hmm, yes, it would really become ridiculous - in all control structures - since it essentially will mean expression-block-starters becomes mandatory in practice (functions are used most all of the time in Onyx, since they often "wrap"/"isolate" values as getters.)

You're also absolutely right on the error message - it will be possible to heuristically figure out what the user probably meant in most cases, and hint in the error, but that's not good enough.

Specifying behaviour per functions is no-go. The main reason in Onyx for compiling faaaast (as possible.. and we want that) is that lexing and parsing can identify everything (essential) without knowing anything of semantics (like what kind of function, or if it even is one, that a certain symbol represents). For instance C++ mixes lexing, parsing and semantics in a big O(n_hell^99999) soup, and we know how fast that compiles :-/

While the indent upon indent for if => call => if-expression-block would work, that certainly is nothing to aim for, it looks like... #!x¤$ck, and given your feedback it's clear that it falls flat to begin with.

Which brings me to an idea: since the primary use case for the call syntax is for enabling DSL'ish constructs, they should not be expected in control structures. So, when in a control-structure any indent is interpreted as belonging to the control structure - not the call.

It's an exception to the call syntax rule that is very reasonable and expected behaviour in my opinion.

Ideas?

stugol commented 8 years ago

they should not be expected in control structures. So, when in a control-structure any indent is interpreted as belonging to the control structure - not the call

Agreed. DSLs are markup, not conditional expressions.

ozra commented 8 years ago

I implemented it and it worked smoothly with the current crazy-specs without a hinge. (Better not get to happy until the trolls under the surface has unleashed their ugly fury)

Sample code using "Temel" (tested):

require "./temel/src/temel"

is-welcome = true

items = [
   "foo ya"
   "boo ya"
   "blargh hya"
]

some-name = "Burns"

my-page = html
   head
      title "Testing it out!"
      script
         { type: "bad-script" }
         "my-fine-file.bad-ass"
   body
      div
         {id: "main-div"}

         if is-welcome ? h1 "Welcome" : h2 "This is it"

         article
            h2 "The fat and the furious"
            p
               "
               Long
               article text
               here
               and stuff, mkay Mr. {some-name}!
               "

               ul items.map(~> li _1).join

      div {id: "footer"},
         nav ul
            li a {href: "asdfsadf"}, "Contact"
            li a {href: "bfadfasdf.se"}, "About"

say my-page

=> `Testing it out!

Welcome

The fat and the furious

Long article text here and stuff, mkay Mr. Burns!

  • foo ya
  • boo ya
  • blargh hya

` I must honestly say, when I whipped it up to test indent-calls, I just realized it when the html came out without a snag, I was like "Aaaaaaah yeah, baby!". Didn't expect it to turn out so well so simply. This is the smoothest way of templating html _I've_ seen (ymmv and all that ;-) ) - and it's all compiled to blazing binary code! Fuck node.js B-) Temel is to thank for not having to code poc-functions just to try it.
stugol commented 8 years ago

How about:

script { type: "bad-script" }
  "my-fine-file.bad-ass"
ozra commented 8 years ago

Was the above not an example, I would've written it:

script {type: "bad-script"}, "my-fine-file.bad-ass"

Of course you can currently write:

script { type: "bad-script" },
  "my-fine-file.bad-ass"

Supporting the same without the comma has the same strings attached as #66 - it can be done, but rules differ depending on if it's a literal or not in that case. Of course with this new perspective of DSL-usage, both are worth thinking about. If you'd like, could you add that to #66? I re-opened it.

stugol commented 8 years ago

Righty ho.