nim-lang / RFCs

A repository for your Nim proposals.
135 stars 26 forks source link

allow style sensibility for declarations #470

Closed levovix0 closed 1 year ago

levovix0 commented 1 year ago

Abstract

Allow style sensibility, when there is two or more "same" declarations.
(from #456)

Motivation

  1. C api may be not style insansative, for example GlInt and GL_INT.
  2. There may be cases like setup and setUp, when meaning of "same" identifiers are diffirent.

Description

allow to declare identifiers with "same" names, and if usage match declaration, use it, else make error

let abc = 1
let aBc = 2

echo abc  # 1
echo aBc  # 2
echo abC  # error: ambigious identifier

but save style insensibility, when there is no "same" name declarations

let abc = 1

echo abc  # 1
echo aBc  # 1
echo abC  # 1

for overrides:

proc abc(i: int) = echo 1
proc aBc(i: int) = echo 2
proc aBc(i: string) = echo 3

abc 1  # 1
aBc 1  # 2
abC 1  # error: ambigious identifier
abC "" # 3

this will keep backwards compatibility, will not introduce any new compiler flags, and will solve situations, when you want to make difference between "same" identifiers.

there may be more smart algorithm, for example a_bc may be more same to aBc, than to abc

let abc = 1
let aBc = 2

echo abc  # 1
echo aBc  # 2
echo a_bc  # 2

issues from @Zectbumo:

  1. If you initially have:
    
    let abc = 1

echo abc # 1 echo aBc # 1

and later edit the code and you forgot that you ever used aBc (or it was a typo) and you declared a new var:
```nim
let abc = 1
let aBc = 2 # new declaration

echo abc  # 1
echo aBc  # 2 (oops)

# new code using aBc goes here
  1. what is the expected behavior here:
    
    let abc = 1

echo abc # 1 echo aBc # ???

let aBc = 2

echo aBc # 2

(with codeReordering second echo must be `2`, i think)

other question: should in this case we keep first letter sensibility?

### Code Examples

```nim
var state: bool

proc setup =
  state = false

proc setUp =
  state = true

proc setDown =
  state = false

setup()
setUp()

Backwards Compatibility

this will not break backwards compatibility.

Clonkk commented 1 year ago

Allowing this would mean that the compiler no longer guarantees that foo_bar and fooBar are identical and we would lose the ability to swap between camelCase and snake_case as will.

Also, foo_bar and fooBar having different meaning is not a good idea.

IcedQuinn commented 1 year ago

this will not break backwards compatibility.

It will because all of those examples are currently the same symbol. This will make them distinct. If module A (an older module) imports module B and module B is refactored to use these mystery case semantics then the older code could suddenly start referencing the wrong values.

this whole proposal also increases cognitive burden overall because it makes symbols conditionally case sensitive (they originally were not, now only but always the first, and with this change who knows because symbols are now arbitrarily case sensitive.)

:-1:

Zectbumo commented 1 year ago

If module A (an older module) imports module B and module B is refactored to use these mystery case semantics then the older code could suddenly start referencing the wrong values.

While the limitation you mention is true, this does not support your statement that it breaks backwards compatibility because the refactoring would have to come after Nim v2 is released and moving forward breaking changes will be made, which is expected. As far as backwards compatibility with Nim v1, this RFC will not break any existing code. This is my understanding of the term "backwards compatible". In addition, refactoring a module with names differing in case possibly changing values in old code, yes this is theoretically possible but it will be frowned upon and considered poor practice for obvious reasons. I'm not worried about this.

increases cognitive burden

I agree, it does add some burden but on the other hand there is a relief. For example there is setup and setUp. Two totally different concepts that currently normalizes to the same name. With this RFC "it just works". No thought needed.

I think we need to all keep in mind that this RFC solves a few needed corner cases. In the big picture this RFC is zero to low impact on sensible code, preserves style flexibility, and adds the ability for case sensitivity when needed. I find this to be an acceptable and good compromise that everyone can enjoy.

IcedQuinn commented 1 year ago

set up vs setup

epistemologically they are identical concepts https://grammarist.com/spelling/set-up-vs-setup/

foo, fOo, foO

relying on case is widely panned as a code smell, bad typographical practice.

also the purpose of style insensitivity is so that snake case and camel case coexist peacefully. not only does this add cognitive burden because symbols "sometimes" normalize and "sometimes" do not, it destroys this precept because you can no longer call set_up because someone defined setUP while a setUp symbol exists and now all the snake case is broken. it is literally better in every way to turn Nim in to every other language by getting rid of insensitivity than adding more edge cases.[3]

really the corner case is if you have a function that implements S.E.T.O.N.[1] and exists in the same symbol space as set_on[2] since they will both normalize to seton. And assuming their function signatures also overlap they will collide. This is indeed annoying although I've primarily only encountered it with C wrappers where our other ways around the problem don't work :/

[1] Doesn't exist. [2] This is also wrong because it should be on= which would not collide, although when porting C libs sometimes setters take weird parameters so they don't always map to a = function well. [3] Which I don't support either.

levovix0 commented 1 year ago

because symbols "sometimes" normalize and "sometimes" do not, it destroys this precept because you can no longer call set_up because someone defined setUP while a setUp symbol exists and now all the snake case is broken.

If someone defined setUP while setUp is defined, without this proposal, you need to specify, which one to use (moduleA.set_up, moduleB.set_up). With this proposal you still can do the same.

epistemologically they are identical concepts

But in practice they may be diffirent concepts.

Clonkk commented 1 year ago

In addition, refactoring a module with names differing in case possibly changing values in old code, yes this is theoretically possible but it will be frowned upon and considered poor practice for obvious reasons

What is the point of adding a feature that is already considered "poor practice" to use ?

For example there is setup and setUp. Two totally different concepts that currently normalizes to the same name.

Then change the name setup to init or prepare; or use an enum for set(Up/Down). We can be even more creative and use a prefix / suffix or a typedesc argument. No need to change the language.

I think we need to all keep in mind that this RFC solves a few needed corner cases.

Which corner case are impossible to solve currently and would be solved by this RFC ?

In the big picture this RFC is zero to low impact

This RFC is a more complicated and less consistent rule (it has exception) and it breaks snake_case / camelCase interchangeability. Definitely not low impact.

If someone defined setUP while setUp is defined, without this proposal, you need to specify, which one to use (moduleA.set_up, moduleB.set_up). With this proposal you still can do the same.

It is much clearer to read on mod1.setup / mod2.setUp than spotting the difference between setUp/setUP/setup.

Zectbumo commented 1 year ago

@IcedQuinn

epistemologically they are identical concepts https://grammarist.com/spelling/set-up-vs-setup/

funny, because I have been looking at it from the point of view of setUp as in up/down direction and setup as in initialize. but, no need to focus on this. it's just a token example representing a general problem.

you can no longer call set_up because someone defined setUP while a setUp symbol exists

This is a good issue example. I can see defining setHttpHeader, and setHTTPHeader making set_http_header ambiguous. This is a very ugly case and I'm fine with this not working perfectly in order to achieve what we can get with this RFC. The workaround would have to be set_HTTP_header or set_Http_header in order to distinguish. Or like the RFC says make set_http_header favor setHttpHeader.

@Clonkk

What is the point of adding a feature that is already considered "poor practice" to use ?

Because poor practice exists. Some people have brought up libraries such as nvidia and opengl that contain naming that only vary in case.

Which corner case are impossible to solve currently and would be solved by this RFC ?

being able to wrap nvidia, opengl and other poor practice libraries and gain sensitivity in the hopes it brings in mass adoption.

Clonkk commented 1 year ago

being able to wrap nvidia, opengl and other poor practice libraries

It is already possible to wrap those lib and it has already been done.

Keep in mind you can rename identifier with importc