Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alias sugar #11686

Closed
wants to merge 1 commit into from
Closed

Alias sugar #11686

wants to merge 1 commit into from

Conversation

mratsim
Copy link
Collaborator

@mratsim mratsim commented Jul 8, 2019

The alias sugar is a popular demand:

This creates an alias template defined and used the following way.

template alias*(name: untyped, bodyOrExpr: untyped): untyped =
  template name(): untyped {.dirty.} = bodyOrExpr

type Node = ref object
  left, right: Node
  val: int

let t = Node(left: Node(left: Node(left: Node(val: 10))))
alias(t3, t.left.left.left)
# or
# alias t3, t.left.left.left
# or
# alias t3:
#   t.left.left.left

doAssert t3.val == 10

I can also introduce a scoped alias similar to with statements in Python.

template withAlias(alias: untyped, to_be_aliased: untyped, body: untyped) =
  template alias(): untyped = to_be_aliased
  body

withAlias pos, game.renderer.ball[ix].pos:
  coolStuffHappenHere(pos)

@zedeus
Copy link
Contributor

zedeus commented Jul 8, 2019

IMO withAlias is more readable, thus performing the "reability enhancement" goal of this template better. The simple alias template doesn't make it clear that a new alias is created, since in your example t3 could appear to be an already existing variable passed in as an argument. I also think alias isn't very descriptive, but if you know what it does it's fine.

@dom96
Copy link
Contributor

dom96 commented Jul 8, 2019

I would honestly prefer this:

alias t3 = t.left.left.left

Can we accomplish this using macros?

Thank you for creating this though! Been wanting this for a long time.

@kaushalmodi
Copy link
Contributor

Just for reference, Emacs Lisp has defalias, which is used as (defalias 'aliased-defun-name 'orig-defun-name)... would be nice to have alias aliasedProcName = origProcName in Nim.

@bluenote10
Copy link
Contributor

@dom96 👍 what should work is to check for nnkExprEqExpr allowing alias(t3 = t.left.left.left).

I'm not sure about the withAlias version. Scoping can always be accomplished by a block already and is nicer with multiple/paired aliases:

withAlias(lll, t.left.left.left):
  withAlias(rrr, t.right.right.right):
    echo lll, rrr

vs

block:
  alias(lll = t.left.left.left)
  alias(rrr = t.right.right.right)
  echo lll, rrr

@krux02
Copy link
Contributor

krux02 commented Jul 8, 2019

I don't understand how this is any better to defining an alias with a template directly:

type Node = ref object
  left, right: Node
  val: int
let t = Node(left: Node(left: Node(left: Node(val: 10))))
template t3: untyped = t.left.left.left
doAssert t3 == 10

I don't like to have yet another syntax to define an alias.

@kaushalmodi
Copy link
Contributor

@mratsim I tried doing alias(foo, echo), but it didn't work. Can the "alias" be made to support aliasing arbitrary procs?

@mratsim
Copy link
Collaborator Author

mratsim commented Jul 9, 2019

@krux02 let t = is 7 characters while template t: untyped = is 21 characters.

Aliases are used in places that favor conciseness in variable names, often single letter, so that the code logic is more apparent. Having a short aliasing macro helps with that.

For example:

for ix in 0 ..< 10:
  alias(pos = game.renderer.ball[ix].pos)
  if pos == 0:
    discard

compared to

for ix in 0 ..< 10:
  template pos: untyped = game.renderer.ball[ix].pos
  if pos == 0:
    discard

When reading the code, the untyped is visual noise and the fact that game.render.ball[ix].pos is so far right compared to the semantically important 10 makes it slower to track the important part.

@bluenote10 seems good, I'll update the PR.

@krux02
Copy link
Contributor

krux02 commented Jul 9, 2019

If this sugar should go into the standard library, then it should be in canon with let and var sections, in other words I think this should be valid syntax:

block:
  alias:
    lll = t.left.left.left
    rrr = t.right.right.right
  echo lll, rrr

But again, I am not a big fan. I also am not a big fan of the : untyped in local templates, but it really isn't that bad either.

@Araq
Copy link
Member

Araq commented Jul 9, 2019

-1 from me. People will complain about alias(x, sideEffectHere()) as not working. Or as "yet another gotcha". Or as "yet another thing to learn"... Or...

@dom96
Copy link
Contributor

dom96 commented Jul 9, 2019

Won't people complain about it too if some package defines it instead of the stdlib?

@Araq
Copy link
Member

Araq commented Jul 9, 2019

Won't people complain about it too if some package defines it instead of the stdlib?

Maybe, but should we add anything to the stdlib then that somebody might otherwise have defined somewhere else?

@Araq
Copy link
Member

Araq commented Jul 10, 2019

I'm sorry but I have to use my BDFL superpowers to reject this already. This is exactly the sort of stuff like sugar.lc that needs to be battle-tested somewhere else before we add it to the stdlib.

@Araq Araq closed this Jul 10, 2019
@timotheecour
Copy link
Member

timotheecour commented Jul 10, 2019

/cc @mratsim I don't think it should be written this way:

  • it gets evaluated each time, which can be misleading:
proc main()=
  var counter = 0
  proc getFoo(): var int =
    counter.inc
    counter
  alias(bar, getFoo())
  echo bar # 1
  echo bar # 2 # my version would print 1 here and still make `bar` an alias (same addr)
main()

another bad case is case of expensive computations hidden inside; it wrongly gives the impression it's evaluated only once.

I wrote another alias that doens't have that issue (evaluated only once, so doesn't have issues raised by araq regarding side effects), and works with more kinds (const, proc, types, lvalues + more)
the trick I used was to use a macro:

# snippet for the lvalue case:
result = quote do:
        let myAddr = addr `exp`
        template `expAlias`: untyped = myAddr[]

It works not too bad but has the same drawback of not great syntax, which limits me wanting to use it much (although the syntax suggested about via alias: foo = someExpr() is an improvement)

What I really want is this syntax:

alias foo = someExpr(bar) # like D's alias except should also work with runtime values

optional but would be good to have too:

import bar
alias bar2 = bar
alias myTemplateAlias = someTemplate
alias myProcAlias = someProc
alias myMacroAlias = myMacro
# avoid the ugly: template myMacroAlias(a: varargs[untyped]): untyped = myMacro
# ditto with iterator, macro etc ;

var a = 1
alias a2 = a
a2.inc
assert a == 2

The only way IMO is to support this in the compiler.

@andreaferretti
Copy link
Collaborator

The only way IMO is to support this in the compiler.

Enough people are confused by let vs var, then we would have alias vs let vs var...

@bluenote10
Copy link
Contributor

@Araq Imho sugar is exactly the right place for something like that. As the name implies, usage can be unhealthy ;).

lc is actually a good example. It was worth a try, and I haven't seen much complaints when it got deprecated / thrown out.

It's weird that currently sugar is almost empty, and on the other hand, I'm spending a lot of time writing my own syntactic sugar snippets or I'm copy/pasting them from project to project. Examples like this simply don't justify a nimble dependency for a single function.

@dom96
Copy link
Contributor

dom96 commented Jul 10, 2019

The only way IMO is to support this in the compiler.

This goes against Nim's philosophy. We do not fill the language with hundreds of features that can be easily implemented using macros (even if the macro implementation isn't perfect).

@krux02
Copy link
Contributor

krux02 commented Jul 10, 2019

lc is actually a good example. It was worth a try, and I haven't seen much complaints when it got deprecated / thrown out

You haven't seen the trouble lc caused internally.

It's weird that currently sugar is almost empty, and on the other hand, I'm spending a lot of time writing my own syntactic sugar snippets or I'm copy/pasting them from project to project. Examples like this simply don't justify a nimble dependency for a single function.

Please write your syntactic sugar snippents and collect them in a nimble package. That is what Nim is about.

@timotheecour
Copy link
Member

timotheecour commented Jul 25, 2019

better fix here:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants