Generics in Nim

import std/algorithm

# As an example of a generic function, `slicesIndex` takes
# a sequence of any comparable type and an element of that
# type and returns the index of the first occurrence of
# v in s, or -1 if not present.
proc slicesIndex[T](s: openArray[T], v: T): int =
  for i, item in s:
    if item == v:
      return i
  return -1

# As an example of a generic type, `List` is a
# singly-linked list with values of any type.
type
  List[T] = ref object
    head, tail: Element[T]

  Element[T] = ref object
    next: Element[T]
    val: T

# We can define methods on generic types just like we
# do on regular types, but we have to keep the type
# parameters in place. The type is `List[T]`, not `List`.
proc push[T](lst: var List[T], v: T) =
  let newElement = Element[T](val: v)
  if lst.tail.isNil:
    lst.head = newElement
    lst.tail = lst.head
  else:
    lst.tail.next = newElement
    lst.tail = lst.tail.next

# allElements returns all the List elements as a sequence.
proc allElements[T](lst: List[T]): seq[T] =
  result = @[]
  var e = lst.head
  while not e.isNil:
    result.add(e.val)
    e = e.next

proc main() =
  var s = @["foo", "bar", "zoo"]

  # When invoking generic functions, we can often rely
  # on type inference. Note that we don't have to
  # specify the type for `T` when calling `slicesIndex` - 
  # the compiler infers it automatically.
  echo "index of zoo: ", slicesIndex(s, "zoo")

  # We could also specify the type explicitly if needed.
  discard slicesIndex[string](s, "zoo")

  var lst = List[int]()
  lst.push(10)
  lst.push(13)
  lst.push(23)
  echo "list: ", lst.allElements()

main()

This Nim code demonstrates the concept of generics, which is similar to the original Go example. Here are some key points about the translation:

  1. Nim uses square brackets [] for generic type parameters, similar to Go.

  2. Instead of ~[]E, Nim uses openArray[T] to represent a slice-like structure.

  3. Nim’s proc is equivalent to Go’s func.

  4. Nim uses var for mutable parameters, similar to how Go uses pointers for mutability.

  5. Nim’s ref object is used to create a reference type, similar to Go’s struct pointers.

  6. Nim doesn’t require explicit type parameters when calling generic functions if they can be inferred.

  7. The main function in Nim is not special; it’s just a regular procedure that we call at the end of the file.

To run this program, save it as generics.nim and use the Nim compiler:

$ nim c -r generics.nim
index of zoo: 2
list: @[10, 13, 23]

This example demonstrates how Nim supports generics, allowing for type-safe, reusable code across different data types.