racket / rhombus

Rhombus programming language
Other
341 stars 61 forks source link

"Identifier already defined" error when `fun` and `statinfo.macro` define the same identifier #561

Open jetjinser opened 2 days ago

jetjinser commented 2 days ago

When I was reading the guide, I tried one of the examples described in it, but an error occurred.

description

When fun zero() and statinfo.macro 'zero' are declared at the same time, an "Identifier already defined: zero" error will appear.

link

online docs: https://docs.racket-lang.org/rhombus/annotation-macro.html source: https://github.com/racket/rhombus/blob/48890894d874df648fcb33de351c9dd60009f86c/rhombus/rhombus/scribblings/guide/annotation-macro.scrbl#L166-L179

reproducible code

#lang rhombus

use_static

import:
  rhombus/meta open

class Posn(x, y)

dot.macro 'vector_dot_provider $left . $right':
  match right
  | 'angle': 'vector_angle($left)'
  | 'magnitude': 'vector_magnitude($left)'

fun vector_angle(Posn(x, y)):
  math.atan(y, x)

fun vector_magnitude(Posn(x, y)):
  math.sqrt(x*x + y*y)

def zero:
  fun():
    Posn(0, 0)
// BUG: Identifier already defined
// fun zero():
//   Posn(0, 0)
statinfo.macro 'zero':
  '(($statinfo_meta.call_result_key,
     $(statinfo_meta.pack(
         '(($statinfo_meta.dot_provider_key,
            vector_dot_provider))'
       ))))'

zero().magnitude

It looks like there are some subtle differences between "def fun" and "fun", is this a bug or is it by design?

usaoc commented 2 days ago

Yes, this is expected. Maybe the example can be adjusted to be less confusing, but generally examples are run in a REPL-like setting, and things can work differently between modules and the REPL (top level).

The long explanation: The difference in fun name .... and def name: fun .... is a known issue. Normally, fun comes with static infos including call results and function arity. The definition fun (former) associates those with the defined name, while the expression fun (latter, inside the def) wraps those onto the function expression. Unfortunately, because fun is both a definition and expression, the conservative shortcut that propagates static infos from the right-hand side of def can’t apply, and only the former gets a binding in the statinfo space. And, as expected, redefinition (here, in the statinfo space) is not allowed within a module. However, examples are run in a REPL-like setting, and so redefinition works.

If anyone has an idea how to solve the mentioned issue, please help!

samth commented 1 day ago

I think we should probably avoid examples in the docs that don't work in a module, so maybe just switching to the def/fun form is the best choice.