命令式编程语言-Nim
2014-10-06 22:04:05 阿炯

Nim是一个指令式、通用、多范式、静态类型、编译型的编程语言,由Andreas Rumpf设计和开发,设计目标是高效、富有表现力以及优雅。Nim支持元编程、函数式、消息传递、过程式以及面向对象编程;支持统一函数调用语法,即并未区分函数、方法的调用语法。亦支持等价标识符,即不同的变量名称可能被视为相同,其忽略非首字母的大小写以及下划线。

Nim编译器可生成优化过的C代码并将编译推迟给外部的编译器(包括 Clang 及 GCC 在内的众多编译器都受支持),还可生成 C++、Objective-C 以及JavaScript的代码来便捷地与其他语言的应用程序接口(API)交互。使得Nim可用以编写iOS和Android的应用程序,其编译器自身已经实现了自举,也就是使用Nim语言自身编写的。采用MIT协议授权。


Nim is a statically typed, imperative programming language that tries to give the programmer ultimate power without compromises on runtime efficiency. This means it focuses on compile-time mechanisms in all their various forms.

Beneath a nice infix/indentation based syntax with a powerful (AST based, hygienic) macro system lies a semantic model that supports a soft realtime GC on thread local heaps. Asynchronous message passing is used between threads, so no "stop the world" mechanism is necessary. An unsafe shared memory heap is also provided for the increased efficiency that results from that model.

Nimrod是一个编译型的具有垃圾收集的系统编程语言,有着极其卓越的性能。Nim的设计集中在三个E上:即效率(efficiency)、表达处理能力(expressiveness)和优雅(elegance)。Nim 是一种适合各种用途的编程语言 —— 但并不适合所有人。它专注于命令式编程范式,通过宏系统对其进行增强。其可定制的内存管理使其非常适合要求苛刻的领域,例如硬实时系统和通用系统编程。

Nim支持统一调用语法以及等价标识符。

语法与Python相似,具体地说它受到了以下语言的影响:
Modula-3:受跟踪的和不受跟踪的指针
Delphi:类型安全的字符集
Ada:subrange types、distinct type、safe variants / case objects
C++:Excessive overloading、泛型
Python:越位规则
Lisp:宏系统、embrace the AST、homoiconicity
Oberon:成员导出标记
C#:async/await、lambda 宏
Go:Defer(延迟执行)

Nim编译器可生成优化过的C代码并将编译推迟给外部的编译器(包括 Clang 及 GCC 在内的众多编译器都受支持)。编译器还可生成 C++、Objective-C 以及JavaScript的代码来便捷地与其他语言的应用程序接口(API)交互。这使得Nim可用以编写iOS和Android的应用程序,Nim编译器自身是自举的,也就是使用Nim语言自身编写的。Nim程序可使用众多来自C语言和C++的类库。语言绑定可用于许多类库,例如GTK+2、OpenGL、WinAPI、zlib、libzip、OpenSSL 以及 cURL;也可与 PostgreSQL、MySQL 以及 SQLite 数据库共同工作,还可与 Lua 和 Python 语言的解释器交互。

效率
Nim 生成原生且无依赖的可执行文件,不依赖于虚拟机,所以它们小巧易分发。
Nim 编译器和生成的可执行文件,对目前的任何主流平台都提供了支持,包括 Windows、Linux、BSD 和 macOS。
Nim 的内存管理是确定性的,可使用析构函数和移动语义进行自定义,其灵感来自C++和Rust。非常适合嵌入式硬实时系统。
零开销迭代器和用户自定义方法的编译期求值等现代概念,结合优先使用分配在栈上的值类型数据,生成高性能代码。支持各种后端:可以被编译为 C、C++ 或 JavaScript, 以便 Nim 可用于所有后端和前端需求。

表现力
Nim 实现了自举:编译器和标准库都是用 Nim 本身来实现的。拥有强大的宏系统,允许直接操纵 AST,提供无限的可能性。

优雅
宏不会改变 Nim 的语法,因为并没有这个必要—— Nim 语法本身已经足够灵活。
具有局部类型推断、元组、泛型和sum类型的现代类型系统。
语句按缩进分组,也可以跨行。


openSUSE 宣布对 Nim 语言的 “一等公民” 支持

openSUSE 于2022年7月中旬宣布,与 Arch Linux 一样,为 Nim 语言提供了最新的软件包,并且静态类型、命令式编程语言现在在 openSUSE 中拥有 “一等公民” 的 Nim 支持。Nim 核心开发者 Dominik Picheta 表示,openSUSE 是第一个对 Nim 提供 “一等公民” 支持的 Linux 发行版。

博客写道:“openSUSE 构建包含自动化测试。Nim 与 openSUSE 的普遍可用性包括将特定架构的损坏测试与安全补丁的反向移植和上游同步。Nim 有一个非常有趣且充满活力的软件包生态系统,可以在许多方面轻松开发;从 Web 开发到系统编程,从科学到数据处理。仅举几例:可以使用 Wea​​ve 开发极其快速和并行化的应用程序,使用 Karax 或 Jester 可以完全使用 Nim 开发前端和后端 Web 应用程序,并使用 ArrayMancer 执行基于计算的繁重数学运算。在游戏方面,Nim 可用于通过使用 Godot-Nim 作为桥梁来开发高性能 3D 可视化和使用 Godot 进行游戏开发。”


除了宏系统和运行时效率之外,Nim 的优势之一是它的标准库,它与其他语言类似,涵盖了大多数标准功能;其中包括字符串处理和格式化、异步代码开发、网络甚至高级语言功能(如编译器本身),以及 NimScript,它是 Nim 的一个子集,专门为脚本构建,可以在运行时嵌入和执行。此外,Nim 附带了许多默认包含的工具。编译器支持使用 C、C++ 和 JavaScript 作为其后端。有一些工具可用于轻松开发:
nim 编译器
nimsuggest(支持语言建议、自动补全、错误/问题检测等)
nimgrep(一个强大的 grep 替代方案,内置 Nim 支持以查找符号和检查 Nim 代码库)
nim-gdb wrapper(对 Nim 类型的 gdb 支持)
nimble(包管理器)


最新版本:1.6
经过一年的厚积薄发,Nim 1.6.0 稳定版已于2021年10月下旬正式发布,主要更新内容如下:

向后兼容性和预览标志
1.6 版本引入了 -d:nimPreviewX 这种形式的预览标志(例如 -d:nimPreviewFloatRoundtrip),这种标志允许用户选择加入新的标准库/编译器行为,加入这些标志是为了尽量减少向后兼容性问题。1.6中还引入了 -d:nimLegacyX 形式的退出标志,例如 -d:nimLegacyCopyFile 。如果你在用老版本的 Nim ,可以用这些标志来适配老版本的语法。 以下是 1.6 版本中引入的所有标志,可以结合后面的新功能介绍了解它们的具体用法。
-d:nimLegacyCopyFile
-d:nimLegacyJsRound
-d:nimLegacyMacrosCollapseSymChoice
-d:nimLegacyParseQueryStrict
-d:nimLegacyRandomInitRand
-d:nimLegacyReprWithNewline
-d:nimLegacySigpipeHandler
-d:nimLegacyTypeMismatch
-d:nimPreviewDotLikeOps
-d:nimPreviewFloatRoundtrip
-d:nimPreviewHashRef
-d:nimPreviewJsonutilsHoleyEnum

主要的新功能:

iterable[T]

加入了 iterable[T] type 类来匹配被调用的迭代器,例子:
iterator iota(n: int): int =
  for i in 0..<n: yield i

# 以前需要用“untyped”,这会导致其他问题,比如缺乏类型推断、重载问题和 MCS
template sumOld(a: untyped): untyped = # 不可能有类型推断
  var result: typeof(block:(for ai in a: ai))
  for ai in a: result += ai
  result

assert sumOld(iota(3)) == 0 + 1 + 2

# 现在可以写成:
template sum[T](a: iterable[T]): T =
  # 也可以写成:`template sum(a: iterable): auto =`
  var result: T
  for ai in a: result += ai
  result

assert sum(iota(3)) == 0 + 1 + 2 # or `iota(3).sum`

更重要的是,现在可迭代参数能和方法调用的语句一起使用了:
import std/[sequtils, os]
echo walkFiles("*").toSeq # now works

关于 iterable[T] 的详细信息,请查看 #17196

严谨的效果

效果系统得到了改进,并且有一个新的 .effectsOf 注释,它可以显式地完成之前隐式完成的工作,更多详细信息请参阅手册。如果你仍要编写老版本 Nim 的代码,请使用以下用法:
when defined(nimHasEffectsOf):
  {.experimental: "strictEffects".}
else:
  {.pragma: effectsOf.}

proc mysort(s: seq; cmp: proc(a, b: T): int) {.effectsOf: cmp.}

要启用新的效果系统,请使用 --experimental:strictEffects 进行编译。详情可查看 #18777和 RFC #408 。

私有导入和私有字段访问

新的导入语句 import foo {.all.} 允许从 foo 里面导入所有符号(无论是私有的还是公开的),这点对于测试或者项目的灵活性都很有帮助,比如:
from system {.all.} as system2 import nil
echo system2.ThisIsSystem # ThisIsSystem is private in `system`
import os {.all.} # weirdTarget is private in `os`
echo weirdTarget # or `os.weirdTarget`

添加了一个新模块 std/importutils和一个 API privateAccess,它允许访问当前作用域对象类型的私有字段。比如:
import times
from std/importutils import privateAccess
block:
  let t = now()
  # echo t.monthdayZero # Error: undeclared field: 'monthdayZero' for type times.DateTime
  privateAccess(typeof(t)) # enables private access in this scope
  echo t.monthdayZero # ok

更多细节请查看 PR #17706 。

nim --eval:cmd

加入 nim --eval:cmd 来直接计算命令,比如 nim --eval:"echo 1" 。它默认为 e (nimscript)。也可以跟其他命令一起用,例如:
find . | nim r --eval:'import strutils; for a in stdin.lines: echo a.toUpper'
# use as a calculator:
nim --eval:'echo 3.1 / (1.2+7)'
# explore a module's APIs, including private symbols:
nim --eval:'import os {.all.}; echo weirdTarget'
# use a custom backend:
nim r -b:js --eval:"import std/jsbigints; echo 2'big ** 64'big"

更多详细信息,请查看PR #15687。

浮点数和字符串的相互转换

system.addFloat 和 system.$ 能将浮点数转化成字符串形式,通过 Dragonbox 算法实现了这些特性:最小规格、可以相互转换、正确的位数舍入。例如:
from math import round
let a = round(9.779999999999999, 2)
assert a == 9.78
echo a # with `-d:nimPreviewFloatRoundtrip`: 9.78, like in python3 (instead of  9.779999999999999)

目前只能通过 -d:nimPreviewFloatRoundtrip 开启这个功能,后面的版本可能会把这个设置成默认功能。

新模块:std/jsbigints

为 JS 目标提供任意精度的整数,详见 PR #16409。例如:
import std/jsbigints
assert 2'big ** 65'big == 36893488147419103232'big
echo 0xdeadbeef'big shl 4'big # 59774856944n

新模块:std/sysrand

用于加密安全的伪随机数生成器,允许从操作系统提供的安全源生成随机数。详见 PR #16459。例子:
import std/sysrand
assert urandom(1234) != urandom(1234) # unlikely to fail in practice

新模块:std/tempfiles

允许创建临时文件和目录,详见 PR #17361

import std/tempfiles
let tmpPath = genTempPath("prefix", "suffix.log", "/tmp/")
# tmpPath looks like: /tmp/prefixpmW1P2KLsuffix.log

let dir = createTempDir("tmpprefix_", "_end")
# created dir looks like: getTempDir() / "tmpprefix_YEl9VuVj_end"

let (cfile, path) = createTempFile("tmpprefix_", "_end.tmp")
# path looks like: getTempDir() / "tmpprefix_FDCIRZA0_end.tmp"
cfile.write "foo"
cfile.setFilePos 0
assert readAll(cfile) == "foo"
close cfile
assert readFile(path) == "foo"

用户自定义字面量

现在支持用户自定义数字型的字面值 (例如 -128'bignum ),除此之外,-1 里面的负号也会作为整形字面常量的一部分被单个解析,这意味着像 -128'i8 这样的边界案例也可以正常运行了,看一个例子:
func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".}
assert 0xffffffffffffffff'big == (1'big shl 64'big) - 1'big

点状运算符

使用 -d:nimPreviewDotLikeOps 的时候,点状运算符 . 的优先级一模一样了,所以 a.?b.c 现在会解析成(a.?b).c ,而不是 a.?(b.c)。如果不是在 -d:nimPreviewDotLikeOps 的情况下使用点状运算符,就会出现警告。建议启用动态字段,比如 std/jsffi, std/json, pkg/nimpy,而不是影响内置的点状运算符 .。看这个例子:

import std/json
template `.?`(a: JsonNode, b: untyped{ident}): JsonNode =
  a[astToStr(b)]
let j = %*{"a1": {"a2": 10}}
assert j.?a1.?a2.getInt == 10

Block 参数可以选择形参

解决了接受 Block 参数的主要痛点,详见 PR #18631。

template fn(a = 1, b = 2, body) = discard
fn(1, 2): # already works
  bar
fn(a = 1): # now works
  bar

多个 block 参数可以用 do 来实现:
template fn(a = 1, b = 2, body1, body2) = discard
fn(a = 1): # now works
  bar1
do:
  bar2

其他特性

更多细节请查看完整更新日志

最新版本:2.0
Nim v2.0 已于2023年8月上旬正式发布,这是 Nim 的一次进化,新版本的一项重要特性是将 ORC 内存管理作为默认设置,以及许多其他新功能和改进。主要新特性如下:

默认内存策略变更为:--mm:orc
具体示例查看:A cost model for Nim 或 Introduction to ARC/ORC in Nim。

增强与 C++ 的互操作
Nim 2.0 通过新的虚拟编译指示和扩展构造函数编译指示将 C++ 互操作提升到了一个新的水平。现开发者可以定义映射到 C++ 构造函数和虚拟方法的构造函数和虚拟过程,从而允许进一步自定义互操作性。此外还扩展了对 codeGenDecl pragma 的支持,以便它适用于类型。

新的标准库模块 (standard library modules)
新版对著名的 os 模块进行了彻底修改。其部分功能可在引入 Path 抽象的新界面下使用。Path 是 distinct string,它提高了处理路径、文件和目录时的类型安全性。

增加可重载的枚举 (Overloadable enums)
可重载枚举已结束实验性阶段,使用示例:
type
    E1 = enum
  value1, value2
E2 = enum
  value1, value2 = 4

const
Lookuptable = [
  E1.value1: "1",
  value2: "2"
]

Strict funcs(严格函数)
“strict funcs” 已达到稳定状态,在未来的版本中将会成为默认选项。

用于对象的默认值
在对象声明字段中现在支持使用默认值:
  type
Rational* = object
  num: int = 0
  den: int = 1

var r = Rational()
  assert $r == "(num: 0, den: 1)"

引入 Unicode 运算符
Nim 2 会默认启用 --experimental:unicodeOperators:诸如⊗或∘的 Unicode 运算符支持被数学库使用。请注意,标准库不使用 Unicode 运算符。


官方主页:
http://nim-lang.org/
https://nim-lang-cn.org/