Re: Pragmas Above Procs

2019-12-02 Thread dwin
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

2019-11-28 Thread mratsim
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

2019-11-27 Thread Araq
> 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

2019-11-27 Thread SolitudeSF
i know where pragmas should be, so i have 0 trouble looking for them. this is 
another nonissue.


Re: Pragmas Above Procs

2019-11-27 Thread demotomohiro
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

2019-11-27 Thread jjzmajic
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

2019-11-27 Thread cumulonimbus
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

2019-11-27 Thread Araq
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

2019-11-27 Thread jjzmajic
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

2019-11-27 Thread jjzmajic
@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

2019-11-26 Thread Araq
> 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

2019-11-26 Thread SolitudeSF
> is a far cry from this in readability terms:

it looks the same


Re: Pragmas Above Procs

2019-11-26 Thread jjzmajic
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

2019-11-26 Thread jjzmajic
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

2019-11-26 Thread cumulonimbus
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

2019-11-26 Thread Araq
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

2019-11-26 Thread enthus1ast
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

2019-11-26 Thread c0ntribut0r
Could be something like this, as ugly as original pragmas :>


foo(callback = [closure, gcsafe] proc() = echo "baa")


Run


Re: Pragmas Above Procs

2019-11-26 Thread enthus1ast
how would you then write inline pragmas? like:


foo(callback = proc() {.closure, gcsafe.} = echo "baa")


Run


Pragmas Above Procs

2019-11-26 Thread jjzmajic
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.