Re: Pass compiler options to file compiled with the compile pragma

2020-07-03 Thread demotomohiro
It is simpler than my code, but there is a problem in your code when you want 
to link multiple C files. If 2 C files have same name static variables outside 
of function, it would generate redefinition error. In C++, redefinition error 
will be also generated when 2 C++ files have same name variables or functions 
in anonymous namespace. You can avoid such error by creating 1 nim file per 1 
C/C++ files, but I don't think that is not good idea when you want to use many 
C/C++ files.

And when you got compile error from C/C++, line number in the error doesn't 
match line number in C/C++ files. I think you can fix it by emitting #line N 
before C code.

My code might looks like complicated but linkModule template can be placed in 
other module and be used in elsewhere without copy it.

For example: test1.c 


static int x = 1;

int getX1() {
  return x;
}


Run

test2.c 


static int x = 2;

int getX2() {
  return x;
}


Run

testc.nim 


const
  Ccode1 = staticRead"test1.c"
  Ccode2 = staticRead"test2.c"

{.emit: Ccode1.}
{.emit: Ccode2.}


Run

sample.nim 


import testc

proc getX1(): cint {.importc.}
proc getX2(): cint {.importc.}

echo getX1(), getX2()


Run


Re: Why does wrapping the code in a top level procedure make it faster?

2020-07-03 Thread cblake
In my experience batteries are almost never fully charged and it's hard to get 
feedback if you only release a perfect cathedral. With no feedback, it's kind 
of unlikely you will build something popular.

To take a topical example, even after 30 years of tuning, the core language 
`dict` in Python still seems to have no easy way to "pre-size" an instance. 
There is no power of 2, no `rightSize`, nuthin'. So, one of your rough edges 
literally cannot arise due to an inflexibility/API design flaw (IMO).

Yes, there must be 3rd party replacements or ways to pre-size in some slow 
subclass or whatever, but you could also just write a `proc initTab` that 
always calls `rightSize` for your own code. What's at issue here is "out of the 
box" (and personally I think the Nim workaround is less onerous than 
workarounds in the Python world).

Do you have to learn your way around? Sure. A sales pitch of "just like Python 
but compiled/with native types" is definitely not quite right. That's 
Cython/similar. Analogies and oversimplifications help some people while others 
want the real story, and you strike me as the latter.

Nim gives programmers more power and flexibility, but responsibilities also 
come with that. Cue Spider Man's Uncle Ben. ;-) It is yet a comparatively tiny 
community. Bugs, workarounds, and rough edges come with that. Unused patterns 
like your `&` are just unused, unoptimized things waiting to be 
discovered/fixed. There was a time when `C` had no proper namespacing of struct 
members and that is why some core Unix types have prefixes like the `st_*` in 
`struct stat` (run "man 2 stat" on any unix machine).

No one using Nim _wants_ it to be hard, but there may also be good reasons some 
things are the way they are (often flexibility, performance, or safety, but yes 
sometimes neglect). I'm really sorry to hear your entry has been tough, but the 
flip side of that is you could probably make a HUGE impact to future 
similar-but-not-quite-you's, and I again encourage you to try! Even just 
documenting everything somewhere would probably help at least a few other 
Python Refugees. :-) Cheers and best wishes whatever you decide.


Generic function resolution

2020-07-03 Thread kartiku
Can someone explain this


type A* = object of RootObj
  p: int

type
  B* = object of A
q: int

type
  C* = object of A
r: string

proc g[T](d: string) =
  when T is B:
let l = (first: -1, last: 0)
  
  echo(d.substr(l.first, l.last))

proc g[T]( k: string, d: string) =
  let l = (first: -1, last: 0)
  echo(d.substr(l.first, l.last))

proc f[T](d: string): seq[T] =
  result = newSeq[T]()
  g[T](d)
  echo "g[T]( d)"

proc f[T](k: string, d: string): seq[T] =
  result = newSeq[T]()
  g[T](k, d)
  echo "g[T](k, d)"

let d = ""
let t  = f[B](d)
let sk = f[C]("SM", d)


Run

Compiling gives


d:\temp\test2.nim(34, 11) template/generic instantiation of `f` from here
d:\temp\test2.nim(24, 7) template/generic instantiation of `g` from here
d:\temp\test2.nim(16, 17) Error: undeclared identifier: 'l'


Run

The second call to f resolves to the first definition of f with arity 1 instead 
of the second one.

Changing the first definition of g to


proc g[T](d: string) =
  echo(d)


Run

makes it compile


Re: Why does wrapping the code in a top level procedure make it faster?

2020-07-03 Thread HashBackupJim
Another lesson: when batteries are included, make sure they are fully charged.


Re: Why does wrapping the code in a top level procedure make it faster?

2020-07-03 Thread Araq
Whenever you leave the C code that Python happens to use like actually looping 
over an array it's slow. Python is in fact an _abstraction inversion_ , 
primitive operations are slow and high level operations are fast (because they 
can avoid Python's slow VM) and so you need to use premade libraries for 
everything. In Nim it's the opposite, primitive operations are fast and custom 
code wins over premade libraries quite often because custom code is specialized 
for the task at hand.

**That 's the fundamental lesson to learn when coming from Python.**

Surely we can and will improve the rough edges you encountered but it won't 
change the fundamental lesson.


Re: Why does wrapping the code in a top level procedure make it faster?

2020-07-03 Thread cblake
As the person who added `rightSize`, I agree with those points actually. The 
only slight gotcha is that doing as you suggest would result in old code that 
calls it with (the correct power of two to avoid a resize, or maybe already a 
`rightSize`) would allocate the _next higher_ power, wasting space. That might 
be a price worth paying ease-of-use wise.

We _could_ only call `rightSize` if the argument is not a power of two, but 
it's probably simpler to just always use `rightSize` and just tell people. 
Wasting space isn't quite a "failure". Anyway, you should feel free to file a 
github issue and/or work on a pull request. Maybe I should have pushed harder 
at the time to change the parameter semantics.

There is definitely a learning curve coming from other languages. There are 
definitely more examples of the stdlib not providing the "best in class" for 
various functionalities. There will likely be even more with time. Nim core 
only has so much manpower to write/maintain/support such things.

We discussed doing a "distribution" over at 
[https://github.com/nim-lang/RFCs/issues/173](https://github.com/nim-lang/RFCs/issues/173)
 and elsewhere. This "fusion" repository (see end of that thread) seems to have 
been the output, but not much has been happening over there.

Anyway, I think if you persevere there are good chances you could be a happy 
Nimmer once you've learned your way around. You seem pretty capable of finding 
these issues and you could probably help round some of the sharp edges, if you 
can sustain the patience. There is probably a Porting to Nim From Python 
guidebook or maybe you could help do one!


Re: Why does wrapping the code in a top level procedure make it faster?

2020-07-03 Thread HashBackupJim
My experience so far with Nim is that there are a lot of "sharp and unexpected 
edges". Some examples:

  * 100M loops of s = s & 'x' is 8100x slower than s.add('x'). Response is 
"nobody writes this way". If something is 8100x slower, IMO it shouldn't be 
allowed at all.
  * 100M loops of s.add('x') uses 325MB of RAM
  * Nim stdlib code is not compiled in production mode unless my code is also 
in production mode, so even simple timing results like hashing data in a loop 
make no sense
  * tables are documented to require a power of 2 size _in the current 
implementation_. The documentation for initTable says to use nextPowerOfTwo or 
rightSize to specify the default size. What if the next implementation of 
tables requires the table size to be a prime number? The only correct, portable 
way to specify a table size is with rightSize, and IMO, that should be done 
inside initTable rather than expecting users to know implementation details. 
Users only know how many things they want to put in a table.
  * if you use a table size not a power of 2, it asserts - fine. But if you 
compile with -d:danger, it loops forever. Another reason initTable should 
always use rightSize on the initialSize arg.
  * everything having value semantics "for consistency" is a little on the 
strange side IMO. My guess is that when someone uses arrays, tables, and 
sequences, it is because they have a largish collection of data. If that gets 
passed around, it is being copied every time. I don't think most people would 
know or expect this, and since the program still works, their conclusion might 
be "Nim is slow".
  * a related problem: I wrote an array sort test with a small 100-element 
array. Very simple, worked great. Then I changed the array size to 10M and it 
fails with "Illegal instruction: 4", I'm guessing because of a stack overflow. 
Changed it to a ref array, added a new stmt, then the high function no longer 
worked (changed that to a constant), then the sort function no longer works 
(have to add a cmp proc), etc. It feels like a bit much just because an array 
size was increased.
  * the standard db_sqlite module is 30% slower than Python on a small test 
script because it doesn't cache prepared SQL. Another guy (tiny_sqlite) added 
that recently and his lib runs 3x faster than Python for the same test. Users 
of a language need to feel like the tools the language provides are "best in 
class" rather than providing minimal required facilities.
  * the SomeInteger type is compatible with all integers. But an int64 can't be 
passed to an int type, even on a 64-bit system.
  * requiring 10'i64 or 8'i32 on integer constants is unwieldy and ugly, and 
it's in Nim system code too.



I'll admit people (including myself) are not great at writing or understanding 
the results of benchmarks. If the language environment has rough edges like 
this, it turns every small test into a head-scratching exercise. And after a 
few weeks of this, it leaves one with the feeling "If I can't even get simple 
10-20 line programs to do what I want, does it make sense to use this for a 
200K line program?" It makes me feel a bit like "to drive this car, I have to 
become a mechanic and know how to rebuild it first or I'll crash".

When Nim works, is fast, and uses reasonable amounts of memory, it's fantastic 
and I love it. But getting there seems more difficult than it should be.


Re: Why does wrapping the code in a top level procedure make it faster?

2020-07-03 Thread cblake
Ah. Sorry that I misunderstood the sarcasm. Maybe my content/xref was not 
worthless anyway. ;-)


Re: Why does wrapping the code in a top level procedure make it faster?

2020-07-03 Thread Stefan_Salewski
> The idea of defaulting to an optimizing mode has come up before.

Sorry, maybe I made my statement not clear enough: Of course it is fine that 
default compiler mode is to use runtime checks and do not use -d:danger. 
@didlybom asked to generate an automatic main function so that newcomers making 
a benchmark would get good results. But generally these newcomers also forget 
to compile with -d:release, and also these newcomers often use too many ref 
objects, too many string allocations and other slow stuff.

So the recommendation is: Before making benchmarks or videos read a tutorial. 


Re: Connection-Pooling Compile-Time ORM

2020-07-03 Thread JohnAD
Can't help myself:


const data: SqlQuery = sqls:
  SELECT Pet.name
  FROM Pet
  INNER JOIN House
ON House.id = Pet.houseId
  WHERE House.country = "CA"


Run

That would be really clean.


Re: Connection-Pooling Compile-Time ORM

2020-07-03 Thread JohnAD
To followup on @Variount 's question. Is the ORM goal to be something like:


type
  Pet = object {. tableName: "pets" .}
name: string
age: int {. fieldName: "age_years" .}

const data: SqlQuery = sqls:
  select Pet.*
  `from` Pet
  where Peg.age = 4


Run


Re: Connection-Pooling Compile-Time ORM

2020-07-03 Thread JohnAD
As to case, in this particular application, one could have a version that 
supports the typical styling guidelines for SQL statements.


SELECT blah FROM xyz WHERE zin=4


Run

could be DSL'd as:


const data: SqlQuery = sqls:
  SELECT "blah"
  FROM "users"
  WHERE "zin = 4"


Run

It would avoid the use of backticks and have a certain visual appeal to SQL 
coders.

But then you would be violating nim's case guidelines. Sometimes one can't win. 
:-) 


Re: Why does wrapping the code in a top level procedure make it faster?

2020-07-03 Thread cblake
@Stefan_Salewski \- The idea of defaulting to an optimizing mode has come up 
before. It seems contrary to what almost any user coming from a "compiled 
language" world would expect. For various reasons (debugability, compile-time 
performance, etc.), almost all compilers default to a non-optimized (or very 
weakly optimized) output and allow various knobs to crank up optimization, as 
does the current `nim` setup.

There is even a whole dark art of "best optimization flags" which can be taken 
[to severe extremes](https://github.com/Acovea/libacovea). More 
simply/efficiently/some might say intelligently, you can often use PGO 
[https://forum.nim-lang.org/t/6295](https://forum.nim-lang.org/t/6295) to get 
1.25..2.0x boosts on object code generated from nim-generated C. Some flags 
like `-ffast-math` can even change semantics in subtle ways that can impact 
correctness or not depending on use cases.

I don't know what to do about people publicizing misleading benchmarks. That 
seems an ineliminable hazard, not only for Nim, but actually **_everywhere and 
all the time_** , and often not on purpose (though most "marketing" wants to do 
strawman comparisons on purpose). Besides compiling wrong, they could also use 
a bad algorithm, misrepresentative inputs, weird machines, benchmarks naive 
relative to intended measurements, and probably several other mistake 
categories. :-/ The best strategy may be trying to be supportive where & when 
we can, educating as we go, though I agree/admit that is a neverending battle.


Re: Pass compiler options to file compiled with the compile pragma

2020-07-03 Thread mratsim
You can probably do something like this


# ccode_wrapper.nim

const Ccode = staticRead"ccode.c"

{.localPassC:"-DMY_CUSTOM_FLAG".}
{.emit: Ccode.}


Run

i.e. replace the direct C file compilation by staticRead + emit + localPassC.


Re: Why does wrapping the code in a top level procedure make it faster?

2020-07-03 Thread Yardanico
We also should ask people to use the main proc even when they do when 
isMainModule, because that doesn't introduce any new scopes, so we should 
advise to do something like 


proc main =
  echo "hi"

when isMainModule:
  main()


Run


Re: Why does wrapping the code in a top level procedure make it faster?

2020-07-03 Thread Stefan_Salewski
> whenever somebody does a benchmark

So should we make -d:danger the default just for the case a fool makes a 
benchmark?

Global code is generally used only for short scripts where nobody really cares 
for performance. And the benefit of a main proc is explained in most tutorials, 
it is in section 
[http://ssalewski.de/nimprogramming.html#_scoping_visibility_and_locality](http://ssalewski.de/nimprogramming.html#_scoping_visibility_and_locality)
 in my book.


Re: Why does wrapping the code in a top level procedure make it faster?

2020-07-03 Thread didlybom
OK, it sense to focus on the higher impact optimizations. That being said, this 
is one of those things that are mentioned whenever somebody does a benchmark, 
so it might be good to optimize it at some point.


Pass compiler options to file compiled with the compile pragma

2020-07-03 Thread demotomohiro
There is still no way to pass C/C++ compiler flags to only c/c++ files added by 
compile pragma. Related RFC: 
[https://github.com/nim-lang/RFCs/issues/137](https://github.com/nim-lang/RFCs/issues/137)

I found workaround that create static link library from the nim file that only 
has passC and compile pragma so that passC affect to only c/c++ files specified 
in that nim file.

Because of this bug, this code works only in devel Nim. 
[https://github.com/nim-lang/Nim/issues/12745](https://github.com/nim-lang/Nim/issues/12745)

For example:

test.h 


int getX();


Run

test.c 


int getX() {
  return MY_X;
}


Run

testc.nim 


# Pass C compiler options only to test.c
{.passC: "-D MY_X=123".}
{.compile: "test.c".}


Run

testmod.nim 


import os, strformat, std/compilesettings

proc getX*(): int {.header: "test.h".}

template linkModule(modulePath, nimCmd = "c", nimOptions: static[string] = 
"") =
  static:
doAssert nimCmd == "c" or nimCmd == "cpp"
  
  block:
const
  modName = splitPath(modulePath).tail
  libpath {.inject.} = querySetting(SingleValueSetting.nimcacheDir) / 
modName & ".staticlib"
  modulePath2 {.inject.} = modulePath.quoteShell
  nimCmd2 {.inject.} = nimCmd
  nimOptions2 {.inject.} = nimOptions.string

{.passL: libpath.}
# `--os:any --gc:arc -d:useMalloc` is used to avoid compiling and 
linking stdlib_io.nim for faster build.
# It doesn't affect this module.
const cmd = fmt"{getCurrentCompilerExe()} {nimCmd2} --app:staticlib 
--os:any --gc:arc -d:useMalloc --out:{libpath.quoteShell} {nimOptions2} 
{modulePath2}"

static: echo cmd
const ret = gorgeEx(cmd)
static: echo ret.output
when ret.exitCode != 0:
  {.error: "Abort due to compile error".}

linkModule "testc.nim"


Run

sample.nim 


import testmod

echo getX()


Run