Re: How to use private variables when you put all types in one module
I don't think private variables on structs are worth it in general. I almost make everything public by default. I don't export low level functions though...
How to use private variables when you put all types in one module
Hi all, Nim experts says that if you faced recursive module dependency, you just put all your types in one module and import it to use in another modules. So this is my question. If you did so, how could you manage private properties ? If you add a star sign, it will be public and exported, but if you don't, then it is not visible in your module. I hope somebody can help me. Thanks in advance
creating a enum type after calling a macro on various modules
Hi again, i want to create a final enum after calling a macro that define a group with actions on various modules, something weird is that i need import the module where the enum is generated before other modules are imported, is there a way for create a macro that will be called after the group macro is called on every module?, Here is an example of what I mean: # Module where is the macro # Actually is implemented using const by incrementing a compileTime var from builder import group # Module A # Module A imports this module before Module B and Module C is imported group Window: Maximize Minimize Close FocusIM UnfocusIM # Module B group SubWindow: Elevate Resize Move # Module C group Color: discard group Layers: discard # Final Type, generated here, using other macro type Groups = enum gWindow gSubWindow gColor gLayers GroupsSet = set[Groups] Run
Re: What’s your favorite programming language and why?
HLA website contains some very good books about win api programming. I once tried to install HLA but for some reasons, it was not worked in my system. There was no way for communication without a forum.
Re: What’s your favorite programming language and why?
Hey, how do you create exe files from Kotlin source code ?
Re: What’s your favorite programming language and why?
1. Visual Basic.Net - For easiness 2. AutoIt - For power & convenience 3. Nim - For elegance, fun 4. D 5. Go
Re: Preview of Weave/Picasso v0.1.0, a message-passing based multithreading runtime.
It's similar to the Rayon or Tokio libraries from Rust. Weave provides a set of fundamental building blocks to create safe and high performance multi-threaded programs. Anyone who wants to write multi-threaded programs in Nim should be very excited about this. like Rust, Nim itself provides a minimal runtime (as it should be for systems languages), and allows libraries to provide fundamental features without modification of the compiler. This is actually a core philosophy of Nim. It has a small core but allows for large extensions (mainly through macros and a few other features.)
Re: Nim In Action worth it with 1.0
[https://www.reddit.com/r/nim/comments/df2t60/nim_in_action_worth_it_with_10](https://www.reddit.com/r/nim/comments/df2t60/nim_in_action_worth_it_with_10)/ another bot
Re: Introducing Synthesis: a Finite State Machine generator
@mratsim. You are awesome as always. Since the state machines can be nested, I wonder if this could be used to facilitate a heap free (and possibly stack free) async implementation, suitable for embedded. hrmm :-)
Re: Preview of Weave/Picasso v0.1.0, a message-passing based multithreading runtime.
Sorry, my question was basically: what does the word runtime mean? Does it replace certain operations in the executable that the Nim compiler produces by default, in that case, it's a low level integration with Nim, the compiler. So, what do the users need to do to use this multithreading runtime. It's likely I'm just misunderstanding everything. But could be that other nim users would be more enthusiastic when they understand what your project means. Thanks
Nim In Action worth it with 1.0
I'm thinking about getting a book about Nim... and as you all know there's only one. Nim In Action has been out a while; is it directly relevant to the 1.0 release? Does 1.0 have anything missing from the book? Would you recommend it?
Editor support for Nim 1.0 ?
Hello. I was tinkering with Nim in the past already and wanted to check out the 1.0 version. Unfortunately, plugins for almost all editors lags behind - I know that 1.0 is just 4 days old, but it seems that even 0.19.6 release doesn't have wide editor support. It looks like only Emacs (and maybe NPP) plugin is actively maintained. Maybe I'm missing something obvious... In what editor are you writing Nim today? Best regards
Nim is the friendliest language to start ?
I've been around writing relatively large applications in mainstream languages as well as personal projects in languages like Erlang, Ocaml, Rust, and Scheme. To sum it up, I'm a PL aficionado. It is very mandatory today that a good programming language is 50% about good tooling and ecosystem. For instance, I love Ocaml, but it's 2020 and it still doesn't own a native concurrent construct and its barrier to entry is too high for busy programmers to spend time learning it. A lot of great languages die because they don't have a straightforward path to success and small wins to warrant users' productivity loss. My go-to language is almost always Go. It's not a great language by itself, but most of my work is about building API servers and its tooling is just very robust and frictionless. Also, it takes little effort to type in Go (at least for me) and the error messages are very debug-friendly.
Re: What’s your favorite programming language and why?
**preface** My priorities are safety, legibility, and expressiveness, in that order. I can program in C and C++, but I much prefer _readable_ languages where the compiler identifies as many errors as possible before you run it, because debugging sucks, and debugging languages that thinks safety and readability are signs of weakness is... better left unsaid. _Languages I like_ (in alphabetical order) **Ada** : C++ has spent 30+ years trying unsuccessfully to make C as featured _and safe_ as Ada 83 (exceptions, generics, multitasking, ability to write large, nontrivial programs without pointers, modules, ... oh, wait, C++ _still_ doesn't have modules, and on and on...) while still looking like C, because `a = (b < c) ? d : e;` is so much more desirable than `a := (if b < c then d else e);` or something. Ada 95 was the first ISO-standardized language with object-oriented features. Ada 2012 has Design By Contract. A dialect of Ada called SPARK offers the ability to prove a program meets specifications laid out in DBC and/or pragmas. When Ada came out, people objected that it was too complicated, so today we program with even more complicated languages that offer fewer features, and no one knows Ada still exists and probably powers the airplane you flew to that last FLOSS conference, so companies cannot find Ada programmers, so they are switching to other languages which are less safe but, hey, the kids coming out of universities these days don't like reading and would rather match curly braces, so what can we do? (Yes, I have heard kids coming out of universities say that they don't like languages that don't use curly braces, with no further reasoning on the matter.) **Kotlin** (please don't kill me): A better Java than Java. Lots less boilerplate. Free & great tooling. The use of `in` and `out` types for generics. A serious and almost successful attempt at `null` safety. I only learned Kotlin because the choices in Android Studio are Java and Kotlin, so I decided to try Kotlin I've never gone back to Java. Kotlin Native may or may not turn out to be a thing one day, too. **Modula-2** : Like others, I learned with Pascal; Modula-2 was Wirth's systems programming language, and back in the early 90s the only options I had on an Amiga were Modula-2, Oberon-2, and C. I couldn't find learning material on Oberon (really) so Modula-2 it was. It was elegant, powerful, and -- given the options -- the safest language available. **Modula-3** : Modula-3 was from DEC, inspired by Modula-2 but different. Surprisingly good, frustratingly boneheaded at times (you have to jump through far too many hoops to instantiate a generic modules), but whenever I finished writing a Modula-3 program, I always felt as if I'd had fun and wanted to do more. **Nim** : Do I have to explain this on a Nim forum? _Language that was a sore disappointment_ (not asked, but some people are writing this anyway) **Oberon** : [Not to be confused with Oberon-2, which is similar] In principle there's a lot to like, but after working through much of Wirth's Compiler Construction textbook with Oberon I found myself increasingly frustrated by choices that were made for the compiler writer's convenience, even when they wouldn't have cost much. Maybe it was unfair because by the time I learned Oberon I'd learned a lot of other OO languages, but, for instance: * There is no way to initialize an object automatically; you must always manually initialize, e.g., `o := NEW(T); o.Init;` * Arrays of unknown size are allowed only as parameters to procedures, and you cannot allocate an array whose compile-time size is not known except through gruesome hacks. That is, you can do `TYPE T=ARRAY 10 OF INTEGER; VAR a : POINTER TO T; BEGIN a := NEW(T); END` but not `TYPE T=ARRAY OF INTEGER; VAR a : POINTER TO T; BEGIN a := NEW(T, 10); END` or something similar. Some Oberon fans claim this defect is evidence of the language's brilliant design, but it's important enough that at least two compilers I examined introduced a `SYSTEM.NEW` command to do this... and each implemented it differently. * In an attempt to minimize the language, some machine-level commands are in the language proper, such as `LSL` and `ASR`, but others are consigned to the `SYSTEM` module, which you are warned to avoid if you want to write portable programs. In particular, masking an `INTEGER` requires you to convert it to a `SET` via `SYSTEM.VAL`, use the `*` operator with another `SET`, then convert back. All this just to avoid using the `&` operator on integers? * Speaking of which, why `&` instead of `AND`? After all, `OR` gets its own keyword. What happened to the aim of readability? * In the command `o.method()`, `o` is not automatically passed to `method()` as an argument, so you have to write `o.method(o)`, which grows really tiresome after a while, especially if you use name variables according to their purpose rather than
Re: Walking trees without recursive iterators
That sounds like a bug/limitation in @sschwarzer's algorithm to me, but maybe he can verify. Good catch, @e. Another pretty different approach that I use in `cligen/tern.nim` (that does not support in-iteration deletion) is something like this: const NUL* = '\0' type NodeOb*[T] {.acyclic.} = object ch*: char cnt*: int when T isnot void: val*: T kid*: array[3, ref NodeOb[T]] #0,1,2 ~ <,=,> Node*[T] = ref NodeOb[T] Tern*[T] = object ## A Tern can be used as either a mapping from strings root*: Node[T]## to type ``T`` or as a set(strings) if ``T`` is void. count: int## Number of elements depth: int## Depth of Tree iterator leaves[T](r: Node[T], depth:int, pfx=""): tuple[k:string, n:Node[T]] = type #Nim iterators should grow Which = enum st0, st1, st2, stR #..recursion capability so State = tuple[st: Which, k: string, n: Node[T]] #..this headache can be as if r != nil: #..easy as `print`. var stack = newSeqOfCap[State](depth) stack.add (st: st0, k: pfx, n: r) while stack.len > 0: let state = stack[^1].addr if state.n == nil: break case state.st of st0: state.st = st1 if state.n.kid[0] != nil: stack.add (st: st0, k: state.k, n: state.n.kid[0]) of st1: state.st = st2 if state.n.ch == NUL: yield (k: state.k, n: state.n) elif state.n.kid[1] != nil: stack.add (st: st0, k: state.k & $state.n.ch, n: state.n.kid[1]) of st2: state.st = stR if state.n.kid[2] != nil: stack.add (st: st0, k: state.k, n: state.n.kid[2]) of stR: discard stack.pop Run The above is probably pretty close to what compilers generate for recursive function calls. Of course, as mentioned I think things like this are more elegant/easy to understand: proc print*[T](p: Node[T], depth=0) = #2,1,0 gives std emoji-orientation if p == nil: return #..i.e. ":-)" head-tilt not "(-:". print(p.kid[2], depth + 1) echo strutils.repeat(" ", depth), cast[int](p), " ch: ", p.ch.repr, " cnt: ", p.cnt print(p.kid[1], depth + 1) print(p.kid[0], depth + 1) Run For `cligen/trie.nim` since I was already using a super wasteful uncompressed trie, I just punted on all that and spent memory proportional to the answer, i.e.: proc collect*[T](n: Node[T], key: var string, d=0, pfx="", i=0): seq[tuple[k: string, n: Node[T]]] = if n == nil: return if n.term: result.add (k: (if i > 0: pfx[0..
Re: What’s your favorite programming language and why?
Funny question of a new forum user, isn't it? So lets wait for nice new spam links: [https://www.reddit.com/r/learnprogramming/comments/7zip4n/whats_your_favorite_programming_language_and_why](https://www.reddit.com/r/learnprogramming/comments/7zip4n/whats_your_favorite_programming_language_and_why)/
Re: Walking trees without recursive iterators
It is always possible to move a recursive algorithm to a non-recursive one; even avoiding psuedo-stacks or tail-recursion games. It is, ironically, something I enjoy doing because it is quite the intellectual puzzle sometimes. The toughest one I did was with the Negamax (Minimax with restrictions) in a python library. (Sorry, I've not moved it to Nim yet.) But, for context: writing a normal negamax algo in any language takes, perhaps, a few hours in any language. But, writing the non-recursive one, involved multiple convoluted arrays and more complex edge cases than I've ever encountered before. It took over a week of intense work. SO, Anything your head can do can be described in an algorithm with enough scrutiny. (BTW, the non-recursive version of negamax is about 3 to 8 times faster. But it uses lots more memory.)
Re: assign an object variant kind for unsafe memory block
thanks, i used -d:nimOldCaseObjects because the compiler source uses it on nim.cfg. i use `alloc0` for arbitrary blocks size, ex: `alloc0(sizeof(AHeader) + blocksize)`
Re: Walking trees without recursive iterators
Does this test if handle.par.len - 1 < handle.kidIx: false #kid@kidIx was deld post yield Run mean that deletion is only supported for the last kid (or all kid s)? E.g., if I don't delete kid 5, but then delete kid 4, isn't len still 4, so the test fails and we process kid 4's kids?
Re: What’s your favorite programming language and why?
Bonjour : 42 year pratique : IBM 34/36/38/AS400 -->RPG2/3/4 project using the “AXIAL” method * object oriented "RPG-ILE" "C-ILE" language * C++ traditional project management create L4G type engine sur pc : 1980: pascal / C 1990 ... 2019: C++ PHP2/3 Javascript creation driver for picking creation concept soft for press 2019/10: NIM : very interesting approach, after trying "RUST", I succumbed to the charm of Nim-lang I work on a project with "NIM" with the option of using the "AXIAL" method reproduce the AS400 BD / INTERACTIVE / EDITION type access methods oriented RPG4(ibm)
Re: nim android tutorial
Also, it's not an "app", but it bears re-iterating that Nim on Termux works just like Nim on a worstation/laptop/other posix-y environment. See, e.g, [https://forum.nim-lang.org/t/2891](https://forum.nim-lang.org/t/2891). I have a couple dozen Unix commands that compile and run just fine in both environments.
Re: Arrays and Sequences in nim
As a point of clarification, depending a bit on OS and sysadmin settings, one can usually increase stack limits with `ulimit -s unlimited` builtin for most shells, maybe `/etc/limits` or `/etc/security/limits.conf` on Linux or even _adjustable in-program_ by `setrlimit` (if not blocked by admin settings). (Nim's stdlib could use an `RLIMIT_STACK` constant to ease that last one.) The default limits are usually much less than say system memory divided by number of expected threads. It would be very rare that all threads used their limit anyway. When the limit is exceeded your thread/process just crashes, much as if you set the limit to be unlimited and used all system memory. The existence of low stack limit defaults historically mostly comes from systems with "spinning rust" rather than than NVMe backing store for swap. On such systems, an unbounded recursion could bring the machine to a crawl for all users. So, the limit was a safety feature. These days, no swap at all is a real possibility. Even when swapping is enabled, the limit is more about not having some unbounded recursion flush filesystem caches forcing re-reads. Also, kernel code often has small stack limits for related but distinct reasons (non-swappability of kernel stack/page fault handlers/etc.). Kernel developers tend to set default limits based on what they think is "good for you" based on their own very stack-conservative styles. Anyway, the short of it is that if stack is how you would prefer to manage your memory (for whatever reasons) there isn't anything intrinsically wrong with that related to "size". There's just a little deployment complexity, like maybe erroring out if admins have blocked stack limit increasing and your `setrlimit` fails which is atypical in my experience. (This is all Unix advice. I have little to no experience for how this plays out on Windows, but some more informed person can perhaps chime in.)
Re: What’s your favorite programming language and why?
> Interesting. Why do you hate Python? Well, it's a bit controversial I know. I have a friend who swears by it. However, whenever I _had_ to write sth in Python, I end up struggling either with the different versions, virtual environments (or I-don't-know-how-it's-called), missing modules that cannot get installed no-matter-what (or basically do not have the patience to learn how to) and last but not least... indentation issues (yes, I know... we're in a Nim forum, but for some reason I've never been a huge fan of the... "off-side" rule). Now, if it was as performant for systems' programming as Nim, I might have given it a second chance... lol
Re: Walking trees without recursive iterators
In the interests of a more self-contained thread and to perhaps more easily see what it's doing, here is a more brief version of @sschwarzer's algorithm: import xmltree type Handle* = tuple[kidIx: int; par, kid: XmlNode] #Ix of kid in parent iterator handlesHiToLo*(node: XmlNode): Handle = ## Recursively yield ``Handle`` objects for direct kids of ``node`` ## from high to low index order (on same level). { This order simplifies ## removing kids in-place without affecting later yielded kid indices. } ## The root node is included in yielded ``Handle``s with a ``nil`` parent. template recordYield(par: XmlNode, kidIx: int, kid: XmlNode=nil) = handle = (kidIx, par, (if kid.isNil: par[kidIx] else: kid)) yield handle proc high(node: XmlNode): int = node.len - 1 var done = false#flag to stop outer loop from inner var stack = newSeq[Handle]() var handle: Handle #Last node yielded recordYield nil.XmlNode, 0, node#Always yield the root first if node.len == 0: done = true #Nothing left to do else: recordYield node, node.high #Start from high node of root & proceed while not done: let kidHasKids = if handle.par.len - 1 < handle.kidIx: false #kid@kidIx was deld post yield else: handle.kid.len > 0 if kidHasKids:#Descend tree, build stack, yield last grandkid stack.add handle recordYield handle.kid, handle.kid.high continue while true: #Yield next (lower index) kid | maybe pop,find new if handle.kidIx >= 1: recordYield handle.par, handle.kidIx - 1 break if stack.len > 0: handle = stack.pop() else: #No more nodes to try on the stack. Done. done = true break Run If you want some other visitation order such as low-to-high then you have to adjust things a bit. For in-order/work in the middle you might need to adjust it a lot. I actually kind of doubt it can be simplified if you want to preserve the deleteability of nodes pointed to by yielded handles. In these sorts of situations, recursive code is often drastically more clear. A good programming language should facilitate clear code. Therefore Nim iterators should grow recursion capability someday. But I'm not signing up to implement it, and I wouldn't assert it's a high priority.
Re: What’s your favorite programming language and why?
I like - in the order of appearance in my life: * pascal for showing me that Apple Basic was crap * forth for showing me how powerful it can be to do things in the reverse order * delphi for simplifying GUI development in 1995 * haxe for giving me the ability to program JavaScript or php (and others) without touching them in a type safe manner * nim for its power and not over OOPing everything I don't like: * C++ for wasting my time * php because its ugly * JavaScript just because
Re: What’s your favorite programming language and why?
HLA: High Level Assembly, used to be like a Nim that compiled to Assembly, 32Bit only, dead. Scala-Native: Dont need Java, has Java as part of its std lib.
Re: how to create a generic type with constraints?
Checking with [compiles](https://nim-lang.github.io/Nim/system.html#compiles%2Cuntyped) should enough when compiles(hash(a)): # a is the T object doWith(a) else: {.error: "need hash implementation of " & T.type.} Run It's checked during compile-time.
Re: how to create a generic type with constraints?
These are great examples of concepts, I had no idea that last one would even be possible
Re: What’s your favorite programming language and why?
Same here, love Erlang's philosophy too :D
Re: What’s your favorite programming language and why?
Interesting. Why do you hate Python? I like: * Erlang for its philosophy * Mathematica for astonishing me * C# for its creator * Nim for its clarity and power * Pascal for bringing me to this wonderful journey, too I hate: * Java for over-designed design patterns * Rust for failing to compile my hello world
Re: What’s your favorite programming language and why?
Having been programming fo over 25 years, I most definitely couldn't say just one. But I could definitely say a few for sure. * _Ruby_ : because it's the one I'm most familiar with, it's dynamic, flexible and I "get the work done" fast * _Haskell_ : because of it's power, elegance and logic - although I'm by no means good at it * _Lisp_ : for inside logic, S-expressions, etc * _Rebol_ : for the same reason as above, only more elegant * _Arturo_ : (shameless plug ahead) because of all the above and because it's my own brain-child * _Nim_ : for its clean code and great performance * _Pascal_ : for sentimental reasons, since it's the one that got me started in this wonderful journey, when I was still a kid
Re: What’s your favorite programming language and why?
I doubt you will get unbiaised answers on the Nim forum. What's yours and why?
Re: how to create a generic type with constraints?
Thanks. I think I'll do without concepts for now.
What’s your favorite programming language and why?
Let’s discuss I would love to hear people’s stories and experiences
Re: Introducing Synthesis: a Finite State Machine generator
Something like `Graphviz` or `GraphML` to display the state machine with its transition visually.
Re: Arrays and Sequences in nim
A couple of notes: * Your stack size of 10_000_000 is very big. That would only work with global arrays as those are statically assigned a size at program start. However if used within a function you will most certainly stack overflow. For example a stack of int would take 80_000_000 bytes = 80MB but max stack size is about 8MB on most machines. If this is used within a function you will have to allcoate it on the heap with a ref array or just use a seq. * In terms of speed, if the address of the seq and the address of the arrays are hot in cache, there is no difference. Data structures on the stack are always hot in cache within a function. * The `base_addr_offset + index * size` computation to address each elements of a seq/array doesn't matter in general for 2 reasons: * The size of an object is power of 2 (due to padding and alignment) and x86 can do offset + index * pow2 addressing in a single instruction without a multiplication via something called SIB addressing (Scaled Index Byte) for types of size 1, 2, 4 or 8: [https://wiki.osdev.org/X86-64_Instruction_Encoding#SIB](https://wiki.osdev.org/X86-64_Instruction_Encoding#SIB) * It is very likely that the bottleneck is either memory or branch predictions and that your CPU has a lot of free time to do those computations in between waiting for data from the L1/L2 cache.
Re: Any way to see the generated assembly?
Great, thanks! :)
Re: how to create a generic type with constraints?
If I didn't mess up my concept syntax type Diffable = concept x, y `==`(x, y) is bool hash(x) is Hash Run See: [https://nim-lang.org/docs/manual_experimental.html#concepts](https://nim-lang.org/docs/manual_experimental.html#concepts) And actual use cases: * Enqueueable concept: [https://github.com/mratsim/weave/blob/a8f42d302e3d68f397374e9b14e964f647cb9afa/unused/channels/channels_mpsc_unbounded.nim#L8](https://github.com/mratsim/weave/blob/a8f42d302e3d68f397374e9b14e964f647cb9afa/unused/channels/channels_mpsc_unbounded.nim#L8) type Enqueueable = concept x, type T x is ptr x.next is Atomic[pointer] Run * IntrusiveStackable concept: [https://github.com/mratsim/weave/blob/ed177fffaea6dbd6b7ddb648fc6e9cb302cae2ff/weave/memory/lookaside_lists.nim#L11-L14](https://github.com/mratsim/weave/blob/ed177fffaea6dbd6b7ddb648fc6e9cb302cae2ff/weave/memory/lookaside_lists.nim#L11-L14) type IntrusiveStackable* = concept x, type T x is ptr x.next is T Run * StealableTask concept: [https://github.com/mratsim/weave/blob/78c9033c1bbee5a8efde2d623d9e7cc8b9605f77/weave/datatypes/prell_deques.nim#L11](https://github.com/mratsim/weave/blob/78c9033c1bbee5a8efde2d623d9e7cc8b9605f77/weave/datatypes/prell_deques.nim#L11) type StealableTask* = concept task, var mutTask, type T ## task is a ptr object and has a next/prev field ## for intrusive doubly-linked list based deque task is ptr task.prev is T task.next is T # A task has a parent field task.parent is T # task has a "fn" field with the proc to run task.fn is proc (param: pointer) {.nimcall.} Run * NeuralNetwork Layer concept: [https://github.com/mratsim/Arraymancer/blob/f06c43db379d1ef0160315162a45a8ab5bdb2b0d/src/nn_dsl/dsl_types.nim#L63-L68](https://github.com/mratsim/Arraymancer/blob/f06c43db379d1ef0160315162a45a8ab5bdb2b0d/src/nn_dsl/dsl_types.nim#L63-L68) type TrainableLayer*[TT] = concept layer block: var trainable: false for field in fields(layer): trainable = trainable or (field is Variable[TT]) trainable Run The last one is a bit hard, basically a concept must satisfy several properties, and that is checked by each statement returning `true`, so what I do there is creating a temporary bool called trainable in the concept and I check that recursively at least one field in the type or its subtypes is of `Variable[TT]` which would be something trainable in my framework. If there is at least one such field, it returns true and it's considered a `TrainableLayer[TT]`.
Re: Any way to see the generated assembly?
See the flags I mentioned here: [https://github.com/nim-lang/Nim/issues/12578](https://github.com/nim-lang/Nim/issues/12578) There is a new --asm flag in Nim devel but it generates a lot of fluff.
Re: Walking trees without recursive iterators
I've written an iterator over an `XMLNode`: [https://hg.sr.ht/~sschwarzer/vppdiff/browse/default/src/vppdiff/cleanxmi.nim#L46](https://hg.sr.ht/~sschwarzer/vppdiff/browse/default/src/vppdiff/cleanxmi.nim#L46) Internally the iterator uses a stack. Iterator features: * You can modify the tree while iterating over it. For example, if you delete a child node, the iterator doesn't yield _its_ child nodes. * For each iteration, the iterator gives you parent node, child index and child node in a `NodeDescription` object, so you can delete the child node with `parent_node.delete(child_index)`. * The iterator goes from higher to lower child indices, so deleting child nodes doesn't shift the indices for the remaining child nodes in subsequent iterations. I suspect the implementation is more complicated than it has to be, but this code is what I've been able to come up with. ;-)
how to create a generic type with constraints?
I'm trying to port Python's difflib's sequence matcher to nim. I want to be able to compare any two sequences of the same types, not just strings. I have this type: type Diff[T] = object a: seq[T] b: seq[T] b2j: Table[T, seq[int]] Run How can I say that type `T` must support `==` and `hash()`?
Any way to see the generated assembly?
I'm on a Mac and using clang. I know where the generated C files are, but when trying to compile them, I see all sorts of missing header files, etc. I can sure make it work, but is there any already-available way to see just the generated assembly without messing around?
Re: Arrays and Sequences in nim
> This is a very interesting point. And to make it more precise: When we access an arbitrary element of a array/seq base address + index * element_size is calculated to determine its memory address and access it. If multiple elements are accessed one after the other, then a compiler is generally smart enough to see that only adding element_size to current address is needed to access next element. Long time ago, when compilers were not that smart, constructs like my_element_ptr++ was used to access next element. But today using a plain index generally generates the same optimized code when possible.
Re: Arrays and Sequences in nim
> If array index is a constant, then offset is constant too. If array index is > a variable, than offset is product of element_size and offset. So there is a > runtime mul in later case. This is a very interesting point. Given that what I'm currently doing is building a stack machine bytecode interpreter (to replace my AST-walking interpreter) - and already seeing some serious perfomance upgrade, I'm trying to use every trick available, so these subtle details do matter. Currently, my simple "stack" is pretty much like that: #[## Constants ==]# const MAX_STACK_SIZE = 10_000_000 #[## Types ==]# type Stack[T]= array[MAX_STACK_SIZE,T] Value = uint64 #[## Global variables ==]# var MainStack* : Stack[Value] # my main stack MSP*: int# pointer to the last element #[## Implementation ==]# template push*(v: Value) = inc(MSP); MainStack[MSP] = v template pop*(): Value = dec(MSP); MainStack[MSP+1] template popN*(x: int) = dec(MSP,x) template top*(x:int=0): Value= MainStack[MSP-x] Run so... normally an **ADD** instruction, in my {.computedGoto.} interpreter loop, would be something like that: case OpCode # other cases of ADD_OP: push(pop()+pop()); inc(ip) # inc(MSP); MainStack[MSP] = ((dec(MSP); MainStack[MSP+1]) + (dec(MSP); MainStack[MSP+1])) # ... Run which I'm optimizing further (I think... lol) by doing it like: case OpCode # other cases of ADD_OP: top(1) = top(0)+top(1); dec(MSP); inc(ip) # MainStack[MSP-1] = MainStack[MSP]+MainStack[MSP-1]; dec(MSP) # ... Run So... lots of different things going on... Any ideas to make it better (and more performant) are more than welcome! :)
Re: Introducing Synthesis: a Finite State Machine generator
> It generates compact code: the code generator is based on the undocumented > goto pragma. Maybe it's time to document it then... :-)
Re: Arrays and Sequences in nim
Maybe one additional note: When we access elements of an array, that generally occurs with an offset to stackpointer. Stackpointer is generally fixed while we are inside a proc, and stackpointer is generally located in a register, so to access an array element a offset plus stackpointer is used to locacte the array element. If array index is a constant, then offset is constant too. If array index is a variable, than offset is product of element_size and offset. So there is a runtime mul in later case. To access an element of a seq, there is some indirection: First the base address of the pointer to payload is moved into a CPU register, and an offset = (elementsize * index) is added to locate element. When we access many elements of a seq, then only one time its payload address needs to be moved into a CPU register, so accessing many elements is not really slower for seq than it is for array.
Re: Arrays and Sequences in nim
Thanks a lot for the explanation! This is pretty much what I expected - but no harm getting a second opinion to make sure you got things right, nope? :) Basically, I have experimented with pretty much everything A LOT **and** benchmarked it **and** checked the produced C code, but wanted to make sure... And from what I've observed and from what you say, we're definitely on the same page. My latest experiments point to the same conclusion as well: var arr: array[10,byte] echo cast[uint64](addr arr) echo cast[uint64](addr arr[0]) echo cast[uint64](addr arr[1]) echo cast[uint64](addr arr[2]) Run The first two - quite obviously - point at the beginning of the array. And the next ones point at the consecutive "cells" of memory, one _byte_ apart... In any case, again: thanks ;-)
Re: Arrays and Sequences in nim
I think that question has been answered many times already, and I am nearly sure you know it already. At least you are no newbie, I have seen you name in this forum for at least a few months already :-) Nim array is basically a C array located on the stack, so just a fixed block of memory. As it is located on the stack there is no alloc/dealloc needed, allocation is just increasing the stack pointer. Dont't ask me how returning a array from a proc works exactly, but I assume it works also without alloc/dealloc. seq and string is basically an object, at least one length field and a pointer to payload, that is the real data content. Content is dynamically allocated and deallocated. When data elements are added, data buffer may get exhausted, then a new payload buffer is allocated and data is copied there. There may be exceptions, as when area after buffer is still unused, OS may allow increasing a buffer already in use, that operation may be called realloc() or something like that. seq and string works very similar to C++ std::vector. Details my vary for Nim V2, newruntime, arc memory management and all that what may come. Note, C may allow dynamically sized arrays living on stack. That can work for special cases, as stack pointer can be increased dynamically at runtime. There exists a forum post to this behaviour. And finally there is openArray, basically a container for passing array or seq to procs, so I assume it is a length field and a pointer to payload.