Nim recently added long awaited overloadable enums, that was huge improvement. Wonder if there any hope to also evolve syntaxes, make it more expressive?
# Destructors
let { id, name } = person
let [first, rest..., last] = [1, 2, 3, 4, 5]
# Shortcut for object creation, instead of { name: name }
let another_person = { name }
# Interpolate without fmt
echo "Hello {name}"
# Multiline strings
text "Hello,
world!"
# Shortcut for callbacks, I know about the `it` macros, the point is that it should work for any proc
let names = list.map .name
let jim = list[.name == "Jim"] # expanded as list[(v) => v.name == "Jim"
# Multiline (x) => or do (x) without auto, it's very annoying to type do (a:auto, b:auto) -> auto
# Postfix if
count += 1 if increment
# Loop comprehensions and postfix loops
squares :=
for v of list
v * v
squares := v * v for v of list
# Inline enums, no need to declare separate EventKind type
type Event = object
kind: enum click, up, down
callSomeProc(addr(SomeType(x: 0, y: 0)))
var foo = Foo(arrayMem[0]: 0, arrayMem[1]: 1)
Probably more common gripes if you're interfacing with a lot of C code but I encounter these situations enough to make me wish Nim had some of the syntactic sugar introduced in more recent C language specifications.
So, for you this
echo list.map do (a:auto, b:auto) -> auto:
a + b
echo fmt"Hello {name}"
is more readable than
echo list.map do (a, b):
a + b
echo fmt"Hello {name}"
Let's gloss over something = 1 if expr or s := x * x for z of y like that makes any sense.
I personally find
echo list.map(`+`)
echo "Hello ", name
much more readable, if only we could solve the procvar issue. JavaScript is a weakly typed dynamic language so syntax like let another_person = { name } or whatever, makes sense. Nim is strongly typed for a reason and that is so that the compiler will catch errors at compile time if I fuck up.
What is {name} in the example above? What is the expected behavior here? In JS, since almost everything is an object, this is easy to grok but in a language that is strongly typed, it is much less obvious what the intent of this code is.
I meant code like this:
type Person = object
name: string
group: string
let name = "Jim"
let a = (name: name, group: "a")
# let a = (name, group: "a")
let b = Person(name: name, group: "b")
# let b = Person(name, group: "b")
I forget to mention the do notation, wold be good to improve it so it would be
sort(cities) do (x,y):
cmp(x.len, y.len)
and not
sort(cities) do (x,y: auto) -> auto:
cmp(x.len, y.len)
What is the reason for colon here? Because it will match val name in 95% cases
Person(group)
Person(group, name) # still Person(name:name, group:group)
### it is ok, because looks like it is not supported at the moment anyway
but if if want to swap it - rare case - old syntax still works
Person(name:group, group:name)
I have some objections to the idea of positional constructs, which I posed in https://github.com/nim-lang/RFCs/issues/418. The proposed solution mentions that RFC, even though its scope is different. #418 only suggests allowing name-matched construction, which is not the same as positional construction.
Please, join the discussions on GH.
I strongly oppose to the suggestions made in the original post, because they are too ambiguous & unclear.
For example, multi-line strings via normal double quotes just reminds me of how fucked up Bash works. A normal string should be double quoted, a multi-line, huge one, with other implications, like the ability to include unescaped quotes, etc. should remain triple double-quoted.
Interpolation without & creates more problems than it fixes. Sure, it is more compact & you don't have to prepend that & to the string, but now you have to look out for additional potential pitfalls. For example, what if I need those braces, without expansion? Do I need to escape them then? I try to avoid backslashes as much as possible. It makes everything less readable.
The best thing that was suggested in this thread is this.
callSomeProc(addr(SomeType(x: 0, y: 0))
I'm yearning for this since a long time. It's really annoying to do all this additional, purposeless code cluttering. The with module is in that regard also very weak. It often doesn't work & has its own issues.
I wish, the standard library would provide functionality, to make the following possible.
let obj = SomeObj().edit:
field1 = "myField"
field2 = -1
field3 = false
assert not obj.field3
It's possible to create something custom for it (which I have done), but this implies manual management of this custom addition made by me. If the standard library would provide it, it would just be there, available everywhere, for everyone.
And fix annoying inconsistencies like this one, sugar macro can't handle void return type. play
import std/sugar
proc on_click(fn: proc(event: string): void): void =
discard
on_click((e) => discard)
the problem is you need wrap discard in paranthes
import std/sugar
proc on_click(fn: (string) -> void): void =
discard
on_click((e: string) => (discard))
but o found more annoying that you need specify auto type
import std/sugar
proc on_click(fn: (string) -> void): void =
discard
on_click((e) => (discard)) # this wont work
One more inconsistencies, inability to resolve (overload) property attr and proc with same name.
import std/tables
type Element* = ref object
tag*: string
attrs*: Table[string, string]
proc attrs*[T](self: T, attrs: tuple): T =
for k, v in attrs.field_pairs:
self.attrs[k] = $v
return self
proc h*(tag: string): Element =
Element(tag: tag)
echo h("dif").attrs((class: "some"))[]
This is explicit design fields are always preferred to procedures when used in dot expressions, this gives an escape if one needs it.
import std/tables
type Element* = ref object
attrs*: Table[string, string]
proc attrs*[T](self: T, attrs: tuple): T =
for k, v in attrs.field_pairs:
self.attrs[k] = $v
return self
proc h*(tag: string): Element =
Element()
static: assert compiles(attrs(h"div", ("some", ))[])
static: assert not compiles(h("div").attrs((class: "some"))[])