Skip to content

Instantly share code, notes, and snippets.

@lokedhs
Created January 2, 2021 10:03
Show Gist options
  • Save lokedhs/72ce0a35dac6744c557a5a3fb18012aa to your computer and use it in GitHub Desktop.
Save lokedhs/72ce0a35dac6744c557a5a3fb18012aa to your computer and use it in GitHub Desktop.

Why the Dyalog-style assignment is problematic

In Dyalog, a function can be assigned like so:

foo ← { ⍵+1 }

Now, compare this with a very similar form:

foo ← { ⍵+1 } 2

As can be seen, the final digit 2 at the end of the line completely changes a function definition into a variable assignment. KAP's parser is flexible enough to handle this situation (with some work), but the code would be ugly and explaining the parsing rules would be difficult at best.

In fact, some of these tricky parsing tricks have already been implemented in order to deal with things like this:

a +.(×/) b

vs.

a (+.×)/ b

The reason this is so complicated is because when seeing an opening parenthesis the parser has to defer the decision as to whather to parse the containing expression as a function or a value until after the entire expression has been parsed. KAP solved this by having a class called ParseResultHolder which can be returned from parsing functions before the decision on how to interpret it can be made.

The above trick works, but is complicated enough that I would rather not have to do something similar to deal with Dyalog-style function definitions.

Function definition syntax in KAP

Requirements:

  • Two forms of definitions:

    • Extended form, takes N arguments on each side of the function name. Current syntax: ∇ (arg0;arg1) functionName (arg2;arg3) { ... }

    • Short syntax. Arguments are always and . In Dyalog, this looks like: functionName ← { ... } KAP currently does not have this.

  • Need to have a natural syntax for defining operators. Currently, KAP does not have any syntax for defining operators.

  • Two different ways of declaring functions:

    • Global (default in KAP)
    • Local (no current syntax for this)

Local vs global functions

The difference between local and global function definitions can be shown in this simple example:

∇ foo (x) {
  x+1
}

∇ bar (x) {
  ∇ foo (x) {
    x+2
  }
  foo x
}

⍝ With local function definitions, the below will return 3.
⍝ If function definitions are global, it will return 2.
foo 1

It may be easy to simply say that function definitions are global if they are executed in the toplevel context. This is however not ideal, since when including another source file, the second file is evaluated in its own context. Thus, no code is ever really evaluated in global scope (except for commands typed on the REPL).

Additional thoughts: Perhaps evaluation of source files should be performed in a special context, which may not be toplevel but for the purpose of function definitions be considered as such. That's possible, but becomes somewhat difficult to document.

It's also important to note that we can't simply parse source files in the toplevel context, since local variables (not functions) declared in files needs to be local to said file.

Possible solution: One idea would be to have a special symbol that indicates global scope. This could be used for both functions as well as the case where a file needs to expose a global variable. The question is: What should such syntax look like? I have no idea right now.

Defining operators

Defined operators are rare enough that using some verbose syntax such as defoperator isn't out of the question. With the short function syntax one can always use and to refer to the left and right function names in a way similar to how GNU APL does it.

Possible solutions

Special function assignment symbol

We could keep the Dyalog style for short function definitions, but instead of using , we can use Thus, a function definition would look like this:

foo ⇐ { ⍵+1 }

This is neat, but doesn't answer the question as to how to deal with local vs. global functions. It also leaves the long-form function definitions unchanged.

Add new function definition symbols

This is an obvious solution. To take ideas from Lisp, let's call it defun. A function definition would them look like this:

defun (arg0;arg1) foo (arg2;arg3) {
  arg1 + arg2 + arg3 + arg4
}

The short form would be:

defun foo {
  ⍺ + ⍵
}

The short form is not too bad compared to Dyalog (4 characters longer). It would also be constent with a defoperator symbol. However, in order to distinguish between the function and variable names, the syntax would look pretty weird. Maybe something like this:

defoperator (leftarg) [leftop] foo [rightop] (rightarg) {
  (1 leftop rightarg) rightop leftarg
}

I'm not a huge fan of this, but I really don't have any better ideas.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment