はじめに
D言語の公式コンパイラである dmd が更新され、最新バージョンである 2.097.0 が 2021/06/05 にリリースされました。
今回は言語機能やライブラリの強化が多く、非推奨関係の変更がいくつか、といった内容です。
特に新しい標準ライブラリのモジュールが追加されていたりするため、特徴的なものご紹介していきます。
より細かい内容は下記リンクからChangeLogをご覧ください。
- ChangeLog
また、前回3月のリリースまとめは以下になります。
- D言語の更新まとめ 2021年3月版(dmd 2.096.0)
変更点目次
コンパイラ変更点
- 曖昧な三項式の非推奨期間が終了し削除されました
-
bodyキーワードの使用が非推奨になりました - インポートを回避するために完全修飾名を使用するケースが非推奨となりました
- 明示的な
package可視性属性が新しいスコープに常に適用されるようになりました - 集成体 (
class,struct,union) にpragma(mangle)を適用できるようになりました - 複素数型および虚数型が非推奨となりました
-
while (auto n = expression)がサポートされました
改善点
- Bugzilla 16140:
while(auto x = y)がif(auto x = y)のように動作しません - Bugzilla 20068: コンストラクタでのユニオン初期化は
@safeであるべきです - Bugzilla 21203: 集約型に対する
pragma(mangle)を受け入れるようにします - Bugzilla 21585: マングルされた型の文字列を既存の型に変換するために
__traits(totype, string)を追加します - Bugzilla 21630:
assert(0)およびassert(false)はカバレッジのためにマークされるべきではありません - Bugzilla 21835:
floatの操作は x87 ではなく XMM レジスタを使うべきです
ランタイム変更点
改善点
- Bugzilla 21789: Codecovはファイルのパーミッションにデフォルトの umask を使うべきです
ライブラリ変更点
- 中央寄せフォーマットが追加されました
-
ImplicitConversionTargetsは非推奨となり、代わりにAllImplicitConversionTargetsを使用するようになります - 書式化の際に
%e、%f、%g、および%aを使って整数型のフォーマットが可能になりました -
pow(f, -2)およびf ^^ -2の実装が変更されました -
std.format:enforceValidFormatSpecは非推奨となりました -
std.format:formatElementは非推奨となりました -
std.format:unformatElementは非推奨となりました -
FieldNameTupleは、インターフェースに対して空のタプルを返すようになりました -
Fields(旧FieldTypeTuple)は、インターフェースに対して空のタプルを返すようになりました - コンパイル時に浮動小数点数をフォーマットすることができるようになりました
- 浮動小数点数をフォーマットすると、GCでの割り当てが行われなくなりました
-
std.formatで、一部のreal型はdoubleにダウンキャストされます -
std.typecons.Nullable: 非推奨のエイリアスであるalias get thisが削除されました -
std.formatのドキュメントが完全に作り直されました -
std.formatモジュールがより小さなモジュールに分割されました -
std.mathモジュールがより小さなモジュールに分割されました -
splitWhenがstd.algorithm.iterationに追加されました -
std.datetimeにあった古いベンチマーク機能が削除されました -
std.exception.enforceExが削除されました - 新しいモジュールとして
std.sumtypeが追加されました -
std.range.Transposed: 非推奨のメンバーであるsaveが削除されました
改善点
- Bugzilla 13595:
std.algorithm.groupByが非等価関係をサポートするよう拡張する - Bugzilla 16200: 整数指数に対するより高速な
powの実装(訳注、Revertされているようなのでこれは含まれていません) - Bugzilla 18024:
checkedint.Abortおよびcheckedint.Warnは@safeであるべきです - Bugzilla 18627:
std.complexは、組み込みの複素数型よりも数値計算がかなり遅い - Bugzilla 20756:
ImplicitConversionTargetsがインターフェースの継承を無視します - Bugzilla 21759:
std.experimental.checkedint.Checkedは、"%d"および"%x"整数フォーマット指定子と互換性がありません - Bugzilla 21760:
std.conv.toは文字列をstd.experimental.checkedint.Checked!Tに変換する方法を知りません - Bugzilla 21761:
Checked!Tが共有されている場合、std.experimental.checkedint.Checked!T.toHashを呼び出し可能にします - Bugzilla 21808:
std.format: AAのキーと値の順序を変更できるようにすべきです - Bugzilla 21847:
std.format:%e,%g,%aを整数でもサポートすべきです - Bugzilla 21858:
std.format: 出力を中央寄せにします
DUB 変更点
-
dubの設定ファイルに省メモリコンパイルのオプションが追加されました
注目トピック
言語・コンパイラ変更点
変更 : 明示的な package 可視性属性が新しいスコープに常に適用されるようになりました
複数指定された場合の package アクセス保護属性の動作が少し変わりました。
package 属性には、アクセス可能なパッケージ範囲を package(foo.bar) void hoge(); といった具合で指定できますが、これがブロック指定などで複数重なった場合の挙動が変わります。
以下のような例がありますが、正直そこまで影響があるものではなさそうです。
package (foo.fuga): // 以降の定義は foo.fuga からのみアクセスできる
package (foo) void test(); // foo 全体からアクセスできるよう直接指定されている
今までは、ブロック指定と直接指定をすべて見て、より細かい設定が優先されていました。
今回からは、直接指定されたほうが優先されるようになります。
この例の場合、 package (foo) void test() は指定通りに foo モジュール全体から参照可能になります。
こちらのほうが定義だけ見れば良いのでわかりやすいですね。
強化 : 集成体 (class, struct, union) に pragma(mangle) を適用できるようになりました
今回の変更は、主に C++ との連携で使用するマングリング指定のためのものです。
C++ で定義されたシンボルは、D から見ると C++ 流にマングリングされた名前になっています。
これを D から正しくリンクするには、定義された名前とは別にマングリングされたシンボル名を教えてやる必要があります。
そこで、この変更が効いてくるのが std::function など、D言語的にキーワードになっている定義です。
今回の強化を受けて、たとえば以下のように pragma(mangle) を指定することができるようになります。
少し特殊な例ですが、std::function あたりが使えるようになると、一気に多くの C++ ライブラリが直接リンクできるようになりそうですね。
extern(C++, "std")
{
template std_function(F)
{
pragma(mangle, "function")
class std_function
{
// member variables and functions
}
}
}
template ScopeClass(C , string name)
{
enum ns = __traits(getCppNamespaces,C);
extern(C++, class) extern(C++,(ns))
{
pragma(mangle, C, name)
struct ScopeClass
{
char[__traits(classInstanceSize, C)] buffer;
// member variables and functions
}
}
}
alias FuncType = void function(int);
alias RawFuncType = typeof(*FuncType.init);
// Mangles as `void funk(std::function<void(int)> a)`
extern(C++) void funk( ScopeClass!(std_function!(RawFuncType)),"function") a );
強化 : while (auto n = expression) がサポートされました
こちらは構文的な強化です。
元々 if 文では if (auto n = expression) といった形で条件式のところに変数宣言が置けました。
何かポインタ変数を宣言して、有効ならそれを使って何かをする、といった用途でよく見るパターンです。
そして今回の強化により、while (auto n = expression) というまったく同じ形で変数宣言が書けるようになりました。
ツリー形式のデータで親を辿っていくとか、リンクリストみたいなデータ構造の実装で上手く使えそうですね。
ちなみにこれは ChangeLog の抜粋ですが、実質以下の糖衣構文となっています。なるほど、という感じでした。
while (true)
{
if (auto n = expression)
{ /* loop body */ }
else
{ break; }
}
ライブラリ変更点
非常に盛沢山なので、少し内容が薄いのはご容赦を。
変更 : pow(f, -2) および f ^^ -2 の実装が変更されました
-2 乗ということで、$\frac{1}{x^2}$ に限定された変更です。
元々の実装にバグがあり、入力値によっては最下位ビットに誤りがあったということです。
厳密に最下位ビットにまで依存するコードは少ないかと思われますが、std.math の isClose などを使い、できるだけ厳密値に依存しない実装としていただければ、といったところです。
変更 : FieldNameTuple は、インターフェースに対して空のタプルを返すようになりました
std.traits には、任意の型に対してフィールドのメンバー名を取り出す FieldNameTuple というテンプレートがあります。
これをインターフェース型に適用したとき、コンパイル時に生成される内部型が読み取られることがあった、ということでそれらが読み取れないように挙動が変更されています。
具体的にどのようなケースがあったのかは調べ切れませんでしたが、新しい実装では単に AliasSeq!() と空のタプルが返されるようです。
変更 : Fields(旧 FieldTypeTuple)は、インターフェースに対して空のタプルを返すようになりました
前述の FieldNameTuple と同様ですので省略します。
フォーマット処理の強化
今回かなり大規模な更新がありました。
新しい書式化文字列の追加、パフォーマンスの改善、挙動の変更と盛りだくさんです。
強化 : 中央寄せフォーマットが追加されました
数値など桁指定可能なフォーマットに対し、実際の桁が足りていない場合は中央寄せできる機能が増えました。
%8d としていたところ、以下のように = 記号を書き足して %=8d とすると中央寄せになります。
assert(format!"|%=8d|"(1234) == "| 1234 |");
また、値が奇数桁になるような場合は1桁左にずらしたくなるようなことがあります。
これは %=-8d と書くと1桁左に詰まります。
assert(format!"|%=-8d|"(123) == "| 123 |");
assert(format!"|%=8d|"(123) == "| 123 |");
公式のサンプルでも意識されていそうですが、Markdownのテーブルなど生成するのが大変楽になりそうですね。
1回文字列化して桁数を調べたりなど、面倒なパディング計算が不要になるだけでもすごくありがたい気がします。
強化 : 書式化の際に %e、%f、%g、および %a を使って整数型のフォーマットが可能になりました
%e, %f, %g, %a は浮動小数点数に対する書式化文字列です。
以前は %f などの指定に整数型を渡すとエラーになっていたものが、今後は普通に表示されるようになります。
assert(format!"%.3e"(ulong.max) == "1.845e+19");
assert(format!"%.3,3f"(ulong.max) == "18,446,744,073,709,551,615.000");
assert(format!"%.3g"(ulong.max) == "1.84e+19");
assert(format!"%.3a"(ulong.max) == "0x1.000p+64");
こちらも auto で型が変わりやすいところで %f など指定しておけば良いのでプログラミングしやすくなって良いですね。
強化 : コンパイル時に浮動小数点数をフォーマットすることができるようになりました
なりました!
これが地味に嬉しいのは std.format だけでなく std.conv の to や text にも影響するところです。
今までコンパイル時処理が難しかったソースコード生成や定数埋め込みの部分も楽に書けるようになる見込みが出てきますね。
改善 : 浮動小数点数をフォーマットすると、GCでの割り当てが行われなくなりました
一連の強化の一環で、GC割り当てが削減されました。
未だ残っているのは例外割り当ての部分です。
逆に言えば、 format!"%d"("TEST") といった感じでテンプレート引数にして、コンパイルエラーにしてしまえば問題は起きません。
ちなみにGCを使わない例外については、延期されている DIP1008 にて作業中の課題となっています。
変更 : std.format で、一部の real 型は double にダウンキャストされます
std.format は内部的に libc の snprintf が呼び出されていましたが、これが D で書かれた浮動小数点数のフォーマット用ルーチンに置き換えられました。
これらの関数は、double、64-bit-reals、80-bit-reals (x87-reals) に対してのみ動作します。
つまり、主要なプラットフォームではありませんが、これ以外の real 型の実装に対しては double にダウンキャストして実行されるため出力精度が低下する可能性がある、ということです。
多くの方に影響はありませんが、特殊な環境であれば注意が必要です。
改善 : std.format のドキュメントが完全に作り直されました
後述のモジュール分割に伴い、いくつかの点でドキュメントの見直しが行われました。
主に文法を最新版に合わせて集成、書式化文字列に関する説明の改善、例の追加、の3点です。
構成はそこまで変わらないので以前と同じように読めるかと思います。
-
std.format
変更 : モジュールが分割されました
std.format と std.math モジュールが大きくなってきていたため、より小さなモジュールに分割されました。
細かく見ると、以下のようなサブモジュールに分割されています。
-
std.format-
std.format.spec- フォーマット文字列に関するシンボル(
FormatSpec構造体やsingleSpecテンプレートなど)
- フォーマット文字列に関するシンボル(
-
std.format.read- 入力の読み取りに関するシンボル(
formattedReadテンプレートやunformatValueテンプレートなど)
- 入力の読み取りに関するシンボル(
-
std.format.write- 出力の書き込みに関するシンボル(
formattedWriteテンプレートやformatValueテンプレートなど)
- 出力の書き込みに関するシンボル(
-
-
std.math-
std.math.constants- 数学定数(主に
PIなど)
- 数学定数(主に
-
std.math.algebraic- 基本的な代数関数(主に
absやsqrtなど)
- 基本的な代数関数(主に
-
std.math.trigonometry- 三角関数(主に
sinやcosなど)
- 三角関数(主に
-
std.math.rounding- 丸めに関する関数(主に
ceilやfloorなど)
- 丸めに関する関数(主に
-
std.math.exponential- 指数関数および対数関数(主に
pow、expやlog)
- 指数関数および対数関数(主に
-
std.math.remainder- 剰余に関する関数(主に
fmodなど)
- 剰余に関する関数(主に
-
std.math.operations- 浮動小数点数の操作(主に
isClose、nextUpやfmin)
- 浮動小数点数の操作(主に
-
std.math.traits- 浮動小数点の性質に関するもの(主に
isNaNやisSubnormal)
- 浮動小数点の性質に関するもの(主に
-
std.math.hardware- ハードウェア制御に関するもの(主に
IeeeFlagsやFloatingPointControl)
- ハードウェア制御に関するもの(主に
-
ちなみに、今まで通り import std.format や import std.math といった呼び出しは可能なので、使い勝手が変わることはありません。
それぞれ package.d というモジュールの仕組みに従っており、 std/format/package.d や std/math/package.d パスのファイルから、個々のサブモジュールを public import することで互換性が維持されています。
参考 : https://dlang.org/spec/module.html#package-module
強化 : 新しいモジュールとして std.sumtype が追加されました
今回の目玉となる強化です。
元々サードパーティ製のライブラリであった sumtype という便利ライブラリが標準に取り込まれました。
この sumtype というのは文字通り「型の和」ということで、 A or B or C のような「いずれかの型を含めることができる型」を作り出す機能を表しています。
ざっくり使い方としては以下のようになります。
import std.sumtype;
alias Value = SumType!(long, double, string);
Value v = 10;
v.match!(
(long n) { writeln("long!"); },
(double v) { writeln("double!"); },
(string s) { writeln("string!"); },
);
使い方については以下の記事にまとめてありますので、こちら見ていただければと思います。
- 安全なunion、SumTypeの紹介と使い方
強化: splitWhen が std.algorithm.iteration に追加されました
std.algorithm の一部として、splitWhen というテンプレート関数が追加されました。
これは、1つの前方レンジ(Forward Range)に対して分割位置を判定するための条件を渡し、複数のレンジに分割する関数です。
このテンプレート関数には、2つの要素を受け取り分割位置で true を返すような関数を指定します。
例を見ていただくとわかりやすいです。
import std.algorithm.comparison : equal;
import std.range : dropExactly;
auto source = [4, 3, 2, 11, 0, -3, -3, 5, 3, 0];
auto result1 = source.splitWhen!((a,b) => a <= b);
assert(result1.save.equal!equal([
[4, 3, 2],
[11, 0, -3],
[-3],
[5, 3, 0]
]));
また、これは元々あった chunkBy とほぼ同じ動きですが、chunkBy で指定する述語はもう少し厳しい条件があります。
一方、splitWhen のほうは Forward Range を要求しますが、述語部分の条件が緩いので扱いやすくなっています。
今後どちらを使えば良いというものではなく、splitWhen でダメなら chunkBy にしてみる、といった使い分けになってきます。
どちらでも動くようなら速度計測をしたほうが良いかもしれませんね。
その他、どのように条件が厳しいのかは詳しくは chunkBy のドキュメントを確認してください。
-
std.algorithm.iteration.chunkBy
DUB変更点
強化 : dub の設定ファイルに省メモリコンパイルのオプションが追加されました
コンパイル時のメモリ消費を抑えるため、DMD や LDC でサポートされる -lowmem というフラグを既定で有効にする設定が追加されました。
これは DUB が持つ settings.json という設定ファイルで defaultLowMemory というキーを設定することで有効にできます。
{
"defaultLowMemory": true
}
settings.json の内容については以下に説明があります。
- DUB settings.json
また、settings.json は以下のパスに置かれているものを認識するようになっています。
Windows
%ProgramData%\dub\settings.json<dub executable folder>\..\etc\dub\settings.json%APPDATA%\dub\settings.json%ROOT_PACKAGE_DIR%\dub.settings.json
Linux
/var/lib/dub/settings.json<dub executable folder>/../etc/dub/settings.json~/.dub/settings.json$ROOT_PACKAGE_DIR/dub.settings.json
非推奨または廃止される機能
今回新たに非推奨となる機能は7点、また以前から非推奨だった機能が5つ廃止されます。
色々あってリリース期間があいているのもあり、大規模アップデートといった様相ですが、正直そこまで影響が大きいものはほとんどありません。
非推奨
-
bodyキーワードの使用が非推奨になりました - インポートを回避するために完全修飾名を使用するケースが非推奨となりました
- 複素数型および虚数型が非推奨となりました
-
ImplicitConversionTargetsは非推奨となり、代わりにAllImplicitConversionTargetsを使用するようになります -
std.format:enforceValidFormatSpecは非推奨となりました -
std.format:formatElementは非推奨となりました -
std.format:unformatElementは非推奨となりました
削除
- 曖昧な三項式の非推奨期間が終了し削除されました
-
std.typecons.Nullable: 非推奨のエイリアスであるget thisが削除されました -
std.datetimeにあった古いベンチマーク機能が削除されました -
std.exception.enforceExが削除されました -
std.range.Transposed: 非推奨のメンバーであるsaveが削除されました
非推奨 : body キーワードの使用が非推奨になりました
この body キーワードは、関数の事前条件や事後条件で使う in と out に対し、関数本体などの実装部分を表すときにマーカーとなるキーワードでした。
やや今更感がある方もいるかもしれませんが、これは2016年に受理された DIP1003 という改善提案を受けたものです。
たとえば以下のように使われます。
void sqrt(float x)
in // ここから事前条件
{
assert(x >= 0);
}
out(y) // ここから事後条件
{
assert(isClose(y * y, x));
}
body // ここから実装
{
// impl
}
この body という命名が様々な外部ライブラリで構造体のメンバーなどに使われやすく、ヘッダー変換時などで微妙だと前々から言われていました。
そして今回ついに非推奨に踏み切った格好になります。
これは代わりに do キーワードで代替できるようになっています。
viud sqrt(float x)
in
{
assert(x > 0);
}
do // body の代わりに do が利用できる
{
// impl
}
非推奨 : インポートを回避するために完全修飾名を使用するケースが非推奨となりました
何のことかというと、モジュールを import していなくても完全修飾名を使うと import 済みかのように扱える機能が非推奨になった、というものです。
割と大穴なので以前にも似たような問題が修正されたのですが、今回も似たようなパターンです。
例としては以下のようになります。
import std.algorithm;
// v2.071で非推奨となり、v2.084でエラーとなったパターンです
auto n = std.range.Take!(int[]); // Error: undefined identifier `range` in package `std`...
// 今回はこのような用法が非推奨となります。 v2.106 からエラーになる予定です。
std.range.Take!(int[]) s;
import std.algorithm しか書かれていないのに、std.range.Take を使おうとしているので何かおかしいですね。
このような場合は普通に import したりすべきです。
ちなみにエラーメッセージに static import を使うべき、というメッセージが出るのでそれに従えばOKです。
非推奨 : 複素数型および虚数型が非推奨となりました
D言語には組み込みの複素数型がありました。純虚数型の ifloat や複素数型の cdouble などです。
今後は標準ライブラリの std.complex を使うべきということで、そちらを利用しましょう。
個人的には 1 + 2i と書けたり大変好きだったんですが、コンパイラに入れるメリットが少ないという感じでしょうか。
長らく検討されていた内容でもあるので仕方なし、といったところです。
非推奨 : std.format : enforceValidFormatSpec は非推奨となりました
元々公開する意図のない関数が誤って公開されていた、というものです。
v2.107.0にて削除される予定です。
こちら何か便利で汎用的な関数でもなく、ドキュメントにも無いので特に言うことはない感じです。
非推奨 : std.format : formatElement は非推奨となりました
こちらも元々公開する意図のない関数が誤って公開されていた、というものです。
v2.107.0にて削除される予定です。
ドキュメントにも記載はないのですが、便利関数であるため代替方法が例示されています。
公式サンプルを少し調整していますが、以下のようになります。
// formatElement(sink, value, fmt); としていたコードの代替例
import std.range : only, put;
.put(sink, format!("%("~fmt~"%)")(only(value)));
非推奨 : std.format : unformatElement は非推奨となりました
こちらも元々公開する意図のない関数が誤って公開されていた、というものです。
v2.107.0にて削除される予定です。
ドキュメントにも記載はないのですが、便利関数であるため代替方法が例示されています。
文字や文字列に対しては std.conv にある parseElement を使い、それ以外のケースについては std.format.read の unformatValue を用いれば良いとのことです。
削除 : 曖昧な三項式の非推奨期間が終了し削除されました
みんなが大好き批判の的になりがちな、三項演算子と代入の組み合わせパターンで出てくる警告になるケースが廃止されました。
v2.082.0 (2018-09-01) から警告になっていて、括弧を付けるよう言われるので、多くの人はあまり気にしていないかと思われます。
曖昧な例としてあるのが以下のものです。
true ? stt = "AA" : stt = "BB"
さて、これは「一見 if 文に見える」という意地の悪いサンプルですが、文法的に括弧を補うと以下のようになります。
(true ? (stt = "AA") : stt) = "BB"
ちょっと読みづらいですが、要するに必ず "BB" になってしまう、という驚きの結果になります。
CやC++でも同じであり許容されるコードなので認められていましたが、今後エラーを見かけたら適切に括弧を補うようにしましょう。
エラー例
ちなみにエラーが出ると以下のようになります。
Error: `b ? stt = "AA" : stt` must be surrounded by parentheses when next to operator `=`
2番目の = の前までで切り取られてしまっているので何か変だと気づけますね。
適切に括弧で囲みなさいと怒られます。
削除 : std.typecons.Nullable : 非推奨のエイリアスである alias get this が削除されました
今回の削除の中でも割と影響の大きなポイントです。
std.typecons.Nullable はいわゆる Optional などと呼ばれる「値がないことを示せるようにする型」です。
クラスに対する null と同じような意味合いで名づけられていますが、それをより安全に使えるようにしたのが今回の Nullable です。
さて、今回はこの Nullable で提供されていた alias get this という暗黙変換に関わる宣言が取り除かれました。
これがどういう影響を与えるかというと、以下のようなコードがコンパイルできなくなります。
void foo(int n) { }
import std.typecons : Nullable;
Nullable!int n = null;
foo(n); // 引数に Nullable!int を渡している
このサンプルに対し、今までは以下のような警告が出ていました。
onlineapp.d(11): Deprecation: function `std.typecons.Nullable!int.Nullable.get_` is deprecated - Implicit conversion with `alias Nullable.get this` will be removed after 2.096. Please use `.get` explicitly.
関数呼び出しのところで .get を使いなさいと言われます。
言われた通りに直すと警告が消えます。
foo(n.get());
さらに言えば、.get は値が null だった場合に例外を発生させるので、事前に値が null かどうかをチェックしてから呼び出します。
if (!n.isNull) {
foo(n.get());
}
やや手間ですが、これでバッチリ安全ですね。
削除 : std.datetime にあった古いベンチマーク機能が削除されました
v2.077.0 から非推奨となっていた std.datetime のベンチマーク機能(StopWatch など)が削除されました。
しかしこちら通常利用の範囲では特に対応の必要はありません。
というのも、 std.datetime.stopwatch というモジュールに同名の定義があり、import std.datetime だけして使っている場合はこちらも読み込まれ、ほぼ同じ記述で動くように配慮されているためです。
時期にして約2年前から警告になっていますので、目にしたら直接 std.datetime.stopwatch などに書き換えていたかと思いますが、今後は特に気にしなくて良くなります。
削除 : std.exception.enforceEx が削除されました
これは v2.079 から非推奨となっており、代わりに std.exception.enforce を使うことが推奨されています。
削除 : std.range.Transposed : 非推奨のメンバーである save が削除されました
std.range.Transposed というのは2次元配列を転置したかのように扱うことができるレンジです。
削除された save というメンバーは、レンジの状態をコピーして残すためのもので、通常の InputRange に加えてこの save があると ForwardRange と呼ばれます。
この Transposed というレンジは内部に持つレンジを透過的に操作するタイプのレンジであるため、一度進めたレンジを一時保存することができません。
今まで特に警告が出ていなかった方は特に関係ありませんが、もしエラーが出るようであれば save を使わないように書き換える必要があります。
まとめ
予定していた2ヶ月おきのリリースルールから少しずれ込み、3ヶ月あけてのリリースとなりました。
今回はフォーマット系の強化や sumtype の追加、モジュール分割などの比較的大規模な修正がまとめて入れられており、ここ数年では大規模なリリースの1つとなったのではないかと思います。
また、今回は利用できるようになっていませんが裏ではC言語連携の強化が行われており、次回も話題に事欠かないリリースとなりそうです。
次回のリリース予定は 2021/08/01 とのことです。
夏真っ盛りかと思いますが、期待して待ちたいと思います!