Nim 发布 1.6.0 版本,命令式编程语言

来源: OSCHINA
编辑: 罗奇奇
2021-10-23

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

展开阅读全文
6 收藏
分享
加载中
最新评论 (8)
现在可以new对象了吗?
2021-10-30 09:45
0
回复
举报
不能。估计永远也不会。Nim是继承C精神,而不是C++的。
2021-10-30 12:38
0
回复
举报
好文章,已经收藏到我的站点https://aznfz.com,感谢作者!
2021-10-24 19:55
0
回复
举报
Nim是C体系最好的接班人。
2021-10-23 14:28
3
回复
举报
xmake是啥
2021-10-23 14:11
0
回复
举报
类似 cmake 的跨平台构建工具,最新版本已经支持对 Nim 项目的构建,可以实现 Nim 和 C/C++ 的混合编译,同时支持 nimble 包的自动集成,还有 C/C++ 包到 nim 项目的自动集成
2021-11-01 09:42
0
回复
举报
2021-10-23 14:04
0
回复
举报
赞,最近 xmake 也支持 nim 了
2021-10-23 10:50
3
回复
举报
更多评论
8 评论
6 收藏
分享
返回顶部
顶部