src/elnim

Author:Kaushal Modi
License:MIT

Introduction

This module implements few Emacs-Lisp equivalent procs.

Source

Repo link

Procs

proc car[T](s: openArray[T]): T

Return the first element of s.

If s has zero elements, throw an error. Unlike Emacs-Lisp, Nim cannot return a true "nil" value in this case.

Example:

doAssert @["abc", "def", "ghi"].car() == "abc"
doAssert [1, 2, 3].car() == 1
try:
  discard seq[string](@[]).car()
except AssertionError:
  echo "Illegal: 'car' was provided a zero-length seq/array."
proc cdr[T](s: openArray[T]): seq[T]

Return a sequence of all elements excluding the first element of s.

If s has one or less elements, an empty sequence of type T is returned.

Example:

doAssert @["abc", "def", "ghi"].cdr() == @["def", "ghi"]
doAssert [1, 2, 3].cdr() == @[2, 3]
doAssert [1].cdr() == seq[int](@[])
doAssert @["a"].cdr() == seq[string](@[])
doAssert seq[string](@[]).cdr() == seq[string](@[]) # zero length seq
doAssert array[0, int]([]).cdr() == seq[int](@[]) # zero length array
proc assoc[T](alist: openArray[seq[T]]; key: T;
              testproc: proc (x, y: T): bool = equal): seq[T]

Return the first nested sequence whose first element matches with key using the testproc proc (which defaults to equal).

Input alist is an array or sequence of sequences of type seq[T].

The alist and key arguments are swapped compared to its Emacs-Lisp version so that we can conveniently do alist.assoc(key).

Example:

doAssert @[@["a", "b"], @["c", "d"]].assoc("a") == @["a", "b"]
doAssert [@[1.11, 2.11, 3.11], @[4.11, 5.11, 6.11], @[4.11, 40.11,
    400.11]].assoc(4.11) == @[4.11, 5.11, 6.11]
# alist containing a zero-length seq
doAssert [@[1, 2, 3], @[], @[4, 40, 400]].assoc(10) == seq[int](@[])
# zero length alist
doAssert seq[seq[string]](@[]).assoc("a") == seq[string](@[])
proc delete[T](s: openArray[T]; el: T; testproc: proc (x, y: T): bool = equal): seq[
    T]
Return a sequence containing all elements from s that do not match with el. The match is done using the testproc proc (which defaults to equal).

Example:

doAssert @[123, 456, 789, 123].delete(123) == @[456, 789]
doAssert ["123", "456", "789", "123"].delete("456") ==
  @["123", "789", "123"]
doAssert seq[string](@[]).delete("a") == seq[string](@[])
proc mapconcat[T](s: openArray[T]; sep = " "; op: proc (x: T): string = dollar): string

Concatenate elements of s after applying op to each element. Separate each element using sep.

The signature of this proc differs from its equivalent mapconcat in Emacs,

  • so that we can do s.mapconcat() in Nim.
  • Also it is more common for a user to change the sep parameter than the op parameter, so move op to the last position.

Example:

doAssert @["abc", "def", "ghi"].mapconcat() == "abc def ghi"
doAssert ["abc", "def", "ghi"].mapconcat() == "abc def ghi"
doAssert [1, 2, 3].mapconcat() == "1 2 3"
doAssert [1, 2, 3].mapconcat("\n", proc(x: int): string = "Ha: " & $x) ==
  "Ha: 1\nHa: 2\nHa: 3"
doAssert seq[string](@[]).mapconcat() == ""
proc member[T](el: T; s: openArray[T]): bool
Return true if el is an element in s. This proc is like find but with args in reverse order, and returns a bool instead.

Example:

doAssert "abc".member(@["abc", "def", "ghi"]) == true
doAssert "abc".member(["abc", "def", "ghi"]) == true
doAssert "a".member(["abc", "def", "ghi"]) == false
doAssert 1.member([1, 2, 3]) == true
doAssert 100.member([1, 2, 3]) == false
doAssert "".member(seq[string](@[])) == false
doAssert "a".member(seq[string](@[])) == false
proc isValid[T](x: T): bool

Return true if x represents something like a non-nil value in Emacs-Lisp.

Note: Compile with -d:debugIfLet to print if x is of unsupported type.

Macros

macro ifLet(letExpr: untyped; trueCond: untyped;
            falseCond: untyped = newEmptyNode()): untyped

Macro similar to Emacs Lisp's if-let* macro. Takes a set of assignments letExpr and checks if all of those are valid (in elisp, "validity" is checked by non-nil-ness) by calling isValid on the values being assigned. If all are valid, the body after do: will be evaluated, otherwise the body after else: is evaluate.

Note: Compile with -d:debugIfLet to see its resulting code.

ifLet:
  a = 5
  b = a * 5
  c = proc(a, b: int): int = a + b
do:
  echo "Output is: ", c(a, b)
else:
  echo "Either a or b had an invalid value."

This macro rewrites the above code to:

block:
  let
    a = 5
    b = a * 5
    c = proc (a, b: int): int =
      a + b
  if [isValid(a), isValid(b), isValid(c)].allIt(it):
    echo "Output is: ", c(a, b)
  else:
    echo "Either a or b had an invalid value."