Arc Forumnew | comments | leaders | submitlogin
2 points by rocketnia 2549 days ago | link | parent

Imagine you were proposing the introduction of first-class functions into our language, trying to conjure the possibilities of returning functions as values. And I were to reply: "Well, you can already return functions as values. They just raise error messages." There are flaws in the analogy, but do you see my point?

In that case, I'd be promoting the ability to return interesting functions. Along the way I'd probably say something like, "See, you can already return functions as values. They just raise error messages. Imagine what else they could do!"

I'm not sure that's your point, though. Maybe this is more relevant:

With the way I like to think of everything as being a function, with some just being more interesting functions than others, the term "function-like value" is pretty meaningless. If we were having a different conversation, I might say "function-like value" to mean just those values that don't throw "we couldn't think of any sensible use for calling this" errors.


Arc allows you to put many types of things in functional position - lists, strings, hashes, functions, macros - and it offers convenient behavior for each of these cases.

This isn't important to your point, but I think you're dividing it up the wrong way.

During compile time, there are four kinds of things in functional position: Expressions, macro names, special form names, and another category to handle metafns (essentially parameterized special forms).

The "expressions" category encompasses all run time invocations of lists, strings, tables, and closures, and since the result of the expression is only determined when we run it, not when we compile it, other kinds of values can (try to) be invoked too. By the time that happens, it's ambiguous whether the error-raising behavior belongs to the language runtime, the value itself, or something intermediate like a 'defcall framework. I blame the behavior on the value because I think it makes the language and frameworks sound like they have fewer special cases.


Meanwhile, your suggested syntax isn't a new category, so don't worry about me crying inconsistency here. ^_^ You just have 'quote joining the ranks of 'andf, 'complement, and 'compose as a metafn: ((quote a) b c).

Alternately, maybe you'd be content with just using (defcall sym self-and-args self-and-args) instead, so that symbols are useful as functions. (That's equivalent to a certain case of having 'quote be a metafn, though; it can just be an uninteresting metafn that acts like it isn't a metafn at all.)


Is there some convenient behavior we could be doing here instead?

The question hasn't changed, and my answer hasn't either: Whatever the syntax does, it'll only be a few characters more convenient than a macro.

That being said, I've thought about it a bit more deeply, and here are six criteria I've come up with for a good ('a b c) syntax, some criteria being more important than others:


(1) It should be a form you desperately want to stuff onto one line. Otherwise (zz a b c) will be good enough despite being two characters longer. This sort of desperation makes 'do and 'fn much more convenient names than 'begin and 'lambda.

(2) It should be a form that takes at least one symbol rather than a complicated expression, since otherwise the symbol 'a in ('a b c) is wasted. Existing forms similar to what we're looking for include (let ...), (fn ...), (assign ...), and (def ...).

(3) It should be a form you never need to combine with ssyntax. Otherwise (?a b c) will probably be better, since it allows for ?a.b, (z:?a b c), and so on. This means it probably needs to be something you always use with two or more arguments (so you don't mind losing a.b and a!b) and as usually as a standalone imperative command (so you don't mind losing a&b and a:b). Existing forms that fit this profile include (assign ...), (def ...), (zap ...), and (push ...).

(4) It should ideally have something to do with the usual meaning of 'quote, just so people don't wonder why we didn't use (`a ...), (#'a ...), or a custom read macro instead.

(5) It should ideally be better than any use we can come up with for symbols as functions. For instance, it should be better than having ('abcdefg 3) behave similarly to ("abcdefg" 3), and it should be better than Jarc's usage, which is to invoke the JVM method of that name if possible. (This is less of a criterion and more like food for thought.)

(6) We should be comfortable with the meaning of ('nil 0), whether it's consistent with list accesses like ('(a b) 0) and ('(a) 0), consistent with the new syntax, consistent with both, or consistent with neither. (This is more of a gotcha than a criterion.)


I think (assign ...) and (def ...) are pretty good options for satisfying both the second and third criteria. How about having ('a x y z) mean (assign a (let _ (errsafe a) (x y z)))? The code ends up looking like this:

  (zap sym:string foo)  ; old
  ('foo sym:string _)   ; new
  (zap [* _ 10] foo)  ; old
  ('foo * _ 10)       ; new
  (def idfn (x) x)  ; old
  ('idfn fn (x) x)  ; new (but without redefinition warnings)
  ; old
  (let _ idfn
    (def idfn (x)
      (prn "traced!")
  ; new (but without redefinition warnings)
  ('idfn fn (x)
    (prn "traced!")
  (= setter (table))  ; old
  ('setter table)     ; new
  (= total 0)    ; old
  ('total do 0)  ; new (but you'd still use the old version, of course)
As for the first of the criteria, it's probably just a matter of how desperate someone is to 'zap things. I use 'zap all the time, so there's a good chance I'd put it to good use, even if it does look a bit weird to me at first.

The sixth criterion is handled somewhat nicely, 'cause ('nil 0) still results in a runtime error for trying to evaluate (0), and even a usage that escapes that error, like ('nil table), stumbles upon a different runtime error for attempting to rebind 'nil. These approximate the errors that would be generated if ('nil 0) and ('nil table) were list accesses.

The rest of the criteria aren't very important. The fourth is totally betrayed here--but it doesn't matter 'cause it's just an apostrophe we're talking about--and the fifth isn't something that can't be betrayed at all unless there's clearly a better idea available.