I think procs could be improved, have better type infer. And to my understanding it shouldn't harm the compilation time and don't require reverse-type infer.
Let's consider analogy, imagine that let a = 1 wouldn't work and you had to use something like:
import std/var_sugar # a special infer macro `:=` for assignments
let a := 1 # a convenient `:=` macro
let b: auto = 1 # you have to use `auto`, you can't use `let b = 1`
And, infer macro somtimes work, somtimes not:
let a := 1
let b := a # Error, some sort of closure in A somehow doesn't match expectation of B
So you had to use:
let a {.some_whatever_closure.} := 1
let b := a # Good, now it works
That's how procs in Nim are working now, the need to import std/macros, need to use auto, the () => failing because of some clousure expectation doesn't match.
The do blocks is a good idea and are needed, but in its current form it's unusable, and so you are forced to use template when it's not needed, and plain proc would do much better. When tepmlates used when they shoudln't be used - it produce all sorts of template related probmlems and complexities - template overload with typed/untyped nuances, attrs default, attrs used as literal in the body you sometimes didn't notice and it causes tricky bugs, template visibility scope nuances, and so on, when plain proc would have none of that.
The need to specify the scope for () => is annoying. You write a tiny code like on_event (e) => counter.inc, wait 17 sec for it to compile, and see error proc {.scope.} X expected but the arg Y has type of Z, so you change it to on_event proc (e: auto) = counter.inc and had to wait for another 17 sec for it to compile again.
I think procs are as important and fundamental as vars and assignment, and should have same support and convenient usage.
This should work:
type Event = object
proc on_click(fn: proc()) = discard
proc on_click(fn: proc(e: Event)) = discard
on_click => echo e
on_click proc = echo e
on_click do:
echo e
on_click (e) => echo e
on_click proc (e) = echo e
on_click do x:
echo e
Different projects require different tools.
Approaches used for 1) Low-Level and System Programming are different from 2) High-Level and Application Development.
Nim community is heavily biased towards 1 kind of projects. And topics like Zero-Overhead, GC, Soft-Realtime, etc. cause lots of discussion in this forum.
Things important for App Dev are different, things like Time to Marked (Simplicity and High Productivity, you had to develop and launch the App before your competitors do), Low Risk and No Surprises (it's ok if App going to consume x2 more memory or even x4, it's not ok if it's going to crash or behave unpredictably), etc.
I think things needed for App Development are neglected by Nim community.
Like this topic, about simplification of proc is exactly about that. From the point of System Programming, it contributes nothing, but it contributes a lot if you look at it from the point of App Development.
but it contributes a lot if you look at it from the point of App Development
Nah, you're just obsessed with syntactic details and you try to justify it retrospectively. It's a very human thing to do but it's completely unconvincing to me.
I do want to complain about something. The () => syntax doesn't work very well with calling conversions. Consider
{.emit: """
int f(int(*g)(int), int x) {
return g(x);
}
""".}
import sugar
proc f(g: (cint {.cdecl.} -> cint), x: cint): cint {.importc.}
echo f((x: cint) {.cdecl.} => x + 1, 1)
In the above example, {.cdecl.} must be specified and cannot be omitted, otherwise one'll either get
Calling convention mismatch: got '{.nimcall.}', but expected '{.cdecl.}'.
or
error: conflicting types for 'f'
...
error: passing 'tyProc__pe5J9c4RjLZ4OoV14kmcwNw' to parameter of incompatible type 'int (*)(int)'
However, () => with pragmas looks not very pretty. I know that due to some limitation calling conversions cannot be inferred from expected types, but I hope it could be written as:
proc f(g: (cint)<cdecl> -> cint, x: cint): cint {.importc.}
echo f((x: cint)<cdecl> => x + 1, 1)
So write it as:
proc f(g: proc (x: cint): cint {.cdecl.}, x: cint): cint {.importc.}
echo f( proc (x: cint): cint {.cdecl.} = x + 1, 1)
It's hardly any longer for your example.
when you really need it, it's not ok to use it everywhere.
So put it in a helper module.
The question is why you need macros and AST magic, for trivial thing, passing a proc to function.
Well as you demonstrated you do not. You do not like the status quo, which is fine. I just provided a way that enables using => in the present state of Nim, whilst being very reusable(Although there is an issue with me using coll).
Simple things should be simple
To play devil's advocate. Is the language not simpler if the syntax for declaring an anonymous procedure matches a top level declaration? It means you only have to learn a single syntax for how a procedure is declared. Much like the word anonymous they just are nameless. You are not really looking for simplicity, but terseness. This of course is not saying arrow operators should not be first class. I personally never use them myself as they are just annoyingly awkward. We already have anonymous procedures which work, I personally do not see much benefit in making them more terse.
type
Event = object
Event2 = object
proc on_click(fn: proc(e: Event)) = discard
proc on_click(fn: proc(e: Event2)) = discard
on_click (e) => echo e # Which `on_click` should be called?
on_click proc (e) = echo e
It seems allowing anonymous procs without parameter types doesn't work unless forbiding overloaded procs like this.
What if there are 2 overloaded procedures that take a proc with one parameter but different parameter type
Then it's not enough data for compiler to decide which one to use and human should specify argument types explicitly. But in most cases Nim compiler does know what code means, yet still annoys human and ask it to do machine job.
It seems allowing anonymous procs...
But it's allowed. I mean, this works:
import sugar
type Event = object
proc on_click(fn: proc(e: Event): int) = discard
on_click (e) => (echo e; 0)
The problem of "on_click (e) => echo e" is that it has void for return type, while `=>` uses auto in its implementation (It transforms the expression into something like proc (e: auto): auto = echo e). However, auto cannot be void (but can be anything else), so it never compiles. This means sugar needs a fix.This also failed:
on_click (proc(e: auto): (auto | void) = echo e)
mismatch at position: 1
required type for fn: proc (e: Event){.closure.}
but expression 'proc (e: auto): (auto | void) = echo e'
is of type: proc (e: GenericParam): auto or void
While this works:
on_click (proc(e: auto): void = echo e)
Now I think the compiler has a problem that it cannot recongnize proc(e: auto): auto = echo e and proc(e: auto): void = echo e are the same thing. The former is not deduced as the later.If on_click (e) => echo e or on_click proc (e) = echo e works on your code and you write it in many places, then new overloaded proc like proc on_click(fn: proc(e: Event2)) cannot be added because existing on_click proc (e) = echo e become 'ambiguous call' compile error. So it might prevent extending your code.
Also when you try to add a module written by someone to your code and it exports same name proc like proc on_click(fn: proc(e: EventXYZ)), it results in same compile error in your code.