Nim 发布1.6.0 版本,命令式编程语言
Nim 是一门专注于性能、可移植性和表现力的系统编程语言。经过一年的厚积薄发,Nim 1.6.0 稳定版已正式发布。
主要更新内容如下:
向后兼容性和预览标志
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
其他特性
查看完整版更新日志,请点击这里。
更新公告:https://nim-lang.org/blog/2021/10/19/version-160-released.html