timotheecour / Nim

Nim is a compiled, garbage-collected systems programming language with a design that focuses on efficiency, expressiveness, and elegance (in that order of priority).
http://nim-lang.org/
Other
2 stars 0 forks source link

WIP #781

Closed timotheecour closed 3 years ago

timotheecour commented 3 years ago

example PRs that were needed despite being breaking changes, demonstrating that the "no breaking change promise" made in 1.0 was broken many times, will continue to be broken, and is untenable in the current state of nim compiler and stdlib maturity level.

rev

see also:

We have this tool already, it's called .deprecated: "use X instead". Old code keeps working, new code avoids the quirky proc since there is an alternative provided.

Great, so if there's a bug (performance, logic, etc) in hash, we just need to deprecate hash and introduce hash2 or std/hashes2 which produces different runtime values to avoid collisions. Since we can't silently change behavior of tables in stdlib by using the new std/hashes because someone might depend on old behavior tied to the old hash, we need to introduce std/tables2, std/sets2, and in turn std/json2 since it depends on tables, and transitively every module needs to be duplicated. Any client of any of those dependencies will have to be updated to use the new modules and in the process cut support for prior nim versions, creating a mess of incompatible duplicate modules that need to be maintained and break nimble ecosystem beyond repair.

This problem is pervasive, with your logic we have to deprecate and find a new symbol name for sugar.=>, os./, we have to deprecate std/json, std/macros

Nowadays I avoid jsonutils.nim and parts of the stdlib because of this cowboy style attitude to breaking changes

I also recommend you should avoid std/[json, tables, strutils, sequtils, os, macros] and indeed, just about any stdlib module because of the same cowboy attitude which views bugs as things that should be fixed, not worked around, if nim is to be taken seriously.

nim 1.0 release made a promise to offer stability guarantee (with an exception for "security vulnerability" and experimental features to a good approximation); I was against making that promise because the immaturity of the language and standard library would require breaking that promise in many occasions in order to allow evolving the language and free stdlib from design bugs. The reality is that the stability guarantee has been broken on many occasions, for good reasons (unrelated to security or experimental features or modules).

Here's a tiny sample of silently changed runtime behavior (aka what your call "cowboy attitude") that would require introducing duplicate modules, renaming $, /, => etc:

# `=>` would need to be renamed or sugar2 would need to be introduced:
import std/sugar
proc bar(a: float, fn: proc(x: float, y: int): string): auto = 1
proc bar[T](a: T, fn: proc(x: int, y: int): string): auto = 2
echo bar(5.0, (x, y: int) => $(x, y)) # since 1.4: prints 2, until 1.2: prints 1

# `$` would need to be renamed because of changed behavior:
echo $(int,).type # 1.2: (int); 1.4: (int,)

# type conversion would have to be deprecated:
let a = not 0'u64
echo int64(a) # 1.2: prints -1; 1.4: RangeDefect

# json would have to be deprecated:
import std/json
let a = 18446744073709551615'u64
echo %(a) # until 1.2: prints -1; 1.5.1: prints 18446744073709551615

let a = [NaN, Inf, -Inf, 0.0, -0.0, 1e20, 1.5]
let j = %a
echo $j # prints json that is invalid according to spec (fixed in 1.5.1 https://github.com/nim-lang/Nim/pull/18026)

# macros would have to be deprecated because of newLit:
import std/[macros,typetraits]
macro fn(): untyped =
  result = newLit((1, 2))
echo fn().type.isNamedTuple # 1.2: true; 1.4: false (refs 408518c9fed9757fd1cc246e3ced05ecbb3bcaab)

# std/os and `/` would have to be deprecated
import std/os
echo isAbsolute("/foo") # 1.2 (nim js): false; 1.4: true

echo "abc" / "/" # 1.0: abc; 1.2: abc/ (fixed in https://github.com/nim-lang/Nim/pull/13467)
echo "abc" / "" # 1.0: abc/; 1.2: abc

# relativePath(rel, abs) and relativePath(abs, rel) used to silently give wrong results (see #13222); instead they now use `getCurrentDir` to resolve those cases, and can now throw in edge cases

# plus lots of other examples of (silent) changed behavior in each release in std/os

How do you know that old code in a foreign Nimble package needs inspection? You compile it as your main project and look at the deprecation warnings.

and what good does it do when you have no control over it and your dependencies generate thousands of warnings due to this policy which would cause most of stdlib APIs/modules to be deprecated?

The fallacy here is that you can somehow secretly "fix" code that used the stdlib by changing the stdlib's behavior. This is only important for security related issues.

The fallacy here is because of transitive dependencies, anything can be considered a "security related issue". Mission critical projects should have proper tests in place when updating dependencies including upgrading to a new compiler release.

juancarlospaco commented 3 years ago

I think the only solution is to hurry up Nim 2.0. so all Deprecated can be removed, all Experimental can be default, all breaking Fixes can be implemented...

timotheecour commented 3 years ago

I think the only solution is to hurry up Nim 2.0.

this won't solve the underlying problem, if a bug is discovered in 2.0, I don't want to wait 10 years until 3.0 comes out. Hence the position I'm articulating in the "stdlib policy" PR in favor of fixing bugs, not working around it.

juancarlospaco commented 3 years ago

I agree, but I think about the way that fixes/improvements gets actually accepted and merged... 🤷

ringabout commented 3 years ago

Hence the position I'm articulating in the "stdlib policy" PR in favor of fixing bugs, not working around it.

Make sense. Alas, I wish no PR would be reverted(Or at least providing the alternative way right after reverting) even if the new policy were to be merged.

juancarlospaco commented 3 years ago

This only shows people using the language wrong, they are never asserting inputs, nor outputs, thats basic primitive type assertiong checking, with or without a revert of any fix their code is still very fragile, but the language currently has nothing to offer to improve on that.

DrNim would be perfect, but it wont work currently, and no one knows when it will work realistically.

Testament/unittest is awesome, but tests the whole thing, so maybe test pass even with wrong inputs and outputs, and very often input is hardcoded in the test itself.

DBC module on stdlib can catch that, theres no way it dont, if this were Ada the DBC would catch it, theres literally an ISO standard and books about this, this has been fixed decades ago.

If you dont add DBC people keep inventing random "aliases" into unittesting tools, like err, oks, fail, skip, ignore, pass, disallow, falsify, checkNot, etc.

Documentation then strongly recommends using DBC for all core/serious/mission critical code. DBC can be enabled/disabled with a define flag -d:nimNoDbc or -d:nimDbc or similar, to get performance back. The DBC module can be Deprecated when DrNim works out-of-the-box for all OS/Arch.

See

ringabout commented 3 years ago

Not needed anymore IMO :)