Csound Csound-dev Csound-tekno Search About

[Csnd-dev] Named Arguments

Date2023-11-06 00:56
FromSteven Yi
Subject[Csnd-dev] Named Arguments
Hi All,

Looking at https://github.com/csound/csound/issues/745 and trying to evaluate for milestone scheduling. Rather than look at it from the view of liking the feature or not, as I think there's plenty of supporters for languages with named arguments, I'm interested to discuss the technical requirements. In particular, it seems like something that will require internal changes and therefore has API stability implications. 

Language-wise, we'd have to figure out the rules for named arguments vs. positional arguments, i.e., sorting out what to do if someone writes:

some_opcode(value: 1, 3, 4)

Internally, opcode would need a map of argument names to positions. For both old-style and new-style UDOs we have information we can parse from either the xin/xout statements or the opcode signature (in new-style UDOs). For native opcodes, we do not have any information for arguments besides position and type. 

One thing that might be interesting internally is to redefine OENTRY to move away from the input and output types as strings and instead use data structures. It would be some tedious work but offers some interesting benefits and drawbacks. Benefits would include:

* having all of the type data pre-parsed at compile-time (saving runtime work)
* better error reporting for opcode used errors
* potential for further arg type enhancements (optional, default values, etc.)

Drawbacks I can think of:

* Likely to take up more memory to add arg names
* Once an arg is named, we'd have to be very careful to check the arg names or new projects might break if the arg name changes

I did a mockup of what the code might look like for static initialization of arguments with names and types:

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

typedef struct {
  char* name;
  int type;
} OPCODE_ARG;

typedef struct {
  char* name;
  int outcount;
  int incount;
  OPCODE_ARG args[];
} OENTRY;

OENTRY a = {
  .name = "my_test_opcode",
  .outcount = 1,
  .incount = 2,
  .args = {{ "out", 0}, { "inarg1", 0}, { "inarg2", 1}},
};

int main(int argc, char** argv) {

  printf("Opcode name: %s\n", a.name);

  printf("Opcode out args\n");

  for (int i = 0; i < a.outcount; i++) {
    OPCODE_ARG* out_type = &a.args[i];
    printf("  %s: %d\n", out_type->name, out_type->type);
  }

  printf("Opcode in args\n");

  for (int i = a.outcount; i < a.incount + a.outcount; i++) {
    OPCODE_ARG* in_type = &a.args[i];
    printf("  %s: %d\n", in_type->name, in_type->type);
  }

}

(In our code, the OPCODE_ARG would have a CS_VAR_TYPE instead of int type; this is just mockup code.) 

One can imagine the OPCODE_ARG having other fields like "void* defaultValue", that the compiler could then treat the arg as optional. 

Regardless, we need to figure out sooner rather than later what we want to do for Issue 745 as I think it would require a change to OENTRY and would have to either go into CS7 or delayed until CS8. 

Steven 

Date2023-11-06 06:44
FromVictor Lazzarini
SubjectRe: [Csnd-dev] [EXTERNAL] [Csnd-dev] Named Arguments
It does look interesting, but also hard work to
make all changes required. Would we use the manual for argument names? We would need consistency there too.

A halfway house would be change OENTRY now and implement this feature for UDOs first, then internal opcodes later. We could have code that takes in old-style OENTRY and outputs new-style ones with dummy arg names so we don't need to change all at once.

Prof. Victor Lazzarini
Maynooth University
Ireland

On 6 Nov 2023, at 00:57, Steven Yi <stevenyi@gmail.com> wrote:



*Warning*

This email originated from outside of Maynooth University's Mail System. Do not reply, click links or open attachments unless you recognise the sender and know the content is safe.

Hi All,

Looking at https://github.com/csound/csound/issues/745 and trying to evaluate for milestone scheduling. Rather than look at it from the view of liking the feature or not, as I think there's plenty of supporters for languages with named arguments, I'm interested to discuss the technical requirements. In particular, it seems like something that will require internal changes and therefore has API stability implications. 

Language-wise, we'd have to figure out the rules for named arguments vs. positional arguments, i.e., sorting out what to do if someone writes:

some_opcode(value: 1, 3, 4)

Internally, opcode would need a map of argument names to positions. For both old-style and new-style UDOs we have information we can parse from either the xin/xout statements or the opcode signature (in new-style UDOs). For native opcodes, we do not have any information for arguments besides position and type. 

One thing that might be interesting internally is to redefine OENTRY to move away from the input and output types as strings and instead use data structures. It would be some tedious work but offers some interesting benefits and drawbacks. Benefits would include:

* having all of the type data pre-parsed at compile-time (saving runtime work)
* better error reporting for opcode used errors
* potential for further arg type enhancements (optional, default values, etc.)

Drawbacks I can think of:

* Likely to take up more memory to add arg names
* Once an arg is named, we'd have to be very careful to check the arg names or new projects might break if the arg name changes

I did a mockup of what the code might look like for static initialization of arguments with names and types:

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

typedef struct {
  char* name;
  int type;
} OPCODE_ARG;

typedef struct {
  char* name;
  int outcount;
  int incount;
  OPCODE_ARG args[];
} OENTRY;

OENTRY a = {
  .name = "my_test_opcode",
  .outcount = 1,
  .incount = 2,
  .args = {{ "out", 0}, { "inarg1", 0}, { "inarg2", 1}},
};

int main(int argc, char** argv) {

  printf("Opcode name: %s\n", a.name);

  printf("Opcode out args\n");

  for (int i = 0; i < a.outcount; i++) {
    OPCODE_ARG* out_type = &a.args[i];
    printf("  %s: %d\n", out_type->name, out_type->type);
  }

  printf("Opcode in args\n");

  for (int i = a.outcount; i < a.incount + a.outcount; i++) {
    OPCODE_ARG* in_type = &a.args[i];
    printf("  %s: %d\n", in_type->name, in_type->type);
  }

}

(In our code, the OPCODE_ARG would have a CS_VAR_TYPE instead of int type; this is just mockup code.) 

One can imagine the OPCODE_ARG having other fields like "void* defaultValue", that the compiler could then treat the arg as optional. 

Regardless, we need to figure out sooner rather than later what we want to do for Issue 745 as I think it would require a change to OENTRY and would have to either go into CS7 or delayed until CS8. 

Steven