Open timotheecour opened 3 years ago
One downside that I can see is this RFC is in direct contrast to https://github.com/nim-lang/RFCs/issues/257 (which was accepted).
If you handle const x = MyRef()
like template x(): untyped = MyRef()
, what's the gain? People can write the template
version too. Programming languages are designed to catch mistakes, if you create a programming language where everything compiles ("this must work, think about generic programming!"), you created a programming language that catches no mistakes.
If you handle const x = MyRef() like template x(): untyped = MyRef(), what's the gain? People can write the template version too.
that's not the same at all.
The point is this feature allows caching the result of a potentially expensive computation or something that should be computed just once for correctness.
with https://github.com/nim-lang/Nim/pull/15528 this works:
import std/json
proc fn(file: static string): JsonNode =
echo "parsing " & file
const s = staticRead(file)
result = parseJson(s)
const j = fn("/tmp/myconfig.json") # fn will be called just once
proc main() =
# CT code can now use `j`, eg:
const version = j["version"].getStr
const name = j["name"].getStr
# RT code can also use `j`, eg:
echo (version, name)
echo j.pretty.static
echo j["name"].getStr.static
echo j["version"].getStr.static
main()
I don't see how template x(): untyped = MyRef()
would help in any way.
I'm using this to great effect in my own patched nim.
And how do you "cache" it? Previously you claimed it doesn't affect GC safety.
@Araq
And how do you "cache" it? Previously you claimed it doesn't affect GC safety.
no contradiction; it's cached in the same way it's cached for non-ref types, after const j = expr
is evaluated (once), the result is stored in j
'ast as a PNode.
Subsequent const accesses (eg const version = j["version"].getStr
) use j
directly, while subsequent runtime accesses paste the resulting PNode litteral as if it was inlined in the code, eg:
echo j["name"].getStr.static
# is same as:
const tmp: string = j["name"].getStr
echo tmp # the `tmp` string gets pasted where it's used, as if the code was: echo "foobar"
when true:
type Foo = ref object
n: int
lhs, rhs: Foo
proc fn(n: int): Foo =
result = Foo(n: n)
if n > 1:
if n mod 2 == 0:
result.lhs = fn(n div 2)
if n mod 2 == 1:
result.rhs = fn((n+1) div 2)
const j1 = fn(20)
let j2 = j1
# same as if we inlined the following
# let j2 = Foo(n: 20, lhs: Foo(n: 10, lhs: Foo(n: 5, rhs: Foo(n: 3, rhs: Foo(n: 2, lhs: Foo(n: 1))))))
# `fn` is not re-evaluated here
Thanks. It's growing on me...
It is in direct contrast to #257 (which was accepted) so maybe for #257 we need a .section(readonly)
pragma. In fact, .section
would be the last missing feature to get us to competitive for embedded programming.
Maybe I'm missing something, but isn't the point of const
not to inline the expression, but to precompute it at compile time and then inline the result or store it in the executable? Or is this more like Rust's lazy_static
, which computes it at runtime, but only the first time it's accessed? (In the latter case, that seems inconsistent with how const
works for other expressions.)
const a = foo()
doesn't codegen a
, only a RT use of a
will codegen, eg:
type Foo = ref object
x1: int # etc; can contain other nested ref types
proc foo(n: int): Foo =
# potentially expensive computation (potentially non-pure or with CT side effects)
const a1 = foo(3) # a is not codegen'd but computed at CT; foo(3) is computed just here
const a2 = a1.x1 # foo() is not recomputed here
let a3 = a2 # only a2 is codegen'd here; or, more directly, `let a3 = a1.x1.static`
const a4 = foo(4) # foo(4) computed just here
let a5 = a4 # a5 is codegen'd, using a literal, eg as if `let a5 = Foo(x1: ...)` was written
This simply extends const to work at CT, it otherwise works the same as types that don't contain (nested) ref
fully implemented in PR https://github.com/nim-lang/Nim/pull/15528, see PR for details, tests, examples, and discussion. (RFC was requested here: https://github.com/nim-lang/Nim/pull/15528#issuecomment-706489325)
highlights under this PR:
const a = expr
now works even if expr (recursively) contains some ref type, instead of giving CT errora
isn't codegen'd, but its value is pasted in each context where it's used at RT, consistent with current behavior with non-ref typesimport tables; const a = {1: "1", 2: "2"}.newTable
now worksprecedent in other languages
enum
(manifest constant, not codegen'd but pasted in each runtime context where it's used) andstatic const
(codegen'd in ROM memory, not pasted). This PR makes nim'sconst
work the same as D's enum (which also works with ref types)