Re: Pragmas Above Procs
I've always felt that pragmas are awkwardly placed. For procedures they come after the signature. For fields, variables and objects, they come before the type. It's also possible to use some as standalone. It was a bit confusing at first. Lots of languages have similar constructs - decorators, metadata, annotations, attributes. They are generally used as a prefix. Although ,I think Nim gets much more use out of them compared to other languages.
Re: Pragmas Above Procs
To expand on cumolonimbus: Instead of having this {.push: thread.} proc worker(f: string, t: guarded ptr Table) = for line in f.lines: for w in line.split: let h = w.hash lock t.locks[h and (0x100-1)]: t.buckets[h and (0x1000-1)].inc(w) {.pop.} Run Having {.pushpop: thread.} proc worker(f: string, t: guarded ptr Table) = for line in f.lines: for w in line.split: let h = w.hash lock t.locks[h and (0x100-1)]: t.buckets[h and (0x1000-1)].inc(w) Run It can be called `once` or anything else. I guess this is similar to the `using` pragma as well.
Re: Pragmas Above Procs
> syntactical complaints are usually coming from people who written ~0 lines of > nim code. Usually but not always and I personally never got over C#'s `type variable` syntax even though I wrote much more C# code than Delphi code. Yes, really, I was a C# programmer. Nim happens to be more similar to Delphi and Python because I took syntax elements from languages with good ideas, not because "Araq was a Pascal programmer".
Re: Pragmas Above Procs
i know where pragmas should be, so i have 0 trouble looking for them. this is another nonissue.
Re: Pragmas Above Procs
Putting pragma above proc only makes sense when a pragma is important more than proc name or parameters but I think it is rare case. When I use a proc, proc name, parameters or return type is more important than importC, noSideEffect, raises, etc. There are many procs with pragma os module source code. [https://github.com/nim-lang/Nim/blob/devel/lib/pure/os.nim](https://github.com/nim-lang/Nim/blob/devel/lib/pure/os.nim) But in the document of os module, pragmas are omitted. [https://nim-lang.org/docs/os.html](https://nim-lang.org/docs/os.html) If you still think pragma should be above proc, I think it is possible to create a macro that adds pragma to a proc below. threadProc: proc worker() = ... withPragma(thread): proc worker() = ... Run I mainly used C++ language before I found Nim language. When I start writing Nim code, type name comes after variable/proc name syntax confused me. But I got used it now.
Re: Pragmas Above Procs
It's not that I don't want to see pragmas. I don't want to see them when I am not interested in them. They are important annotations, but annotations nonetheless. So when I want to zero in on the annotations I can, and when I wish to ignore them and look at the pure function signature, I can do that too. Either way, no time is wasted disentangling on from the other. Both the function and the pragma become easier to read. You can just as easily make the challenge above "Tell me the arguments and the return types of the functions as quickly as you can". You'll be able to do that faster in the 2nd example too.
Re: Pragmas Above Procs
The one that's a thread is the one that has a table parameter There are two not-entirely-orthogonal questions here: 1\. What's more important to emphasize? That's the thing we work on is thread code, oh, and it's a proc rather than a variable? Or that we're going to define a proc. Oh, and it's intended to run in its own thread?) Well, that obviously depends on your specific circumstances, and in the proc() case I might agree that the attributes (side effects, thread, async, ...) are as important as this being a proc, though no more important. In the case of variables, though - I totally disagree; the name is the most important thing (given that Nim chose suffix everything unlike C) -- see e.g. [https://github.com/captainbland/nim-validation](https://github.com/captainbland/nim-validation) for example (and there was a DAL/ORM with similar good use, but I can't find it now). I had used prefix annotations in C# when using WCF and stuff, and even people who like C# (I don't) said it sucked. So, unless you think you should have annotations before for procs and after for vars, I think after wins. 2\. What are you used to? Well, this is totally subjective; I'm used to both, and have no preference for procs (but the preference I stated above for vars) and do have a preference for uniformity. And I offer again: If "thread" or "async" is the important character - use a macro to make a thread_proc or async_proc the same way func makes a "nosideeffect" proc; no need for language changes for that. The only language change that might make sense that I can think of (and perhaps everyone happy) is {.next_is thread}, which is like {.push thread} wiith automatic pop at the end of the scope; But I really think that this discussion should only be carried if you've _tried_ to get used to suffix for a while and couldn't -- not because it looks weird at first. Have you tried?
Re: Pragmas Above Procs
Well, _if_ you want to highlight the "thread" aspect, then yes, putting it on its own line helps with that, no question about it. But when everything is `async` in your codebase anyway, the desire to stress it vanishes. The problem is not unique to Nim or to pragmas either, newcomers to Python also complain about the fact that doc comments are under the `def` declarations, splitting up the code unnecessarily and they too have a point. But in the end the older and less ambiguous syntax wins.
Re: Pragmas Above Procs
On that we agree. And in fact, I'm glad the community doesn't shy away from them. They're definitely a useful tool. I just wish this useful tool was easier to read. I hope you don't mind I stole some of your code for the example above. I understand we don't agree on how the problem should be solved, but can we at least agree that the second example above is more _expressive_ than the first, even if it is less _elegant_ in terms of implementation? Is there a way to deal with this that you find agreeable?
Re: Pragmas Above Procs
@SolitudeSF sure, until there is a substantial amount of code. 10 second pop-quiz, which of these is a thread proc: type Bucket = shared ptr BucketObj BucketObj = object next: Bucket counter: int word: array[0..30, char] Table = object buckets {.guard: locks.}: array[0x1000, Bucket] locks: array[0x100, Lock[0]] proc inc(b: var Bucket, word: string) = var it = b while it != nil: if strcmp(it.word, word) == 0: inc it.counter return it = it.next var x = allocShared0(BucketObj) copyMem(addr x.word[0], addr word[0], word.len+1) x.counter = 1 x.next = b b = x proc worker(f: string, t: guarded ptr Table) {.thread.} = for line in f.lines: for w in line.split: let h = w.hash lock t.locks[h and (0x100-1)]: t.buckets[h and (0x1000-1)].inc(w) var t: Table # results are stored here proc listing {.nothread.} = # no need to lock 't.buckets' here: for b in t.buckets: var it = b while it != nil: echo "word: ", it.word, " occurances: ", it.counter it = it.next proc setup() {.nothread.} = for i in 0 .. <0x100: t.locks[i] = initLock() setup() for s in walkFiles(paramStr(0)): spawn worker(s, addr t) sync() listing() Run Now let's do that again: type Bucket = shared ptr BucketObj BucketObj = object next: Bucket counter: int word: array[0..30, char] Table = object buckets {.guard: locks.}: array[0x1000, Bucket] locks: array[0x100, Lock[0]] proc inc(b: var Bucket, word: string) = var it = b while it != nil: if strcmp(it.word, word) == 0: inc it.counter return it = it.next var x = allocShared0(BucketObj) copyMem(addr x.word[0], addr word[0], word.len+1) x.counter = 1 x.next = b b = x +[thread] proc worker(f: string, t: guarded ptr Table) = for line in f.lines: for w in line.split: let h = w.hash lock t.locks[h and (0x100-1)]: t.buckets[h and (0x1000-1)].inc(w) var t: Table # results are stored here +[nothread] proc listing = # no need to lock 't.buckets' here: for b in t.buckets: var it = b while it != nil: echo "word: ", it.word, " occurances: ", it.counter it = it.next +[nothread] proc setup() = for i in 0 .. <0x100: t.locks[i] = initLock() setup() for s in walkFiles(paramStr(0)): spawn worker(s, addr t) sync() listing() Run My argument is not centered on aesthetics, but simple readability.
Re: Pragmas Above Procs
> As you said in another thread, pragmas are not something the compiler can > discard and still get the same thing implemented differently as they are in > other languages. Well, they are a necessary evil.
Re: Pragmas Above Procs
> is a far cry from this in readability terms: it looks the same
Re: Pragmas Above Procs
I get where you're coming from. But there is something to be said about being pragma-tic (heh). Rust (admittedly, not exactly a model citizen in terms of syntactic choices) has suffix types, and attribute and derive macros have been a part of the language basically from the beginning. It's just easier to tell what's what. This: proc tryInsertID*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): int64 {.tags: [FWriteDb], raises: [].} = ## executes the query (typically "INSERT") and returns the ## generated ID for the row or -1 in case of an error. ... Run is a far cry from this in readability terms: +[tags: [FWriteDb], raises: []] proc tryInsertID*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): int64 = ## executes the query (typically "INSERT") and returns the ## generated ID for the row or -1 in case of an error. ... Run
Re: Pragmas Above Procs
Are they implementation details? As you said in another thread, pragmas are not something the compiler can discard and still get the same thing implemented differently as they are in other languages. I can understad the drive for consistency however. There is a way to implement this in a way that is even more consistent however. Making pragmas operate on their current scope like so: proc foo(x: int): string = +[exportc: "int3_tostring"] ... Run That way its obvious what file level pragmas are. They are _inside_ the file, therefore they belong to the _scope_ of the file. The inline syntax would be clear in meaning too: foo(callback = proc() = +[closure, gcsafe] echo "baa") Run The rule is simple: apply to current scope. As things stand, pragmas that are a part of the function signature (but not the function content) apply to a function, while pragmas that are a part of the file _contents_ apply to the file. And the improvement in readability is still quite significant I feel. Compare: proc foo(x: int): string = +[exportc: "int3_tostring"] ... # or proc foo(x: int): string = +[exportc: "int3_tostring"] ... # vs. proc foo(x: int): string {.exportc: "int3_tostring.} = ... # or proc foo(x: int): string {.exportc: "int3_tostring.} = ... Run If anyone is skimming through a file, I doubt the last two options win out, especially as the number of arguments grows. Placing pragmas above is still better IMHO, and expressiveness is above elegance in the nim priority list, but ultimately you are the BDFL.
Re: Pragmas Above Procs
C# has prefix everything (types, annotations) because it inherits the C syntax Nim has suffix everything (types, pragmas). Python has it mixed because ... well, they didn't think about it at the same time, and added @ annotations as prefix and type annotations as suffix. But there's a sort-of-precedent in nim too, which might generalize: -`func` is `proc {.noSideEffecots.}` ; you could just make `aproc` or `asyncproc` into a `proc {.async.}` using a macro (maybe even a template), and get your prefix without having to modify the language.
Re: Pragmas Above Procs
As pragmas are little more than implementation details I don't agree that they should come first, on a separate line or not. It's an alien concept for Nim, Nim reads from left to right.
Re: Pragmas Above Procs
I must say I do not see the benefit. the pragma above the proc reminds me of this special python syntax, though
Re: Pragmas Above Procs
Could be something like this, as ugly as original pragmas :> foo(callback = [closure, gcsafe] proc() = echo "baa") Run
Re: Pragmas Above Procs
how would you then write inline pragmas? like: foo(callback = proc() {.closure, gcsafe.} = echo "baa") Run
Pragmas Above Procs
Hi guys, I've been keeping a eye on nim development for about a year now, but only got into using the language recently (about a month ago). There is one language feature don't think I will be able to get used to though --- the post-proc pragmas. And it seems I am [not the only one](https://github.com/nim-lang/RFCs/issues/15). They're very hard to read, especially skim so far on the right, and rather hard to type. I would like to implement one of these four option as a PR: [[async]] proc foo(x: int): string = ... +[async] proc foo(x: int): string = ... [.async.] proc foo(x: int): string = ... Run The last has the added advantage of already being reserved as tkBracketDotLe and tkBrackedDotRi, though I like it only slightly more than using curly braces above (which, in turn, I like far better than the current pragma position). +[async] is probably my favorite for reasons I bring up in the issue I linked to. I feel like it would be much easier for the maintainers to deliberate over several complete implementations as opposed to musings. However, I would very much appreciate any pointers to topic-relevant resources on the nim lexer + parser. Right now, I feel like I am trying to replicate behavior by sutchering copy-pasted code.