Skip to content

Redoing variable declaration macro pragmas #220

Closed
nim-lang/Nim
#19406
@metagn

Description

@metagn

This supersedes nim-lang/Nim#6696, which didn't get moved to this repo for some reason.

nim-lang/Nim#13508 implemented the following macro syntax for variables:

macro foo(varName, varType, varValue) = discard

let b {.foo.} = 4

This feature has a few shortcomings and seems unfinished. Before I get to the proposal I'll mention the few bugs that the current implementation has:

  1. This feature cannot be used in let/var sections. (since they're sequential you can just break them up for this)
  2. This feature cannot be used for const variables. (the PR just forgot to include const variables, it's in semVarOrLet but not in semConst)
  3. Attaching types to the value argument in the template/macro gives weird errors. Try the snippet I posted with varValue: string, it says b is undefined or something. This is fine because even proc pragma macros can't use types.

Now to the design problems.

Problem 1: Additional arguments

let b {.foo: 3.} = 4 # invalid pragma: foo: "abc"

How do you even define foo for this? Where is the argument supposed to go in its signature?

Problem 2: var/let/const information

The current design gives no information if the variable being changed is var, let or const. The only arguments you get are the variable name (ident or accent), variable type and the value. You could pass information through the type I guess, the parser allows having something like var x: var T = 3, but this still requires work on the user and bloats code.

Problem 3: Incompatible with other pragmas

The current design does not allow using other pragmas. Not just other pragma macros, any pragmas.

# order doesn't matter
let b {.global, foo.} = 4 # invalid pragma: foo

This is disharmonious with proc pragma macros, they keep the other pragma regardless of the order, and if multiple macros are used the first one is evaluated.

I will add more problems if I find more, I think this many is enough.

Proposal

Instead of foo(varName, varType, varValue), we use this:

macro foo(decl) = discard

foo:
  let b = 3
# equivalent to
let b {.foo.} = 3

You can add arguments like how you would to proc pragma macros like:

macro foo(arg, decl) = discard

let b {.foo: 1.} = 3 # arg is 1, decl is `let b = 3`

We have the information of whether or not this is a let, var or const, so problem 2 is solved. It works slightly differently for sections:

let
  a = 1
  b = 2
  c {.foo.} = 3
  d = 4
  e = 5

# becomes

let
  a = 1
  b = 2
foo:
  let
    c = 3
let
  d = 4
  e = 5

This doesn't break any code since let/var/const sections are evaluated sequentially unlike type sections.

Here's an example of the usage of a user defined unpack macro in conjunction with this syntax:

let _ {.unpack: (a, (b, c)).} = (1, (2, 3))
echo a, b, c # 123

Flaws and backwards compatibility

If you use the current variable declaration pragma macros your code will be broken, but since it's such a fragile feature in regards to bugs I don't think we have many problems.

One flaw is that there's no way to supply type information here, unless we had some kind of VariableStmt like ForLoopStmt that had generic arguments that we could use here. I think that would be too complex though and no one really needs types here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions