nim-lang/Nim

alias declarations

wizzardx opened this issue · 15 comments

More or less what you have in C, with the 'define' preprocessor directive, but in a type-safe manner.

This may be possible in eg a macro or template that could be added to stdlib.

Basically what D describes over here:
https://dlang.org/spec/declaration.html#alias

Meaning that eg, code like this (from Platformer tut):

proc handleInput(game: Game) =
  var event = defaultEvent
  while pollEvent(event):
    case event.kind
    of QuitEvent:
      game.inputs[Input.quit] = true
    of KeyDown:
      game.inputs[event.key.keysym.scancode.toInput] = true
    of KeyUp:
      game.inputs[event.key.keysym.scancode.toInput] = false
    else:
      discard

Can be expressed as something like this, for instance:

proc handleInput(game: Game) =
  alias inputs = game.inputs[event.key.keysym.scancode.toInput]
  var event = defaultEvent
  while pollEvent(event):
    case event.kind
    of QuitEvent:
      game.inputs[Input.quit] = true
    of KeyDown:
      inputs = true
    of KeyUp:
      inputs = false
    else:
      discard

It's possible currently to get a lot of these kinds of effects by making use of some combination of const or template, or inlined function, but it's a bit untidy or verbose.

Here's one basic example of it from the Nim forum:

https://forum.nim-lang.org/t/1515

It's also something that Ada lets you do with its "rename" syntax, eg:

http://www.adaic.org/resources/add_content/docs/95style/html/sec_5/5-7-2.html

https://en.wikibooks.org/wiki/Ada_Programming/Basic#"Hello,_world!"_with_renames

This is possible with a template already, no?

template inputs = game.inputs[event.key.keysym.scancode.toInput]

Ah, thanks!
I've struggled with this in the past, too.
So in the linked forum thread, this solution would also work?

template bar = foo

It probably will

Doesn't look like it...

This works, as per forum thread:

proc foo(x: int) = echo x
const bar = foo
bar 10

This doesn't:

proc foo(x: int) = echo x
template bar = foo
bar 10

It gets this error:

test.nim(3, 5) Error: type mismatch: got (int literal(10))
but expected one of: 
template bar()

However, it looks like this does work, which is pretty nice:

proc foo(x: int) = echo x
template bar(x) = echo x
bar 10

However, this one sadly doesn't (it seems close to your original template example):

var a = 200
template b = a
b = 300
echo a

It gives this error:

test.nim(3, 1) Error: expression 'a' is of type 'int' and has to be discarded

This one does however:

var a = 200
let b = addr a
b[] = 300
echo a

However I think that syntax is a bit unsafe, ideally shouldn't be used.

I've edited that forum thread with this updated info.

You need to declare the return value. The error is saying that a returns an int, but it wasn't expecting any return value.

var a = 200
template b: untyped = a
b = 300
echo a

Ah, thanks, that helps a lot!

There's a section in the Nim manual which talks about untyped:

https://nim-lang.org/docs/manual.html#overloading-resolution-lazy-type-resolution-for-untyped

But it's not immediately obvious from that, that you can use template to swap in the left hand side of an assignment.

However, looks like I can't say eg, substitute a proc directly:

proc foo(x, y, z: int) =
  echo x + y + z

template bar: untyped = foo

bar(1,2,3)

Or eg rename modules, eg like this:

import os

echo os.fileExists("/tmp/")

template my_os: untyped = os

echo my_os.fileExists("/tmp/a.txt")

I think I'm asking for something pretty close to a source code string substitution macro, eg something like this, where "aliasing" is the macro, and "alias" is a special identifier recognized by the macro, like this:

(kind of, operating a lot like how C's "define" preprocessor directive works):

aliasing:

  # os is a module
  alias(my_os, "os")
  echo my_os.fileExists("/")  # Same as: echo os.fileExists("/")

  # bar is a function
  alias(foo, "bar")
  foo(1, 2, 3, 4, "abcd")  # Same as: bar(1, 2, 3, 4, "abcd")

  # a is a variable
  alias(b, "a")
  var a: int
  b = 123        # Same as: a = 123

  # x.a.b is a structure, where b is a seq.
  alias(s, "x.a.b[idx]")
  var idx = 999
  s = 123        # Same as: x.a.b[idx] = 123

The proc example can be done too. Note that specifying a return type (which can also be untyped) is only necessary if it returns a value.

proc foo(x, y, z: int): int =
  x + y + z
proc foo2(x, y, z: int) =
  echo foo(x, y, z)
template bar(v: varargs[untyped]): untyped = foo(v)
template bar2(v: varargs[untyped]) = foo2(v)
echo bar(1,2,3)
bar2(1,2,3)

I don't think there is a solution for the module alias at the moment.

@wizzardx @jcosborn

import os as my_os
echo my_os.fileExists("hello")

Module aliasing IS possible :)

Ah, yes, and that even works in addition to other import names.

import os
import os as my_os
echo os.dirExists("/tmp")
echo my_os.dirExists("/tmp")
Araq commented

Workarounds have been sorted out. Closing.

the workarounds don't work in many cases; this issue is properly fixed here: #11822

dom96 commented

@timotheecour I'm going to repeat my request again, please stop cross-referencing your PRs across every single related issue. You can see that GitHub shows that you've referenced this issue above. Writing a comment here is just noise in our notifications.

sure, will do. my reasoning was that issues may be referenced in a PR, but the PR is not intending to fix or close the issue referenced (eg workaround or just part of PR description). But fine.