The YSLib project - main repository
Revision | 5575c569a1e8314f0c53d4202d9732f50a482e9f (tree) |
---|---|
Time | 2022-04-26 02:57:13 |
Author | FrankHB <frankhb1989@gmai...> |
Commiter | FrankHB |
更新主分支版本: build 943 rev 10 。
@@ -54,7 +54,7 @@ | ||
54 | 54 | (eval (string->symbol (SHBuild_SDot_ var)) env); |
55 | 55 | |
56 | 56 | "#", "Whole script variables."; |
57 | -$def! extra-opts (restv (() cmd-get-args)); | |
57 | +$def! extra-opts restv (() cmd-get-args); | |
58 | 58 | $def! (host-arch host-os) () SHBuild_GetPlatformStrings; |
59 | 59 | $assert-nonempty host-arch, |
60 | 60 | $assert-nonempty host-os; |
@@ -139,6 +139,9 @@ | ||
139 | 139 | $let ((outdir get-lib-outdir_ dynamic debug)) |
140 | 140 | ( |
141 | 141 | putss "Building " target-name " ..."; |
142 | + "XXX", "No need to set %LDFLAGS, since it is always set in the", | |
143 | + " call to 'build-with-conf-opt' for dynamic libraries."; | |
144 | + $if dynamic (safeenv-restore "LIBS"); | |
142 | 145 | build-with-conf-opt outdir host-os debug dynamic |
143 | 146 | "-xid,alternative -xid,data -xid,include -xid,Android" #f |
144 | 147 | ld-ext-noadjust_ ($lambda (CXX CXXFLAGS SHBOPT LIBPFX) |
@@ -207,7 +210,7 @@ | ||
207 | 210 | ( |
208 | 211 | $def! extra () add-fix-flags; |
209 | 212 | $unless (string-empty? extra) (putss |
210 | - "WARNING: Additional option are prepended to work" | |
213 | + "WARNING: Additional options are prepended to work" | |
211 | 214 | " around the YFramework build: '" extra "'."); |
212 | 215 | extra |
213 | 216 | ); |
@@ -322,8 +325,6 @@ | ||
322 | 325 | "NOTE", "Following settings are shared by stage 2 builds.", |
323 | 326 | "See user documentation Tools/Scripts.zh-CN.md.", |
324 | 327 | "Only SHBuild environment variables not otherwise set need to be restored."; |
325 | -safeenv-restore "LDFLAGS", | |
326 | -safeenv-restore "LIBS"; | |
327 | 328 | safeenv-set "LIBS" (SHBuild_TrimOptions_ (cons-cmd (safeenv-get "LIBS") "-L" |
328 | 329 | (system-quote-m_ SR_Lib_) "-lYFramework -lYBase")); |
329 | 330 | $def! INCLUDES_ ++ (safeenv-get "INCLUDES") " -I" (system-quote-m_ SR_Include_); |
@@ -1,5 +1,5 @@ | ||
1 | 1 | #!/usr/bin/env bash |
2 | -# (C) 2014-2021 FrankHB. | |
2 | +# (C) 2014-2022 FrankHB. | |
3 | 3 | # Common options script to build projects in the shell. |
4 | 4 | |
5 | 5 | # NOTE: This is mainly for stage 1 SHBuild bootstrap and the test. The options |
@@ -51,8 +51,11 @@ | ||
51 | 51 | # also https://clang.llvm.org/docs/ClangCommandLineReference.html#linker-flags. |
52 | 52 | : "${LDFLAGS_STRIP:=-s}" |
53 | 53 | |
54 | +if [[ "$SHBuild_Host_OS" != 'Win32' ]]; then | |
55 | + : "${C_CXXFLAGS_EXT="-D_POSIX_C_SOURCE=200809L"}" | |
56 | +fi | |
54 | 57 | : "${C_CXXFLAGS_COMMON:= \ |
55 | -"-pipe $C_CXXFLAGS_GC $C_CXXFLAGS_ARCH -pedantic-errors"}" | |
58 | +"-pipe $C_CXXFLAGS_GC $C_CXXFLAGS_ARCH -pedantic-errors $C_CXXFLAGS_EXT"}" | |
56 | 59 | : "${C_CXXFLAGS_OPT_LV:=-O3}" |
57 | 60 | : "${C_CXXFLAGS_WARNING:="-Wall \ |
58 | 61 | -Wcast-align \ |
@@ -1,5 +1,5 @@ | ||
1 | 1 | #!/usr/bin/env bash |
2 | -# (C) 2014-2018, 2020-2021 FrankHB. | |
2 | +# (C) 2014-2018, 2020-2022 FrankHB. | |
3 | 3 | # SHBuild installation script. |
4 | 4 | |
5 | 5 | set -e |
@@ -22,12 +22,6 @@ | ||
22 | 22 | : "${YSLib_BaseDir:="$SHBuild_ToolDir/../.."}" |
23 | 23 | |
24 | 24 | SHBuild_Puts 'Configuring ...' |
25 | -: "${SHBuild_BuildOpt:="-xj,6"}" | |
26 | -export SHBuild_BuildOpt | |
27 | -: "${SHBuild_LogOpt:="-xlogfl,128"}" | |
28 | -export SHBuild_LogOpt | |
29 | -: "${SHBuild_Opt:="$SHBuild_LogOpt $SHBuild_BuildOpt"}" | |
30 | -export SHBuild_Opt | |
31 | 25 | : "${SHBuild_SysRoot:="$YSLib_BaseDir/sysroot"}" |
32 | 26 | YSLib_BaseDir=$(cd "$YSLib_BaseDir"; pwd) |
33 | 27 | export YSLib_BaseDir |
@@ -39,9 +33,6 @@ | ||
39 | 33 | : "${SHBuild_BuildDir:="$YSLib_BaseDir/build/$(SHBuild_GetBuildName)"}" |
40 | 34 | export SHBuild_BuildDir |
41 | 35 | SHBuild_Puts "Build directory is \"$SHBuild_BuildDir\"." |
42 | -if [[ -z ${SHBuild_UseRelease+x} ]]; then | |
43 | - SHBuild_UseRelease=true | |
44 | -fi | |
45 | 36 | SHBuild_Puts 'Done.' |
46 | 37 | |
47 | 38 | SHBuild_Puts 'Bootstraping ...' |
@@ -54,8 +45,7 @@ | ||
54 | 45 | # shellcheck disable=2154 |
55 | 46 | if [[ "$SHBuild_Rebuild_S1" == '' ]]; then |
56 | 47 | if command -v "$S1_SHBuild" > /dev/null ; then |
57 | - SHBuild_Puts \ | |
58 | - "Found stage 1 SHBuild \"$S1_SHBuild\", building skipped." | |
48 | + SHBuild_Puts "Found stage 1 SHBuild \"$S1_SHBuild\", building skipped." | |
59 | 49 | else |
60 | 50 | SHBuild_Puts 'Stage 1 SHBuild not found.' |
61 | 51 | SHBuild_Rebuild_S1_=true |
@@ -71,18 +61,19 @@ | ||
71 | 61 | SHBuild_Puts 'Finished building stage 1 SHBuild.' |
72 | 62 | fi |
73 | 63 | |
74 | -# Stage 1 SHBuild done. Following code call stage 1 SHBuild by default. | |
64 | +# NOTE: Stage 1 SHBuild done. The following code call stage 1 SHBuild by | |
65 | +# default. | |
75 | 66 | : "${SHBuild:="$S1_SHBuild"}" |
76 | 67 | SHBuild_AssertNonempty SHBuild |
77 | 68 | # XXX: Variable %SHBuild_Opt can have whitespaces. |
78 | 69 | # XXX: Variables here are assigned locally and guaranteed to be expanded to the |
79 | 70 | # same values to avoid 'export' pollution. |
80 | -# shellcheck disable=2086,2097,2098,2154 | |
71 | +# shellcheck disable=2097,2098,2154 | |
81 | 72 | SHBuild="$SHBuild" SHBuild_Epoch=0 SHBuild_ToolDir="$SHBuild_ToolDir" \ |
82 | 73 | SHBuild_NoStatic="$SHBuild_NoStatic" \ |
83 | 74 | SHBuild_NoDynamic="$SHBuild_NoDynamic" \ |
84 | 75 | SHBuild_UseDebug="$SHBuild_UseDebug" \ |
85 | 76 | SHBuild_UseRelease="$SHBuild_UseRelease" \ |
86 | 77 | "$SHBuild" -xcmd,RunNPLFile \ |
87 | - "$SHBuild_ToolDir/SHBuild-YSLib-build.txt" -- $SHBuild_Opt | |
78 | + "$SHBuild_ToolDir/SHBuild-YSLib-build.txt" -- "$@" | |
88 | 79 |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file Dependency.h |
12 | 12 | \ingroup NPL |
13 | 13 | \brief 依赖管理。 |
14 | -\version r549 | |
14 | +\version r557 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 623 |
17 | 17 | \par 创建时间: |
18 | 18 | 2015-08-09 22:12:37 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-26 09:25 +0800 | |
20 | + 2022-04-20 19:22 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -205,10 +205,19 @@ | ||
205 | 205 | */ |
206 | 206 | //@{ |
207 | 207 | /*! |
208 | +\brief 加载续延模块。 | |
209 | +\since build 943 | |
210 | + | |
211 | +加载一等续延和相关操作。 | |
212 | +*/ | |
213 | +YF_API void | |
214 | +LoadModule_std_continuations(REPLContext&); | |
215 | + | |
216 | +/*! | |
208 | 217 | \brief 加载代理求值模块。 |
209 | 218 | \since build 856 |
210 | 219 | |
211 | -加载 promise 等类型和延迟求值等操作。 | |
220 | +加载 promise 类型和延迟求值等操作。 | |
212 | 221 | */ |
213 | 222 | YF_API void |
214 | 223 | LoadModule_std_promises(REPLContext&); |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA.h |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA 公共接口。 |
14 | -\version r9422 | |
14 | +\version r9455 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 663 |
17 | 17 | \par 创建时间: |
18 | 18 | 2016-01-07 10:32:34 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-04-05 15:16 +0800 | |
20 | + 2022-04-22 19:31 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -446,7 +446,7 @@ | ||
446 | 446 | \since build 939 |
447 | 447 | */ |
448 | 448 | inline PDefH(void, ClearCombiningTags, TermNode& term) ynothrowv |
449 | - ImplExpr(EnsureValueTags(term.Tags), AssertValueTags(term)) | |
449 | + ImplExpr(yimpl(EnsureValueTags(term.Tags)), AssertValueTags(term)) | |
450 | 450 | |
451 | 451 | /*! |
452 | 452 | \brief 判断项是规约合并项。 |
@@ -454,8 +454,13 @@ | ||
454 | 454 | */ |
455 | 455 | YB_ATTR_nodiscard YB_PURE inline |
456 | 456 | PDefH(bool, IsCombiningTerm, const TermNode& term) ynothrow |
457 | - ImplRet(!(term.empty() | |
458 | - || (term.Value && !IsTyped<TokenValue>(term.Value.type())))) | |
457 | +#if true | |
458 | + // XXX: This might be slightly more efficient. | |
459 | + ImplRet(IsBranch(term) && (IsList(term) || IsTyped<TokenValue>(term))) | |
460 | +#else | |
461 | + ImplRet(IsBranchedList(term) | |
462 | + || (IsBranch(term) && IsTyped<TokenValue>(term))) | |
463 | +#endif | |
459 | 464 | |
460 | 465 | |
461 | 466 | /*! \defgroup TermAccessAuxiliary Term Access Auxiliary API |
@@ -631,7 +636,6 @@ | ||
631 | 636 | 若引用计数来源都是 Environment 、EnvironmentReference 或 TermReference , |
632 | 637 | 则表示正常;否则,使用 YTraceDe 输出错误消息。 |
633 | 638 | 注意绑定析构顺序不确定,可能导致依赖不确定而误报。 |
634 | -因对性能有影响,默认仅调试配置下启用。 | |
635 | 639 | */ |
636 | 640 | #ifndef NPL_NPLA_CheckEnvironmentReferenceCount |
637 | 641 | # ifndef NDEBUG |
@@ -1141,23 +1145,6 @@ | ||
1141 | 1145 | IsReferenceTerm(const TermNode&); |
1142 | 1146 | |
1143 | 1147 | /*! |
1144 | -\brief 判断项(的值数据成员)是否表示被绑定的左值引用。 | |
1145 | -\sa TermReference::IsReferencedLValue | |
1146 | -\since build 871 | |
1147 | - | |
1148 | -判断项是否表示引用且 TermReference::IsReferencedLValue 的结果为 true 。 | |
1149 | -*/ | |
1150 | -YB_ATTR_nodiscard YF_API YB_PURE bool | |
1151 | -IsBoundLValueTerm(const TermNode&); | |
1152 | - | |
1153 | -/*! | |
1154 | -\brief 判断项(的值数据成员)是否表示未折叠的引用。 | |
1155 | -\since build 869 | |
1156 | -*/ | |
1157 | -YB_ATTR_nodiscard YF_API YB_PURE bool | |
1158 | -IsUncollapsedTerm(const TermNode&); | |
1159 | - | |
1160 | -/*! | |
1161 | 1148 | \brief 判断项(的值数据成员)是否表示非引用项或唯一引用。 |
1162 | 1149 | \sa TermReference::IsUnique |
1163 | 1150 | \since build 859 |
@@ -1180,6 +1167,23 @@ | ||
1180 | 1167 | */ |
1181 | 1168 | YB_ATTR_nodiscard YF_API YB_PURE bool |
1182 | 1169 | IsTemporaryTerm(const TermNode&); |
1170 | + | |
1171 | +/*! | |
1172 | +\brief 判断项(的值数据成员)是否表示被绑定的左值引用。 | |
1173 | +\sa TermReference::IsReferencedLValue | |
1174 | +\since build 871 | |
1175 | + | |
1176 | +判断项是否表示引用且 TermReference::IsReferencedLValue 的结果为 true 。 | |
1177 | +*/ | |
1178 | +YB_ATTR_nodiscard YF_API YB_PURE bool | |
1179 | +IsBoundLValueTerm(const TermNode&); | |
1180 | + | |
1181 | +/*! | |
1182 | +\brief 判断项(的值数据成员)是否表示未折叠的引用。 | |
1183 | +\since build 869 | |
1184 | +*/ | |
1185 | +YB_ATTR_nodiscard YF_API YB_PURE bool | |
1186 | +IsUncollapsedTerm(const TermNode&); | |
1183 | 1187 | //@} |
1184 | 1188 | |
1185 | 1189 | //! \since build 859 |
@@ -2413,7 +2417,10 @@ | ||
2413 | 2417 | ImplRet(ystdex::ref_eq<>()(x, y)) |
2414 | 2418 | }; |
2415 | 2419 | |
2416 | - //! \brief 规约守卫:暂存当前动作序列。 | |
2420 | + /*! | |
2421 | + \ingroup guards | |
2422 | + \brief 规约守卫:暂存当前动作序列。 | |
2423 | + */ | |
2417 | 2424 | class YF_API ReductionGuard |
2418 | 2425 | { |
2419 | 2426 | private: |
@@ -2594,6 +2601,8 @@ | ||
2594 | 2601 | DefGetter(const ynothrow, Environment::BindingMap&, BindingsRef, |
2595 | 2602 | GetRecordRef().GetMapRef()) |
2596 | 2603 | DefGetter(const ynothrow, const ReducerSequence&, Current, current) |
2604 | + //! \since build 943 | |
2605 | + DefGetter(ynothrow, ReducerSequence&, CurrentRef, current) | |
2597 | 2606 | DefGetter(const ynothrow |
2598 | 2607 | -> decltype(std::declval<Reducer>().target_type()), auto, |
2599 | 2608 | CurrentActionType, IsAlive() ? current.front().target_type() |
@@ -2785,6 +2794,7 @@ | ||
2785 | 2794 | |
2786 | 2795 | /*! |
2787 | 2796 | \brief 转移第二参数指定的位置之前的当前动作序列的动作到第一参数。 |
2797 | + \pre 第二参数是当前动作的迭代器。 | |
2788 | 2798 | \since build 895 |
2789 | 2799 | */ |
2790 | 2800 | PDefH(void, Shift, ReducerSequence& rs, ReducerSequence::const_iterator i) |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA1.h |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA1 公共接口。 |
14 | -\version r9423 | |
14 | +\version r9447 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 472 |
17 | 17 | \par 创建时间: |
18 | 18 | 2014-02-02 17:58:24 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-29 18:07 +0800 | |
20 | + 2022-04-26 00:10 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -1088,6 +1088,13 @@ | ||
1088 | 1088 | PDefHOp(ReductionStatus, (), TermNode& term, ContextNode& ctx) const |
1089 | 1089 | ImplRet(CallN(Wrapping, term, ctx)) |
1090 | 1090 | |
1091 | + /*! | |
1092 | + \brief 调用上下文处理器。 | |
1093 | + \since build 943 | |
1094 | + */ | |
1095 | + ReductionStatus | |
1096 | + CallHandler(TermNode&, ContextNode&) const; | |
1097 | + | |
1091 | 1098 | private: |
1092 | 1099 | /*! |
1093 | 1100 | \pre 断言:对异步实现,参数指定的项和下一求值项相同。 |
@@ -1375,6 +1382,7 @@ | ||
1375 | 1382 | \exception bad_any_cast 异常中立:子对象引用持有的值不是 ContextHandler 类型。 |
1376 | 1383 | \throw ListReductionFailure 规约失败:枝节点的第一个子项不表示上下文处理器。 |
1377 | 1384 | \sa ContextHandler |
1385 | +\sa IsCombiningTerm | |
1378 | 1386 | \sa Reduce |
1379 | 1387 | \sa TermTags::Temporary |
1380 | 1388 | \since build 766 |
@@ -1389,13 +1397,14 @@ | ||
1389 | 1397 | 可使用 TermNode::SetContent 代替 LiftOther 提升项。 |
1390 | 1398 | */ |
1391 | 1399 | //@{ |
1392 | -//! \note 对不满足 IsCombiningTerm 的项直接返回 ReductionStatus::Regular 。 | |
1400 | +//! \note 对非规约合并项直接返回 ReductionStatus::Regular 。 | |
1393 | 1401 | YF_API ReductionStatus |
1394 | 1402 | ReduceCombined(TermNode&, ContextNode&); |
1395 | 1403 | |
1404 | +//! \pre 断言:第一参数是规约合并项。 | |
1405 | +//@{ | |
1396 | 1406 | /*! |
1397 | 1407 | \brief 规约列表合并项:同 ReduceCombined ,但只适用于枝节点。 |
1398 | -\pre 断言:项满足 IsCombiningTerm 。 | |
1399 | 1408 | \since build 882 |
1400 | 1409 | */ |
1401 | 1410 | YF_API ReductionStatus |
@@ -1409,6 +1418,7 @@ | ||
1409 | 1418 | YF_API ReductionStatus |
1410 | 1419 | ReduceCombinedReferent(TermNode&, ContextNode&, const TermNode&); |
1411 | 1420 | //@} |
1421 | +//@} | |
1412 | 1422 | |
1413 | 1423 | /*! |
1414 | 1424 | \brief 规约提取名称的叶节点记号。 |
@@ -1632,7 +1642,9 @@ | ||
1632 | 1642 | |
1633 | 1643 | /*! |
1634 | 1644 | \brief 设置参数指定的上下文为尾上下文。 |
1635 | -\note 在不支持 TCO 的实现忽略。 | |
1645 | +\pre 断言:第二参数是规约合并项。 | |
1646 | +\note 在不支持 TCO 的实现忽略设置上下文。 | |
1647 | +\sa IsCombiningTerm | |
1636 | 1648 | \since build 895 |
1637 | 1649 | */ |
1638 | 1650 | YF_API void |
@@ -1740,8 +1752,8 @@ | ||
1740 | 1752 | YB_ATTR_nodiscard YB_PURE inline _fCallable |
1741 | 1753 | NameTypedReducerHandler(_fCallable&& x, string_view desc) |
1742 | 1754 | { |
1743 | - return A1::NameExpandedHandler<Reducer, | |
1744 | - ReducerFunctionType>(yforward(x), desc); | |
1755 | + return A1::NameExpandedHandler<Reducer, ReducerFunctionType>(yforward(x), | |
1756 | + desc); | |
1745 | 1757 | } |
1746 | 1758 | //@} |
1747 | 1759 |
@@ -1841,20 +1853,23 @@ | ||
1841 | 1853 | using GKeptGuardAction = decltype(std::bind(KeepGuard<_tGuard>, |
1842 | 1854 | std::declval<_tGuard&>(), std::placeholders::_1)); |
1843 | 1855 | |
1844 | -//! \brief 创建保持环境守卫。 | |
1856 | +/*! | |
1857 | +\brief 转移保持环境守卫。 | |
1858 | +\since build 943 | |
1859 | +*/ | |
1845 | 1860 | //@{ |
1846 | 1861 | template<class _tGuard> |
1847 | 1862 | YB_ATTR_nodiscard inline GKeptGuardAction<_tGuard> |
1848 | -MakeKeptGuard(_tGuard& gd) | |
1863 | +MoveKeptGuard(_tGuard& gd) | |
1849 | 1864 | { |
1850 | 1865 | return A1::NameTypedReducerHandler(std::bind(KeepGuard<_tGuard>, |
1851 | 1866 | std::move(gd), std::placeholders::_1), "eval-guard"); |
1852 | 1867 | } |
1853 | 1868 | template<class _tGuard> |
1854 | 1869 | YB_ATTR_nodiscard inline GKeptGuardAction<_tGuard> |
1855 | -MakeKeptGuard(_tGuard&& gd) | |
1870 | +MoveKeptGuard(_tGuard&& gd) | |
1856 | 1871 | { |
1857 | - return A1::MakeKeptGuard(gd); | |
1872 | + return A1::MoveKeptGuard(gd); | |
1858 | 1873 | } |
1859 | 1874 | //@} |
1860 | 1875 | //@} |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA1Forms.h |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA1 语法形式。 |
14 | -\version r8664 | |
14 | +\version r8723 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 882 |
17 | 17 | \par 创建时间: |
18 | 18 | 2020-02-15 11:19:21 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-26 05:26 +0800 | |
20 | + 2022-04-25 18:07 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -48,6 +48,23 @@ | ||
48 | 48 | { |
49 | 49 | |
50 | 50 | /*! |
51 | +\brief 续延帧检查。 | |
52 | +\note 因对性能有影响,默认仅调试配置下启用。 | |
53 | +\sa Continuation | |
54 | +\since build 943 | |
55 | + | |
56 | +若定义为 true ,则在续延调用时断言被捕获的帧在当前动作序列中存在。 | |
57 | +*/ | |
58 | +#ifndef NPL_NPLA1Forms_CheckContinuationFrames | |
59 | +# ifndef NDEBUG | |
60 | +# define NPL_NPLA1Forms_CheckContinuationFrames true | |
61 | +# else | |
62 | +# define NPL_NPLA1Forms_CheckContinuationFrames false | |
63 | +# endif | |
64 | +#endif | |
65 | + | |
66 | + | |
67 | +/*! | |
51 | 68 | \brief 判断字符串值是否可构成符号。 |
52 | 69 | \note 不依赖具体字符集。 |
53 | 70 | \note 不排除假阴性结果。 |
@@ -1666,8 +1683,8 @@ | ||
1666 | 1683 | /*! |
1667 | 1684 | \since build 898 |
1668 | 1685 | |
1669 | -对 <object1> 指定的抽象列表进行处理,取得部分和。 | |
1670 | -当谓词 <predicate> 对列表应用结果不为假时,处理的结果为参数 <object2> 指定的对象; | |
1686 | +对 \<object1> 指定的抽象列表进行处理,取得部分和。 | |
1687 | +当谓词 \<predicate> 对列表应用结果不为假时,处理的结果为参数 \<object2> 指定的对象; | |
1671 | 1688 | 否则,继续处理抽象列表中余下的元素。 |
1672 | 1689 | 处理抽象的列表的操作通过余下的应用子分别定义: |
1673 | 1690 | 取列表头、取列表尾和部分和的二元合并操作。 |
@@ -1698,11 +1715,11 @@ | ||
1698 | 1715 | \brief 在列表元素上应用右结合的二元操作。 |
1699 | 1716 | \since build 899 |
1700 | 1717 | |
1701 | -对 <list> 指定的列表进行处理,取得部分和。 | |
1718 | +对 \<list> 指定的列表进行处理,取得部分和。 | |
1702 | 1719 | 当列表非空时,处理的结果为参数 <object> 指定的对象; |
1703 | 1720 | 否则,继续处理列表中余下的元素。 |
1704 | -参数 <applicative> 定义部分和的二元合并操作,应为列表构造器。 | |
1705 | -名称中的 1 指 <list> 参数的个数。 | |
1721 | +参数 \<applicative> 定义部分和的二元合并操作,应为列表构造器。 | |
1722 | +名称中的 1 指 \<list> 参数的个数。 | |
1706 | 1723 | |
1707 | 1724 | 参考调用文法: |
1708 | 1725 | <pre>foldr1 \<applicative> \<object> \<list></pre> |
@@ -1942,6 +1959,58 @@ | ||
1942 | 1959 | YF_API ReductionStatus |
1943 | 1960 | ImportRef(TermNode&, ContextNode&); |
1944 | 1961 | |
1962 | +/*! | |
1963 | +\since build 943 | |
1964 | + | |
1965 | +若不存在相等的元素,结果为空列表右值;否则是同 first% 访问得到的等价的列表的值。 | |
1966 | +*/ | |
1967 | +//@{ | |
1968 | +/*! | |
1969 | +\brief 取关联列表中和参数的引用相同的元素。 | |
1970 | +\since build 943 | |
1971 | + | |
1972 | +以 eq? 依次判断第二参数指定的列表中的第一个元素是否和第一参数指定的元素等价。 | |
1973 | + | |
1974 | +参考调用文法: | |
1975 | +<pre>assq \<object> \<list></pre> | |
1976 | +*/ | |
1977 | +YF_API ReductionStatus | |
1978 | +Assq(TermNode&); | |
1979 | + | |
1980 | +/*! | |
1981 | +\brief 取关联列表中和参数的值相等的元素。 | |
1982 | + | |
1983 | +以 eqv? 依次判断第二参数指定的列表中的第一个元素是否和第一参数指定的元素等价。 | |
1984 | + | |
1985 | +参考调用文法: | |
1986 | +<pre>assv \<object> \<list></pre> | |
1987 | +*/ | |
1988 | +YF_API ReductionStatus | |
1989 | +Assv(TermNode&); | |
1990 | +//@} | |
1991 | + | |
1992 | + | |
1993 | +//! \since build 943 | |
1994 | +//@{ | |
1995 | +/*! | |
1996 | +\brief 捕获一次续延,具现为一等续延作为参数调用合并子。 | |
1997 | + | |
1998 | +参考调用文法: | |
1999 | +<pre>call/1cc \<combiner></pre> | |
2000 | +*/ | |
2001 | +YF_API ReductionStatus | |
2002 | +Call1CC(TermNode&, ContextNode&); | |
2003 | + | |
2004 | +/*! | |
2005 | +\brief 捕获一次续延,具现为一等续延作为参数调用合并子。 | |
2006 | + | |
2007 | +参考调用文法: | |
2008 | +<pre>continuation->applicative \<continuation></pre> | |
2009 | +*/ | |
2010 | +YF_API ReductionStatus | |
2011 | +ContinuationToApplicative(TermNode&); | |
2012 | +//@} | |
2013 | + | |
1945 | 2014 | |
1946 | 2015 | /*! |
1947 | 2016 | \brief 调用 UTF-8 字符串的系统命令,并保存 int 类型的结果到项的值中。 |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file Dependency.cpp |
12 | 12 | \ingroup NPL |
13 | 13 | \brief 依赖管理。 |
14 | -\version r6880 | |
14 | +\version r6902 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 623 |
17 | 17 | \par 创建时间: |
18 | 18 | 2015-08-09 22:14:45 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-31 02:43 +0800 | |
20 | + 2022-04-25 18:07 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -66,7 +66,7 @@ | ||
66 | 66 | #include <regex> // for std::regex, std::regex_replace, std::regex_match; |
67 | 67 | #include <ostream> // for std::endl; |
68 | 68 | #include "NPLA1Internals.h" // for NPL_Impl_NPLA1_Enable_Thunked, |
69 | -// ReduceSubsequent, A1::RelayCurrentNext, MakeKeptGuard; | |
69 | +// ReduceSubsequent, A1::RelayCurrentNext, MoveKeptGuard; | |
70 | 70 | #include YFM_YSLib_Core_YCoreUtilities // for YSLib::LockCommandArguments, |
71 | 71 | // YSLib::FetchCommandOutput, YSLib::RandomizeTemplatedString; |
72 | 72 | #include <ystdex/string.hpp> // for ystdex::begins_with; |
@@ -560,11 +560,11 @@ | ||
560 | 560 | RegisterUnary(ctx, "branch?", ComposeReferencedTermOp(IsBranch)); |
561 | 561 | RegisterUnary(ctx, "branchv?", IsBranch); |
562 | 562 | RegisterUnary(ctx, "reference?", IsReferenceTerm); |
563 | - RegisterUnary(ctx, "bound-lvalue?", IsBoundLValueTerm); | |
564 | - RegisterUnary(ctx, "uncollapsed?", IsUncollapsedTerm); | |
565 | 563 | RegisterUnary(ctx, "unique?", IsUniqueTerm); |
566 | 564 | RegisterUnary(ctx, "modifiable?", IsModifiableTerm); |
567 | 565 | RegisterUnary(ctx, "temporary?", IsTemporaryTerm); |
566 | + RegisterUnary(ctx, "bound-lvalue?", IsBoundLValueTerm); | |
567 | + RegisterUnary(ctx, "uncollapsed?", IsUncollapsedTerm); | |
568 | 568 | RegisterStrict(ctx, "deshare", [](TermNode& term){ |
569 | 569 | return CallRawUnary([&](TermNode& tm){ |
570 | 570 | if(const auto p = NPL::TryAccessLeaf<const TermReference>(tm)) |
@@ -952,6 +952,8 @@ | ||
952 | 952 | // XXX: This is from 'first-null?' in the alternative derivation. |
953 | 953 | ThrowInsufficientTermsError(nd, x.second); |
954 | 954 | }); |
955 | + RegisterStrict(renv, "assq", Assq); | |
956 | + RegisterStrict(renv, "assv", Assv); | |
955 | 957 | { |
956 | 958 | const auto a(context.Allocator); |
957 | 959 | // NOTE: As %MakeEncapsulationType. |
@@ -1310,6 +1312,12 @@ | ||
1310 | 1312 | ((unwrap list%) .)) (symbols->imports symbols)) (eval e d); |
1311 | 1313 | $defl! nonfoldable? (&l) |
1312 | 1314 | $if (null? l) #f ($if (first-null? l) #t (nonfoldable? (rest& l))); |
1315 | +$defl%! assq (&x &alist) $cond ((null? alist)) | |
1316 | + ((eq? x (first& (first& alist))) first% alist) | |
1317 | + (#t assq (forward! x) (rest& alist)); | |
1318 | +$defl%! assv (&x &alist) $cond ((null? alist)) | |
1319 | + ((eqv? x (first& (first& alist))) first% alist) | |
1320 | + (#t assv (forward! x) (rest% alist)); | |
1313 | 1321 | $def! (box% box? unbox) () make-encapsulation-type; |
1314 | 1322 | $defl! box (x) box% (move! x); |
1315 | 1323 | )NPL" |
@@ -1366,9 +1374,6 @@ | ||
1366 | 1374 | { |
1367 | 1375 | context.ShareCurrentSource("<root:core>"); |
1368 | 1376 | context.Perform(R"NPL( |
1369 | -$defl%! assv (&x &alist) $cond ((null? alist) ()) | |
1370 | - ((eqv? x (first& (first& alist))) first% alist) | |
1371 | - (#t assv (forward! x) (rest% alist)); | |
1372 | 1377 | $defw%! map-reverse (&appv .&ls) d |
1373 | 1378 | accl (move! ls) nonfoldable? () list-extract-first list-extract-rest% |
1374 | 1379 | ($lambda (&x &xs) cons% (apply appv (forward! x) d) xs); |
@@ -1659,7 +1664,7 @@ | ||
1659 | 1664 | # if NPL_Impl_NPLA1_Enable_Thunked |
1660 | 1665 | return A1::RelayCurrentNext(ctx, term, trivial_swap, std::bind( |
1661 | 1666 | std::move(reduce), std::ref(term), std::ref(ctx), std::ref(context)), |
1662 | - trivial_swap, MakeKeptGuard(EnvironmentGuard(ctx, | |
1667 | + trivial_swap, MoveKeptGuard(EnvironmentGuard(ctx, | |
1663 | 1668 | ctx.SwitchEnvironmentUnchecked(std::move(p_env))))); |
1664 | 1669 | # else |
1665 | 1670 | const EnvironmentGuard gd(ctx, |
@@ -1696,6 +1701,16 @@ | ||
1696 | 1701 | } |
1697 | 1702 | |
1698 | 1703 | void |
1704 | +LoadModule_std_continuations(REPLContext& context) | |
1705 | +{ | |
1706 | + auto& renv(context.Root.GetRecordRef()); | |
1707 | + | |
1708 | + RegisterStrict(renv, "call/1cc", Call1CC); | |
1709 | + RegisterStrict(renv, "continuation->applicative", | |
1710 | + ContinuationToApplicative); | |
1711 | +} | |
1712 | + | |
1713 | +void | |
1699 | 1714 | LoadModule_std_promises(REPLContext& context) |
1700 | 1715 | { |
1701 | 1716 | #if NPL_Impl_NPLA1_Native_Forms |
@@ -2595,6 +2610,7 @@ | ||
2595 | 2610 | }); |
2596 | 2611 | const auto p_ground(rctx.ShareRecord()); |
2597 | 2612 | |
2613 | + load_std_module("continuations", LoadModule_std_continuations), | |
2598 | 2614 | load_std_module("promises", LoadModule_std_promises); |
2599 | 2615 | load_std_module("math", LoadModule_std_math), |
2600 | 2616 | load_std_module("strings", LoadModule_std_strings); |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA.cpp |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA 公共接口。 |
14 | -\version r3966 | |
14 | +\version r3989 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 663 |
17 | 17 | \par 创建时间: |
18 | 18 | 2016-01-07 10:32:45 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-07 04:12 +0800 | |
20 | + 2022-04-18 00:19 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -436,21 +436,6 @@ | ||
436 | 436 | } |
437 | 437 | |
438 | 438 | bool |
439 | -IsBoundLValueTerm(const TermNode& term) | |
440 | -{ | |
441 | - return ystdex::invoke_value_or(&TermReference::IsReferencedLValue, | |
442 | - NPL::TryAccessLeaf<const TermReference>(term)); | |
443 | -} | |
444 | - | |
445 | -bool | |
446 | -IsUncollapsedTerm(const TermNode& term) | |
447 | -{ | |
448 | - return ystdex::call_value_or(ystdex::compose(IsReferenceTerm, | |
449 | - std::mem_fn(&TermReference::get)), | |
450 | - NPL::TryAccessLeaf<const TermReference>(term)); | |
451 | -} | |
452 | - | |
453 | -bool | |
454 | 439 | IsUniqueTerm(const TermNode& term) |
455 | 440 | { |
456 | 441 | return ystdex::invoke_value_or(&TermReference::IsUnique, |
@@ -473,6 +458,21 @@ | ||
473 | 458 | bool(term.Tags & TermTags::Temporary)); |
474 | 459 | } |
475 | 460 | |
461 | +bool | |
462 | +IsBoundLValueTerm(const TermNode& term) | |
463 | +{ | |
464 | + return ystdex::invoke_value_or(&TermReference::IsReferencedLValue, | |
465 | + NPL::TryAccessLeaf<const TermReference>(term)); | |
466 | +} | |
467 | + | |
468 | +bool | |
469 | +IsUncollapsedTerm(const TermNode& term) | |
470 | +{ | |
471 | + return ystdex::call_value_or(ystdex::compose(IsReferenceTerm, | |
472 | + std::mem_fn(&TermReference::get)), | |
473 | + NPL::TryAccessLeaf<const TermReference>(term)); | |
474 | +} | |
475 | + | |
476 | 476 | |
477 | 477 | void |
478 | 478 | LiftOtherOrCopy(TermNode& term, TermNode& tm, bool move) |
@@ -936,14 +936,19 @@ | ||
936 | 936 | ReductionStatus |
937 | 937 | ContextNode::ApplyTail() |
938 | 938 | { |
939 | - // TODO: Add check to avoid stack overflow when the current action is | |
939 | + // TODO: Add check to avoid native stack overflow when the current action is | |
940 | 940 | // called? |
941 | 941 | YAssert(IsAlive(), "No tail action found."); |
942 | + // NOTE: This also overwrites any previously stored value of %TailAction. | |
942 | 943 | TailAction = std::move(current.front()); |
943 | 944 | stashed.splice_after(stashed.cbefore_begin(), current, |
944 | 945 | current.cbefore_begin()); |
945 | 946 | TryExpr(LastStatus = TailAction(*this)) |
946 | 947 | CatchExpr(..., HandleException(std::current_exception())) |
948 | + // NOTE: To make PTC works, %TailAction is not released after the call. It | |
949 | + // should be overwritten by the next call to %ApplyTail normally. When | |
950 | + // abnormally exited (e.g. in a call to %HandleException), %TailAction is | |
951 | + // usually to be cleanup separately. | |
947 | 952 | return LastStatus; |
948 | 953 | } |
949 | 954 |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA1.cpp |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA1 公共接口。 |
14 | -\version r22297 | |
14 | +\version r22322 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 472 |
17 | 17 | \par 创建时间: |
18 | 18 | 2014-02-02 18:02:47 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-04-03 02:07 +0800 | |
20 | + 2022-04-26 00:32 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -299,7 +299,8 @@ | ||
299 | 299 | { |
300 | 300 | #if NPL_Impl_NPLA1_Enable_Thunked |
301 | 301 | // XXX: No %SetupNextTerm call is needed here because it should have been |
302 | - // called before entering %ContextState::DefaultReduceOnce. | |
302 | + // called before entering %ContextState::DefaultReduceOnce (e.g. in | |
303 | + // %ReduceOnce). | |
303 | 304 | AssertNextTerm(ctx, term); |
304 | 305 | // XXX: Be cautious with overflow risks in call of %ContextNode::ApplyTail |
305 | 306 | // when TCO is not enabled. |
@@ -1526,6 +1527,18 @@ | ||
1526 | 1527 | |
1527 | 1528 | |
1528 | 1529 | ReductionStatus |
1530 | +FormContextHandler::CallHandler(TermNode& term, ContextNode& ctx) const | |
1531 | +{ | |
1532 | +#if NPL_Impl_NPLA1_Enable_Thunked | |
1533 | + // XXX: The %std::reference_wrapper instance is specialized enough | |
1534 | + // without %trivial_swap. | |
1535 | + return RelayCurrentOrDirect(ctx, std::ref(Handler), term); | |
1536 | +#else | |
1537 | + return Handler(term, ctx); | |
1538 | +#endif | |
1539 | +} | |
1540 | + | |
1541 | +ReductionStatus | |
1529 | 1542 | FormContextHandler::CallN(size_t n, TermNode& term, ContextNode& ctx) const |
1530 | 1543 | { |
1531 | 1544 | // NOTE: This implementes arguments evaluation in applicative order when |
@@ -1537,9 +1550,7 @@ | ||
1537 | 1550 | // NOTE: Optimize for cases with no argument. |
1538 | 1551 | if(n == 0 || term.size() <= 1) |
1539 | 1552 | // XXX: Assume the term has been setup by the caller. |
1540 | - // XXX: The %std::reference_wrapper instance is specialized enough | |
1541 | - // without %trivial_swap. | |
1542 | - return RelayCurrentOrDirect(ctx, std::ref(Handler), term); | |
1553 | + return CallHandler(term, ctx); | |
1543 | 1554 | // XXX: The empty type is specialized enough without %trivial_swap. |
1544 | 1555 | return A1::RelayCurrentNext(ctx, term, [](TermNode& t, ContextNode& c){ |
1545 | 1556 | YAssert(!t.empty(), "Invalid term found."); |
@@ -1554,7 +1565,7 @@ | ||
1554 | 1565 | // calls is almost PTC in reality. |
1555 | 1566 | while(n-- != 0) |
1556 | 1567 | ReduceArguments(term, ctx); |
1557 | - return Handler(term, ctx); | |
1568 | + return CallHandler(term, ctx); | |
1558 | 1569 | #endif |
1559 | 1570 | } |
1560 | 1571 |
@@ -1814,9 +1825,7 @@ | ||
1814 | 1825 | // This shall be cleared if the object represented by %fm is not a prvalue. |
1815 | 1826 | if(p_ref_fm) |
1816 | 1827 | { |
1817 | - // XXX: This is nothing to do with %EnsureValueTags, so keep it | |
1818 | - // explicit. | |
1819 | - term.Tags &= ~TermTags::Temporary; | |
1828 | + ClearCombiningTags(term); | |
1820 | 1829 | // XXX: The following irregular term conversion is not necessary. It is |
1821 | 1830 | // even better to be avoid for easier handling of reference values. |
1822 | 1831 | #if false |
@@ -1884,9 +1893,9 @@ | ||
1884 | 1893 | ReductionStatus |
1885 | 1894 | ReduceCombinedReferent(TermNode& term, ContextNode& ctx, const TermNode& fm) |
1886 | 1895 | { |
1896 | + YAssert(IsCombiningTerm(term), "Invalid term found for combined term."); | |
1887 | 1897 | // XXX: %SetupNextTerm is to be called in %CombinerReturnThunk. |
1888 | - // XXX: As %ReduceCombinedBranch, keep it explicit. | |
1889 | - term.Tags &= ~TermTags::Temporary; | |
1898 | + ClearCombiningTags(term); | |
1890 | 1899 | if(const auto p_handler = NPL::TryAccessLeaf<const ContextHandler>(fm)) |
1891 | 1900 | return CombinerReturnThunk(*p_handler, term, ctx); |
1892 | 1901 | return ThrowCombiningFailure(term, fm, true); |
@@ -2063,10 +2072,11 @@ | ||
2063 | 2072 | void |
2064 | 2073 | SetupTailContext(ContextNode& ctx, TermNode& term) |
2065 | 2074 | { |
2075 | + YAssert(IsCombiningTerm(term), "Invalid term found for combined term."); | |
2066 | 2076 | #if NPL_Impl_NPLA1_Enable_TCO |
2067 | 2077 | yunused(EnsureTCOAction(ctx, term)); |
2068 | 2078 | #else |
2069 | - yunused(ctx), yunused(term); | |
2079 | + yunused(ctx); | |
2070 | 2080 | #endif |
2071 | 2081 | } |
2072 | 2082 |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA1Forms.cpp |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA1 语法形式。 |
14 | -\version r26431 | |
14 | +\version r26599 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 882 |
17 | 17 | \par 创建时间: |
18 | 18 | 2014-02-15 11:19:51 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-29 02:31 +0800 | |
20 | + 2022-04-26 00:40 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -115,7 +115,7 @@ | ||
115 | 115 | YB_ATTR_nodiscard bool |
116 | 116 | CondTest(TermNode& clause, TNIter j) |
117 | 117 | { |
118 | - if(ExtractBool(*j)) | |
118 | + if(ExtractBool(NPL::Deref(j))) | |
119 | 119 | { |
120 | 120 | clause.erase(j); |
121 | 121 | return true; |
@@ -267,8 +267,8 @@ | ||
267 | 267 | { |
268 | 268 | if(first1 != last1) |
269 | 269 | { |
270 | - auto& x(ReferenceTerm(*first1)); | |
271 | - auto& y(ReferenceTerm(*first2)); | |
270 | + auto& x(ReferenceTerm(NPL::Deref(first1))); | |
271 | + auto& y(ReferenceTerm(NPL::Deref(first2))); | |
272 | 272 | |
273 | 273 | if(TermUnequal(x, y)) |
274 | 274 | { |
@@ -298,8 +298,8 @@ | ||
298 | 298 | { |
299 | 299 | if(first1 != last1) |
300 | 300 | { |
301 | - auto& x(ReferenceTerm(*first1)); | |
302 | - auto& y(ReferenceTerm(*first2)); | |
301 | + auto& x(ReferenceTerm(NPL::Deref(first1))); | |
302 | + auto& y(ReferenceTerm(NPL::Deref(first2))); | |
303 | 303 | |
304 | 304 | if(TermUnequal(x, y)) |
305 | 305 | return {}; |
@@ -669,7 +669,8 @@ | ||
669 | 669 | const auto tr([&](TNIter iter){ |
670 | 670 | return ystdex::make_transform(iter, [&](TNIter i) -> ValueObject{ |
671 | 671 | // XXX: Like %LiftToReturn, but for %Value only. |
672 | - if(const auto p = NPL::TryAccessLeaf<const TermReference>(*i)) | |
672 | + if(const auto p | |
673 | + = NPL::TryAccessLeaf<const TermReference>(NPL::Deref(i))) | |
673 | 674 | { |
674 | 675 | if(nonmodifying || !p->IsMovable()) |
675 | 676 | return p->get().Value; |
@@ -1027,6 +1028,19 @@ | ||
1027 | 1028 | return ReductionStatus::Clean; |
1028 | 1029 | } |
1029 | 1030 | |
1031 | +//! \since build 943 | |
1032 | +ReductionStatus | |
1033 | +WrapO(TermNode& term, ContextHandler& h, ResolvedTermReferencePtr p_ref) | |
1034 | +{ | |
1035 | + // XXX: Allocators are not used on %FormContextHandler for performance in | |
1036 | + // most cases. | |
1037 | + return WrapH(term, MakeValueOrMove(p_ref, [&]{ | |
1038 | + return h; | |
1039 | + }, [&]{ | |
1040 | + return std::move(h); | |
1041 | + }), 1); | |
1042 | +} | |
1043 | + | |
1030 | 1044 | //! \since build 913 |
1031 | 1045 | ReductionStatus |
1032 | 1046 | UnwrapResolved(TermNode& term, FormContextHandler& fch, |
@@ -1110,7 +1124,7 @@ | ||
1110 | 1124 | BindMoveNextNLocalSubobjectInPlace(TNIter i, size_t n) |
1111 | 1125 | { |
1112 | 1126 | while(n-- != 0) |
1113 | - BindMoveLocalObjectInPlace(*++i); | |
1127 | + BindMoveLocalObjectInPlace(NPL::Deref(++i)); | |
1114 | 1128 | } |
1115 | 1129 | |
1116 | 1130 | // NOTE: The bound term cannot be reused later because %o can be the referent. |
@@ -1796,8 +1810,8 @@ | ||
1796 | 1810 | { |
1797 | 1811 | if(first1 != last1) |
1798 | 1812 | { |
1799 | - auto& x(ReferenceTerm(*first1)); | |
1800 | - auto& y(ReferenceTerm(*first2)); | |
1813 | + auto& x(ReferenceTerm(NPL::Deref(first1))); | |
1814 | + auto& y(ReferenceTerm(NPL::Deref(first2))); | |
1801 | 1815 | |
1802 | 1816 | if(TermUnequal(x, y)) |
1803 | 1817 | r = {}; |
@@ -1904,6 +1918,8 @@ | ||
1904 | 1918 | YB_FLATTEN ReductionStatus |
1905 | 1919 | Acc(_func f, TermNode& term, ContextNode& ctx) |
1906 | 1920 | { |
1921 | + YAssert(term.size() >= 8, "Invalid recursive call found."); | |
1922 | + | |
1907 | 1923 | auto i(term.begin()); |
1908 | 1924 | auto& nterm(*i); |
1909 | 1925 | auto& l(*++i); |
@@ -2219,7 +2235,7 @@ | ||
2219 | 2235 | PrepareFoldRList(con.back()); |
2220 | 2236 | |
2221 | 2237 | auto i(con.begin()); |
2222 | - auto& rterm(*i); | |
2238 | + auto& rterm(NPL::Deref(i)); | |
2223 | 2239 | |
2224 | 2240 | rterm.Clear(); |
2225 | 2241 | rterm.GetContainerRef().splice(rterm.end(), con, ++i, con.end()); |
@@ -3147,6 +3163,48 @@ | ||
3147 | 3163 | }, ctx.ShareRecord()), "import-bindings")); |
3148 | 3164 | } |
3149 | 3165 | |
3166 | +//! \since build 943 | |
3167 | +YB_FLATTEN YB_NONNULL(2) ReductionStatus | |
3168 | +AssocImpl(TermNode& term, bool(*eq)(const ValueObject&, const ValueObject&)) | |
3169 | +{ | |
3170 | + RetainN(term, 2); | |
3171 | + RemoveHead(term); | |
3172 | + | |
3173 | + auto i(term.begin()); | |
3174 | + auto& nd_x(ReferenceTerm(*i)); | |
3175 | + | |
3176 | + return ResolveTerm( | |
3177 | + [&](TermNode& nd, ResolvedTermReferencePtr p_ref) -> ReductionStatus{ | |
3178 | + if(IsList(nd)) | |
3179 | + { | |
3180 | + for(auto& tm : nd) | |
3181 | + { | |
3182 | + auto& sub(ReferenceTerm(tm)); | |
3183 | + | |
3184 | + CheckResolvedListReference(sub, true); | |
3185 | + | |
3186 | + auto& sub2(ReferenceTerm(AccessFirstSubterm(sub))); | |
3187 | + | |
3188 | + if(IsLeaf(nd_x) && IsLeaf(sub2) ? eq(nd_x.Value, sub2.Value) | |
3189 | + : ystdex::ref_eq<>()(nd_x, sub2)) | |
3190 | + { | |
3191 | + if(!p_ref) | |
3192 | + LiftOther(term, tm); | |
3193 | + else | |
3194 | + LiftOtherOrCopyPropagateTags(term, tm, | |
3195 | + p_ref->GetTags()); | |
3196 | + return ReductionStatus::Retained; | |
3197 | + } | |
3198 | + } | |
3199 | + term.Clear(); | |
3200 | + return ReductionStatus::Clean; | |
3201 | + } | |
3202 | + // XXX: This is known to be different to the derivation using 'first&' | |
3203 | + // in the exception message. | |
3204 | + ThrowListTypeErrorForNonlist(nd, true); | |
3205 | + }, *++i); | |
3206 | +} | |
3207 | + | |
3150 | 3208 | } // unnamed namespace; |
3151 | 3209 | |
3152 | 3210 | bool |
@@ -3783,13 +3841,7 @@ | ||
3783 | 3841 | { |
3784 | 3842 | return WrapOrRef<WrapN>(term, |
3785 | 3843 | [&](ContextHandler& h, ResolvedTermReferencePtr p_ref){ |
3786 | - // XXX: Allocators are not used on %FormContextHandler for performance | |
3787 | - // in most cases. | |
3788 | - return WrapH(term, MakeValueOrMove(p_ref, [&]{ | |
3789 | - return h; | |
3790 | - }, [&]{ | |
3791 | - return std::move(h); | |
3792 | - }), 1); | |
3844 | + return WrapO(term, h, p_ref); | |
3793 | 3845 | }); |
3794 | 3846 | } |
3795 | 3847 |
@@ -4348,6 +4400,119 @@ | ||
4348 | 4400 | return ImportImpl(term, ctx, true); |
4349 | 4401 | } |
4350 | 4402 | |
4403 | +ReductionStatus | |
4404 | +Assq(TermNode& term) | |
4405 | +{ | |
4406 | + return AssocImpl(term, YSLib::HoldSame); | |
4407 | +} | |
4408 | + | |
4409 | +ReductionStatus | |
4410 | +Assv(TermNode& term) | |
4411 | +{ | |
4412 | + return AssocImpl(term, [] YB_LAMBDA_ANNOTATE( | |
4413 | + (const ValueObject& x, const ValueObject& y), , pure){ | |
4414 | + return x == y; | |
4415 | + }); | |
4416 | +} | |
4417 | + | |
4418 | + | |
4419 | +ReductionStatus | |
4420 | +Call1CC(TermNode& term, ContextNode& ctx) | |
4421 | +{ | |
4422 | + RetainN(term); | |
4423 | + yunused(NPL::ResolveRegular<const ContextHandler>( | |
4424 | + NPL::Deref(std::next(term.begin())))); | |
4425 | + RemoveHead(term); | |
4426 | + | |
4427 | + auto& current(ctx.GetCurrentRef()); | |
4428 | + // NOTE: The checker is a barrier to mark the capture beginning. When | |
4429 | + // called, the continuation is invalidated. | |
4430 | +#if !NPL_Impl_NPLA1_Enable_TCO | |
4431 | + OneShotChecker check(ctx); | |
4432 | + | |
4433 | + RelaySwitched(ctx, | |
4434 | + MoveKeptGuard(ystdex::guard<OneShotChecker>(check))); | |
4435 | +#endif | |
4436 | + | |
4437 | + const auto i_captured(current.begin()); | |
4438 | + | |
4439 | + // TODO: Blocked. Use ISO C++14 lambda initializers to simplify | |
4440 | + // the implementation. | |
4441 | + term.GetContainerRef().push_back(NPL::AsTermNode(term.get_allocator(), | |
4442 | + // XXX: The name is usually uninterested by direct invocation, but when | |
4443 | + // the continuation is extended, it can be somewhat useful to identify | |
4444 | + // the source in the capture-time instead of extending-time (although | |
4445 | + // currently there is just one source here). | |
4446 | + A1::Continuation(A1::NameTypedContextHandler( | |
4447 | + ystdex::bind1([&, i_captured](TermNode& t, OneShotChecker& osc){ | |
4448 | + Retain(t); | |
4449 | + osc.Check(); | |
4450 | + | |
4451 | + // XXX: It is necessary to back %current and %i_captured up since the | |
4452 | + // unwinding below may likely to invalidate the reducer here, hence | |
4453 | + // the captured variables will also be destroyed. | |
4454 | + auto& cur(current); | |
4455 | + // XXX: Assume the frame referenced by %i_captured is in the reduction | |
4456 | + // sequence, as the check of %NPL_NPLA1Forms_CheckContinuationFrames. | |
4457 | + const auto i_top(i_captured); | |
4458 | + // NOTE: The term %t is not owned by the reducers. It shall be backed up | |
4459 | + // to skip the possible calls for the local cleanup (e.g. | |
4460 | + // %TCOAction::GuardFunction). It shall not be saved directly to %term, | |
4461 | + // as the existing subnodes in %term are usually also subject to the | |
4462 | + // cleanup and shall not be invalidated before the cleanup. | |
4463 | + // XXX: This is not guarded. If any exception is throw, the values are | |
4464 | + // to be discarded. | |
4465 | + auto con(std::move(t.GetContainerRef())); | |
4466 | + auto vo(std::move(t.Value)); | |
4467 | + | |
4468 | +#if NPL_NPLA1Forms_CheckContinuationFrames | |
4469 | + { | |
4470 | + auto i(cur.begin()); | |
4471 | + | |
4472 | + while(i != cur.end() && i != i_captured) | |
4473 | + ++i; | |
4474 | + YAssert(i != cur.end(), "The top frame of the reinstated captured" | |
4475 | + " continuation is missing."); | |
4476 | + } | |
4477 | +#endif | |
4478 | + // NOTE: Now unwind the reducer sequence upon to the delimiter. This | |
4479 | + // switches the control to the position to the captured one. | |
4480 | + while(cur.begin() != i_top) | |
4481 | + cur.pop_front(); | |
4482 | + // NOTE: Drop the top check frame itself. | |
4483 | + cur.pop_front(); | |
4484 | + // NOTE: After the cleanup, %t is usually invalidated, but %term is not. | |
4485 | + yunseq(term.GetContainerRef() = std::move(con), | |
4486 | + term.Value = std::move(vo)); | |
4487 | + SetupNextTerm(ctx, term); | |
4488 | + ClearCombiningTags(term); | |
4489 | + RemoveHead(term); | |
4490 | + return ReductionStatus::Retained; | |
4491 | + }, | |
4492 | +#if NPL_Impl_NPLA1_Enable_TCO | |
4493 | + RefTCOAction(ctx).MakeOneShotChecker() | |
4494 | +#else | |
4495 | + std::move(check) | |
4496 | +#endif | |
4497 | + ), "captured-one-shot-continuation"), ctx))); | |
4498 | + // NOTE: Tail call. | |
4499 | + // XXX: Only the underlying operative is used and the operand is a | |
4500 | + // self-evaluating value, so the wrapper count is irrelevant. However, just | |
4501 | + // leave it untouched to avoid dig into the internals of | |
4502 | + // %FormContextHandler, as well as to be neutral to extended combiners not | |
4503 | + // having %FormContextHandler at all. | |
4504 | + return ReduceCombinedBranch(term, ctx); | |
4505 | +} | |
4506 | + | |
4507 | +ReductionStatus | |
4508 | +ContinuationToApplicative(TermNode& term) | |
4509 | +{ | |
4510 | + return Forms::CallRegularUnaryAs<Continuation>( | |
4511 | + [&](Continuation& cont, ResolvedTermReferencePtr p_ref){ | |
4512 | + return WrapO(term, cont.Handler, p_ref); | |
4513 | + }, term); | |
4514 | +} | |
4515 | + | |
4351 | 4516 | |
4352 | 4517 | void |
4353 | 4518 | CallSystem(TermNode& term) |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA1Internals.h |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA1 内部接口。 |
14 | -\version r22014 | |
14 | +\version r22076 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 882 |
17 | 17 | \par 创建时间: |
18 | 18 | 2020-02-15 13:20:08 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-04-05 13:40 +0800 | |
20 | + 2022-04-25 01:04 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 非公开模块名称: |
@@ -33,11 +33,12 @@ | ||
33 | 33 | // ReductionStatus, Reducer, YSLib::map, lref, Environment, |
34 | 34 | // set, NPL::Deref, IsTyped, EnvironmentList, EnvironmentReference, tuple, |
35 | 35 | // YSLib::get, YSLib::forward_list, size_t, list, std::declval, |
36 | -// EnvironmentGuard, MakeKeptGuard, A1::NameTypedContextHandler, TermReference, | |
36 | +// EnvironmentGuard, MoveKeptGuard, A1::NameTypedContextHandler, TermReference, | |
37 | 37 | // ThrowTypeErrorForInvalidType, NPL::TryAccessLeaf, type_id, |
38 | 38 | // TermToNamePtr, IsIgnore, ystdex::exclude_self_t, ParameterMismatch; |
39 | 39 | #include <ystdex/compose.hpp> // for ystdex::get_less; |
40 | 40 | #include <ystdex/scope_guard.hpp> // for ystdex::unique_guard; |
41 | +#include <ystdex/optional.h> // for ystdex::optional; | |
41 | 42 | #include <ystdex/utility.hpp> // for ystdex::exchange; |
42 | 43 | #include <ystdex/ref.hpp> // for std::reference_wrapper, std::ref, |
43 | 44 | // ystdex::unref; |
@@ -89,10 +90,45 @@ | ||
89 | 90 | |
90 | 91 | // NOTE: See $2018-09 @ %Documentation::Workflow for rationale of the |
91 | 92 | // implementation. |
92 | -// XXX: First-class continuations are not implemented yet, due to lack of term | |
93 | -// replacement mechanism in captured continuation. Kernel-style continuation | |
94 | -// interception is also unsupported because no reference for parantage is | |
95 | -// maintained in the context currently. | |
93 | +// XXX: First-class continuations are not implemented completely yet, due to | |
94 | +// lack of term replacement mechanism in captured continuation. Kernel-style | |
95 | +// continuation interception is also unsupported because no reference for | |
96 | +// parantage is maintained in the context currently. | |
97 | + | |
98 | +/*! | |
99 | +\brief 一次续延检查器。 | |
100 | +\since build 943 | |
101 | +*/ | |
102 | +class OneShotChecker final | |
103 | +{ | |
104 | +private: | |
105 | + shared_ptr<bool> p_shot; | |
106 | + | |
107 | +public: | |
108 | + OneShotChecker(ContextNode& ctx) | |
109 | + : p_shot(YSLib::allocate_shared<bool>(ctx.get_allocator())) | |
110 | + {} | |
111 | + | |
112 | + void | |
113 | + operator()() const ynothrow | |
114 | + { | |
115 | + // XXX: To allow the move of %p_shot, this check is necessary. | |
116 | + if(p_shot) | |
117 | + NPL::Deref(p_shot) = true; | |
118 | + } | |
119 | + | |
120 | + void | |
121 | + Check() const | |
122 | + { | |
123 | + auto& shot(NPL::Deref(p_shot)); | |
124 | + | |
125 | + if(!shot) | |
126 | + shot = true; | |
127 | + else | |
128 | + throw NPLException("One-shot continuation expired."); | |
129 | + } | |
130 | +}; | |
131 | + | |
96 | 132 | |
97 | 133 | #if NPL_Impl_NPLA1_Enable_Thunked |
98 | 134 | # if false |
@@ -290,6 +326,13 @@ | ||
290 | 326 | //! \since build 896 |
291 | 327 | mutable ValueObject OperatorName; |
292 | 328 | |
329 | +private: | |
330 | + /*! | |
331 | + \brief 一次调用检查守卫。 | |
332 | + \since build 943 | |
333 | + */ | |
334 | + mutable ystdex::optional<ystdex::guard<OneShotChecker>> one_shot_guard; | |
335 | + | |
293 | 336 | public: |
294 | 337 | //! \since build 819 |
295 | 338 | TCOAction(ContextNode& ctx, TermNode& term, bool lift) |
@@ -304,7 +347,7 @@ | ||
304 | 347 | "Invalid value for combining term found."); |
305 | 348 | return std::move(term.Value); |
306 | 349 | // XXX: After the move, %term.Value is unspecified. |
307 | - }()) | |
350 | + }()), one_shot_guard() | |
308 | 351 | // XXX: Do not call %AssertValueTags on %term, as it is usually a |
309 | 352 | // combinitation instead of the representation of some object language |
310 | 353 | // value. |
@@ -316,8 +359,12 @@ | ||
316 | 359 | // object always live longer than the older one. |
317 | 360 | : term_guard(std::move(a.term_guard)), |
318 | 361 | req_lift_result(a.req_lift_result), stashed(ystdex::exchange(a.stashed, |
319 | - ContextHandler())), EnvGuard(std::move(a.EnvGuard)) | |
320 | - {} | |
362 | + ContextHandler())), EnvGuard(std::move(a.EnvGuard)), | |
363 | + one_shot_guard() | |
364 | + { | |
365 | + if(a.one_shot_guard.has_value()) | |
366 | + one_shot_guard.emplace((*a.one_shot_guard).func); | |
367 | + } | |
321 | 368 | DefDeMoveCtor(TCOAction) |
322 | 369 | // XXX: Out of line destructor here is inefficient. |
323 | 370 |
@@ -405,6 +452,18 @@ | ||
405 | 452 | } |
406 | 453 | //@} |
407 | 454 | |
455 | + //! \since build 943 | |
456 | + YB_ATTR_nodiscard OneShotChecker | |
457 | + MakeOneShotChecker() | |
458 | + { | |
459 | + if(!one_shot_guard.has_value()) | |
460 | + // XXX: The context is only used to determine the allocator, which | |
461 | + // is an implementation detail. Usually the context in caller | |
462 | + // should be the same, though. | |
463 | + one_shot_guard.emplace(EnvGuard.func.Context.get()); | |
464 | + return (*one_shot_guard).func; | |
465 | + } | |
466 | + | |
408 | 467 | //! \since build 940 |
409 | 468 | YB_ATTR_nodiscard ContextHandler |
410 | 469 | MoveFunction() const |
@@ -809,7 +868,7 @@ | ||
809 | 868 | // TODO: Blocked. Use C++14 lambda initializers to simplify the |
810 | 869 | // implementation. |
811 | 870 | return A1::RelayCurrentNext(ctx, term, yforward(cur), trivial_swap, |
812 | - MakeKeptGuard(gd)); | |
871 | + MoveKeptGuard(gd)); | |
813 | 872 | #else |
814 | 873 | yunused(gd); |
815 | 874 | return A1::RelayDirect(ctx, cur, term); |
@@ -823,7 +882,7 @@ | ||
823 | 882 | { |
824 | 883 | // XXX: See %RelayNextGuarded. |
825 | 884 | #if NPL_Impl_NPLA1_Enable_Thunked |
826 | - auto act(MakeKeptGuard(gd)); | |
885 | + auto act(MoveKeptGuard(gd)); | |
827 | 886 | // TODO: Blocked. Use C++14 lambda initializers to simplify the |
828 | 887 | // implementation. |
829 | 888 | // XXX: Term reused. Call of %SetupNextTerm is not needed as the next |
@@ -854,7 +913,7 @@ | ||
854 | 913 | #if NPL_Impl_NPLA1_Enable_Thunked |
855 | 914 | // TODO: Blocked. Use C++14 lambda initializers to simplify the |
856 | 915 | // implementation. |
857 | - auto act(MakeKeptGuard(gd)); | |
916 | + auto act(MoveKeptGuard(gd)); | |
858 | 917 | |
859 | 918 | if(lift) |
860 | 919 | { |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file ChangeLog.V0.9.txt |
12 | 12 | \ingroup Documentation |
13 | 13 | \brief 版本更新历史记录 - V0.9 。 |
14 | -\version r8973 | |
14 | +\version r9118 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 800 |
17 | 17 | \par 创建时间: |
18 | 18 | 2020-10-12 17:19:23 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-04-05 16:32 +0800 | |
20 | + 2022-04-26 01:15 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -32,6 +32,136 @@ | ||
32 | 32 | |
33 | 33 | $now |
34 | 34 | ( |
35 | + / %Tools $= | |
36 | + ( | |
37 | + / %Scripts $= | |
38 | + ( | |
39 | + + $re_add(b942) "support of varaible %C_CXXFLAGS_EXT" | |
40 | + @ "%SHBuild-common-options.sh", | |
41 | + // This should better be consistent between stage 1 and stage \ | |
42 | + 2. It is also actually relied on in the stage 1 \ | |
43 | + bootstraping. | |
44 | + / @ "%SHBuild-YSLib-build.txt" $= | |
45 | + ( | |
46 | + * "minor descriptions when adding addtional fixes of the \ | |
47 | + options" $since b906, | |
48 | + * $re_ex(b900) $revert_ex(b904) "missing restoring %LIBS \ | |
49 | + before building the YBase library" $since b904 | |
50 | + // This fixes the regression since b904, which was an old \ | |
51 | + bug since b796 fixed by b900 and revised in b902. The \ | |
52 | + restore should not be removed even it is set in the \ | |
53 | + YFramework library build later, because it would \ | |
54 | + affect the YBase library build. | |
55 | + $= (+ "restore of %LIBS before building the libraries of \ | |
56 | + each configuration"), | |
57 | + // This fix is different to b902, because %LIBS is \ | |
58 | + printed in the call to 'build-with-conf-opt' with \ | |
59 | + possible changes on %LIBS by the call to \ | |
60 | + 'SHBuild_Extend_CallVariables' since b906. The \ | |
61 | + value must include these changes (if applicable). | |
62 | + // There is no need to restore %LDFLAGS as \ | |
63 | + 'build-with-conf-opt' will always set it with \ | |
64 | + 'safeenv-set' based on the initial value get by \ | |
65 | + 'safeenv-get' implied by '$env-de!' since b840. | |
66 | + - DLDI $revert(b838) "restore of the saved %LDFLAGS before \ | |
67 | + stage 2 %SHBuild build", | |
68 | + // Ditto. Note the original one used ad-hoc saved value \ | |
69 | + instead of the cached environment variable and \ | |
70 | + 'safeenv-restore' was later introduced in b840. | |
71 | + - DLDI $revert(b900) | |
72 | + "restore of %LIBS before stage 2 %SHBuild build", | |
73 | + // The value from 'safeenv-get "LIBS"' is enough. | |
74 | + / DLDI "simplified the definition of 'extra-opts'" | |
75 | + ) | |
76 | + ), | |
77 | + / DLI @ "%install-sysroot.sh" $= | |
78 | + ( | |
79 | + - "unused setting of %SHBuild_UseRelease", | |
80 | + // The concrete value is not used. | |
81 | + ( | |
82 | + / "stage 2 SHBuild call" ^ '"$@"' ~ 'SHBuild_Opt' | |
83 | + $dep_to "removal the use of SHBuild_Opt"; | |
84 | + - DLD "ShellCheck directive on the last command to eliminate \ | |
85 | + warning SC2086" | |
86 | + ), | |
87 | + ( | |
88 | + - $revert(b546) "exports of variables \ | |
89 | + %(SHBuild_LogOpt, SHBuild_BuildOpt, SHBuild_Opt)"; | |
90 | + // They are only used in this script. | |
91 | + - $revert(b546) "variables %(SHBuild_Opt; SHBuild_LogOpt, \ | |
92 | + SHBuild_BuildOpt)" $dep_from "removal the use of SHBuild_Opt" | |
93 | + ) | |
94 | + ) | |
95 | + ), | |
96 | + / %YFramework.NPL $= | |
97 | + ( | |
98 | + / %NPLA $= | |
99 | + ( | |
100 | + / DLDI "reordered object predicate functions", | |
101 | + / DLDI "simplified function %IsCombiningTerm", | |
102 | + + DD "Doxygen command '\ingroup guards'" | |
103 | + @ "class %ContextNode::ReductionGuard", | |
104 | + + DLDI 'yimpl' @ "function %ClearCombiningTags", | |
105 | + + "function %GetCurrentRef" @ "class %ContextNode" | |
106 | + ), | |
107 | + / %NPLA1 $= | |
108 | + ( | |
109 | + + $lib "assertion for comining terms" | |
110 | + @ "functions %(ReduceCombinedReferent, SetupTailContext)", | |
111 | + / "function templates %MakeKeptGuard" => "%MoveKeptGuard", | |
112 | + / DLDI "simplified functions %(ReduceCombined, \ | |
113 | + ReduceCombinedReferent)" ^ "%ClearCombiningTags", | |
114 | + / @ "class %FormContextHandler" $= | |
115 | + ( | |
116 | + + "function %CallHandler"; | |
117 | + / DLDI "simplified internal calls" @ "%operator()" | |
118 | + ) | |
119 | + ), | |
120 | + / %NPLA1Internals $= | |
121 | + ( | |
122 | + + "class %OneShotChecker"; | |
123 | + / @ "class %TCOAction" $= | |
124 | + ( | |
125 | + + "data member %OneShotGuard"; | |
126 | + + "support of early check by moved %OneShotGuard away" | |
127 | + @ "%operator()" | |
128 | + ) | |
129 | + ), | |
130 | + / %NPLA1Forms $= | |
131 | + ( | |
132 | + ( | |
133 | + + "macro %NPL_NPLA1Forms_CheckContinuationFrames"; | |
134 | + + "function %Call1CC" $dep_from %NPLA1Internals | |
135 | + ), | |
136 | + + "function %ContinuationToApplicative", | |
137 | + ( | |
138 | + + DLI "assertion for the common internal call" | |
139 | + @ "functions %(AccL, AccR)"; | |
140 | + / DLDI "internal calls for iterator arguments with narrow \ | |
141 | + contracts and no other implied assertions" ^ "%NPL::Deref" ~ '*' | |
142 | + ), | |
143 | + * DD "missing escaping '\' before '<'" @ "Doxygen comment" | |
144 | + @ ("functions %(AccL, AccR)" $since b898, | |
145 | + "function %FoldR1" $since b899) | |
146 | + ), | |
147 | + / %Dependency $= | |
148 | + ( | |
149 | + / DLI @ "function %LoadGroundContext" $= | |
150 | + ( | |
151 | + / $design "reordered object predicates", | |
152 | + / "simplified derivation" @ "applicative %assv" !^ '()', | |
153 | + / $lib "added native implementation enabled by default" | |
154 | + @ "applicative %assv" $dep_from ("%Assv" @ %NPLA1Forms), | |
155 | + + "applicative %assq" $dep_from ("%Assq" @ %NPLA1Forms) | |
156 | + ), | |
157 | + + "function %LoadModule_std_continuations" ^ $dep_from | |
158 | + "%(Call1CC, ContinuationToApplicative)" @ %NPLA1Forms | |
159 | + ) | |
160 | + ) | |
161 | +), | |
162 | + | |
163 | +b942 | |
164 | +( | |
35 | 165 | / %YBase.YStandardEx $= |
36 | 166 | ( |
37 | 167 | + "transformation_traits %(empty_pack_t, sizeof_pack_t)" @ %Meta, |
@@ -7589,10 +7719,10 @@ | ||
7589 | 7719 | ), |
7590 | 7720 | / %Tools.Scripts $= |
7591 | 7721 | ( |
7592 | - / DLDI @ "%SHBuild-YSLib-build.txt" $= | |
7593 | - ( | |
7594 | - - "redundant restore of %LIBS before dynamic library building", | |
7595 | - / "simplified import of 'env-get'" | |
7722 | + / $dev @ "%SHBuild-YSLib-build.txt" $= | |
7723 | + ( | |
7724 | + - "restore of %LIBS before dynamic library building", | |
7725 | + / $lib $impl $design "simplified the import of 'env-get'" | |
7596 | 7726 | ), |
7597 | 7727 | / @ "%SHBuild-BuildApp.txt" $= |
7598 | 7728 | ( |
@@ -8168,7 +8298,7 @@ | ||
8168 | 8298 | / DLDI "simplified 'system-quote' with 'SHBuild_2m' calls", |
8169 | 8299 | / "YSLib libraries building" $= |
8170 | 8300 | ( |
8171 | - / DLDI "removed unused %LIBS restoring", | |
8301 | + / $dev "restored %LIBS only for dynamic libraries", | |
8172 | 8302 | * $re_add(b838) "possibly missing quotes on '-L' argument \ |
8173 | 8303 | of output directory on the %LIBS environment" |
8174 | 8304 | $since b796 ^ 'system-quote', |
@@ -8754,16 +8884,17 @@ | ||
8754 | 8884 | @ "platform %MinGW64" @ "YFramework DLL build", |
8755 | 8885 | // See $2020-10 @ %Documentation::Workflow. |
8756 | 8886 | * "missing restoring %LIBS before %YBase build" $since b796, |
8757 | - // This makes dynamic library in %release configuration having \ | |
8758 | - polluted %LIBS by the %debug configuration of YFramework \ | |
8759 | - (so YBase release dynamic library would be then linked to \ | |
8760 | - YBase debug dynamic library) when both %debug and \ | |
8761 | - %release configurations are enabled in the same script run. | |
8762 | - * "missing restoring %LIBS before stage 2 %SHBuild build" | |
8763 | - $since b838, | |
8764 | - // Ditto, although this is only bad when after a debug-only \ | |
8765 | - build of YBase + YFramework if YBase dynamic library are \ | |
8766 | - built correctly. | |
8887 | + // This makes the dynamic library of YBase in the %release \ | |
8888 | + configuration having polluted %LIBS by the dynamic library \ | |
8889 | + of YFramework in the %debug configuration (so YBase \ | |
8890 | + release dynamic library would be then linked to YBase \ | |
8891 | + debug dynamic library) when both %debug and %release \ | |
8892 | + configurations are enabled in the same script run. | |
8893 | + + $dev "restore of %LIBS before stage 2 %SHBuild build", | |
8894 | + // Ditto, although this is only bad after a debug-only build \ | |
8895 | + of YBase + YFramework if the YBase dynamic library is \ | |
8896 | + built correctly and 'env-get' is used instead of \ | |
8897 | + 'safeenv-get'. | |
8767 | 8898 | ( |
8768 | 8899 | + $deploy "installing directory %var/NPLA1 under SysRoot"; |
8769 | 8900 | / "NPLA1 module directory" -> "%var/NPLA1" ~ "%bin"; |
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - © 2010-2021 FrankHB. | |
2 | + © 2010-2022 FrankHB. | |
3 | 3 | |
4 | 4 | This file is part of the YSLib project, and may only be used, |
5 | 5 | modified, and distributed under the terms of the YSLib project |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file Definitions.txt |
12 | 12 | \ingroup Documentation |
13 | 13 | \brief 方法和公共域定义与说明。 |
14 | -\version r14423 | |
14 | +\version r14440 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since 早于 build 132 |
17 | 17 | \par 创建时间: |
18 | 18 | 2010-01-26 19:34:51 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2021-12-27 12:18 +0800 | |
20 | + 2022-04-22 19:28 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -175,6 +175,7 @@ | ||
175 | 175 | $header IntegralConstant |
176 | 176 | { |
177 | 177 | #__cpp_lib_bool_constant |
178 | +#__cpp_lib_logical_traits | |
178 | 179 | } |
179 | 180 | $header TypeInspection |
180 | 181 | { |
@@ -215,6 +216,10 @@ | ||
215 | 216 | #__cpp_lib_is_swappable |
216 | 217 | #__cpp_lib_exchange_function |
217 | 218 | } |
219 | +$header Enum | |
220 | +{ | |
221 | +!__cpp_lib_to_underlying | |
222 | +} | |
218 | 223 | $header IntegerSequence |
219 | 224 | { |
220 | 225 | #__cpp_lib_integer_sequence |
@@ -278,18 +283,22 @@ | ||
278 | 283 | YF_Impl_* // 保留用于 YFramework 实现。 |
279 | 284 | YFM_* // 保留作为 YFramework 模块包含文件宏。 |
280 | 285 | CHRLib_* // 保留给 YFramework.CHRLib 使用。 |
281 | -YCL_* // 保留给 YFramework.YCLib 使用。 | |
286 | +YCL_* // 保留给 YFramework.YCLib 使用。对特定平台 $plat : | |
282 | 287 | YCL_INC_* // YCLib 公开头文件包含守卫宏。 |
288 | + YCL_$plat_INC_* // YCLib 特定平台公开头文件包含守卫宏。 | |
283 | 289 | YCL_Inc_* // YCLib 非公开头文件包含守卫宏。 |
290 | + YCL_$plat_Inc_* // YCLib 特定平台非公开头文件包含守卫宏。 | |
284 | 291 | YSL_* // 保留给 YFramework.YSLib 使用。 |
285 | 292 | YSL_DEBUG_* |
286 | 293 | YSL_INC_* // YSLib 公开头文件包含守卫宏。 |
287 | 294 | YSL_Inc_* // YSLib 非公开头文件包含守卫宏。 |
288 | 295 | NPL_* // 保留给 YFramework.NPL 使用。 |
289 | 296 | NPL_Impl_* // 保留用于 YFrameowrk.NPL 实现。 |
290 | -// 对 YFramework 的次级子项目 $lib ,同时保留以下名称: | |
297 | +// 对 YFramework 的次级子项目 $lib 和特定平台 $plat ,同时保留以下名称: | |
291 | 298 | INC_$lib_* // 保留作为(除 YCLib 和 YSLib 外)公开头文件守卫宏使用。 |
299 | +INC_$platform_$lib_* // 保留作为(除 YCLib 和 YSLib 外)公开头文件守卫宏使用。 | |
292 | 300 | Inc_$lib_* // 保留作为(除 YCLib 和 YSLib 外)非公开头文件守卫宏使用。 |
301 | +Inc_$platform_$lib_* // 保留作为(除 YCLib 和 YSLib 外)非公开头文件守卫宏使用。 | |
293 | 302 | $lib_Impl* // 保留作为实现相关的宏。 |
294 | 303 | |
295 | 304 | $dir CHRLib |
@@ -528,6 +537,10 @@ | ||
528 | 537 | #NPL_Impl_NPLAMath_UseQuadMath |
529 | 538 | #NPL_Impl_NPLAMath_Has_UInt128 |
530 | 539 | } |
540 | +$unit NPLA1Forms | |
541 | +{ | |
542 | +NPL_NPLA1Forms_CheckContinuationFrames | |
543 | +} | |
531 | 544 | $internal $unit NPLA1Internals |
532 | 545 | { |
533 | 546 | NPL_Impl_NPLA1_Enable_InlineDirect |
@@ -972,6 +985,7 @@ | ||
972 | 985 | always_inline, |
973 | 986 | dllimport, |
974 | 987 | dllexport, |
988 | + empty_bases, | |
975 | 989 | flatten, |
976 | 990 | may_alias, |
977 | 991 | noinline, |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPL.txt |
12 | 12 | \ingroup Documentation |
13 | 13 | \brief NPL 规范和实现规格说明。 |
14 | -\version r27151 | |
14 | +\version r27818 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 304 |
17 | 17 | \par 创建时间: |
18 | 18 | 2012-04-25 10:34:20 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-31 08:09 +0800 | |
20 | + 2022-04-25 22:40 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -70,6 +70,10 @@ | ||
70 | 70 | — The Rust Reference ([Rust]) |
71 | 71 | https://doc.rust-lang.org/reference/index.html |
72 | 72 | 不定期更新。 |
73 | +— On the Expressive Power of Programming Languages ([Fl91]) | |
74 | + https://www.ccs.neu.edu/racket/pubs/scp91-felleisen.ps.gz | |
75 | +— Representing Monads ([Fi94]) | |
76 | + https://dl.acm.org/doi/pdf/10.1145/174675.178047 | |
73 | 77 | — Racket Documentation ([Racket]) |
74 | 78 | https://docs.racket-lang.org/ |
75 | 79 | 不定期更新。 |
@@ -78,6 +82,10 @@ | ||
78 | 82 | — Scheme Requests for Implementation |
79 | 83 | https://srfi.schemers.org/ |
80 | 84 | 汇总多个对 [RnRS] 的修订提案。 |
85 | +— Representing Control in the Presence of First-Class Continuation ([Hi90]) | |
86 | + https://legacy.cs.indiana.edu/~dyb/pubs/stack.pdf | |
87 | +— Representing Control in the Presence of One-Shot Continuations ([Br96]) | |
88 | + https://legacy.cs.indiana.edu/~dyb/pubs/call1cc.pdf | |
81 | 89 | — ISO/IEC 10967-1, Information technology — Language independent arithmetic |
82 | 90 | — Part 1: Integer and floating point arithmetic |
83 | 91 | — IEC 60559, Standard for floating-point arithmetic |
@@ -140,7 +148,7 @@ | ||
140 | 148 | NPL 是独立设计的语言,但它和 [RnRK](@1.1) 定义的 Kernel 有许多核心设计的相似之处,尽管设计的一些基本特征(如资源可用性基本约定(@2.1.1.1) )以及基本哲学(@1.5) 相当不同。 |
141 | 149 | NPL 的主要实现(@7) 的核心部分实质上支持了 Kernel 的形式模型(@2.1.1) —— vau 演算(vau calculi) 。(另见操作语义(@2.2.3) 。)具体的 NPL 语言在这些模型的基础上提供。 |
142 | 150 | NPL 的命名(@2.1) 即体现了 vau 演算和传统 λ 演算(@4.5) 为模型的语言的核心差异: |
143 | -强调表达式(@3.4.2) 求值(@4.1) 前后的不同,特别地,关注在语言中直接表达的名称(@1.2.4) 和求值(@4.1) 后指称的实体的不同(@1.2.4) 。 | |
151 | +强调允许在对象语言中指定求值上下文的显式求值(explicit evaluation)(而非 Lisp 方言中以 quote 为代表的显式干预默认的隐式求值)的风格以及表达式求值前后的不同(@1.4) ;表达式(@3.4.2) 求值(@4.1) 前后的不同,特别地,关注在语言中直接表达的名称(@1.2.4) 和求值(@4.1) 后指称的实体的不同(@1.2.4) 。 | |
144 | 152 | 更进一步地,NPL 普遍地支持区分一等引用(@4.2.3) 和被引用的一等实体(@4.1) 并具有更精确的资源控制机制,这是与 Kernel 的主要设计上的差异。 |
145 | 153 | 关于 vau 演算的形式模型和其它相关内容,详见 [Shu10] 。 |
146 | 154 | 历史上,vau 演算提供了 fexpr 类似的抽象,参见: |
@@ -216,7 +224,7 @@ | ||
216 | 224 | 这条规则利用需求和设计内容陈述中概念外延普遍蕴含的局域性(locality) ,提供给定代价下更多的可行性或求解给定问题时使用较小的代价,用于: |
217 | 225 | 应对不可控复杂条件下使问题可解; |
218 | 226 | 局部可复用现有解的子集。 |
219 | -此外,尽管并非总是必要,应用知识内容的简单假设、@1.5.2.3 和本原则可在认识论上导出还原论。 | |
227 | +此外,尽管并非总是必要,应用知识内容的简单假设、最小接口原则(@1.5.2.3) 和本原则可在认识论上导出还原论。 | |
220 | 228 | |
221 | 229 | @1.5.3 形而上学(metaphysics) : |
222 | 230 | 根据作为需求的 @1.5.2 ,归纳适用于通用目的语言应有的构成及其性质的设计规则,包括三条价值判断实现公理: |
@@ -328,15 +336,18 @@ | ||
328 | 336 | |
329 | 337 | @1.5.5.1 统一性(uniformity) : |
330 | 338 | 接口的设计应尽可能避免特例。 |
331 | -这是 @1.5.2.3 的推论之一,以一致性(@1.5.3.1.2) 作为非模式规则输入。 | |
332 | -无限制的特例要求指定更多的附加规则,而违反 @1.5.2.3 。 | |
339 | +这是要求变化的自由(@1.5.2.3) 的推论之一,以一致性(@1.5.3.1.2) 作为非模式规则输入。 | |
340 | +无限制的特例要求指定更多的附加规则,而违反这个要求。 | |
333 | 341 | **原理** |
334 | 342 | 在语言设计上,这类似 [RnRK] 的设计原则 G1 : |
335 | 343 | G1a 对象状态(object status) :语言操作一等对象(@4.1); |
336 | -G1b 可扩展性(extensibility) :用户定义的设施能重现内建特性的能力。 | |
337 | -以上原则在 NPL 中略有变化。关于 G1a 的改变,详见 @4.2 。 | |
338 | -整体上的 G1b 在和 @1.5.3.1 冲突时不被要求。 | |
339 | -注意 G1b 仅表示用户使用语言的扩展,不表示语言自身的可扩展性(这通过满足需求的能力 @1.5.2.1 体现),因为语言规范(@1.2.1.2) 不依赖使用对象语言(@1.2.4) 表达。 | |
344 | +G1b 可扩展性(extensibility) :用户定义的设施能重现内建特性的能力(capability) 。 | |
345 | +以上原则在 NPL 中略有变化。关于 G1a 的改变,详见一等实体(@4.2) 。 | |
346 | +同 [RnRK] ,被 G1b 重现能力的特性是内建的(built-in) 。这不同于如 [RnRK] G2 指定的基本的(primitive) 特性。 | |
347 | +[RnRK] 的基本特性指不要求作为派生的(derived) ,即以对象语言程序实现的特性。而内建特性适合整个语言规范(@1.2.1.2) 的接口设计约定,不论其实现是否被派生。不被要求重现的部分是实现细节。 | |
348 | +但是,因为基本特性不要求能通过对象语言特性的组合实现,在不考虑派生特性的可实现性时,G1b 不会限定基本特性的能力。 | |
349 | +整体上的 G1b 在和正确性(@1.5.3.1) 冲突时不被要求。(因而避免了 [RnRK] 0.1.1 指定的“妥协”)。 | |
350 | +G1b 仅表示用户使用语言的扩展,不表示语言自身的可扩展性(这通过满足需求的能力(@1.5.2.1) 体现),因为语言规范不依赖使用对象语言(@1.2.4) 表达。 | |
340 | 351 | |
341 | 352 | @1.5.5.2 适用性(usability) : |
342 | 353 | 设计应提供合乎预期满足的问题领域的特性。 |
@@ -348,9 +359,9 @@ | ||
348 | 359 | 设计应允许但难以偶然实现的危险操作。 |
349 | 360 | 同 [RnRK] 的设计原则 G3 。 |
350 | 361 | 这里的危险的操作指引起较大代价的不预期或无法预期结果的操作。 |
351 | -这是 @1.5.2.1 和 @1.5.2.2 的推论,包含两方面: | |
362 | +这是变化的自由(@1.5.2.1) 和 @1.5.2.2 的推论,包含两方面: | |
352 | 363 | 避免危险操作的风险是 @1.5.1.1 和 @1.5.2.2 的推论; |
353 | -不直接禁止危险的操作以满足 @1.5.2.1 的要求。 | |
364 | +不直接禁止危险的操作以满足上述的允许变化的要求。 | |
354 | 365 | 避免危险的操作在许多上下文中可减少程序中易错(error-prone) 的实现的风险。 |
355 | 366 | |
356 | 367 | @1.5.5.2.2 可用性(availablity) : |
@@ -405,8 +416,8 @@ | ||
405 | 416 | 作为典型的 NPL 的一个派生实现(@1.2.4) ,NPLA1(@7) 具有以下和 Kernel 相似的核心设计: |
406 | 417 | 相似的求值(@4.1) 算法(差异参见 NPLA1 求值算法(@9.7.1) ); |
407 | 418 | 环境(@4.6.1) 可作为一等对象(@4.1) ; |
408 | -支持 vau 抽象(@4.5.2.3) ,且使用词法作用域(@4.6.1.2) ; | |
409 | -强调表达式求值前后的不同(详见 @1.4 ); | |
419 | +支持 vau 抽象(@4.5.2.5) ,且使用词法作用域(@4.6.1.2) ; | |
420 | +强调支持对象语言中的显式求值风格及表达式(@3.4.2) 求值前后的不同(详见 @1.4 ); | |
410 | 421 | 强调直接求值而非传统 LISP 方言的 quote(@9.7.1.1) 。 |
411 | 422 | |
412 | 423 | @2 整体设计: |
@@ -519,12 +530,14 @@ | ||
519 | 530 | 满足错误条件时,实现可引起(signal) 错误。 |
520 | 531 | |
521 | 532 | @2.6 实现行为(@1.2.4) : |
522 | -实现的行为由具有存在非特定空间上限的存储的抽象机描述。 | |
533 | +实现的行为由具有存在非特定空间上限的存储的抽象机描述。这种描述对应的语言的语义是抽象机语义。 | |
523 | 534 | 若语言规则明确特定的行为可被忽略,则被忽略之后的实现行为与之前在语言规则中视为等价。翻译的实现可选取这些等价行为中的任一具体行为。 |
524 | 535 | 派生实现可通过显式的未指定规则(@2.4) 定义附加的等价性。 |
525 | 536 | 不论程序是否满足正确性规则(@2.5) ,实现对程序的执行都可能存在未定义行为(@1.2.4) ,此时实现的行为不需要满足正确性规则指定的行为要求。 |
526 | 537 | 特定的语言规则引入未定义行为。程序的执行在适用这些规则指定的条件时,引起未定义行为。 |
527 | 538 | 特定的语言规则排除未定义行为的引入,以满足一定的可用性(@1.5.5.2.2) 。这不排除程序的执行可能因同时使用的其它语言规则引起的未定义行为。 |
539 | +**注释** | |
540 | +抽象机语义是一种操作语义(@2.2.3) 。 | |
528 | 541 | |
529 | 542 | @2.7 简单实现模型约定: |
530 | 543 |
@@ -689,7 +702,7 @@ | ||
689 | 702 | |
690 | 703 | @4 语义: |
691 | 704 | NPL 的语义规则(@1.2.4) 构成演绎系统(deductive system) ,通过对翻译单元(@3.1) 中的表达式(@3.4.2) 的求值(@4.1) 表达。 |
692 | -除非派生实现另行指定,仅使用表达式指定对象语言的语义。 | |
705 | +除非派生实现另行指定,仅使用表达式(@3.4.2) 指定关于对象语言中的计算的语义。 | |
693 | 706 | 基本语义规则要求: |
694 | 707 | 所有不需要诊断消息(@1.2.4) 的规则由派生实现定义; |
695 | 708 | 本章内的规则应不引入未定义行为(@1.2.4) 。 |
@@ -713,11 +726,12 @@ | ||
713 | 726 | 常量(constant) :满足某种不变量的约束以和不可变状态(@1.2.1.2) 关联的实体。具体由派生实现定义。注意不和变量对立(表示不可变状态的变量可能是常量)。 |
714 | 727 | 值(value) :表达式关联的不可变状态。 |
715 | 728 | 修改(modification) :使状态(@1.2.1.2) 改变的操作。 |
716 | -副作用(side effect) :对表达式的值以外的表示的改变的作用(@1.2.4) 。 | |
729 | +作用(effect) :语言支持的一定上下文内的表达式规约的结果的计算作用(@1.2.4) 。 | |
730 | +副作用(side effect) :对表达式的值以外的表示的改变的作用。 | |
717 | 731 | 幂等性(idempotence) :重复后即不改变状态的操作性质。 |
718 | 732 | 项(term) :特定的演绎系统(deductive system) 特别是项重写系统(term rewriting system) 中处理的对象,是带有基本递归构造的元素,可对应语法中的表达式。 |
719 | 733 | 子项(subterm) :具有递归形式构造的文法描述的参与构成项的项。 |
720 | -约束变量(bound variable) :子项中出现的被约束的变量。 | |
734 | +约束变量(bound variable) :子项中出现的名称被约束,即任意的重命名不保证不改变语义的变量。 | |
721 | 735 | 自由变量(free variable) :子项中出现的非约束变量。 |
722 | 736 | 组合子(combinator) :不是变量也不含相对任何项的自由变量的子项。 |
723 | 737 | 转换(conversion) :根据基于特定等价性(假设)前提的两个项之间的自反的演绎。 |
@@ -736,6 +750,7 @@ | ||
736 | 750 | **注释** |
737 | 751 | 在实现执行的上下文,生存期概念兼容 IEC 2382 的 lifetime 定义: |
738 | 752 | portion of the execution duration during which a language construct exists |
753 | +典型地,作用包括计算得到的值、产生的副作用以及其它可由区域和变化的状态二元组描述的实体。 | |
739 | 754 | 一等对象同时是对象。 |
740 | 755 | 为满足可作为表达式被求值,一等实体总是能关联表达求值结果值,称为实体的值。 |
741 | 756 | 作为实体,对象总是关联值作为它的内容,称为对象的值。 |
@@ -781,7 +796,7 @@ | ||
781 | 796 | 在实现外部访问某个状态的操作(输入/输出操作)是副作用。 |
782 | 797 | 若存在状态等价性以外描述的行为描述,由派生实现指定。 |
783 | 798 | 可观察行为如有其它外延,由派生实现指定;否则存在副作用是存在可观察行为的充分条件。 |
784 | -实现应满足实现行为(@2.6) 和语义蕴含的可观察行为等价,其余行为等价性未指定:不严格要求按抽象机(@2.6) 实现操作语义。 | |
799 | +实现应满足实现行为(@2.6) 和语义蕴含的可观察行为等价,其余行为等价性未指定:不严格要求按抽象机语义(@2.6) 。 | |
785 | 800 | 实现可支持实体具有对外部不引起可观察行为(@4.1.3) 差异的隐藏状态。 |
786 | 801 | 隐藏状态和程序约定的一些状态作为管理状态(administrative state) ,以隐藏局部的状态变化对程序中其它状态的影响。 |
787 | 802 | 非管理状态是数据状态(data state) 。 |
@@ -804,7 +819,7 @@ | ||
804 | 819 | @4.1.4.1 实体的同一性: |
805 | 820 | 同一性(@4.1) 是实体上的等价关系(@1.2.2) 的一个实例。 |
806 | 821 | 同一性决定任意两个实体可在语言中不依赖具体操作的行为被直接区分,即满足 Leibneiz 法则(Leibneiz's law) 。 |
807 | -基于这个性质,可在实体上定义和 [So90] 相容的更强的(不依赖语言设计中不存在副作用(@4.1.4) 的)引用透明性(referential transparency) 。 | |
822 | +基于这个性质,可在实体上定义和 [So90] 相容的更强的(不依赖语言设计中不存在副作用的)引用透明性(referential transparency) 。 | |
808 | 823 | 同一性决定的等价类之间的实体相同,即其整体和任意的属性(property) 在任意上下文中等价。 |
809 | 824 | 相同的实体在语言中不需要被区分,可直接替换而不影响程序的语义和行为。后者蕴含可观察行为等价(@4.1.3) 。 |
810 | 825 | 在语言规则中,实体的同一性体现在以下隐含的默认规则: |
@@ -814,8 +829,8 @@ | ||
814 | 829 | 关于语言在一等实体上提供的同一性的具体判断依据,和具体语言支持的特性相关,参见 @4.2.1 。 |
815 | 830 | **原理** |
816 | 831 | 同一性的引入默认是名义的,即断言具有同一性的实体和其它实体上的行为相互独立,而不需要附加证明。这种假设避免了一般地证明任意实体具有同一性的困难。 |
817 | -若不依赖直接在实体上标记等价类等依赖名义同一性假设的方法,证明一个实体具有同一性而非已知的其它实体,需证明任意的其它允许在程序中构造的实体和这个实体上的任意作用的可观察行为无关。在不限定具体的计算作用属于会影响可观察行为的计算作用的确切集合时,这是计算上不可能的。因此,支持这类证明会有效地限制语言在支持不同的计算作用种类上的可扩展性。 | |
818 | -反之,从不同的对象上取消同一性(而允许实现共享资源等目的)一般是容易的:只要证明不存在影响可观察行为的计算作用即可。这种证明可以由程序名义地表达,例如标记某个实体上只涉及纯计算而没有副作用。 | |
832 | +若不依赖直接在实体上标记等价类等依赖名义同一性假设的方法,证明一个实体具有同一性而非已知的其它实体,需证明任意的其它允许在程序中构造的实体和这个实体上的任意作用的可观察行为无关。在不限定具体的计算作用(@1.2.4) 属于会影响可观察行为的计算作用的确切集合时,这是计算上不可能的。因此,支持这类证明会有效地限制语言在支持不同的计算作用种类上的可扩展性。 | |
833 | +反之,从不同的对象上取消同一性(而允许实现共享资源等目的)一般是容易的:只要证明不存在影响可观察行为的计算作用(@1.2.4) 即可。这种证明可以由程序名义地表达,例如标记某个实体上只涉及纯计算而没有副作用。 | |
819 | 834 | 另一方面,这也提示纯计算在各种计算作用中具有的特殊性不足以使其作为唯一的可扩展配置的起点。 |
820 | 835 | 最平凡的起点应是没有任何计算作用的空计算。这无法表达计算,而必须要求扩展才具有实用性。而通用目的语言需要支持一般的计算作用,这同时包含支持纯计算。 |
821 | 836 | 从一般的计算作用排除副作用而得到纯计算,只需要添加可被系统证明的假设,这种机制可以嵌入到系统的规约规则中;而以支持纯计算的系统扩展表达一般的计算,需要引入不足以被对象语言求值规则描述其语义的间接表示(即需要被规约以外的规则翻译),并暴露更多和表达一般计算的目的无关的实现细节。 |
@@ -863,15 +878,21 @@ | ||
863 | 878 | 数据结构也可在对象语言中通过实体包含关系以外的途径定义。例如,限定包含关系构成的图中的所有权关系(@4.2.2.3) 附加限制,详见 @4.2.4 。 |
864 | 879 | |
865 | 880 | @4.1.6 作用使用原则: |
866 | -不同副作用(@4.1.4) 对行为(@1.2.4) 的影响可能依赖作用之间的顺序。 | |
881 | +不同副作用对行为(@1.2.4) 的影响可能依赖作用之间的顺序。 | |
867 | 882 | 为保持可组合性(@1.5.4.5) ,副作用仅在必要时引入,且通常需明确区分是否依赖副作用以避免非预期的行为。 |
868 | 883 | |
869 | 884 | @4.1.7 续延(continuation) : |
870 | 885 | 续延是特定上下文(@4.6) 中描述未来的抽象的实体。 |
871 | 886 | 续延描述的计算在后继的规约中实现,在此之前可能被调度(schedule) 或指定不同的计算内容。上下文可决定这些计算中可变的参数化部分。 |
872 | -续延可由符合演绎规则(@4.1.2) 的规约步骤的集合或未指定的其它形式表示。 | |
873 | 887 | 引起控制转变的计算可以通过切换指定当前计算之后的计算的续延(@4.1) 。 |
874 | 888 | 当前规约的上下文中对应的续延是当前续延(current continuation) 。 |
889 | +对控制的限制,续延可分为无界续延(undelimited continuation) 和有界续延(delimited continuation) 等不同形式。 | |
890 | +不同形式的续延的调用都能具有类似的控制作用(@4.1) ,但表达能力不尽相同。 | |
891 | +在仅使用局部转换(local transformation) 即 Felleisen 宏表达(macro-expressible) ([Fl91]) 的意义上,[Fi94] 指出: | |
892 | +有界续延(delimited continuation) 和可变状态(存储)可实现无界续延(undelimited continuation) ; | |
893 | +嵌入在语言中的任意表达计算作用(@1.2.4) 的单子(monad) 的构造可用有界续延实现。 | |
894 | +**注释** | |
895 | +续延可由符合演绎规则(@4.1.2) 的规约步骤(@4.4) 的集合或未指定的其它形式表示。 | |
875 | 896 | |
876 | 897 | @4.2 一等实体设计原理: |
877 | 898 | 基于统一性(@1.5.5.1) ,仅在特定局部上下文中操作非一等实体。(根据 @4.2.1 ,@1.5.5.1 中的规则 G1a 是此规则的推论。) |
@@ -896,7 +917,7 @@ | ||
896 | 917 | 一等实体的具体表现形式通常是实现细节而要求不被依赖,为了支持前者不被显式表达,满足 @1.5.2.4 ; |
897 | 918 | 一等实体的普遍支持允许以统一(@1.5.5.1) 的方式抽象可变状态(@4.1.3) ,且扩展使便于满足 @1.5.2.1 。 |
898 | 919 | 为简化设计,NPL 约定以下默认规则: |
899 | -除非另行指定,名义同一性属性指定为对象在抽象机(@2.6) 实现的操作语义下的存储位置;对象占据存储位置起始的若干存储;存储位置的表示(@1.2.4) 未指定;派生实现可指定具体的表示。 | |
920 | +除非另行指定,名义同一性属性指定为对象在抽象机语义(@2.6) 下的存储位置;对象占据存储位置起始的若干存储;存储位置的表示(@1.2.4) 未指定;派生实现可指定具体的表示。 | |
900 | 921 | 在语言规则中,一等对象满足 @4.1.4.1 的默认规则。 |
901 | 922 | |
902 | 923 | @4.2.1.2 可变状态和普遍性: |
@@ -908,7 +929,7 @@ | ||
908 | 929 | @4.2.1.3 Kernel 中的一等对象: |
909 | 930 | 尽管没有显式指出一等实体和一等对象的区别,在 [RnRK] 中的一等对象和此处的一等实体在目的上一致。因为 Kernel 不直接支持区分对象同一性,一等实体退化为一等对象。 |
910 | 931 | 并不需要修改一等对象的判定准则(@4.1) 限定为后者并使前者依赖后者的定义,因为作为抽象,前者通常并非是后者的操作上进行限制得到(正相反,一般是通过补充约定假设得到,如 @4.1 的定义)。 |
911 | -类似的一个例子是不可修改对象(nonmodifiable object) 可以但不必要是对应的可修改对象(modifiable object) 的子类型(@4.7.2) 。 | |
932 | +类似的一个例子是不可修改对象(nonmodifiable object) 可以但不必要是对应的可修改对象(modifiable object) 的子类型(@4.7.7) 。 | |
912 | 933 | 作为 @1.5.5.1 的实例,一等实体避免特殊规则,和 [RnRK] 设计原则 G1a 一致。 |
913 | 934 | 除非另行指定,本节中的以下描述提供允许派生实现提供保证或假设的机制,并非要求。派生实现可附加规则改变此处对一等对象的保证或性质。 |
914 | 935 |
@@ -925,7 +946,17 @@ | ||
925 | 946 | 注意并非所有一等对象都需要支持一等状态;否则几乎总是会付出本不必要的代价(@1.5.2.2) 也难以避免违反适用性(@1.5.5.2) ;因此有必要区分一等状态的对象和非一等状态的对象。 |
926 | 947 | 这种区分实质上更普遍地对具体的计算操作也存在意义,自然地引入了值类别(value category) ;最简单的设计如区分左值(lvalue) 和右值(rvalue) 分别关联是否需要支持一等状态的对象。 |
927 | 948 | 为允许一等状态和外部环境的互操作(@1.2.3) ,不能总是假定只有一类总是可被程序局部预知的修改操作(@4.1.4.2)(典型地,定义为“设置被引用对象(@4.2.3)”操作,如 [RnRK] 3.1 )影响状态,而应允许和特定对象关联的求值时的不透明的副作用(一个实例是 ISO C 和 ISO C++ 的 volatile 类型对象)。 |
928 | -若不考虑互操作,则一等对象用有限的不同等价谓词(@4.1.4) 即能提供区分同一性的操作;否则,等价谓词的设计即便保持正交,也需区分不同的一等对象对各种副作用的不同支持情况。 | |
949 | +若不考虑互操作,则一等对象用有限的不同等价谓词(@4.1.4) 即能提供区分同一性的操作;否则,等价谓词的设计即便保持正交(@1.5.5.4) ,也需区分不同的一等对象对各种副作用的不同支持情况。 | |
950 | +一等状态是否通过其它特性派生(@1.5.5.1) 未指定的。 | |
951 | +**原理** | |
952 | +基于 [Fi94] ,结合可变状态能被表达为单子(如 https://www.pauldownen.com/publications/delimited-control-effects.pdf )的事实,有界续延(@4.1.7) 可实现状态。 | |
953 | +相对地,基于 [Fl91] ,无界续延(@4.1.7) 和异常不能实现一般意义的可变状态,参见 https://www.researchgate.net/profile/Hayo-Thielecke/publication/2487383_Contrasting_Exceptions_and_Continuations/links/53e12dba0cf2235f352738e5/Contrasting-Exceptions-and-Continuations.pdf 推论 5.13 。 | |
954 | +因为同一性可以在引入状态时被编码而在之后不需改变,使用有界续延等非一等的状态可支持实现状态的同一性。因此,在此不对是否基本要求作出限定。 | |
955 | +但是,使用有界续延实现状态仅仅是实现细节,且通常具有一些非预期的实现性质: | |
956 | +这实质要求实现同一性无界续延具有区分同一性的能力(相当于左值),而引起不正交的内部设计; | |
957 | +同时,在现有实现普遍提供状态的原生支持(存储器)的常见情况下,编码状态会付出本不必要的代价。 | |
958 | +为满足非常规的实现环境或更优先的原则(如变化的自由(@1.5.2.1) 和正确性(@1.5.3.1) ),派生实现仍可使用有界续延派生一等状态,同时提供访问更基本的不依赖可变状态的接口,以使上述影响不再是非预期的。 | |
959 | +用户程序仍不被禁止使用这种方式自行提供类似的实现,以确保不约定一等状态作为基本的内建特性时(@1.5.5.1) ,语言的设计不违反 G1b(@1.5.5.1) 。 | |
929 | 960 | **注释** |
930 | 961 | 实现在一般实体上支持的隐藏状态(@4.1.3) 不被程序可编程地指定,不是一等状态。 |
931 | 962 |
@@ -1062,7 +1093,7 @@ | ||
1062 | 1093 | |
1063 | 1094 | @4.2.3.5 可变对象问题: |
1064 | 1095 | 仅使用引用操作一等对象在涉及对象修改(@4.1) 的存在一些问题。 |
1065 | -一般地,和对象改变操作(@4.1.4.2) 对应的作用(@1.2.4) 及其类似的副作用(@1.2.4) 也和修改对象有类似的问题。 | |
1096 | +一般地,和对象改变操作(@4.1.4.2) 对应的作用(@1.2.4) 及其类似的副作用(@4.1) 也和修改对象有类似的问题。 | |
1066 | 1097 | |
1067 | 1098 | @4.2.3.5.1 可变对象操作限制: |
1068 | 1099 | 在使用普遍引用的设计中,实体的可修改性(@4.1.4.2) 可能以被引用对象的可变性提供:通过允许改变对象中包含的引用指定对象可变,而体现可修改性。 |
@@ -1092,7 +1123,8 @@ | ||
1092 | 1123 | 基于 [RnRK] 的封装类型(encapsulation type) 可实现装箱,但装箱实际上依赖的是共享的引用,并非封装类型自身的名义类型(@4.7.2) 特性,不符合 @1.5.2.4 。并且,在扩展针对个别对象的属性(如不可变性(@4.1.4.2) )时它仍存在一些不容易扩展的较复杂的问题,如: |
1093 | 1124 | https://groups.google.com/forum/?fromgroups#!topic/klisp/pLz-uqJ0WfE 。 |
1094 | 1125 | 使用 [RnRK] 的设计而在此之后引入一等引用(而不仅是一等被引用的对象)可能是对语言特性的实现的复杂的改动,但直接消除隐式的共享(@4.2.3.4) 而使用一等引用的设计并不需要这样的复杂性。 |
1095 | -封装类型作为装箱以外仍有意义。封装类型仍可用于实现箱,但这只是实现细节,更一般的情形两者是无关的——这也符合 @1.5.2.3 和 @1.5.2.4 的要求。 | |
1126 | +封装类型作为装箱以外仍有意义。 | |
1127 | +封装类型仍可用于实现箱,但这只是实现细节,更一般的情形两者是无关的——这也符合最小接口原则(@1.5.2.3) 和关注点分离原则(@1.5.2.4) 的要求。 | |
1096 | 1128 | |
1097 | 1129 | @4.2.3.5.4 退化的间接改变: |
1098 | 1130 | 退化的箱(@4.2.3.5.3) 可能不共享改变。这样的箱和一等引用没有实质区别。 |
@@ -1102,6 +1134,22 @@ | ||
1102 | 1134 | 一等引用外的其它类型在这个意义上仅因为共享的引用而具有可变性,这是实现细节而不应被依赖。它们应具有其它的特定的含义,以避免违反 @1.5.2.3 、@1.5.2.4 和 @1.5.3.2 ,并允许实现支持 @1.5.2.2 。 |
1103 | 1135 | 例如,[RnRK] 中的 pair 因为 set-car! 等操作相当于这里的可改变的箱类型。在不保证共享改变的引用也不引入一等引用时,改变不会被扩散。 |
1104 | 1136 | |
1137 | +@4.2.3.5.5 非必要的实现依赖: | |
1138 | +尽管箱(@4.2.5.2.3) 的主要目的是允许对共享敏感的副作用(@4.1) 以符合用户预期的方式可见,也有一些非典型的其它使用。 | |
1139 | +**注释** 特别地,这种方式关注副作用和其它计算作用(@1.2.4) 的顺序(@4.4.3) 。 | |
1140 | +一个代表是 Haskell 使用箱实现惰性的中间值(lazy thunk) 。 | |
1141 | +这种实现引入了一些性能问题。 | |
1142 | +在此,Haskell 提供未装箱的值以变通这些缺陷,乃至在类型系统上单独区分不同的种类(@4.7.9) 并提供新的种类多态(@4.7.9) ,称为多变(levity) 多态: | |
1143 | +https://www.microsoft.com/en-us/research/wp-content/uploads/2016/11/levity-pldi17.pdf | |
1144 | +这本质上关于对象表示的抽象泄露。 | |
1145 | +若需要解决性能问题,原则上完全避免惰性的中间值是更适合表达一般含义的做法,但这和 Haskell 的基本设计(求值算法不显含非纯求值,默认依赖惰性求值而提供普遍的等式理论(equational theory) )矛盾。 | |
1146 | +这也暗示默认纯函数式的限制并不仅仅是关于计算作用的种类上的,也会影响计算作用的渐进性质——尽管这些包括 Haskell 在内的绝大多数语言都不支持抽象这类性质而允许在对象语言中表达。 | |
1147 | +引入多变多态解决泛型实现在代码生成(编译)上的结论仍然有意义,但如文献中提及的其它替代做法中,问题最少的是 .NET 的运行时单态化(monomorphization) ,后者也不需要类似的对象表示的抽象泄露。 | |
1148 | +本质上,这里的问题不仅仅是关于一等对象的设计,还有静态类型(@4.7.1) 系统的滥用。类似的设计包含以下原则性问题: | |
1149 | +违反变化的自由(@1.5.2.1) ; | |
1150 | +使避免不必要付出的代价(@1.5.2.2) 难以实现; | |
1151 | +通过种类的分类对一等对象进行划分,而不允许表达静态不确定种类的对象,事实上取消了一等对象。 | |
1152 | + | |
1105 | 1153 | @4.2.3.6 非必要性和其它限制: |
1106 | 1154 | 对语言设计中的一些关键规则,要求使用引用同时保留非引用的形式访问对象并非必要,因为满足以上准则实际上仅关心对象(类似作为表达式)直接关联的值,而非其它属性。 |
1107 | 1155 | 这也体现是否仅允许通过引用访问对象应是实现细节,而不影响一等对象的判定。 |
@@ -1126,7 +1174,7 @@ | ||
1126 | 1174 | @4.2.4.1 循环引用和所有权: |
1127 | 1175 | 循环引用支持和一等对象引用(@4.2.3) 的一些性质密切相关,特别地,隐含所有权(@4.2.2.3) 的假定。 |
1128 | 1176 | 数据结构的实现蕴含对资源(如存储空间)的使用,其中必然存在不被外部所有的资源,表示为内部的资源对象。 |
1129 | -这包含宿主语言对象对具有所有权的对象的情形,如宿主语言对象的子对象。后者统称为内部对象(internal object) 。 | |
1177 | +这包含对象对其它对象具有所有权的对象的情形,如一些语言(包括宿主语言)的对象可指定具有子对象(subobject) 。后者统称为内部对象(internal object) 。 | |
1130 | 1178 | 为维护数据结构的边界,满足最小依赖原则(@1.5.4.3) ,数据结构作为宿主语言对象,其中的内部对象应具有以下性质: |
1131 | 1179 | 数据结构对内部对象具有整体所有权。 |
1132 | 1180 | 推论:数据结构不引用自身。 |
@@ -1189,7 +1237,7 @@ | ||
1189 | 1237 | 名称验证确定可见名称(@4.3.2) 的基础上确定名称是否有效。 |
1190 | 1238 | 名称查找是从已知有效名称确定唯一指称的实体的过程,仅在名称验证成功后进行。 |
1191 | 1239 | 不同名称经过名称查找的结果可能等效。等效的有效名称视为同一的,规则由派生实现定义。 |
1192 | -除非另行指定,成功的名称解析没有副作用(@1.2.4) 。 | |
1240 | +除非另行指定,成功的名称解析没有副作用(@4.1) 。 | |
1193 | 1241 | 名称解析依赖从保存名称的目标中查找名称。若查找失败,解析可继续从替代的其它目标中进行。这种机制称为重定向(redirection) 。 |
1194 | 1242 | 以上约定以外的具体规则以及失败的行为由派生实现定义。 |
1195 | 1243 | 另见作用域(@4.6.1.2) 。 |
@@ -1226,7 +1274,7 @@ | ||
1226 | 1274 | 除非派生实现另行指定,规约蕴含 NPL 程序的执行,可完全表示程序执行的语义。 |
1227 | 1275 | 推论:NPL 规约规则形式地蕴含 NPL 语义规则。 |
1228 | 1276 | 为表达明确的目的,语言规则(@1.2.1.2) 也可约定其它更抽象形式的求值规则(@4.4.7) ,以蕴含这些规约规则,而不是直接描述规约规则的形式语义。 |
1229 | -描述 NPL 对象语言的操作语义也可被视为特定的对象语言,其规约可以视为求值。但除非另行指定,以下表达式仅指对象语言的表达式,其求值仅指关于对象语言中表达式的求值,而非一般的规约。 | |
1277 | +描述 NPL 对象语言的操作语义也可被视为特定的对象语言(@2.6) ,其规约可以视为求值。但除非另行指定,以下表达式仅指对象语言的表达式,其求值仅指关于对象语言中表达式的求值,而非一般的规约。 | |
1230 | 1278 | 规约规则可要求被规约的项符合一定的结构(如具有特定类型的值)作为前提,否则规约出错,程序执行中止。 |
1231 | 1279 | 根据规约规则描述的行为是否对应对象语言中的求值,规约分为两类:表达式的求值规约(@4.4.1) 和管理规约(@4.4.2) 。 |
1232 | 1280 |
@@ -1247,7 +1295,7 @@ | ||
1247 | 1295 | 表示非一等对象(@4.1) 的项的规约总是管理规约。 |
1248 | 1296 | 抽象求值(@4.1) 中不在对象语言求值结果(@4.1) 中可表达的中间规约是管理规约实现。 |
1249 | 1297 | **注释** |
1250 | -管理规约描述语言的表达式以外的操作语义。 | |
1298 | +管理规约描述语言的表达式以外的操作语义(@2.2.3) 。 | |
1251 | 1299 | 实现也可使用的管理规约描述特定于实现的(而在对象语言中未指定的)语义性质。 |
1252 | 1300 | |
1253 | 1301 | @4.4.3 顺序(order) : |
@@ -1256,7 +1304,7 @@ | ||
1256 | 1304 | 非决定性有序(indeterminately sequenced) 是先序或后序的并集。 |
1257 | 1305 | 无序(unsequenced) 是非决定性有序在求值二元关系全集上的补集。 |
1258 | 1306 | 规约规则的顺序直接适用于求值,其顺序为求值顺序(evaluation order) 。 |
1259 | -规约规则的顺序也适用在能以以其形式地描述相对顺序的事件(称为规约事件)上,如两个对象的生存期(@4.1) 的起始,或者某个计算作用。 | |
1307 | +规约规则的顺序也适用在能以以其形式地描述相对顺序的事件(称为规约事件)上,如两个对象的生存期(@4.1) 的起始,或者某个计算作用(@1.2.4) 。 | |
1260 | 1308 | 一些事件的顺序是通过推理具有因果性(causality) 的依赖关系决定的,包括: |
1261 | 1309 | 规约中值的计算依赖规约的输入; |
1262 | 1310 | 从一个实体上确定作为值的属性的读(read) 依赖这个属性; |
@@ -1317,9 +1365,9 @@ | ||
1317 | 1365 | 顺序依赖规则:子表达式值的计算先序(@4.4.3) 所在的表达式值的计算。 |
1318 | 1366 | 平凡求值规则:指定一个表达式是纯求值或空求值(@4.4.4) 对应实质蕴含其子表达式的求值被指定为纯求值或空求值。 |
1319 | 1367 | **注释** |
1320 | -一般地,一些求值策略(@4.4.6.5) 可以不遵循求值依赖规则。一个具体的典型例外是 WHNF 可约定只求值部分子表达式实现这些求值策略;参见 vau 抽象(@4.5.2.3) 。 | |
1368 | +一般地,一些求值策略(@4.4.6.5) 可以不遵循求值依赖规则。一个具体的典型例外是 WHNF 可约定只求值部分子表达式实现这些求值策略;参见 vau 抽象(@4.5.2.5) 。 | |
1321 | 1369 | 顺序依赖规则是因果性(@4.4.3) 的具体表现之一。对不被求值的表达式,此规则不生效。构造不同的表达式进行计算可实现和直接违反此规则等效的作用,但因为是不同的表达式,实际上不违反此规则。 |
1322 | -附加的顺序依赖规则可由特定的实体构成的表达式的求值隐含指定,如过程调用(@4.5.2.1) 可利用不同的求值策略(@4.4.6.5) 。 | |
1370 | +附加的顺序依赖规则可由特定的实体构成的表达式的求值隐含指定,如过程调用(@4.5.2.2) 可利用不同的求值策略(@4.4.6.5) 。 | |
1323 | 1371 | |
1324 | 1372 | @4.4.6.2 严格性(strictness) : |
1325 | 1373 | 若表达式的任意子表达式的求值总是非空求值(@4.4.4) 且先序表达式求值,则这个表达式的求值是严格的(strict) ;反之,求值是非严格的(non-strict) 。 |
@@ -1335,11 +1383,12 @@ | ||
1335 | 1383 | 递归文法表示的表达式和子表达式之间存在相对内外顺序:子表达式在表达式的内部。此求值顺序可对应表达式树的遍历顺序。 |
1336 | 1384 | |
1337 | 1385 | @4.4.6.4 替换策略: |
1338 | -对应项的规约规则的表达式的重写规则由派生实现定义,基本的可选项包括: | |
1339 | -名称替换:保证替换前后项对应的名称不变; | |
1340 | -实体替换:保证替换前后项关联的实体不变; | |
1341 | -值替换:保证替换前后项关联的表达式的值满足实现定义的相等关系。 | |
1342 | -引用替换:保证替换前后项关联的表达式的值以实现定义的方式引用同一实体。 | |
1386 | +对应项的规约规则的表达式的重写规则由派生实现定义,典型的可选项包括: | |
1387 | + 名称替换:保证替换前后项对应的名称不变。 | |
1388 | + 实体替换:保证替换前后项关联的实体不变。 | |
1389 | + 值替换:保证替换前后项关联的表达式的值满足实现定义的相等关系。这包括以下不同的变体: | |
1390 | + 值副本替换:保证替换前后项关联的表达式的值满足值替换的关系,且以实现定义的方式引用不同的实体副本。 | |
1391 | + 引用替换:保证替换前后项关联的表达式的值满足值替换的关系,且以实现定义的方式引用同一实体。 | |
1343 | 1392 | |
1344 | 1393 | @4.4.6.5 求值策略: |
1345 | 1394 | 组合严格、顺序求值和替换策略可得到不同性质的求值策略。 |
@@ -1349,6 +1398,7 @@ | ||
1349 | 1398 | 应用序(applicative order) :以最左最内(leftmost innermost) 优先的顺序求值。 |
1350 | 1399 | 顺序仅在操作数是有序数据结构(@4.4.5.1) 时有意义;不考虑操作数内部构造时,仅表示操作数作为子表达式总是被求值,和严格求值等价。 |
1351 | 1400 | 按值传递(pass by value) :使用值替换的严格求值。 |
1401 | +按值的副本传递(pass by value copy) :使用值副本替换的严格求值。 | |
1352 | 1402 | 按引用传递(pass by reference) :使用引用替换的严格求值。 |
1353 | 1403 | 共享对象传递(pass by shared object) :使用的共享机制以及对象和值或引用的关系由派生实现定义。 |
1354 | 1404 | 部分求值(partial evaluation) :允许求值分为多个阶段(phase) 。 |
@@ -1389,7 +1439,7 @@ | ||
1389 | 1439 | 匿名函数(anonymous function) ; |
1390 | 1440 | 函数应用(function application) 。 |
1391 | 1441 | 具体含义见以下各节。 |
1392 | -注意 λ 演算可保证以上除函数应用外求值的强规范化,但此处不要求,参见 @4.4.5 。 | |
1442 | +注意 λ 演算可保证以上除函数应用外求值的强规范化(@4.4.5) ,但此处不要求。 | |
1393 | 1443 | |
1394 | 1444 | @4.5.1 名称表达式 : |
1395 | 1445 | 名称表达式是表示变量的 λ 项。 |
@@ -1397,43 +1447,70 @@ | ||
1397 | 1447 | 名称表达式不被进一步规约;其求值是值替换规则(@4.4.1) 的平凡形式。 |
1398 | 1448 | |
1399 | 1449 | @4.5.2 函数(function) : |
1400 | -函数是一种参与特定规约规则的实体,也可以指求值为函数对象的表达式。 | |
1450 | +函数是一种参与特定规约规则的实体,也可以指求值为函数实体的表达式。 | |
1401 | 1451 | 一般地,函数表达式在 WHNF 下作为操作符(@4.4.5.1) 被求值,其最终求值结果(@4.4.5) 为函数实体,或函数对象(若函数在语言中允许作为对象)。 |
1402 | 1452 | NPL 中,作为一等对象(@4.1) 的函数表达式的最终求值结果(@4.4.5) 是合并子(@4.5.3.2) 。 |
1403 | 1453 | 一个函数表达式是以下两种表达式之一: |
1404 | 1454 | 保持等价地(@4.4.4) 求值到其它函数表达式上的名称表达式,称为具名函数(named function) 表达式,简称具名函数; |
1405 | 1455 | 满足本节以下规则的由派生实现定义的匿名函数(anonymous function expression) 表达式,简称匿名函数。 |
1406 | 1456 | 函数应确定替换重写规则被替换的目标(@4.4.6.4) (称为函数体(function body) )。 |
1407 | -匿名函数可以捕获(capture) 若干变量,并在函数体引入这些变量。 | |
1408 | -匿名函数可以显式指定(绑定(bind) )包含若干变量使之成为约束变量(@4.1) 的语法构造。这种变量称为函数的形式参数(formal parameter, parameter) 。 | |
1457 | +除非派生实现另行指定,函数不需要被进一步规约,此时其求值是值替换规则(@4.4.1) 的平凡形式。 | |
1458 | + | |
1459 | +@4.5.2.1 函数内部的变量: | |
1460 | +匿名函数可以显式指定(绑定(bind) )包含若干变量使之成为约束变量(@4.1) 的语法构造。 | |
1461 | +通过创建函数时的显式的语法构造引入的这种变量称为函数的形式参数(formal parameter, parameter) 。 | |
1462 | +除绑定外,匿名函数可以捕获(capture) 若干在函数体以外的同名变量,即自由变量(@4.1) 。 | |
1463 | +通过绑定或捕获引入的变量允许在函数体中允许使用。 | |
1409 | 1464 | 使用词法作用域(@4.6.1.2) 时,若匿名函数所在作用域(@4.1) 的存在同名的名称,则被捕获的名称隐藏(@4.1) 。形式参数隐藏被捕获的变量名。 |
1410 | 1465 | 派生实现的语义规则应满足和 λ 演算语义(@4.5) 的 α-转换(alpha-conversion) 规则不矛盾。注意 vau 演算(@1.4) 在没有限定环境(@4.6.1) 时不考虑一般意义上的自由变量(@4.1) 。 |
1411 | -除非派生实现另行指定,函数不需要被进一步规约,此时其求值是值替换规则(@4.4.1) 的平凡形式。 | |
1412 | -除非另行指定,作为项的函数应具有符合类型构造器(type constructor) * → * 结果的类型(@4.7) ,如为简单类型 λ 演算(STLC, simply-typed lambda calculus) 兼容的函数类型实例。 | |
1413 | - | |
1414 | -@4.5.2.1 过程(procedure) : | |
1415 | -过程是操作符具现(@1.2.4) 的实体,决定特定的可提供求值的作用(包括决定求值结果)的计算。 | |
1466 | +函数应用的求值决定被绑定的变量和函数体内的变量之间的关系,参见函数合并(@4.5.3) 。此时,求值策略(@4.4.6.5) 蕴含的替换策略(@4.4.6.5) 蕴含被绑定的变量和函数体内的变量之间的同一性。 | |
1467 | +类似地,在被捕获的变量到函数体内捕获的变量之间,也有和替换策略一一对应的不同捕获策略。 | |
1468 | +除非另行指定,变量被按引用捕获(captured by reference) 而非按值的副本捕获(captured by value copy) ,即通过捕获引入的变量是被捕获变量的引用(@4.2.3) 而不是副本。 | |
1469 | +**原理** | |
1470 | +捕获为引用而不是副本,保持被捕获的变量和函数体内同名变量的同一性(@4.1) ,在实体是对象时不影响可观察行为。若这些捕获未被使用,可被实现直接移除。 | |
1471 | + | |
1472 | +@4.5.2.2 过程(procedure) : | |
1473 | +过程是操作符具现(@1.2.4) 的可调用(callable) 的实体,决定特定的可提供求值的作用(包括决定求值结果)的计算。 | |
1416 | 1474 | 函数表达式的最终求值结果(@4.4.5) 由过程实体的计算结果决定,以派生实现定义的方式关联。 |
1475 | +通过函数表达式可指定可选的实际参数(@4.5.3) ,过程被调用(call) 。过程的调用蕴含计算。 | |
1417 | 1476 | 过程中和过程外的计算的组合满足因果性(causality) : |
1418 | 1477 | 以求值描述的过程中的计算整体非后序(@4.4.3) 于引起过程中计算的外部环境的计算; |
1419 | 1478 | 以求值描述的过程中的任意计算非后序(@4.4.3) 于对应计算结果的值的计算,即值的计算结果是决定值的计算的依赖(dependency) 。 |
1420 | -主调函数(caller function) 等调用者(caller) 或其它引起过程中的计算的实体转移控制(control) 到过程中的计算而使之进入(enter) 过程。 | |
1421 | -过程调用时控制可能通过调用被再次转移,即嵌套调用。 | |
1422 | -一些被调用的过程可能被多次进入,即重入(reenter) 。 | |
1479 | +主调函数(caller function) 等调用者(caller) 或其它引起过程中的计算的实体转移控制(control) 到过程中的计算而使之进入(enter) 被调用者(callee) 的过程。 | |
1480 | +过程可能被限制只有一次(one-shot) 调用有效;其它过程是多次(multi-shot) 的。 | |
1481 | +多次过程调用时控制可能通过调用被再次转移,即嵌套调用。 | |
1482 | +一些被多次调用的过程可能被多次进入,即重入(reenter) 。 | |
1423 | 1483 | 通过嵌套调用直接(总是以自身作为调用者)或间接(通过其它调用者转移控制)的重入是递归调用(recursive call) 。 |
1424 | -过程可以返回(return) 改变控制状态(@4.1) ,影响之后的计算。 | |
1425 | -具体实现中的过程按计算的顺序约束和默认返回控制的方式,可能有不同的形式: | |
1426 | -例程(routine) 的求值不交叉(interleave) ,即例程中的计算和之外的计算非决定性有序(@4.4.3) ; | |
1427 | -作为不同的例程,不考虑被保存续延时,子例程(subroutine) 在返回一次后不重新进入,协程(coroutine) 则可能引起多次返回; | |
1428 | -一般的续延支持返回多次并可能支持和调用者并发的计算。 | |
1429 | -过程可能同时使用这些形式的一种或多种,使用的具体形式由提供过程的派生实现指定。 | |
1430 | -注意过程不一定具有可被对象语言(@1.2.4) 直接表达的一等(first-class) 函数而在元语言(@1.2.4) 中可能是,如无界续延(undelimited continuation) ,因为其不符合函数的类型要求(@4.5.2) 。 | |
1431 | - | |
1432 | -@4.5.2.2 λ 抽象(lambda abstraction) : | |
1484 | +过程可以返回(return) 取得计算的值并可同时改变控制状态(@4.1) ,影响之后的计算。 | |
1485 | +**原理** | |
1486 | +一次过程,特别是在其内部涉及和续延(@4.1.7) 或闭包(@4.6.1.2) 的实现交互时,相对多次过程可能具有因其对持有资源的要求较宽松,而具有较小的性能开销。 | |
1487 | +**注释** | |
1488 | +对象语言中的过程在描述操作语义的元语言中可表示为函数,其应用可对应对象语言中过程的隐式调用。 | |
1489 | +违反一次过程调用有效地约束的程序典型地引起错误(@2.5.2) 。 | |
1490 | +注意过程不一定具有可被对象语言(@1.2.4) 直接表达的一等(first-class) 函数,而在元语言(@1.2.4) 中可能是,如无界续延(@4.1.7) ,因为其不符合函数的类型要求(@4.5.2) 。 | |
1491 | +参见 https://okmij.org/ftp/continuations/undelimited.html#introduction 。 | |
1492 | + | |
1493 | +@4.5.2.3 过程调用的计算顺序: | |
1494 | +按计算的顺序约束和默认返回控制的方式,可能有不同的形式。 | |
1495 | +例程(routine) 的求值不交叉(interleave) ,即例程中的计算和之外的计算非决定性有序(@4.4.3) 。 | |
1496 | +作为不同的例程,不考虑被保存续延(@4.1.7) 时: | |
1497 | +子例程(subroutine) 在返回一次后不重新进入; | |
1498 | +协程(coroutine) 则可能引起多次返回。 | |
1499 | +引起多次返回对应改变控制作用(@4.1) 。 | |
1500 | +一般的续延(@4.1.7) 支持返回多次并可能支持和调用者并发的计算,而协程蕴含的控制作用的改变对应不同续延的替换,也能实现类似的支持。 | |
1501 | +NPL 支持函数求值得到过程。对象语言中的过程可能支持使用这些形式的一种或多种,具体形式由派生实现指定。 | |
1502 | +**注释** | |
1503 | +关于过程的参数和过程调用之间的计算顺序,参见求值策略(@4.4.6.5) 。 | |
1504 | +典型的设计中,函数表达式默认创建例程,而协程使用特设的语法(如 yield 等关键字)标记过程得到。 | |
1505 | +作为实现,根据是否持有活动记录帧(@4.5.3.4) ,协程分为有栈(stackful) 和无栈(stackless) 的。两者提供不同的资源所有权(@4.2.2.3) 。 | |
1506 | +协程可能限制控制的返回方向,即调用者和被调用者被通过创建其的语法构造确定,而不能在之后改变。 | |
1507 | +非对称的协程(如 http://www.inf.puc-rio.br/~roberto/docs/MCC15-04.pdf )本质是限制了控制转移的一次续延(@4.5.3.3) ,作为一等对象时,可作为一等续延(@4.5.3.3) 的替代实现方式。 | |
1508 | + | |
1509 | +@4.5.2.4 λ 抽象(lambda abstraction) : | |
1433 | 1510 | λ 抽象是典型的操作符,是 λ 演算中的基本构成之一。 |
1434 | 1511 | λ 抽象创建的过程是应用合并子(@4.5.3.2) 。 |
1435 | 1512 | |
1436 | -@4.5.2.3 vau 抽象(vau abstraction) : | |
1513 | +@4.5.2.5 vau 抽象(vau abstraction) : | |
1437 | 1514 | Vau 抽象是 vau 演算中的基本构成之一。 |
1438 | 1515 | Vau 抽象创建的过程是操作合并子(@4.5.3.2) 。 |
1439 | 1516 | 使用 vau 抽象可实现 λ 抽象,如 Kernel 提供的 $vau 操作合并子(@4.5.3.2) 。 |
@@ -1441,7 +1518,7 @@ | ||
1441 | 1518 | @4.5.3 函数合并(function combination) : |
1442 | 1519 | 具有至少一个约定位置的子项 E1 的复合表达式(@3.4.2.2) E ,当且仅当 E1 是被求值作为操作符(@4.4.5.1) 的函数时,E 是函数合并表达式,简称函数合并。 |
1443 | 1520 | 求值函数合并时,子项(@4.1) E1 总是被求值; |
1444 | -除 E1 外表达式的剩余子项 E2 是操作数(@4.4.5.1),在 E 被求值时以操作数决定的值等效替换(substitute) 函数的形式参数(@4.5.2) 。 | |
1521 | +除 E1 外表达式的剩余子项 E2 是操作数(@4.4.5.1),在 E 被求值时以操作数决定的值等效替换(substitute) 函数的形式参数(@4.5.2.1) 。 | |
1445 | 1522 | 替换形式参数的值是实际参数(actual argument, argument) 。 |
1446 | 1523 | 函数合并的求值是替换规则(@4.4.1) 的非平凡形式。 |
1447 | 1524 | 若替换操作数 E2 在合并中被求值,函数合并 E 是函数应用表达式,简称函数应用。 |
@@ -1451,24 +1528,38 @@ | ||
1451 | 1528 | 函数应用匹配实际参数和对应的引入形式参数的构造。这个过程可能失败。确定匹配参数成功的条件是等价关系,称为参数匹配一致性,由参数匹配的等价关系指定。 |
1452 | 1529 | 匹配成功的每个实际参数和被匹配的目标(可能是形式参数)具有一对一或多对一的对应关系。 |
1453 | 1530 | 伴随参数匹配,实现可引入其它必要的操作(如为匹配分配资源和确定上述对应关系)。这些操作可具有和确定参数对应关系的匹配之间非决定性有序(@4.4.3) 的副作用。 |
1454 | -仅当上述必要操作及所有实际参数的匹配成功,替换 E1 决定的某个关联表达式中和形式参数(@4.5.2) 结构一致的子表达式为实际参数(@4.5.2) 。替换参数的结构一致性是等价关系。 | |
1531 | +仅当上述必要操作及所有实际参数的匹配成功,替换 E1 决定的某个关联表达式中和形式参数结构一致的子表达式为实际参数。替换参数的结构一致性是等价关系。 | |
1455 | 1532 | 表达式相等蕴含参数匹配一致性和替换结构一致性。实现可分别定义其它规则扩充这些等价关系的外延。 |
1456 | 1533 | 替换参数的值蕴含对实际参数的计算的依赖,即参数的值的计算先序(@4.4.3) 函数应用的求值;但其它求值顺序没有保证。 |
1457 | - | |
1458 | -@4.5.3.1 函数调用: | |
1534 | +**注释** | |
1535 | +过程(@4.5.2.2) 及其调用在其操作语义的元语言中通常表达为函数及函数合并(@4.5.3) 。 | |
1536 | +若过程的结果被忽略,则通常表达为单元类型(@4.7) 的值。 | |
1537 | +此外,一些语言中忽略过程的结果是空类型(@4.7) ,以检查错误的使用。NPL 不要求语言具有静态类型(@4.7.1) 规则,也不要求这些检查。 | |
1538 | + | |
1539 | +@4.5.3.1 函数调用(function call) : | |
1459 | 1540 | 求值函数合并包含子表达式的求值:总是求值操作符,并可能求值操作数。若这些求值都没有退出,则函数被调用(call) 。 |
1460 | 1541 | 若被调用的函数存在形式参数,函数调用首先以操作数的直接子表达式作为实际参数,匹配实际参数和形式参数。 |
1461 | -若实际参数匹配的目标可指定一个变量,则伴随参数匹配的操作包括以特定规则绑定(@4.5.2) 的形式参数(@4.5.2) 。 | |
1542 | +若实际参数匹配的目标可指定一个变量,则伴随参数匹配的操作包括以特定规则绑定(@4.5.2) 的形式参数(@4.5.2.1) 。 | |
1462 | 1543 | 绑定的实际参数和对应的形式参数作为不同的实体时,作为伴随参数匹配的必要操作的一部分,发生参数传递(parameter passing) 。参数传递使形式参数具有作为实际参数值的副本。参数传递可能使和实际参数相关的资源被复制或转移。 |
1463 | 1544 | 实现在函数合并的求值中应提供函数调用的支持。 |
1464 | 1545 | 函数调用确定副作用的边界:保证参数表达式在函数应用被求值之前被求值。 |
1465 | -在控制返回(@4.5.2.1) 主调函数(@4.5.2.1) 时,函数调用内部确定的函数值最终替换被求值的函数合并表达式,即为返回值(return value) 。 | |
1466 | -典型实现的函数指称过程(@4.5.2.1) ,函数调用为过程调用(procedure call) 。 | |
1546 | +在控制返回(@4.5.2.2) 主调函数(@4.5.2.2) 时,函数调用内部确定的函数值最终替换被求值的函数合并表达式,即为返回值(return value) 。 | |
1547 | +若函数是过程,对应的函数调用是过程调用(procedure call) 。 | |
1467 | 1548 | 若一个函数的调用仍待返回,则该函数调用是活动的(active) 。 |
1468 | -一般地,被调用的函数及函数调用的作用的等价性通常不能被确定。一个重要的子类是不能确定具体表示的合并子的情形(@4.4.5.1) ,参见 @4.5.3.2 。其它函数一般也有类似限制。 | |
1469 | 1549 | 调用总是不蕴含非纯求值(@4.4.4) 的函数是纯函数(pure function) 。 |
1470 | -**注释** | |
1471 | -另见函数调用的终止保证(@4.8.2) 。 | |
1550 | +函数调用的中蕴含的求值对应的规约步骤(@4.4) 的集合是它的动态范围(dynamic extent) 。 | |
1551 | +函数中被捕获(@4.5.2) 的实体的引用和求值函数中的计算创建的对象的引用构成函数计算的结果时,引用可能逃逸(escape) ,即在调用的动态范围以外可访问。 | |
1552 | +**注释** | |
1553 | +典型实现的函数指称过程,函数调用是过程调用。 | |
1554 | +一般地,被调用的函数及函数调用的作用的等价性通常不能被确定。 | |
1555 | +一个重要的子类是不能确定具体表示的情形(@4.4.5.1) ,参见合并子(@4.5.3.2) 。其它函数一般也有类似限制。 | |
1556 | +关于函数调用中的求值,另见函数调用的终止保证(@4.8.2) 。 | |
1557 | +和 [RnRS] 不同,动态范围仅对求值定义,而不是关于环境中的绑定显示计算作用(@1.2.4) 的属性。这种属性事实上对象的生存期(@4.1) ,仅对对象而非更一般的实体有效。 | |
1558 | +续延(@4.1.7) 可用其动态范围表示。 | |
1559 | +本文档的动态范围的概念定义和 [RnRK] §7.1 的定义兼容,但不依赖其对续延的描述,也适用抽象机语义(@2.6) ,是 [RnRK] 的一般化。 | |
1560 | +Racket 使用求值的规约步骤在表达式上定义动态范围。NPL 不在表达式上采用类似的定义,因为: | |
1561 | +类似 [RnRK] ,NPL 强调支持对象语言中的显式求值风格及表达式求值前后的不同(@1.4) ; | |
1562 | +类似 [RnRK] ,进一步地,NPL 派生语言(如 NPLA1(@7) )可明确支持在对象语言中指定求值环境(@4.6.1) 而改变求值的上下文,表达式不能被预期通常以上下文(@4.4.8) 无关的方式被求值。 | |
1472 | 1563 | |
1473 | 1564 | @4.5.3.2 合并子: |
1474 | 1565 | 除非另行指定,NPL 假定函数合并满足以下典型情形,即函数合并的操作符(@4.4.5.1) 求值为以下类型的合并子(combiner) 之一: |
@@ -1479,7 +1570,7 @@ | ||
1479 | 1570 | 由派生实现定义的扩展合并子(extended combiner) 。 |
1480 | 1571 | 应用子总是对应一个底层(underlying) 合并子,可通过底层合并子上的一元的包装(wrap) 操作得到;其逆操作为解包装(unwrap) 。 |
1481 | 1572 | 解包装结果不是扩展合并子的合并子称为真合并子(proper combiner) 。 |
1482 | -合并子被调用时通常返回(@4.5.3.1) 且仅返回一次(详见 @4.5.3.3)。 | |
1573 | +合并子被调用(@4.5.3.1) 时通常返回(@4.5.3.1) 且仅返回一次(详见 @4.5.3.3 )。 | |
1483 | 1574 | 合并子上可以定义若干等价关系,这些等价关系蕴含关于函数应用替换的基本形式: |
1484 | 1575 | 若对任意上下文,替换一个应用中的合并子为另一个不改变函数应用替换的结果,则这两个合并子等价(对应 λ 演算的 β-规约等价)。 |
1485 | 1576 | 由于程序可能引入未知具体表示的合并子(如从其它模块链接),以上等价可能无法判定,不要求实现提供。 |
@@ -1488,12 +1579,23 @@ | ||
1488 | 1579 | @4.5.3.3 续延的捕获和调用: |
1489 | 1580 | 语言可提供一等实体(@4.2.1) 形式的续延即一等续延(first-class continuation) 。 |
1490 | 1581 | 续延捕获把当前续延(@4.1.7) 具现(@1.2.4) 为对象语言中可操作的一等续延。 |
1491 | -**注释** 续延捕获在语法上类似函数对变量的捕获(@4.5.2) 。 | |
1492 | -符合函数类型要求(@4.5.2) 的一等续延可作为函数以直接作为合并子(@4.5.3.2) 构成函数合并(@4.5.3) 以外的方式进行函数调用,称为续延调用(continuation call) 。 | |
1582 | +类似过程(@4.5.2) ,续延可被一次或多次调用(@4.5.2) ,称为续延调用(continuation call) 。 | |
1583 | +对象语言可支持符合函数类型要求(@4.5.2) 的一等续延作为函数。作为一等续延的函数可直接作为合并子(@4.5.3.2) 构成函数合并(@4.5.3) 进行函数调用,而实现续延调用。 | |
1584 | +除非派生实现另行指定,NPL 的一等续延不是函数。 | |
1493 | 1585 | 函数应用(如合并子调用(@4.5.3.2) )可隐含(非一等对象的)续延调用。 |
1494 | 1586 | 续延调用的其它的具体形式由派生实现定义。 |
1495 | 1587 | 除非在捕获的续延上存在特定的控制作用(@4.1) ,合并子被调用时以当前续延(@4.1.7) 返回(@4.5.3.1) 且仅返回一次。 |
1496 | 1588 | 参见续延调用对程序控制的改变(@4.8) 。 |
1589 | +**原理** | |
1590 | +在 Scheme 中,一等续延即过程。 | |
1591 | +在限制元语言的函数不蕴含控制作用时,类似 Scheme 等支持的无界续延(@4.1.7) 不是函数。参见 https://okmij.org/ftp/continuations/undelimited.html#introduction 。 | |
1592 | +在 Kernel 和其它一些语言中,续延不是过程,而具有不同的名义类型(@4.7.2) 。这种不同于 Scheme 的设计是有意的。 | |
1593 | +NPL 一等续延不限制是否和函数类型同一,因此无界续延仍可被视为函数(或更确切地,即程序入口作为边界的有界续延)。 | |
1594 | +类似 [RnRK] 的设计,因为一等续延的调用可引起和更常见的过程调用显著不同的控制作用,续延调用有必要和过程调用在对象语言的语法上显式区分以满足易预测性(@1.5.2.2.1) ,因此一等续延一般不是函数。 | |
1595 | +**注释** | |
1596 | +类似过程(@4.5.2) ,续延及其调用在其操作语义的元语言中能表示为元语言的函数应用(@4.5.2) ,通常表达为函数及函数合并(@4.5.3) 。 | |
1597 | +续延捕获在语法上类似函数对变量的捕获(@4.5.2) 。被捕获的实体通常以引用保存。被捕获的实体通常是隐式的,即不在对象语言程序中出现。 | |
1598 | +在支持一等续延且捕获的续延可被复制的语言中,实现需要考虑活动记录的复制,参见 [Hi90] 。 | |
1497 | 1599 | |
1498 | 1600 | @4.5.3.4 活动记录: |
1499 | 1601 | 活动的(@4.5.3.1) 合并子分配的对象称为活动记录(activation record) 。 |
@@ -1517,7 +1619,7 @@ | ||
1517 | 1619 | 一个上下文是显式的(explicit) ,当且仅当它可以通过名称表达式(@4.5.1) 访问。 |
1518 | 1620 | 一个上下文是隐式的(implicit) ,当且仅当它不是显式的。 |
1519 | 1621 | 隐式的上下文通常是管理状态(@4.1.3) 。 |
1520 | -确定上下文的状态或对可变上下文的修改称为对上下文的访问(access) 。 | |
1622 | +确定上下文的状态或对可变上下文的修改(@4.1) 称为对上下文的访问(access) 。 | |
1521 | 1623 | 规约规则中,以未指定子项参数化的项是一个上下文。 |
1522 | 1624 | 过程实体(@4.5.2) 决定函数表达式(@4.5.2) 关联的上下文。 |
1523 | 1625 | 本节以外其它具体规则由派生实现定义。 |
@@ -1540,10 +1642,10 @@ | ||
1540 | 1642 | 除非派生实现另行指定,语言支持的求值环境和这些机制蕴含的求值环境的交集为空。语言可以库的形式提供 API 另行支持。 |
1541 | 1643 | |
1542 | 1644 | @4.6.1.2 函数和函数应用的求值环境: |
1543 | -在典型的对象语言中 λ 抽象(@4.5.2.2) 中指定的替换构造具有局部作用域(local scoping) ,其中可访问 λ 抽象外部词法意义上包含的(enclosing) 的求值环境的变量,对应求值环境为局部环境(local environment) 。 | |
1645 | +在典型的对象语言中 λ 抽象(@4.5.2.4) 中指定的替换构造具有局部作用域(local scoping) ,其中可访问 λ 抽象外部词法意义上包含的(enclosing) 的求值环境的变量,对应求值环境为局部环境(local environment) 。 | |
1544 | 1646 | 在基于词法作用域(lexical scoping) 的对象语言中,引入 λ 抽象对应的语言构造支持捕获引入函数时所在的作用域的环境,称为静态环境(static environment) 。 |
1545 | 1647 | 相对地,动态作用域(dynamic scoping) 根据求值时的状态指定指称。 |
1546 | -Vau 抽象(@4.5.2.3) 进一步支持在局部环境中提供访问函数应用(@4.5.3) 时的求值环境,即动态环境(dynamic environment) 的机制。 | |
1648 | +Vau 抽象(@4.5.2.5) 进一步支持在局部环境中提供访问函数应用(@4.5.3) 时的求值环境,即动态环境(dynamic environment) 的机制。 | |
1547 | 1649 | 除非另行指定,按词法闭包(lexical closure) 规则捕获,即只根据词法作用域确定捕获的指称;若需要支持依赖求值状态动态确定指称时,使用派生实现提供的对求值环境(@4.6.1) 的操作,而不依赖动态作用域。 |
1548 | 1650 | 除非另行指定,NPL 只存在一种作用域,即所有作用域都使用相同的名称解析(@4.3.3) 和捕获规则。 |
1549 | 1651 |
@@ -1557,11 +1659,15 @@ | ||
1557 | 1659 | 实体关联的类型可能被显式地指定,或通过隐式的限定规则推断确定。符合指定和限定要求的类型可有任意多个。 |
1558 | 1660 | 实体的类型是被显式指定的实体关联的类型。实体具有实体的类型以及通过其它规则限定的类型。实体是类型的实例(instance) 。 |
1559 | 1661 | 类型可用集合表示(@4.1.1) 。集合的元素是具有其表示的类型的实体。 |
1662 | +表示类型的集合为空时,表示类型没有实例,是空类型(empty type) 。 | |
1663 | +推论:由集合的形式表达,空类型是唯一的。 | |
1664 | +表示类型的集合只有一个元素时,类型只有一个不可区分的实例,这样的类型是单元类型(unit type) 。 | |
1560 | 1665 | 和表达式直接关联的类型满足起始阶段不变量约束,称为静态类型(static type) 。 |
1561 | 1666 | 和表达式的值(@4.1) 关联的类型满足运行阶段(@2.4.1) 的不变量约束,称为动态类型(dynamic type) 。 |
1562 | 1667 | 其它可能存在类型或实现执行阶段的扩展由派生实现定义。 |
1563 | 1668 | 除非另行指定,对象的类型是对象的值(@4.1) 的类型。 |
1564 | 1669 | NPL 对象类型和存储的值(@4.1.1) 的类型之间的关联未指定。 |
1670 | +类型在元语言中可作为对象。生成对象的元语言函数是类型构造器(type constructor) 。 | |
1565 | 1671 | |
1566 | 1672 | @4.7.1 类型系统和类型机制: |
1567 | 1673 | 称为类型的具体实体和之间的关联由派生实现的类型系统(type system) 规则指定。 |
@@ -1577,7 +1683,7 @@ | ||
1577 | 1683 | |
1578 | 1684 | @4.7.2 类型等价性: |
1579 | 1685 | 通过显式指定标识(如名称)的方式定义类型的方法是名义类型(nominal typing) ,否则是结构化类型(structrual typing) 。 |
1580 | -名义类型之间没有隐含的等价关系。结构化类型之间的等价关系由实现定义。 | |
1686 | +除非另行指定,不同的名义类型不蕴含等价关系。结构化类型之间的等价关系由实现定义。 | |
1581 | 1687 | 类型的相等关系是一种类型之间的等价关系。两个类型相等,当且仅当它们的实例作为元素的两个集合对应相等。 |
1582 | 1688 | 除非另行指定,相等的类型不在语言中区分,且元语言(描述对象语言的规则)中类型作为实体的同一性(@4.1) 即类型相等性。 |
1583 | 1689 | 推论:除非另行指定,不同的类型不等价。 |
@@ -1599,16 +1705,20 @@ | ||
1599 | 1705 | 潜在类型是隐式类型的实例;除此之外,隐式类型还包括类型推断(type interferece) ,即通过隐含的上下文信息判断表达式关联的类型。 |
1600 | 1706 | 若类型机制可保证在某个执行阶段(@2.4.1) 内有确定强规范性质(@4.4.5)(即保证终止)的算法确定类型,则类型机制在该阶段是静态类型(static typing) 。 |
1601 | 1707 | 语言可能个别指定引入这些类型相关的规则,在保持逻辑相容的前提下可混合使用。 |
1708 | +显式类型可编码接口的要求,即类型签名(type signature) 。 | |
1709 | +类型签名通常直接指定名义类型,但同时也可允许非特定的满足结构类型约束的类型。这些类型和类型签名兼容(compatible) 。 | |
1602 | 1710 | **原理** |
1603 | 1711 | 历史上,表达式的类型和变量的类型在简单类型 λ 演算(@4.5.2) 中同时被引入。后者修饰 λ 抽象中的自由变量(@4.1) ,而前者限定剩余的所有项。 |
1604 | 1712 | 即便从项重写系统(@4.1) 中两者是形式上统一的,在实际语用中具有很不同的差异。这集中体现在后者是名义的(@4.7.2) ,除非附加其它不同的语法设施,并不具有结构化推导的性质,原则上只适合描述接口;而前者能兼容化结构类型(@4.7.2) ,同时适合描述接口及其实现。 |
1605 | -作为接口的名义类型在作为自由变量以外的上下文中重新复用为不关心其类型(并消除依赖这些信息的其它机制(@4.7.4) )的其它程序构造(一般意义上的表达式),通常需要类型擦除等更复杂的机制和支持的类型系统规则,以消去不再预期和其它类型系统规则交互的类型。 | |
1713 | +作为接口的名义类型(@4.7.2) 在作为自由变量以外的上下文中重新复用为不关心其类型(并消除依赖这些信息的其它机制(@4.7.4) )的其它程序构造(一般意义上的表达式),通常需要类型擦除等更复杂的机制和支持的类型系统规则,以消去不再预期和其它类型系统规则交互的类型。 | |
1606 | 1714 | 和 [RnRK] 类似,NPL 不要求使用清单类型,以避免一些一般意义上的全局设计缺陷。这些缺陷包括: |
1607 | 1715 | 过于积极地(非预期地)排除危险但对程序有用的使用,而违反易预测性(@1.5.5.2.1) 。 |
1608 | 1716 | 因为移除类型标注需要上述的复杂机制和类型系统规则,具体的清单类型阻碍派生语言定义其它不容易冲突的类型标注规则而使语言具有更好的可扩展性(@1.5.2.1) 。 |
1609 | 1717 | 因为名义类型的相关规则更容易直接拒绝一些和类型规则不兼容的程序构造而难以简单地变通,往往对程序构造的组合具有更多直接的表达性限制(@1.5.3.1.1) 而破坏通用计算意义上的正确性(@1.5.3.1) 。 |
1610 | 1718 | 例如,许多类型系统不允许表达 Y 组合子(@4.1) 的构造良型(@4.7.1) 。 |
1611 | 1719 | 若有必要,派生语言仍可限定使用清单类型。一般仍然建议仅在局部引入而避免全局复杂性和因此带来的限制。 |
1720 | +**注释** | |
1721 | +类型签名来自数理逻辑术语。 | |
1612 | 1722 | |
1613 | 1723 | @4.7.4 类型检查(typechecking) : |
1614 | 1724 | 类型检查解答程序是否满足类型规则的判定性问题。 |
@@ -1617,6 +1727,8 @@ | ||
1617 | 1727 | 语言可能个别指定引入类型检查相关的规则,在保持逻辑相容的前提下可混合使用。 |
1618 | 1728 | 注意静态类型检查和静态类型(@4.7.3) 以及动态类型检查和动态类型(@4.7.3) 的区别。类型检查和类型机制是不同的规则,不必然包含蕴含关系。 |
1619 | 1729 | 类型检查失败引起的错误(@2.5.2) 称为类型错误(type error) 。 |
1730 | +**注释** | |
1731 | +类型检查的一个典型的使用场景是类型签名的兼容性(@4.7.3) 校验。 | |
1620 | 1732 | |
1621 | 1733 | @4.7.5 类型全集: |
1622 | 1734 | 作为论域(@1.5.3.6) 的实例,类型全集(type universe) 是语言规则中允许表达的类型的总称。 |
@@ -1628,9 +1740,10 @@ | ||
1628 | 1740 | 判断值是否满足类型居留(inhabitant) 的谓词是类型谓词。 |
1629 | 1741 | 和 [RnRK] 的基本类型谓词不同,类型谓词定义为只接受一个参数。 |
1630 | 1742 | |
1631 | -@4.7.7 子类型: | |
1743 | +@4.7.7 类型序: | |
1744 | +类型之间可能具有序关系。 | |
1632 | 1745 | 被定型(@4.7.1) 的类型的实体可完全地满足其它类型的约束。前者具有后者的子类型(subtype) 。 |
1633 | -子类型关系是一种预序(preorder) 关系,即自反的、反对称的二元关系。 | |
1746 | +子类型(subtyping) 关系是一种预序(preorder) 关系,即自反的、反对称的二元关系。 | |
1634 | 1747 | 相等的类型符合子类型关系,是平凡的(trivial) 。排除平凡的子类型关系是严格子类型关系。 |
1635 | 1748 | 严格子类型是严格预序关系,即反自反、反对称的二元关系。 |
1636 | 1749 | 子类型和严格子类型对应的逆关系是超类型(super type) 和严格超类型(strict supertype) 关系。 |
@@ -1639,8 +1752,47 @@ | ||
1639 | 1752 | **原理** |
1640 | 1753 | 相同类型涵盖不相等的子类型允许使不同的子类型实现公共严格超类型公开的接口行为,而有效地实现封装性(@1.5.6.2) 。 |
1641 | 1754 | |
1755 | +@4.7.7.1 边界元素: | |
1756 | +一个类型系统可指定唯一的底类型(bottom type) 作为其它任何不同类型的严格子类型,记作⊥。若类型全集包含空类型,则底类型是空类型。 | |
1757 | +一个类型系统可指定唯一的顶类型(top type) 作为其它任何不同类型的严格超类型,记作⊤。这种类型即通用类型(universal type) 。 | |
1758 | +NPL 支持空类型作为底类型,但不要求在对象语言中支持其表示。 | |
1759 | +NPL 避免要求唯一的顶类型的存在以符合开放世界假设(@1.5.3.6) 。 | |
1760 | +派生语言可指定不同的规则。 | |
1761 | +**原理** | |
1762 | +以空类型作为子类型在类型序的推理上是自然的。 | |
1763 | +就非特定的类型全集,通用类型的的构造和表示不唯一,因此不能直接断言其存在。 | |
1764 | +否则,假定存在这种类型,则断言不存在其超类型,这可能和其它语义规则冲突。 | |
1765 | +即使在名义上定义具体的超类型(如 Java 的 java.lang.Object 类型),也面临不能向上扩展(得到比 Object 更基本的类型)的问题,违反最小接口原则(@1.5.2.3) 和通用性(@1.5.7) 。 | |
1766 | +具体的顶类型在断言当前类型系统不存在公共超类型可能仍然有实用意义;此时,顶类型即一等实体(@4.1) 构成的类型,而不需要定义具体名义类型(@4.7.2) 。 | |
1767 | + | |
1768 | +@4.7.8 多态(polymorphism) : | |
1769 | +特定的类型系统支持类型签名(@4.7.3) 能对应多种不同的兼容(@4.7.3) 类型。 | |
1770 | +一般地,类型上的多态有: | |
1771 | + 特设(ad-hoc) 多态:仅对项上局部的项上的类型作用使之满足上下文兼容要求的多态: | |
1772 | + 函数(@4.5.3) 重载(overload) :同一个名称(@1.2.4) 对应的不同的函数实体,允许按实际参数(@4.5.3) 的类型选择调用不同的函数。 | |
1773 | + 强制(coercion):求值时使值向某个上下文要求的类型的隐式转换。 | |
1774 | + 参数(parameteric) 多态:接口签名指定以具体类型作为值的变量,组合为函数或者其它接口对应实体的类型。 | |
1775 | + 子类型(@4.7.7) 多态:接口签名编码接受子类型关系作为兼容类型。 | |
1776 | + 行(row) 多态:对组成具有名称和实体对构成的元素作为成员(member) 的实体,兼容限定部分成员的类型。 | |
1777 | +多型(polytipic) 的接口在同一个接口签名上以结构化类型的隐式类型(@4.7.3) 构造支持不同的类型而支持多态。 | |
1778 | +**注释** | |
1779 | +重载在一些语言中自动地对函数对应的具体可调用实体(@4.5.2.2) 适用。 | |
1780 | +行多态以结构化类型(@4.7.2) 约束取代通常通过名义类型(@4.7.3) 指定的子类型关系。 | |
1781 | + | |
1782 | +@4.7.9 种类(kind) : | |
1783 | +种类是静态类型系统(@4.7.1) 的语法表示中具有特定类型模式(pattern) 的分类。 | |
1784 | +一定意义上,种类是类型系统的元语言中一种元静态类型。 | |
1785 | +一般地,实体类型的种类记作 * 。 | |
1786 | +除非另行指定,作为项的函数应具有函数类型,即符合类型种类为 * → * 的结果的类型(@4.7) ,如为简单类型 λ 演算(STLC, simply-typed lambda calculus) 兼容的函数类型实例。 | |
1787 | +其中,→ 是函数类型的类型构造器。 | |
1788 | +种类作为元语言中的类型多态(@4.7.8) ,实现种类多态:接口签名接受类型的编码中对应位置具有不同种类的类型。 | |
1789 | +**注释** | |
1790 | +在实现中,种类也被作为互操作的归类,如调用约定(calling convention) :https://www.microsoft.com/en-us/research/uploads/prod/2020/03/kacc.pdf 。 | |
1791 | +但这不足以涵盖一般的形式定义;特别地,调用(@4.5.2.2) 是仅仅关于过程这类实体的互操作,而种类适合一般实体的静态类型。例如,在不考虑进一步地实现时,多变多态(@4.2.3.5.5) 的类型不需要限定过程(函数)。 | |
1792 | +种类也可扩展到特定的计算作用的作用系统(effect system) 上,此处从略。 | |
1793 | + | |
1642 | 1794 | @4.8 程序的控制执行条件(execution condition) : |
1643 | -和 @4.5.2.1 类似,程序的控制状态(@4.1) 决定求值使用的续延。 | |
1795 | +和过程的调用(@4.5.2.2) 类似,程序的控制状态(@4.1) 决定求值使用的续延。 | |
1644 | 1796 | 更一般地,规约规则指定语言的实现决定程序行为时使用的(对程序不保证可见的)续延,这种在实现中对应的控制状态称为控制执行条件。 |
1645 | 1797 | 和控制状态不同,控制执行条件描述语言提供的不同控制机制的分类,而不被作为语言可编程的特性提供。 |
1646 | 1798 | 除非另行指定,仅由默认规约规则决定的执行条件是正常(normal) 的。 |
@@ -1667,7 +1819,7 @@ | ||
1667 | 1819 | 确认满足异常条件和进入异常执行状态之间,上述执行线程内程序仅在引发异常的线程上的程序允许存在计算作用(@1.2.4)(这保证不被引起可观察行为改变的其它线程的操作中断)。 |
1668 | 1820 | 除非派生实现另行指定,未捕获的异常总是确定性地(deterministically) 持续引发异常的执行线程中引起控制的转移: |
1669 | 1821 | 若捕获操作有效的上下文,控制转移捕获构造处理对应异常的异常处理器(exception handler) ; |
1670 | -否则,若在活动函数调用中,则单向地从当前活动(@4.5.3.4) 的函数向其主调函数(@4.5.2.1) 转移控制,使后者活动; | |
1822 | +否则,若在活动函数调用中,则单向地从当前活动(@4.5.3.4) 的函数向其主调函数(@4.5.2.2) 转移控制,使后者活动; | |
1671 | 1823 | 否则,若没有找到剩余的活动函数调用,则程序异常终止。 |
1672 | 1824 | 除非派生实现另行指定,上述转移活动函数若成功(包括异常在活动的主调函数嵌套的特定语言构造中被捕获),先前不再活动的活动记录中的资源在控制成功转移后应立即被释放。 |
1673 | 1825 | 典型的设计中,求值规则使的正常状态的函数调用要求的活动记录分配和释放满足 FIFO(Last-In First-Out ,后入先出)的顺序,构成了栈(stack) ,活动记录是栈帧(stack frame) 。 |
@@ -1737,7 +1889,7 @@ | ||
1737 | 1889 | 保证名称表达式求值的强规范化(@4.4.5)。 |
1738 | 1890 | 不要求提供命名空间(@4.3.4) 实现的可变实体。 |
1739 | 1891 | 不保证求值都是纯求值(@4.4.4) ;非特殊形式使用热情求值;其它情形使用热情求值或惰性求值(@2.5.2) 由具体特殊形式约定。 |
1740 | -对象语言的函数(@4.5.2) 默认为过程(@4.5.2.1) 。过程默认实现为子例程(@4.5.2.1) 。过程指定的计算结果和函数表达式最终返回结果的关联(@4.5.2.1) 是过程调用(@4.5.3.1) 结果的一一映射。 | |
1892 | +对象语言的函数(@4.5.2) 默认为过程(@4.5.2.2) 。过程默认实现为子例程(@4.5.2.3) 。过程指定的计算结果和函数表达式最终返回结果的关联(@4.5.2.2) 是过程调用(@4.5.3.1) 结果的一一映射。 | |
1741 | 1893 | 除非另行指定,实现函数(@4.5.2) 的宿主数据结构生存期要求默认同宿主语言;循环引用(@4.2.4) 可能行为未定义(@5.4)(另见内存泄漏(@5.6.4) )。 |
1742 | 1894 | 除非另行指定,按值传递(@4.4.6.5) 支持复制初始化(@5.8.2) 对象的一等作用(@4.2.2.2) 。这和宿主语言的对应语义也保持兼容。 |
1743 | 1895 | 派生实现使用的项不满足特定的表示(@6.10.11) 。 |
@@ -1755,7 +1907,7 @@ | ||
1755 | 1907 | 本机实现可以具有 C++ 的实现兼容的二进制接口的函数提供,这些函数称为本机函数(native function) 。 |
1756 | 1908 | 本机实现可直接支持本机函数在实现中被调用。若被支持,具体接口由派生实现指定。 |
1757 | 1909 | 本机函数作为函数的实现,其调用的求值可具有和非本机的函数一致的作用(@1.2.4) ,但不需要具有可被对象语言表达的函数体(@4.5.2) 。 |
1758 | -为确保函数求值的作用可能保持一致,本机函数应符合和本机函数调用时使用的规约一致的方式使用,即至少符合以下规约调用约定(calling convention) : | |
1910 | +为确保函数求值的作用可能保持一致,本机函数应符合和本机函数调用时使用的规约一致的方式使用,即至少符合以下规约调用约定(@4.7.9) : | |
1759 | 1911 | 被调用时的子项被作为以 WHNF(@4.4.5.1) 形式表示的被调用的表达式使用; |
1760 | 1912 | 调用后具有项被重写为必要的值以表示函数调用的返回值(@4.5.3.1) 。 |
1761 | 1913 | 本机函数的返回值应能表达任意的非本机函数调用的返回值,即通过求值函数调用(@4.5.3.1) 中函数体(@4.5.2) 的非本机函数的求值结果(@4.1) 。 |
@@ -1794,8 +1946,8 @@ | ||
1794 | 1946 | 对象语言的值(@4.1) 被对象语言的实体类型表示蕴含它被映射的宿主类型表示,反之亦然。 |
1795 | 1947 | 类型映射可以是非空的多对一、一对多或一一映射。 |
1796 | 1948 | 若类型映射是一一映射,其类型等价性同宿主语言的语义规则;否则,由类型的语义规则约定。 |
1797 | -类型系统(@4.7.1) 是开放(@1.5.3.6) 的,可能提供不被对象语言支持的宿主语言类型和值,如中间值(@6.8) 。 | |
1798 | -但符合已指定的类型的实体需能被视为同种类型的实体使用,即子类型(@4.7.2) 。 | |
1949 | +类型系统(@4.7.1) 是开放(@4.7.5) 的,可能提供不被对象语言支持的宿主语言类型和值,如中间值(@6.8) 。 | |
1950 | +但符合已指定的类型的实体需能被视为同种类型的实体使用,即子类型(@4.7.7) 。 | |
1799 | 1951 | 因需提供与作为宿主语言的 C++ 的互操作(@5.3) 支持,所以明确约定实现中部分实体类型对应的 C++ 类型: |
1800 | 1952 | 用于条件判断的单一值的宿主类型是 bool 。 |
1801 | 1953 | 字符串(@3.2) 及和字符串的子集一一对应的词素(@3.3.1) 的宿主类型都是 string 类型(@6.1.5) 。 |
@@ -1871,7 +2023,7 @@ | ||
1871 | 2023 | 运行时检查可能帮助排查内存安全的实现行为。这包括蕴含运行时检查的接口约束(失败时抛出异常或断言)。 |
1872 | 2024 | 此外,实现可能提供可选的运行时检查。这些可选的检查帮助排查未定义行为,而不应被程序实现依赖。 |
1873 | 2025 | |
1874 | -@5.6.4 内存泄漏(memory leak) : | |
2026 | +@5.6.4 资源泄漏: | |
1875 | 2027 | 资源泄漏(resource leak) 是不能预期地(决定性地)访问之前被分配的资源的情形。 |
1876 | 2028 | 内存泄漏(memory leak) 是存储资源的泄漏。 |
1877 | 2029 | 强内存泄漏状态是指存在存储无法通过任何途径访问的状态。若存在存储不被任意对象或其它另行指定的代替对象的实体(如宿主环境)所有权的传递闭包包含,即所有权依赖不可达(unreachable) ,则存在强内存泄漏。 |
@@ -1880,15 +2032,19 @@ | ||
1880 | 2032 | 弱内存泄漏的预期的可实现性和实现细节相关,因此 NPLA 不指定具体预期。 |
1881 | 2033 | |
1882 | 2034 | @5.6.4.1 资源回收策略: |
1883 | -单一作用域(@5.6.2) 内的资源回收策略有删除(deletion) 和保留(retention) 的策略(详见 [Cl98] )。 | |
2035 | +单一作用域(@5.6.2) 内的资源回收有删除(deletion) 和保留(retention) 的策略。 | |
1884 | 2036 | NPLA 不限定具体使用的回收策略,但应支持释放一等对象时副作用(@4.2.2.1) 且不放弃确定性(@4.2.3.6) 。 |
1885 | 2037 | 为简化语义规则同时避免限制特定的可用资源(如系统中剩余的内存)的变化被派生实现抽象为副作用,除非派生实现指定,不对内存使用保留策略,不使内存超出对象生存期(@4.1) 。 |
1886 | 2038 | NPLA 要求完全避免强内存泄漏,但不要求实现 GC(@4.2.3.4.4) 。 |
1887 | -不依赖 GC 同时允许不依赖释放可能具有的副作用顺序的存储资源和其它资源共享更普遍的所有权抽象资源所有权语义(@4.2.2.3) 上的操作,以更好地满足资源管理操作的可复用性(@1.5.4.4) 和作用使用原则(@4.1.6) 的要求。 | |
2039 | +不依赖 GC 同时允许不依赖释放可能具有的副作用顺序的存储资源和其它资源共享更普遍的所有权抽象资源所有权语义(@4.2.2.3) 上的操作: | |
2040 | +使用删除策略时,活动的过程调用(@4.5.3.1) 对其中分配的资源具有所有权。 | |
1888 | 2041 | 由于 GC 通常基于具有特定操作的单一资源所有权的所有者的对象池的这一实现特例,不依赖共享所有者的 GC 一般也更容易满足 @1.5.5.1 、@1.5.2.3 和 @1.5.2.4 。 |
1889 | 2042 | 多个对象构成的系统中,仅存在平等的所有权(@4.2.2.3) 时的循环引用造成强内存泄漏: |
1890 | 2043 | 除非即从循环引用的对象中区分出具有不同类所有权的对象子集实现所有权正规化,总是存在无法被释放资源的对象(@4.2.4.2) 。 |
1891 | 2044 | 基于非预期的循环引用不可避免地造成实现开销而违反 @1.5.2.2(即使这种开销可能并不总是可观察),NPLA 不要求实现 GC 和对一般对象区分强弱引用等机制避免循环引用(@5.2) 。 |
2045 | +**原理** | |
2046 | +关于策略的讨论,详见 [Cl98] 。 | |
2047 | +使用所有权抽象活动记录的资源能更好地满足资源管理操作的可复用性(@1.5.4.4) 和作用使用原则(@4.1.6) 的要求。 | |
1892 | 2048 | |
1893 | 2049 | @5.6.4.2 安全性: |
1894 | 2050 | 内存泄漏是和内存安全(@5.6.3) 不同的另一类非预期的问题,表明语言设计、实现或程序存在缺陷。 |
@@ -1897,16 +2053,17 @@ | ||
1897 | 2053 | 存在其它语言使用类似的区分内存泄漏和非内存安全(@5.6.3.1) 的设计,如 [Rust](详见 https://doc.rust-lang.org/book/second-edition/ch15-06-reference-cycles.html )。 |
1898 | 2054 | |
1899 | 2055 | @5.6.5 自动存储管理: |
1900 | -和 C++ 的自动变量类似,函数调用结束、控制退出函数体(@4.5.2) 的作用域(对应 C++ 的函数体最外层块作用域)后,被变量独占所有权的资源被释放。 | |
2056 | +和 C++ 的自动变量类似,函数调用结束、控制退出函数体(@4.5.2) 的作用域(对应 C++ 的函数体最外层块作用域)后,被变量独占所有权(@4.2.2.3) 的资源被释放。 | |
1901 | 2057 | 资源回收策略(@5.6.4.1) 允许存储资源和宿主语言对象之间的明确对应,且允许以环境持有所有作用域内的所有资源。 |
1902 | 2058 | 使用自动变量释放的机制,自动存储管理的基本设计可不依赖 GC(@5.6.4) ,也不需显式对资源进行释放。 |
2059 | +**注释** | |
1903 | 2060 | 把右值(@5.8.1) 的值传递视为传递独占的引用,这类似 newLISP 的 ORO(One Reference Only) :http://www.newlisp.org/MemoryManagement.html 。 |
1904 | 2061 | |
1905 | 2062 | @5.6.6 子对象: |
1906 | -特定的对象可具有子对象(subobject) 。 | |
2063 | +特定的对象可具有子对象(@4.2.4.1) 。 | |
1907 | 2064 | 除非另行指定,子对象及其性质同宿主语言的约定:在宿主语言的表示中表现为子对象的对象语言中的对象,也是对象语言的子对象。 |
1908 | 2065 | 对象语言的其它具有子对象的情形由派生实现定义。 |
1909 | -对象对它的子对象具有平凡的所有权。 | |
2066 | +对象对它的子对象具有平凡的所有权(@4.2.2.3) 。 | |
1910 | 2067 | 对象的子对象的生存期(@4.1) 不先序(@4.4.3) 对象的生存期起始,对象的子对象的生存期结束不后序(@4.4.3) 对象的生存期结束。 |
1911 | 2068 | 对象的子对象的生存期起始后序(@4.4.3) 对象的生存期起始,对象的子对象的生存期结束先序(@4.4.3) 对象的生存期结束。 |
1912 | 2069 | 除非另行指定,同一个的对象不同子对象的存储期起始、存储期结束、生存期起始、生存期结束之间分别无序。 |
@@ -1921,22 +2078,34 @@ | ||
1921 | 2078 | @5.7.1 环境对象: |
1922 | 2079 | 环境作为可保持可变状态的对象,是环境对象。 |
1923 | 2080 | 环境对象包含变量名称到表示被绑定实体(bound entity) 的映射,称为名称绑定映射(name binding map) 实现变量绑定(@4.1) 。 |
1924 | -**注释** 变量名以字符串表示,没有直接值的限制,例如可能为空串。 | |
1925 | -被绑定实体是对象时,称为被绑定对象(bound object) 。NPLA 环境对象中的被绑定实体是一等对象,总是被绑定对象。 | |
1926 | -名称绑定映射对被绑定实体具有所有权。 | |
2081 | +被绑定实体是对象时,称为被绑定对象(bound object) 。NPLA 环境对象中的被绑定实体包含一等对象,因此被绑定实体总是被绑定对象。 | |
2082 | +名称绑定映射对被绑定实体具有所有权(@4.2.2.3) 。 | |
1927 | 2083 | 推论:环境对象对被绑定实体具有所有权。 |
1928 | -环境对象非 NPLA 一等对象。 | |
2084 | +环境对象可能不是 NPLA 一等对象:环境记录(environment record) 是直接对被绑定的实体具有所有权的非一等环境对象。 | |
2085 | +环境是名称解析(@4.3.3) 时查找名称的目标(@4.3.3) 。 | |
2086 | +父环境一般不共享环境记录。通过这种方式实现重定向(@4.3.3) 的环境表示是链接的(linked) 而非平坦的(flat) 。 | |
2087 | +**注释** | |
2088 | +变量名以字符串表示,没有直接值的限制,可能为空串。 | |
2089 | +被绑定对象除具有和一等对象相同的表示外,可能还具有附加的非一等对象表示的状态,参见临时对象标签(@6.2.2) 。 | |
2090 | +若环境记录直接持有被引用对象,则这些对象是环境记录的子对象(@5.6.6) 。 | |
1929 | 2091 | 关于环境的内部表示,参见环境数据结构(@6.11.1) 。 |
1930 | -环境是名称解析(@4.3.3) 时查找名称的目标(@4.3.3) 。 | |
1931 | -通过其它环境实现重定向(@4.3.3) 的方式(@5.7) 表示环境是链接的(linked) 而非平坦的(flat) 。 | |
1932 | 2092 | |
1933 | 2093 | @5.7.2 环境引用: |
1934 | -对象语言中的环境以表示环境的引用的一等对象(@4.2.1) 表示,称为环境引用。 | |
1935 | -环境引用共享环境对象的所有权。 | |
2094 | +对象语言中的环境以表示环境记录的引用的一等对象(@4.2.1) 表示,称为环境引用。 | |
2095 | +环境引用共享环境对象的所有权(@4.2.2.3) 。 | |
1936 | 2096 | 按值传递(@4.4.6.5) 环境引用不引起其中所有的对象被复制。另见引用(@4.2.3) 。 |
1937 | -根据所有权(@4.2.2.3) 管理机制的不同,环境引用包括环境强引用(strong reference) 和环境弱引用(weak reference) 。 | |
2097 | +根据所有权管理机制的不同,环境引用包括环境强引用(strong reference) 和环境弱引用(weak reference) 。 | |
1938 | 2098 | 引入环境弱引用作为一般的引用机制,且仅在必要时使用环境强引用,以避免过于容易引入循环引用引起强内存泄漏(@5.6.4.1) ,符合 @1.5.5.2 。 |
1939 | 2099 | 传递环境引用不引起被引用的环境对象被复制。 |
2100 | +**原理** | |
2101 | +区分环境对象和环境引用在纯函数式语言不是必要的,因为不需要关心环境中的子对象(@5.7.1) 的复制影响可观察行为(@4.1.3) 。 | |
2102 | +否则,为支持影响可观察行为的环境的修改(@4.1) ,非环境记录的环境引用是必要的。 | |
2103 | +环境引用也是一种较简单且一般普遍高效的父环境的实现表示,可直接实现链接的(@5.7.1) 环境而不需要证明和实现特设的其它内部表示能和抽象机(@2.6) 意义上链接的环境保持语义等价。 | |
2104 | +续延捕获(@4.5.3.3) 若复制续延,可能引起关联的环境的复制,影响可观察行为并引起不必要的实现开销。为此,区分环境引用是必要的。 | |
2105 | +以环境引用作为一等对象使访问被引用对象等环境记录的子对象时需要间接访问,在环境实际不需要被复制的大部分其它场景引起开销。这种开销是可接受的,因为: | |
2106 | +考虑到一等环境的普遍性,有必要有效支持对象语言中创建环境临时对象(而不仅仅是环境对象的引用值(@5.8.3) )的使用使之避免复制; | |
2107 | +实现可能提供附加的证明以在优化的翻译(@2.4.1) 过程中替换环境引用为环境记录或其它不需要间接访问的中间表示,以消除这些开销。 | |
2108 | +不论这样的证明是否存在,环境强引用和弱引用仍在对象语言中区分,以明确接口上的所有权语义(@4.2.2.3) 。 | |
1940 | 2109 | |
1941 | 2110 | @5.7.3 当前环境: |
1942 | 2111 | NPLA 对象语言中,表达式的求值隐含对应一个环境对象作为求值算法(@4.4.1) 需要的上下文(@4.4.8) 输入,称为当前环境(current environment) 。 |
@@ -2037,7 +2206,7 @@ | ||
2037 | 2206 | 有效的引用值应通过特定的构造方式引入,包括: |
2038 | 2207 | 在对象语言通过被引用对象初始化引用值; |
2039 | 2208 | 互操作(@5.3) 引入的保证不引起未定义行为的引用值。 |
2040 | -一些对象语言的操作可能引起引用值无效。。 | |
2209 | +一些对象语言的操作可能引起引用值无效。 | |
2041 | 2210 | **注释** 例如,改变(@4.1.4.2) 被引用对象可以使已被初始化的有效的引用值成为悬空引用(dangling reference) 。 |
2042 | 2211 | 派生实现可指定能使引用值无效化的操作。 |
2043 | 2212 | **注释** 这避免在可能访问引用值的操作随意引入具有潜在未定义行为风险。 |
@@ -2113,7 +2282,7 @@ | ||
2113 | 2282 | 内部表示可支持间接的引用,以允许在对象语言中实现一等引用(@4.2.3) 。 |
2114 | 2283 | 引用折叠对不可修改的传播性质的要求和 ISO C++ 的引用折叠对 const 限定符的处理类似。 |
2115 | 2284 | 引用折叠对唯一引用的要求和 ISO C++ 的右值引用仅通过被折叠的引用都是右值引用类型折叠类似。注意 ISO C++ 右值引用推断仅用于推断转发引用(forwarding reference) 参数,而非直接声明特定的右值引用类型。 |
2116 | -和唯一引用不同,临时对象相对唯一引用更接近 ISO C++ 的声明的右值引用类型信息(而非推断值类别时使用的消亡值表达式的右值引用类型),一般不预期被折叠。使用被引用对象的临时对象标签单独决定折叠结果更容易直接保留被引用对象的状态。 | |
2285 | +和唯一引用不同,临时对象相对唯一引用更接近 ISO C++ 的声明的右值引用类型信息(而非推断值类别时使用的消亡值表达式的右值引用类型),一般不预期被折叠。使用被引用对象的临时对象标签(@6.2.2) 单独决定折叠结果更容易直接保留被引用对象的状态。 | |
2117 | 2286 | |
2118 | 2287 | @5.8.3.6 对象的可转移条件: |
2119 | 2288 | 根据项是否具有特定元数据的引用值可判断使用复制代替对象转移的条件(@5.8.2.3) 。 |
@@ -2168,7 +2337,7 @@ | ||
2168 | 2337 | |
2169 | 2338 | @5.8.5.1 实质化转换上下文: |
2170 | 2339 | 可要求临时对象实质化转换(@5.8.4) 的上下文包括: |
2171 | -使用纯右值初始化按引用绑定的变量(如函数的引用类型的形式参数(@4.5.2) ); | |
2340 | +使用纯右值初始化按引用绑定的变量(如函数的引用类型的形式参数(@4.5.2.1) ); | |
2172 | 2341 | 求值函数调用以初始化返回值对象。 |
2173 | 2342 | 其中,按引用绑定的变量被可选地支持。 |
2174 | 2343 | 一般地,按引用绑定的变量在活动调用(@4.5.3.1) 关联的环境分配临时对象。此时,对象被调用表达式的项独占所有权,同时被绑定的环境独占资源所有权。 |
@@ -2766,17 +2935,29 @@ | ||
2766 | 2935 | |
2767 | 2936 | @6.4.6.3 间接值的消除: |
2768 | 2937 | 访问间接值涉及维护内存安全保证(@5.6.3) 时,可能需要提升项(@6.9.5) 以移除允许非内存安全(@5.6.3.1) 访问的间接值。 |
2938 | +**原理** | |
2939 | +使用删除策略(@5.6.4.1) 实现过程调用(@4.5.3.1) ,其中分配的资源随包含资源引用的间接值返回可能逃逸(@4.5.3.1) 。 | |
2940 | +若其被引用对象(@4.2.3) 在调用后不再存在,则引用不再有效,构成悬空引用(@5.8.3.1) 。因此,对这些间接值的访问非内存安全。 | |
2941 | +为维护内存安全保证,这些情形应被证明不存在。通过间接值的消除移除这些间接值是一种直接的方式。 | |
2942 | +替代消除引用值的方式包括通过逃逸分析(escape analysis) 替换间接值,这也能减少间接值的访问而提供更优化的实现。例如,通过对环境中被绑定对象的使用进行逃逸分析(@5.7.2) 提供优化实现。 | |
2943 | +但是,这不在 NPLA 中被要求,因为: | |
2944 | +逃逸分析需要完整的所有权信息,这需要附加的开销,否则不总是可行(例如涉及跨多个过程的调用); | |
2945 | +对删除策略,逃逸分析也没有提供不可替代的优化。 | |
2769 | 2946 | **注释** 引用值作为间接值(@6.4.6.1) 可被消除(@5.8.3.4) 。 |
2770 | 2947 | 用于按值传递参数时,一般使用 NPL::LiftTerm(@6.9.5.2) 和确保创建值副本的 NPL::SetContentWith(@6.2.3) 实现;前者取非引用类型的右值(@5.8.1) ,后者提升间接值确保结果不是中间值。 |
2771 | 2948 | 用于按值传递返回值时(@4.5.3.1) ,除显式分别对是否为引用值的情形进行处理,可使用 NPL::LiftToReturn(@6.9.5.3)(其中也使用以上方式实现),实现临时对象实质化转换(@5.8.4) ,详见 @6.4.6.4 。 |
2772 | 2949 | |
2773 | 2950 | @6.4.6.4 返回值转换(@5.8.4.2) : |
2774 | 2951 | 作为提升项的使用用例(@6.4.6.3) ,在返回值转换上下文(@5.8.5.2) 中确定函数返回值的实质化转换上下文(@5.8.5.1) 的部分操作消除引用值(@5.8.3.4) ,即返回值转换。 |
2775 | -这可约束作为间接值的引用值不逃逸(escape)(即使用被引用对象的值时不超出指向对象的生存期),而保证只考虑项的值数据成员(@6.2) 可能是引用值时的内存安全。 | |
2776 | -返回值转换不保证未折叠的引用值(@5.8.3.5) 在消除引用值(@6.4.6.3) 后的结果不逃逸。为确保内存安全,程序仍需要保证被引用的对象的间接引用的对象生存期结束后,不能访问间接引用的对象。 | |
2777 | -除非证明不需要临时对象(当前未实现),返回值转换中初始化临时对象作为返回值的项对象(@6.4.5) 。 | |
2952 | +这可约束作为间接值的完全折叠(@5.8.3.2) 的引用值不逃逸(@4.5.2)(因此被引用对象的值时不超出指向对象的生存期(@6.4.6.3) ),而保证只考虑项的值数据成员(@6.2) 可能是引用值时的内存安全。 | |
2953 | +除非证明不需要临时对象,返回值转换中初始化临时对象作为返回值的项对象(@6.4.5) 。 | |
2954 | +是否存在这种证明是未指定行为。 | |
2778 | 2955 | 是否需要返回值转换由实质化转换上下文中的被调用的函数而非上下文是否需要使用右值决定,无关被转换的表达式是否是左值,因此返回值转换不是左值到右值转换(@5.8.4) 。 |
2779 | 2956 | 不论是否存在返回值转换,返回值的项对象来自返回的右值关联的临时对象实质化转换(@5.8.5) 。这可能在 NPL::LiftToReturn 或之前的求值规约的调用中蕴含。 |
2957 | +**注释** | |
2958 | +返回值转换不保证未折叠的引用值(@5.8.3.5) 在消除引用值后的结果不逃逸。 | |
2959 | +为确保内存安全,程序仍需要保证被引用的对象的间接引用的对象生存期结束后,不能访问间接引用的对象。 | |
2960 | +当前未实现是否需要临时对象的证明。 | |
2780 | 2961 | |
2781 | 2962 | @6.4.7 被归约项稳定性: |
2782 | 2963 | 基于 TermNode 的性质,未被规约修改的项的子项的迭代器、指针和引用保持稳定(@6.2) 。 |
@@ -2865,8 +3046,8 @@ | ||
2865 | 3046 | 符号可能是空值,尽管语法上不能通过记号值从词法分析得到。 |
2866 | 3047 | **注释** 不能通过通常词法分析得到的符号,一般可通过空的代码字面量(@5.2.4) 或其它数据转换得到。 |
2867 | 3048 | |
2868 | -@6.8.2 环境引用(@6.11.1.3) : | |
2869 | -环境引用是引用 Environment 类型的环境(@6.11.1) 并支持其共享所有权(@5.6.2) 的中间值。 | |
3049 | +@6.8.2 环境引用: | |
3050 | +环境引用是引用 Environment 类型(@6.11.1) 的环境记录(@5.7.1) 并支持其共享所有权(@5.6.2) 的中间值。 | |
2870 | 3051 | 环境引用类似引用值(@6.3.4) ,但隐含更复杂的所有权关系,因此不具有相同的类型(@5.8.6) 。 |
2871 | 3052 | shared_ptr<Environment> 可引用环境对象,其非空值作为环境强引用(@6.11.1) 。 |
2872 | 3053 | NPL::EnvironmentReference 可引用环境对象,封装 weak_ptr<Environment> 和锚对象(anchor object) 指针,其非空值表示可能具有共享所有权的环境弱引用(@6.11.1) 。 |
@@ -2877,6 +3058,8 @@ | ||
2877 | 3058 | 非空的 shared_ptr<Environment> 和 NPL::EnvironmentReference 分别可作为对象语言的环境强引用和环境弱引用的宿主值。 |
2878 | 3059 | 预期作为表达式或一等对象的表示的宿主值时,这些值应非空,这可通过接口约束被检查(@6.3) 。 |
2879 | 3060 | NPL::EnvironmentReference 值通过锁定后取得的 shared_ptr<Environment> 值确定是否为空值(因为当前实现不保证这些值在多线程环境下蕴含同步操作,存在数据竞争时行为未定义,可忽略并发访问冲突)。 |
3061 | +**注释** | |
3062 | +主要使用方式参见环境相关的宿主类型(@6.11.1.3) 和上下文数据结构(@6.11.3) 。 | |
2880 | 3063 | |
2881 | 3064 | @6.8.2.1 空引用检查: |
2882 | 3065 | NPLA 实现提供在以下情形保证检查 shared_ptr<Environment> 的值非空: |
@@ -2932,7 +3115,8 @@ | ||
2932 | 3115 | 由被转发(@5.8.2.2) 表达式的值确定:除上述条件外,表示绑定临时对象的引用值(具有临时对象标签(@6.2.2) )的项蕴含被引用对象(@4.2.3) ,也是可转移的(用例如 @10.5 )。 |
2933 | 3116 | |
2934 | 3117 | @6.8.3.3 子对象引用: |
2935 | -特定的引用值是子对象引用(subobject reference) ,通过一个引用值和它的子对象构造,引用被对象所有的、作为前者子对象(@5.6.6) 的一等对象。 | |
3118 | +特定的引用值是子对象引用(subobject reference) ,其被引用对象是被另一个对象所有的、作为这个对象的子对象(@5.6.6) 的非一等对象。 | |
3119 | +子对象引用通过一个引用值和它的子对象构造。 | |
2936 | 3120 | 子对象引用的表示详见非平凡非正规表示(@6.10.11) 。 |
2937 | 3121 | 子对象引用使用的这种形式的表示通常因为需要更多的操作比其它引用值的类似操作低效,但这种表示可避免依赖宿主语言中的本机对象内部表示(如成员布局)的依赖。 |
2938 | 3122 | 一般地,在 C++ 的意义上不存在能满足语义要求且总是更简单高效的可移植表示,所以这种表示在和宿主语言的互操作上是必要的。 |
@@ -2943,13 +3127,14 @@ | ||
2943 | 3127 | |
2944 | 3128 | @6.9.1 规约合并项 API : |
2945 | 3129 | 规约合并项是满足以下结构约束的被规约项(@6.2) : |
2946 | -具有子项的枝节点(@6.2.1) ,满足 IsBranchedList(@6.2.3) ; | |
2947 | -或符合 @6.10.11.2 的非平凡非正规表示。 | |
3130 | +具有子项的枝节点(@6.2.1) ; | |
3131 | +或符合作为带有记号值的非空列表(@6.10.11.2) 的非平凡非正规表示。 | |
2948 | 3132 | 这些条件可使用函数 NPL::IsCombiningTerm 判断。 |
2949 | 3133 | 函数 NPL::ClearCombiningTags 清除可能在规约合并项中遗留的标签而使之能作为一等对象的值的表示(@6.3) 。 |
2950 | 3134 | **原理** |
2951 | 3135 | 规约合并项支持求值算法(@4.4.1) 中函数合并(@4.5.3) 的实现。 |
2952 | 3136 | **注释** |
3137 | +具有子项的枝节点满足 IsBranchedList(@6.2.3) 。 | |
2953 | 3138 | 利用规约合并项的一个实例参见 NPLA1 求值算法(@7.8.2) 。 |
2954 | 3139 | 另见对规约合并项的检查(@7.6.1.3) 。 |
2955 | 3140 |
@@ -3096,7 +3281,7 @@ | ||
3096 | 3281 | |
3097 | 3282 | @6.10.2.1 子项的清理(cleanup) : |
3098 | 3283 | 纯值规约(@6.10.1) 要求清理操作,即移除不被需要(@6.10.1) 的子项。 |
3099 | -清理时子项所有的对象被销毁,可具有副作用(@1.2.4) 。 | |
3284 | +清理时子项所有的对象被销毁,可具有副作用(@4.1) 。 | |
3100 | 3285 | 子项的清理属于删除子项的操作(@6.4.8) 。 |
3101 | 3286 | |
3102 | 3287 | @6.10.3 规约范式: |
@@ -3118,7 +3303,7 @@ | ||
3118 | 3303 | |
3119 | 3304 | @6.10.3.2 规范化规约(@4.4.5) 约定: |
3120 | 3305 | 除非另行指定(@6.10.9) ,一次终止的规约迭代中若存在规范化规约,其发生的次数和时机未指定;一般在最后一遍(@6.10.4) 或之前存在一次即可。 |
3121 | -注意规范化规约可能有副作用(@1.2.4) ,实现应保证规约行为可被预期。 | |
3306 | +注意规范化规约可能有副作用(@4.1) ,实现应保证规约行为可被预期。 | |
3122 | 3307 | |
3123 | 3308 | @6.10.4 规约迭代: |
3124 | 3309 | 对项的一次规约可分解为若干个对这个项的分步规约的有序的迭代过程,每个过程称为一个遍(pass) ;另见 @6.10.4.3 。 |
@@ -3272,8 +3457,10 @@ | ||
3272 | 3457 | 值数据成员持有 NPL::TermReference 类型的值,保留某个子项的引用。 |
3273 | 3458 | 子对象引用的项的子项数应为 1 ,该子项持有 shared_ptr 的实例的非空值且其指向的对象和值数据成员持有的 NPL::TermReference 值的 get() 结果应引用同一个项对象。 |
3274 | 3459 | |
3275 | -@6.10.11.2 带有记号值的列表: | |
3276 | -表达式在求值过程中的中间表示可在列表的表示的基础上,用值数据成员附加记号值的表示作为辅助信息。 | |
3460 | +@6.10.11.2 带有记号值的非空列表: | |
3461 | +表达式在求值过程中的中间表示可在非空列表的表示的基础上,用值数据成员附加记号值的表示作为辅助信息。 | |
3462 | +**注释** | |
3463 | +非空列表满足 IsBranch(@6.2.3) 。 | |
3277 | 3464 | |
3278 | 3465 | @6.10.12 辅助规约函数: |
3279 | 3466 | 可作为规约函数(@6.10.6) 的不依赖上下文的 API 的辅助函数称为辅助规约函数,能处理一般情形的项,不存在作为断言的要求项非空的前置条件。 |
@@ -3302,7 +3489,8 @@ | ||
3302 | 3489 | NPLA 提供维护程序运行状态的数据结构的接口,包括环境、上下文和相关的其它 API 。 |
3303 | 3490 | |
3304 | 3491 | @6.11.1 环境数据结构: |
3305 | -环境对象(@5.7.1) 以 Environment(@6.11.1.1) 类相关的类型表示。 | |
3492 | +类 Environment(@6.11.1.1) 表示环境记录(@5.7.1) 。 | |
3493 | +环境对象(@5.7.1) 以类 Environment 相关的类型表示。 | |
3306 | 3494 | 显式的数据结构直接支持求值环境是一等环境(@5.7) 。 |
3307 | 3495 | TermNode 对象在名称绑定映射中表示被绑定对象(@5.7) 。 |
3308 | 3496 | 当前实现中,环境中的绑定是平坦的映射,未依赖节点的递归性质;但是其中的被绑定对象(@6.11.1) 使用 TermNode ,也适用以下操作。 |
@@ -3328,7 +3516,6 @@ | ||
3328 | 3516 | 成员函数 IsOrphan 判断是否为孤立的环境,即锚对象(@6.8.2) 未被外部引用。 |
3329 | 3517 | 成员函数 GetAnchorPtr 取锚对象指针(@6.7.2) 。 |
3330 | 3518 | 成员函数 GetMapRef 取名称绑定映射的引用。 |
3331 | -静态成员函数 DefaultResolve | |
3332 | 3519 | 成员函数 Define 、Redefine 和 Remove 修改上下文中的值。 |
3333 | 3520 | 成员函数 Define 和 DefineChecked 添加定义。后者在定义已存在时抛出异常。 |
3334 | 3521 | 静态成员函数 EnsureValid 提供失败时抛出异常的环境强引用空值检查(@6.8.1) 。 |
@@ -3368,38 +3555,60 @@ | ||
3368 | 3555 | @6.11.3 上下文数据结构: |
3369 | 3556 | 上下文(@4.6) 使用 ContextNode 类表示。 |
3370 | 3557 | ContextNode 对象保存上下文相关的公共状态。 |
3371 | -上下文的公共状态包括当前处理的环境,称为环境记录(environment record) 。 | |
3372 | -环境记录的引用指定了活动记录中当前的处理的帧(@4.5.3.4) ,指定对象语言中对应当前处理的帧的当前环境(@5.7.3) 。 | |
3373 | -上下文对环境具有共享所有权(@5.6) ,通过环境对象的引用可以取指向它的引用(@6.11.1.1) 。 | |
3558 | +上下文的公共状态包括当前处理的环境的强引用(@5.7.2) ,指定活动记录中当前的处理的帧(@4.5.3.4) ,表示对象语言中对应当前处理的帧的当前环境(@5.7.3) 。 | |
3559 | +环境强引用决定上下文对环境具有共享所有权(@5.6) ,通过环境对象的引用可以取指向它的引用(@6.11.1.1) 。 | |
3374 | 3560 | 只使用上下文中包含的当前环境时,可使用 ContextNode(@6.11.3.1) 和 Environment(@6.11.1.1) 的成员访问环境的数据。 |
3375 | 3561 | 需要上下文时,一般使用现有(通过规约函数的参数得到的)上下文,可能替换和重置状态;或通过现有上下文和环境创建新的上下文。 |
3376 | 3562 | 上下文支持设置对应规约迭代(@6.10.4) 中对应的规约动作(action) ,保存特定的当前动作(current action) 和若干后继的定界动作(delimited actions) 组成当前动作序列(current action sequence) 。 |
3377 | -其中,当前动作和后继的动作分别表示元语言中处理当前和后继的求值上下文中的操作。 | |
3563 | +其中,当前动作和后继的动作分别表示元语言中处理当前和后继的求值上下文(@4.4.8) 中的操作。 | |
3378 | 3564 | 规约动作可选地指定上下文参数。 |
3379 | 3565 | 当前动作作为宿主语言的函数对象被调用,激活(activate) 其中的规约操作,其中可覆盖当前动作为后继的动作。 |
3380 | 3566 | NPLA 上下文中,被激活的动作被移除。因为没有全局的垃圾回收(@4.2.2.3) ,这保证激活的动作在执行后能保证其中直接或间接捕获的资源被及时释放。 |
3381 | 3567 | 若需通过重复使用动作(实现非正常控制(@4.8) 等),需在动作被激活前进行处理(例如,复制或另行构造动作)。 |
3382 | 3568 | 以循环实现重写(@4.1.2) 时,有效的当前动作可用于作为保持重写循环的条件。当不存在有效的当前动作时,规约在完成已激活的规约操作后终止。 |
3383 | 3569 | 以抽象机描述操作语义(@2.2.3) 时,定界动在尾上下文(@4.4.8) 外提供剩余的动作,作为一个序列,称为尾部动作(trailing actions) ,是当前动作的后继动作(subsequent action) 。另见 http://www.brics.dk/RS/03/41/BRICS-RS-03-41.pdf 。 |
3384 | -当前动作起始的当前动作序列表示语言实现中(不作为一等对象(@4.1) 的)未被捕获的当前续延(@4.5.2.1) 。 | |
3385 | -动作和尾部动作整体构成求值上下文(@4.4.8) 的实现。后者在元语言中提供操作,又称为元上下文(meta-context) 。另见 http://www.brics.dk/RS/05/16/BRICS-RS-05-16.pdf 。 | |
3570 | +当前动作起始的当前动作序列表示语言实现中(不作为一等对象(@4.1) 的)未被捕获的当前续延(@4.1.7) 。 | |
3571 | +动作和尾部动作整体构成求值上下文的实现。后者在元语言中提供操作,又称为元上下文(meta-context) 。 | |
3386 | 3572 | 当前动作可作为尾上下文的实现,称为尾动作(tail action) 。特定的尾动作可实现符合 PTC 的尾调用(@5.10.2) 。 |
3387 | -配合重写循环提供基于尾动作的循环 API 可实现 CPS(contiuation passing style) 风格的异步调用。相关 API 参见 @6.11.4 。 | |
3573 | +**原理** | |
3574 | +配合重写循环提供基于尾动作的循环 API 可实现 CPS(contiuation passing style) 风格的异步调用(asynchnorous call) 。 | |
3575 | +参见其它相关 API(@6.11.4) 。 | |
3576 | +**注释** | |
3577 | +关于元上下文,另见 http://www.brics.dk/RS/05/16/BRICS-RS-05-16.pdf 。 | |
3388 | 3578 | |
3389 | 3579 | @6.11.3.1 上下文类: |
3390 | 3580 | 类 ContextNode 提供若干 API 。 |
3391 | -数据成员 Resolve :名称解析算法。 | |
3581 | +成员类型 ExceptionHandler | |
3582 | +成员类型 ReducerSequence | |
3583 | +数据成员 Resolve | |
3584 | +数据成员 TailAction | |
3585 | +数据成员 HandleException | |
3392 | 3586 | 数据成员 LastStatus 保存最后一次规约状态,配合当前动作序列(@6.11.3) 决定之后的续延中的动作调用。 |
3587 | +数据成员 Trace | |
3588 | +成员函数 IsAlive | |
3393 | 3589 | 成员函数 GetBindingsRef 取环境记录(@6.11.3) 中的名称绑定映射(@6.11.1) 引用。 |
3394 | 3590 | 成员函数 GetCurrent :访问上下文内部的保存当前动作序列,是类型为 NPL::ReducerSequence 的一遍规约序列的 const 引用。 |
3395 | 3591 | 当前动作序列可被作为尾动作(@6.11.3) 。 |
3592 | +成员函数 GetCurrentActionType | |
3396 | 3593 | 成员函数 GetMemoryResourceRef 取存储资源(@6.4.1) 引用。 |
3594 | +成员函数 GetRecordRef | |
3595 | +成员函数 GetRecordPtr | |
3596 | +成员函数模板 AccessCurrentAs | |
3597 | +成员函数模板 AccessCurrentAsUnchecked | |
3598 | +成员函数 ApplyTail | |
3599 | +静态成员函数 DefaultHandleException | |
3600 | +静态成员函数 DefaultResolve | |
3601 | +成员函数 Rewrite | |
3397 | 3602 | 成员函数 RewriteLoop 以 IsAlive 的结果是条件的循环调用 ApplyTail ,作为跳板(@6.12.1) 实现一般规约重写。 |
3398 | 3603 | 成员函数 SaveExceptionHandler |
3399 | 3604 | 成员函数模板 SetupCurrent 和成员函数模板 SetupFront 设置当前动作序列。前者具有前置条件 !IsAlive() 以避免重复设置覆盖当前动作的原始状态。 |
3605 | +成员函数 Switch | |
3400 | 3606 | 成员函数 SwitchEnvironment 和 SwitchEnvironmentUnchecked 切换环境,即设置参数指定的环境并返回之前的环境。两者接受 shared_ptr<Environment> 的参数,区别为对空参数抛出异常或引起未定义行为。环境切换便于实现续延或过程调用(@4.5.3.1) 。 |
3401 | 3607 | 切换环境时,旧的环境若没有被引用,则被释放。由于环境具有对象的所有权(@5.6.2) ,内部绑定的对象作为自动变量也被一并释放(@5.6.5) 。 |
3402 | 3608 | 成员函数 ShareRecord 和 WeakenRecord 取环境的引用。 |
3609 | +成员函数 get_allocator | |
3610 | +成员函数 shrink_to_fit | |
3611 | +友元函数 swap | |
3403 | 3612 | 部分 API 使用的参数类似规约函数(@6.10.6) 。 |
3404 | 3613 | 这些函数的参数可能在内部实现被预先绑定,调用时对应的实际参数被忽略:如 TermNode& 类型的形式参数被绑定,内部使用绑定时确定的项而不是实际参数指定的任意的项。 |
3405 | 3614 | 因为绑定参数引入附加的函数调用,一般仅在有必要时使用。 |
@@ -3415,15 +3624,18 @@ | ||
3415 | 3624 | NPL::EnvironmentSwitcher 类型用于切换上下文(@6.11.3.1) 中的当前环境(@6.11.3) 。 |
3416 | 3625 | |
3417 | 3626 | @6.12 异步规约(asynchronous reduction) : |
3418 | -直接式(direct style) 的语言实现利用宿主语言的函数调用实现图规约(@6.2) ,满足规约函数(@6.10.5) 的调用包含子项的规约,即同步规约(synchronized reduction) 。 | |
3627 | +直接风格(direct style) 的语言实现利用宿主语言的函数调用实现图规约(@6.2) ,满足规约函数(@6.10.5) 的调用包含子项的规约,即同步规约(synchronized reduction) 。 | |
3419 | 3628 | 非同步规约称为异步规约。异步规约允许在宿主语言中分离部分规约(@6.10.1) 的不同部分的实现,使之公开为一等对象表示控制状态(@4.1) 及被调度。 |
3629 | +异步规约支持异步调用(@6.11.3) 。 | |
3420 | 3630 | NPLA 支持同步规约和异步规约中立(neutral) 的接口设计(@6.12.5) 。具体的异步规约在派生实现使用。 |
3421 | 3631 | NPLA 实现支持异步规约,以避免 C++ 本机(@2.4.1) 资源限制下不限定数量(@5.10.2) 调用深度时可能引起的宿主语言的未定义行为(@5.4) 。 |
3422 | 3632 | 若资源分配失败,应满足常规宿主资源分配要求(@5.4.1) 。 |
3633 | +**注释** | |
3634 | +在不适合使用异步规约时,实现也可使用其它方式实现异步调用。 | |
3423 | 3635 | |
3424 | 3636 | @6.12.1 异步规约动作: |
3425 | 3637 | NPLA 中,这通过以规约动作(@6.11.3) 作为 CPS(@6.11.3) 的被跳板(trampoline) 执行的中间值(@4.4.6.2) 例程的机制实现。此时,当前动作(@6.11.3) 是尾动作(@6.11.3) 。 |
3426 | -跳板例程设置的规约操作将被异步调用(@6.11.3) ,即当前动作(@6.11.3) 设置为异步动作,在跳板中被循环调用。上下文的重写循环(@6.11.3) 提供一个跳板调用当前动作(@6.11.3) 。 | |
3638 | +跳板例程设置的规约操作将被异步调用,即当前动作(@6.11.3) 设置为异步动作,在跳板中被循环调用。上下文的重写循环(@6.11.3) 提供一个跳板调用当前动作(@6.11.3) 。 | |
3427 | 3639 | 这种方式支持嵌套调用安全(@5.11) 。其它方式实现可能具有更好的性能,但存在限制而不被使用,如: |
3428 | 3640 | 依赖不可移植的宿主语言扩展及实现假定, |
3429 | 3641 | 对对象语言源代码中使用的语言构造(特别是影响控制流的合并子(@4.5.3.2) )进行限制,使用单独的阶段进行抽象求值(@4.1) 。(这允许使用静态 TCO (@5.10.4.2) 所需的变换。) |
@@ -3443,7 +3655,7 @@ | ||
3443 | 3655 | |
3444 | 3656 | @6.12.3 和同步规约的对应关系: |
3445 | 3657 | 因为不再通过宿主的活动记录保留同步操作对应的项,当明确异步动作及对应的项时,规约函数返回的规约结果(@6.10.1) 和对应同步操作不再具有一一对应关系;上述外层循环的循环条件仅考虑当前动作的有效性,具体同 ContextNode::Rewrite(@6.11.3.1) 的约定。 |
3446 | -当前动作对应单一项的到当前被求值项(@6.3) 的有界续延(delimited continuation) 。后继动作(@6.11.3) 是这个边界外的被定界的续延。 | |
3658 | +当前动作对应单一项的到当前被求值项(@6.3) 的有界续延(@4.1.7) 。后继动作(@6.11.3) 是这个边界外的被定界的续延。 | |
3447 | 3659 | 实现过程调用(@4.5.3.1) 时,设置子项的当前动作前保存当前动作,和同步操作时保存活动记录帧(@4.5.3.4) 等价。保存当前动作的方式(具体调用的 API )由调用方的实现指定。 |
3448 | 3660 | 异步规约所在的重写循环(@6.12.1) 中,每个动作返回的规约状态的含义不变;除当前项(@6.10)(若保持副作用顺序,也包括每个子项)上最后一步外,使用当前动作实现一个项内非最终步骤的异步操作,都为部分规约(@6.10.1) ,规约函数返回值应为 ReductionStatus::Partial 。 |
3449 | 3661 | 因为不记录规约的具体项,求值遍(@7.4.1.2) 中的动作序列在需要重规约时无法判断是否保留剩余的规约动作(@6.10.1) 。 |
@@ -3461,6 +3673,11 @@ | ||
3461 | 3673 | 一般地,当前异步动作可以是一般的可调用对象,设置组合的动作后不保证可以拆分,也没有限制异步动作的组合必须显式地保留外部可见的边界(保存到活动记录),因此边界需在当前动作外部。 |
3462 | 3674 | 考虑到性能开销和对象语言的基于规约实现表达式求值,约定仅在表达式边界允许保存续延状态。 |
3463 | 3675 | 若支持一等续延捕获,被捕获的续延应不包含在对当前环境具有所有权的定界动作(@6.11.3) ,以避免当前环境强引用的循环引用。这需要进一步在一等续延操作上的扩展支持,当前未实现。 |
3676 | +**注释** | |
3677 | +续延由 NPL::ContextNode::ReducerSequence(@6.11.3.1) 表示,提供续延捕获直接支持(@6.12.4) 。 | |
3678 | +当前 NPL::ContextNode::ReducerSequence 使用 YSLib::forward_list 的示例实现且作为 public 基类。因此,可从中取迭代器。 | |
3679 | +使用 YSLib::forward_list 的特性不被作为稳定的公开接口支持。 | |
3680 | +特别地,其中元素的稳定性不被保证。这允许未来可能的一些优化,参见 [Hi90] 和 [Br96] 。 | |
3464 | 3681 | |
3465 | 3682 | @6.12.5 异步规约中立(neutrality) : |
3466 | 3683 | 一些实现对是否存在异步规约中立,即实现中被调用的操作可以是同步规约或异步规约。 |
@@ -3518,7 +3735,7 @@ | ||
3518 | 3735 | double |
3519 | 3736 | long double |
3520 | 3737 | 文法表示: |
3521 | -支持的数值类型以 <number> 表示,具有以下表示数值的子类型: | |
3738 | +支持的数值类型以 <number> 表示,具有以下表示数值的子类型(@4.7.7) : | |
3522 | 3739 | <complex> :复数; |
3523 | 3740 | <real> :实数; |
3524 | 3741 | <rational> :有理数; |
@@ -3568,7 +3785,7 @@ | ||
3568 | 3785 | @6.14.2 数值操作约定: |
3569 | 3786 | 在对象语言中,数值操作是可使用数值作为算法输入的值的操作。NPLA1 提供本机 API 支持这些操作的实现。 |
3570 | 3787 | 数值操作数和非数值操作数分别是具有和不具有数值类型的操作数。 |
3571 | -数值操作蕴含对应的数值计算,接受至少一个数值或非数值操作数,预期得到计算结果(@4.5.2.1) 。 | |
3788 | +数值操作蕴含对应的数值计算,接受至少一个数值或非数值操作数,预期得到计算结果(@4.5.2.2) 。 | |
3572 | 3789 | **注释** 非预期情形可引起错误(@2.5.2) 。 |
3573 | 3790 | 其中,计算结果依赖影响计算结果的操作数,并依赖至少一个数值操作数。 |
3574 | 3791 | 除非另行指定: |
@@ -3795,8 +4012,13 @@ | ||
3795 | 4012 | 修改这些选项的配置的兼容性和符合性的影响和 NPLA 的约定相同。 |
3796 | 4013 | 和 NPLA 不同,这些选项主要用于提供参考实现,不应被直接作为公开接口;通常情形不需要更改。 |
3797 | 4014 | 特定的 API 可间接依赖这些特性,以便派生实现提供不同的优化实现,包括: |
3798 | -A1::REPLContext::IsAsynchronous(@7.8.1) | |
3799 | -部分 NPLA 安全保证(@6.1.3) 依赖特定的实现选项支持(如 NPL_Impl_NPLA1_Enable_ThunkedSeparatorPass(@7.9.2) ),当没有启用时可能不满足。 | |
4015 | + A1::REPLContext::IsAsynchronous(@7.8.1) | |
4016 | +部分 NPLA 安全保证(@6.1.3) 依赖: | |
4017 | + 特定的实现选项支持(如 NPL_Impl_NPLA1_Enable_ThunkedSeparatorPass(@7.9.2) ),当没有启用时可能不满足。 | |
4018 | + 被调用的互操作(@5.3) 实现支持相应的安全保证。 | |
4019 | +**注释** | |
4020 | +互操作的本机实现(@5.3) 接口的最简实现不一定允许在合理的开销内满足这些安全保证。 | |
4021 | +为满足整体保证,实现可能需要提供附加的机制。参见本机实现(@7.1.5) 。 | |
3800 | 4022 | |
3801 | 4023 | @7.1.1 对象语言约定: |
3802 | 4024 | NPLA1 仅使用宿主语言的类型和值作为状态。 |
@@ -3804,7 +4026,8 @@ | ||
3804 | 4026 | 类型等价性(@4.7.2) 基于类型映射(@5.5) 及其实现(@6.1.7) ,由 C++ 的语义规则定义。 |
3805 | 4027 | 值等价性由宿主实现的 == 表达式的结果定义。 |
3806 | 4028 | 除非另行指定,所有类型的外部表示(@4.1.1) 都是允许从作为内部表示的项节点结构确定的同宿主类型(@5.5) 的空字符结尾的字符串(即 ISO C++ 的 NTCTS )。 |
3807 | -引入过程(@4.5.2.1) 的具体形式参见 @8.4.5 ,也可约定通过特定的本机函数(@5.3) 等其它方式提供。 | |
4029 | +**注释** | |
4030 | +引入过程(@4.5.2.2) 的具体形式参见过程抽象(@8.4.5) ,也可约定通过特定的本机函数(@5.3) 等其它方式提供。 | |
3808 | 4031 | |
3809 | 4032 | @7.1.1.1 字面量: |
3810 | 4033 | 扩展字面量同 NPLA(@5.2) ,其中至少包括 #t 、#f 和数值的处理。 |
@@ -3876,6 +4099,16 @@ | ||
3876 | 4099 | 其它 NPLA1 非内存安全操作(@5.6.3.1) 包括实现中调用的明确取无所有权引用的 API ,参见 @8.1.2 关于间接值的约定以及 @8 中具体函数的描述。 |
3877 | 4100 | NPLA1 对后者的使用可选地使用 NPLA 运行时检查(@6.1.2.1) 补充基于项引用(@6.8.3) 和环境的引用计数(@6.11.3.1) 的检查机制(如 @8.4.5.2 )。 |
3878 | 4101 | |
4102 | +@7.1.5 互操作约定: | |
4103 | +非 NPLA1 实现提供的类型的宿主 == 操作不要求支持嵌套调用安全(@5.11) 。 | |
4104 | +作为嵌套调用安全的约定(@6.1.3) 的扩展,若存在 == 操作不支持嵌套调用安全的类型,具体类型由派生实现的定义。 | |
4105 | +**原理** | |
4106 | +调用假定异步规约不存在的实现允许使用直接风格(@6.12) 代替异步调用(@6.11.3) ,一般能显著节约调用开销,但对非特定类型,这不保证提供(在宿主语言无法提供的)嵌套调用安全保证。 | |
4107 | +本机实现的 == 因为宿主语言的接口缺乏一般异步调用支持,不提供在宿主语言无法提供的嵌套调用安全保证。 | |
4108 | +由于宿主语言的接口限制,要求本机实现的 == 操作使用 NPLA1 异步规约(@6.12) 需要显著增加接口设计的复杂性和实现开销,因此在此不作要求。 | |
4109 | +为满足嵌套调用安全保证,实现需要避免异步调用,或自行使用其它的异步调用机制。 | |
4110 | +NPLA1 中提供的类型仍需要支持嵌套调用安全,以满足嵌套调用安全的约定中的要求。 | |
4111 | + | |
3879 | 4112 | @7.2 NPLA1 中间值: |
3880 | 4113 | NPLA1 扩展了 NPLA 的中间值(@6.8) 机制。 |
3881 | 4114 | NPLA1 规约使用 @6.8 约定的类型影响求值状态。 |
@@ -3915,7 +4148,7 @@ | ||
3915 | 4148 | 语言规范(@1.2.1.2) 可使用 A1::ValueToken::Unspecified 作为没有附加限制条件的未指定值(unspecified value) 的内部表示,但实现的 API(如 A1::Forms(@8.1) )会明确指定使用这个值(如设置这个值作为对象语言中函数的返回值),用户代码仍然不应依赖此处使用的具体值。 |
3916 | 4149 | 注意当前 A1::Reduce 规约时不对空节点(表示空列表)和 A1::ValueToken 类型的值进行操作。 |
3917 | 4150 | |
3918 | -@7.2.3 NPLA1 续延(@4.5.2.1) : | |
4151 | +@7.2.3 NPLA1 续延(@4.5.2.2) : | |
3919 | 4152 | 续延是依赖上下文状态(@7.4.3) 的一等中间值(@6.8.1) ,参见合并遍(@7.4.2) 。 |
3920 | 4153 | |
3921 | 4154 | @7.3 错误处理例程: |
@@ -3986,12 +4219,18 @@ | ||
3986 | 4219 | 续延依赖 NPLA1 上下文状态(@7.4.3) 。 |
3987 | 4220 | A1::Continuation 对象保存 A1::ContextHandler(@7.6.1) 对象实现具体操作。 |
3988 | 4221 | A1::Continuation::operator() 以 ContextNode& 参数直接调用续延。 |
3989 | -注意 NPLA 上下文中的续延由 NPL::ReducerSequence(@6.11.3.1) 表示,提供续延捕获直接支持(参见 @6.12.4),而 NPLA1 续延是这个机制上的扩充。 | |
3990 | -NPLA1 续延的必要性体现为的状态支持下一求值项(@7.4.3) 的支持(这隐含运行时对上下文状态类型的要求)。 | |
3991 | 4222 | 调用续延要求上下文对象的动态类型包含 NPLA1 上下文,取其中的下一求值项作为当前项(@6.10) 。 |
3992 | -此外,也可存在其它续延的对象。例如,Forms::Sequence(@8.4.9) 中的内部续延不支持访问(因此不依赖)下一求值项,而不需要实现为 A1::Continuation 。 | |
4223 | +**原理** | |
4224 | +NPLA 上下文中的续延提供续延捕获直接支持(@6.12.4) ,而 NPLA1 续延是这个机制上的扩充。 | |
4225 | +NPLA1 续延的必要性体现为对下一求值项(@7.4.3) 的支持(这隐含运行时对上下文状态类型(@7.4.3) 的要求),以支持对象语言中的一等续延(@4.5.3.3) 。 | |
4226 | +此外,也可存在其它的表示不支持被捕获为一等续延的内部续延的对象。例如,Forms::Sequence(@8.4.9) 中的内部续延不支持访问(因此不依赖)下一求值项,而不需要实现为 A1::Continuation 。 | |
4227 | +**注释** | |
3993 | 4228 | 另见动作帧(@6.12.6) 。 |
3994 | 4229 | |
4230 | +@7.4.2.1 一次续延调用检查: | |
4231 | +NPLA1 内部提供调用检查类在一次续延(@4.5.3.3) 被捕获时共享状态,标记续延已被调用而确保不可重入(@4.5.2.2) 。 | |
4232 | +进入来自同一具现的一次续延的任何副本只允许一次显式(续延应用)或者隐式(如被函数调用的返回蕴含)的调用时,共享状态都应被标记。 | |
4233 | + | |
3995 | 4234 | @7.4.3 NPLA1 上下文状态: |
3996 | 4235 | 类 A1::ContextState 是 ContextNode 的 public 派生类,其对象表示 NPLA1 上下文状态,包含 NPLA 上下文的状态(@6.11.3) 。 |
3997 | 4236 | A1::ContextState 包含作为之后的异步求值规约动作(@6.12.1) 使用的当前项(@6.10) 的下一求值项。 |
@@ -4045,7 +4284,7 @@ | ||
4045 | 4284 | NPLA1 规约函数可能使用上下文,一般通过 ContextNode& 参数表明依赖上下文的状态(@6.11.3) ,且进一步依赖 NPLA1 上下文状态(@7.4.3) 。 |
4046 | 4285 | 若使用异步实现,满足和同步规约的对应关系(@6.12.3) 以能兼容其它操作使用不同的实现。 |
4047 | 4286 | A1::ReduceArguments 从第二个子项起逐项规约。 |
4048 | -这允许单一子项表示没有参数的情形,即便这不会通过 A1::Reduce(@7.4.4) 得到(因为子节点数为 1 时不调用列表遍),也可简化某些实现如 @8.4.5 。 | |
4287 | +这允许单一子项表示没有参数的情形,即便这不会通过 A1::Reduce(@7.4.4) 得到(因为子节点数为 1 时不调用列表遍),也可简化某些实现如过程抽象(@8.4.5) 。 | |
4049 | 4288 | A1::ReduceChildren 规约子项。 |
4050 | 4289 | A1::ReduceFirst 对可能存在的第一个子项进行规约。 |
4051 | 4290 | 若第一个子项规约结果(@6.10.1) 可被作为列表遍(@7.4.3) 的事件处理器,整个项可继续规约。能进行此规约的形式称为 fexpr ,其中 f 表示 first 。 |
@@ -4123,7 +4362,7 @@ | ||
4123 | 4362 | 若 Check 数据成员非空则调用 Check 进行检查,失败时抛出异常; |
4124 | 4363 | 调用被包装的处理器 Handler 数据成员。 |
4125 | 4364 | 除此之外,附加操作参见 @7.6.1.3 。 |
4126 | -注意直接在处理器中调用规约实现可导致无限递归;另见 @4.4.5 。 | |
4365 | +注意直接在处理器中调用规约实现可导致无限递归;另见强规范化(@4.4.5) 。 | |
4127 | 4366 | |
4128 | 4367 | @7.6.1.3 前置条件检查和异常处理: |
4129 | 4368 | A1::FormContextHandler::operator() 调用时,按包装数(@7.6.1.1) 判断是否需要调用(被包装的处理器 Handler 数据成员)。 |
@@ -4200,7 +4439,7 @@ | ||
4200 | 4439 | 对叶节点直接返回 ReductionStatus::Clean ,无其它作用。 |
4201 | 4440 | 配合 A1::FormContextHandler 在第一个子项上提供中间值(@6.8) ,得到的项为 WHNF(@4.4.5.1) ; A1::ReduceCombined 实现 WHNF 的求值。 |
4202 | 4441 | 调用合并子时,合并子和操作数中的临时对象被延长生存期(@5.8.5.4) ,但在尾上下文(@4.4.8) 之前结束(@5.10.2) 。 |
4203 | -按 @5.6.2 ,实现可选择不同的内部存储保存这些临时对象,具体策略和是否支持异步规约(@7.9) 相关,详见 @7.9.3 ;另见 TCO 实现(@7.11.5) 。 | |
4442 | +按 @5.6.2 ,实现可选择不同的内部存储保存这些临时对象,具体策略和是否支持异步规约(@7.9) 相关,详见 @7.9.3 ;另见 TCO 实现(@7.11.6) 。 | |
4204 | 4443 | 函数 A1::ReduceCombinedBranch 同 A1::ReduceCombined ,但断言项符合 IsBranch 。 |
4205 | 4444 | 函数 A1::ReduceCombinedReferent 同 ReduceCombined ,但使用第三参数指定的值。 |
4206 | 4445 | 规约合并项不判断处理器内部的操作,以同时支持宿主语言的本机实现和对象语言(@8.4.5) 引入的合并子。 |
@@ -4245,11 +4484,11 @@ | ||
4245 | 4484 | A1::RelayForCall 和 A1::RelayForEval(@7.7.2) 间接依赖 NPL::LiftToReturn(@6.9.5.3) 。 |
4246 | 4485 | 对间接值的相关使用和检查另见 @7.6.5 。 |
4247 | 4486 | 非引用值间接值(@6.4.6.2) 对应复制或转移间接值参见 @8.1.2 。 |
4248 | -关于具体操作,另见 @8 中的保证,如 @8.4.5 。 | |
4487 | +关于具体操作,另见 @8 中的保证,如过程抽象(@8.4.5) 。 | |
4249 | 4488 | |
4250 | 4489 | @7.7.3 绑定操作: |
4251 | 4490 | 绑定操作决定符号或具有符号的数据结构与项的对应关系,并初始化被绑定对象(@6.11.1) 而引入变量(@4.1) 。 |
4252 | -作为函数语法的推广,两者分别由绑定操作使用形式参数(@4.5.2) 和操作数(@4.4.5.1) 指定。 | |
4491 | +作为函数语法的推广,两者分别由绑定操作使用形式参数(@4.5.2.1) 和操作数(@4.4.5.1) 指定。 | |
4253 | 4492 | 作为 TermNode ,操作数(@4.4.5.1) 的表示具有树的构造,即操作数树(@4.4.5.1) ,它可以具有子项。 |
4254 | 4493 | 匹配的形式参数和操作数树对应也可具有树的构造,即形式参数树(formal parameter tree) 。 |
4255 | 4494 | 绑定操作初始化的变量的名称和值分别由形式参数树和操作数树决定。 |
@@ -4257,7 +4496,7 @@ | ||
4257 | 4496 | 成功的匹配决定形式参数对应的操作数或其子项,作为其实际参数(@4.5.3) 。这种对应关系是单射但不一定是满射,即匹配成功后,每个参数总存在对应的操作数或其子项,而操作数和子项允许不对应形式参数而被忽略。 |
4258 | 4497 | NPLA 形式参数树的叶节点为符号(@6.8.1) 或其引用值(@6.3.4) 。不符合要求的对象构造形式参数树时可引起错误(@2.5.2) 。 |
4259 | 4498 | 被绑定的项的操作数是待匹配的项的子项(而不会是此项自身,满足 @6.10.6 )。 |
4260 | -被绑定的参数可以作为函数的形式参数(@4.5.2) ,也可以其它初始化变量的语法构造的基础。 | |
4499 | +被绑定的参数可以作为函数的形式参数(@4.5.2.1) ,也可以其它初始化变量的语法构造的基础。 | |
4261 | 4500 | 和 Kernel 类似,形式参数树是 DAG ,但通过真列表(@6.2.1) 的性质蕴含而不需要另行限制。形式参数树中的节点在匹配时被视为右值。 |
4262 | 4501 | 和 Kernel 不同,操作数树同时支持作为引用的左值(@5.8.1) 和非引用的右值(@5.8.1) ,在实现上需要解析引用(类似 @6.9.2 的部分操作)。 |
4263 | 4502 | 绑定操作的 API 详见绑定支持(@7.7.4) 。 |
@@ -4369,7 +4608,7 @@ | ||
4369 | 4608 | 仅在绑定临时对象到引用时使用复制消除(@5.8.5.3) 。 |
4370 | 4609 | **原理** |
4371 | 4610 | 绑定的默认行为对引用值特殊处理,是为了满足 G1b(@1.5.5.1) ,而不是像某些语言(如 ISO C 和 [Rust] )仅通过内建的机制提供特定左值上下文(lvalue context) 。 |
4372 | -绑定的默认行为不使用析构性转移(@6.3.2) 的操作(类似 [Rust] 的设计),原因是考虑到绑定的副作用影响操作数(即便因为对象被销毁而不一定是修改操作)和破坏幂等性(@4.1) (特别是指定过程调用的形式参数时,参见 @8.4.5 )违反易预测性原则(@1.5.5.2) 。 | |
4611 | +绑定的默认行为不使用析构性转移(@6.3.2) 的操作(类似 [Rust] 的设计),原因是考虑到绑定的副作用影响操作数(即便因为对象被销毁而不一定是修改操作)和破坏幂等性(@4.1) (特别是指定过程调用的形式参数时,参见过程抽象(@8.4.5) )违反易预测性原则(@1.5.5.2) 。 | |
4373 | 4612 | 为允许调用宿主对象的转移构造函数(@5.8.2.3) ,限制复制消除。初始化引用之外的参数创建也不是 ISO C++17 约定要求消除复制的上下文。 |
4374 | 4613 | 作为操作数的引用值中的唯一引用可能蕴含绑定临时对象标签(@7.7.3.2) ,这使绑定为变量的消亡值可能以名称表达式求值结果(不会是消亡值)的引用值访问时,能和其它引用值区分。提供这种设计的理由是: |
4375 | 4614 | 以消亡值初始化一个变量,通常需要使变量指称消亡值引用的资源,而不仅仅是表示即将被转移的消亡值自身。 |
@@ -4443,7 +4682,7 @@ | ||
4443 | 4682 | 函数 A1::MoveGuard |
4444 | 4683 | 别名模板 A1::GKeptGuardAction |
4445 | 4684 | 函数模板 A1::MakeKeptGuard |
4446 | -**注释** 另见续延名称(@7.11.6) 和解释(@8.6.2) 。 | |
4685 | +**注释** 另见续延名称(@7.11.7) 和解释(@8.6.2) 。 | |
4447 | 4686 | |
4448 | 4687 | @7.8 REPL API : |
4449 | 4688 | NPLA1 提供 API 通过解释器的各个子模块组装 REPL(read-eval-print loop) 的交互式界面。 |
@@ -4536,12 +4775,17 @@ | ||
4536 | 4775 | 以下讨论的由上下文(@6.11.3) 支持的 NPLA API 使用方式和 NPLA1 相关。其它非 NPLA1 的派生实现可使用其它方式或选择不支持。 |
4537 | 4776 | |
4538 | 4777 | @7.9.1 和同步规约实现兼容性: |
4539 | -和同步实现的对应关系参见 @6.12.3 。 | |
4778 | +基本内容参见和同步实现的对应关系(@6.12.3) 。 | |
4540 | 4779 | 同步规约时,在当前被求值项(@6.3) 之后添加的操作可包括调用 A1::ReduceCombined(@7.6.4.2) 的清理操作(@6.10.2.1) 。 |
4541 | 4780 | 和 NPL::CheckedReduceWith(@6.10.3) 单独判断规约结果(@6.10.1) 保持循环条件不同,清理操作对应的异步规约默认不清除当前动作。 |
4542 | 4781 | 这种实现允许通常没有 TCO 且不访问当前动作或不暴露当前动作状态改变的同步实现兼容被调函数实现的子项规约为异步操作的实现,即异步调用安全(asynchronous call safety) 。 |
4543 | 4782 | 允许这种兼容性有利于迁移 API 的实现至异步操作的实现,也允许支持按不同运行时配置启用 TCO 以平衡时间和空间开销及优化性能(但当前不考虑支持)。 |
4544 | -注意支持异步规约的实现不保证能被同步调用而维持当前动作状态的正确。这允许异步规约的实现中最后的操作以直接内联(direct inline) 同步的实现代替异步规约动作组合(@6.12.7) ;其主要的应用参见 @7.9.4 。 | |
4783 | +注意支持异步规约的实现不保证能被同步调用而维持当前动作状态的正确。 | |
4784 | +这允许异步规约的实现中最后的操作以直接内联(direct inline) 同步的实现代替异步规约动作组合(@6.12.7) 。其主要的应用参见动作内联(@7.9.4) 。 | |
4785 | +异步规约允许提供同步规约实现不支持的控制作用(@1.2.4) 的实现,一些步骤可能被跳过。特别地,NPLA 的上下文总是使用依赖异步调用的错误处理等机制。 | |
4786 | +一般应避免在异步调用中直接清理资源(包括直接依赖宿主语言的析构函数的调用),而使用在异步规约动作(@6.12.7) 内蕴含生存期的守卫(guard) 值代替。否则,若调用被跳过,资源泄露,和同步规约的行为也不再一致。 | |
4787 | +**注释** | |
4788 | +异步调用安全的位置相对同步调用更受限。 | |
4545 | 4789 | 例如,异步实现的规约合并求值(@7.6.4.2) 应仅在最后一遍的最后的操作中被同步调用。 |
4546 | 4790 | |
4547 | 4791 | @7.9.2 异步规约 API 支持: |
@@ -4618,7 +4862,7 @@ | ||
4618 | 4862 | @7.10 TCO 支持: |
4619 | 4863 | 因为要求对宿主语言中立(@5.10.4.1) ,基于 CPS 调用(@6.11.3) 的上下文 API 实现异步规约(@7.9) ,带有上下文状态的前置条件(@6.11.3.1) 。 |
4620 | 4864 | 这样的实现方式性能相对直接使用体系结构相关的方式较低,但仍可被接受。 |
4621 | -这样实现的 TCO 同时允许可移植地在对象语言中实现某些引入控制作用(@1.2.4) 的一等实体(@4.1) (如续延(@4.5.2.1) )。 | |
4865 | +这样实现的 TCO 同时允许可移植地在对象语言中实现某些引入控制作用(@1.2.4) 的一等实体(@4.1) (如续延(@4.5.2.2) )。 | |
4622 | 4866 | 当前未支持宿主栈展开(stack unwinding) 的互操作(@5.3) 。 |
4623 | 4867 | C++ 异常调用重写循环(@6.11.3) 内资源对象的析构函数顺序是未指定的。但是,对 C++ API 异常安全保证的要求(@7.4.1.4) 仍然不变。 |
4624 | 4868 | 在 NPLA1 API 中同时提供对应的异步规约但不使用 TCO 的实现,不满足本节要求,仅供参考。 |
@@ -4642,7 +4886,7 @@ | ||
4642 | 4886 | 基于异步重规约和 TCO 的互操作提供目标 TCO 。 |
4643 | 4887 | |
4644 | 4888 | @7.10.2 整体设计约束: |
4645 | -TCO 要求尾上下文的续延(@4.5.2.1) 保持等效。考虑到计算资源的限制,排除构造相同的外部的求值环境(@4.6.1.2) 的实现方式,这蕴含同一性。 | |
4889 | +TCO 要求尾上下文的续延(@4.5.2.2) 保持等效。考虑到计算资源的限制,排除构造相同的外部的求值环境(@4.6.1.2) 的实现方式,这蕴含同一性。 | |
4646 | 4890 | NPLA 的 TCO 基于异步规约(@7.9) ,基本方式是尾调用合并(@7.10) 。TCO 动作(@7.10.3) 是支持了尾调用合并的动作。 |
4647 | 4891 | 考虑到当前动作(@6.11.3) 预期直接构成当前续延(@6.11.3.1) ,对 NPLA1 ,这需要限制尾动作(@6.11.3) 的下一动作(@6.12.2) 不是 TCO 动作。 |
4648 | 4892 |
@@ -4659,7 +4903,8 @@ | ||
4659 | 4903 | 操作结果(@7.10.6.2) 请求状态; |
4660 | 4904 | 已知的函数对象(@7.10.7.1) ; |
4661 | 4905 | 当前处理的尾环境(@7.10.6.1) 的守卫; |
4662 | -帧记录列表(@7.10.5.1) 。 | |
4906 | +帧记录列表(@7.10.5.1) ; | |
4907 | +一次调用检查守卫(@7.10.5.3) 。 | |
4663 | 4908 | 此外,TCO 动作还包含用于诊断的操作数名称。 |
4664 | 4909 | 当前项的引用可在当前项的守卫中蕴含。 |
4665 | 4910 |
@@ -4706,8 +4951,8 @@ | ||
4706 | 4951 | 当前设计中的操作压缩是同步操作。 |
4707 | 4952 | 连续出现的幂等操作不受递归嵌套深度的影响。若操作序列(@7.10.3.3) 最后一项以外的操作都是幂等的,则可支持不受限制递归尾调用,即实现了 PTC 。 |
4708 | 4953 | 由于语义规则限制,不在任意情形下避免非幂等的操作而保证 PTC(这些操作随着嵌套深度的增加随之需要分配存储): |
4709 | -捕获动态环境(@4.6.1.2)(通过 vau 抽象(@4.5.2.3) 的创建(@8.4.5) )时,所在的环境记录(@6.11.3) 作为帧(@4.5.3.4) 占据的存储不保证有上界(unbounded) ; | |
4710 | -包含非平凡析构(trivially destructible) 对象的帧的存储是否支持 PTC 未指定(另见 TCO 相关的生存期的实现注记(@7.11.5) )。 | |
4954 | +捕获动态环境(@4.6.1.2)(通过 vau 抽象(@4.5.2.5) 的创建(@8.4.5) )时,所在的环境记录(@6.11.3) 作为帧(@4.5.3.4) 占据的存储不保证有上界(unbounded) ; | |
4955 | +包含非平凡析构(trivially destructible) 对象的帧的存储是否支持 PTC 未指定(另见 TCO 相关的生存期的实现注记(@7.11.6) )。 | |
4711 | 4956 | |
4712 | 4957 | @7.10.4.1 非幂等操作: |
4713 | 4958 | 非幂等的操作和生存期相关,包括: |
@@ -4720,14 +4965,14 @@ | ||
4720 | 4965 | 但注意调整语义规则或通过区分出非平凡析构等方式证明可观察行为不变后,减少父环境被隐藏的变量而完全实现 PTC 是可能的。 |
4721 | 4966 | 基于语义规则的许可,实现应支持部分非幂等操作的被合并,以支持 PTC : |
4722 | 4967 | 若设置前的环境是被设置的环境的直接或间接父环境,其中可能影响可观察行为的变量都被新的环境隐藏,可确定父环境不再使用且不影响可观察行为,则仍然合并操作,并释放该父环境的资源。 |
4723 | -NPLA 尾上下文约定允许可合并临时对象分配的非幂等操作实现 PTC ,即若存在已在 TCO 动作中分配的相等(以宿主环境(@2.7.1) 约定,参见 @7.11.5)的临时对象,则不再分配。 | |
4968 | +NPLA 尾上下文约定允许可合并临时对象分配的非幂等操作实现 PTC ,即若存在已在 TCO 动作中分配的相等(以宿主环境(@2.7.1) 约定,参见 @7.11.6)的临时对象,则不再分配。 | |
4724 | 4969 | |
4725 | 4970 | @7.10.4.2 非幂等操作合并约束: |
4726 | 4971 | 同一个 TCO 动作内,以下尾调用合并的效果仍然有效: |
4727 | 4972 | 相同类别的不同实例的操作仍然被尾调用合并集中; |
4728 | 4973 | 非幂等操作和幂等操作之间的顺序被调整; |
4729 | 4974 | 两类非幂等操作之间的顺序被调整,不再交错。 |
4730 | -最后一项变换是被允许的,因为尾上下文内环境的生存期被子表达式内的调用(如 @8.4.5 的实现)约束;而临时对象生存期在尾上下文中可以延长到整个 TCO 操作结束(@5.10) 。 | |
4975 | +最后一项变换是被允许的,因为尾上下文内环境的生存期被子表达式内的调用(如过程抽象(@8.4.5) 的实现)约束;而临时对象生存期在尾上下文中可以延长到整个 TCO 操作结束(@5.10) 。 | |
4731 | 4976 | |
4732 | 4977 | @7.10.5 TCO 相关的资源管理: |
4733 | 4978 | TCO 动作(@7.10.3) 持有尾上下文的活动记录帧(@4.5.3.4) ,包括环境和被规约表达式中的右值对应的临时对象。 |
@@ -4757,6 +5002,13 @@ | ||
4757 | 5002 | 被从 TCO 对象中移除的环境对象是没有 NPL::TermReference 引用其锚对象的对象。当强引用计数为 1 时,压缩后最终会被清除而释放,持有的对应资源被回收。 |
4758 | 5003 | 这一过程在局部上类似基于引用计数的 GC(@5.6.4) 机制。 |
4759 | 5004 | |
5005 | +@7.10.5.3 一次调用检查: | |
5006 | +TCO 动作的一次调用检查守卫(@7.10.3.1) 保存能在一次续延(@4.5.3.3) 被捕获时的检查对象(@7.4.2.1) 。 | |
5007 | +因为捕获续延通过对象语言的合并子调用进入,可假定上下文中总是存在 TCO 动作。 | |
5008 | +这使捕获续延的调用满足 PTC 。 | |
5009 | +**注释** | |
5010 | +续延捕获调用满足 PTC 的一个例子参见 [Hi90] 。 | |
5011 | + | |
4760 | 5012 | @7.10.6 状态维护: |
4761 | 5013 | 和 TCO 操作序列(@7.10.3.3) 对应,规约调用动作时影响状态。 |
4762 | 5014 | 在最后一项操作前,状态需要被维护,保证被语义和非 TCO 的实现语义的差异不违反语义规则。 |
@@ -4851,11 +5103,11 @@ | ||
4851 | 5103 | 即使替换扩展实现实现对象语言语义等价但削弱 NPLA(@6) 和 NPLA1 API 的变体,一般也不能预期能和常见其它典型动态语言的解释器实现。这主要是由于: |
4852 | 5104 | 使用了解释开销较大的 AST 解释器(@6.1.2) ; |
4853 | 5105 | 缺乏清单类型(@4.7.3) 等提供支持更明确执行路径的元数据; |
4854 | -使用异步规约(@6.12) 和局部的异步调用保证嵌套调用安全(@7.11.7) ; | |
5106 | +使用异步规约(@6.12) 和局部的异步调用保证嵌套调用安全(@7.11.8) ; | |
4855 | 5107 | 为支持合并子可能具有的动态环境(@4.6.1.2) ,规约合并时(@7.6.4.2) 没有优化,可能有不必要的重复开销。 |
4856 | 5108 | 进一步优化的一般目标是寻找更多可消除的解释开销,这一般可通过以下方法(可能不再使用 NPLA ): |
4857 | 5109 | 派生实现提供契约(contract) 和类型标注(type annotation) 等附加元数据,使对象语言程序可利用这些设施提供更利于优化执行的实现; |
4858 | -在特定的函数构造器(如通过 @8.4.5 实现的对象语言操作符(@4.4.5.1) )支持缓存等调用优化; | |
5110 | +在特定的函数构造器(如通过过程抽象(@8.4.5) 实现的对象语言操作符(@4.4.5.1) )支持缓存等调用优化; | |
4859 | 5111 | 实现直接接近机器表示的翻译,通常为 JIT(just-in-time) 编译; |
4860 | 5112 | 使用和 NPLA 不同的表示以支持特定体系结构相关优化。 |
4861 | 5113 |
@@ -4866,7 +5118,7 @@ | ||
4866 | 5118 | **注释** klisp 实现的 kapply_cc 是通过续延并保证异步调用的一个例子。 |
4867 | 5119 | |
4868 | 5120 | @7.11.3 函数合并(@4.5.3) 的实现: |
4869 | -NPLA1 规范求值算法(@7.8.2) 相对 Kernel 更简单以支持函数合并的 TCO 实现(@7.11.5) 。 | |
5121 | +NPLA1 规范求值算法(@7.8.2) 相对 Kernel 更简单以支持函数合并的 TCO 实现(@7.11.6) 。 | |
4870 | 5122 | 对应地,函数合并在处理器调用(@7.6.1.2) 实现参数列表中参数的求值。 |
4871 | 5123 | **注释** 这和 klisp 的 eval 实现略有不同。 |
4872 | 5124 | NPLA1 使用包装数(@7.6.1.1) 处理包装调用,支持多重包装(这在 [RnRK] 被求值算法蕴含);而当前 klisp 的实现没有正确地支持多重包装调用,参见:https://bitbucket.org/AndresNavarro/klisp/issues/11 。 |
@@ -4879,14 +5131,14 @@ | ||
4879 | 5131 | 在 NPLA1 规范求值算法(@7.8.2) 的 PTC 通过显式构造 TCO 动作以及在此基础上的操作压缩(@7.10.4) 实现。 |
4880 | 5132 | 可能引入新的环境的操作,如 [RnRK] 的 eval(对应 NPLA1 对象语言函数 eval 和 eval% 等,参见 @11.3.7 ),需要另行调用比 A1::ReduceOnce 更复杂的操作压缩。 |
4881 | 5133 | 和 Kernel 要求以外,因为维护资源(@7.10.5) 的需要,TCO 动作在函数合并的底层操作子合并前被构造,因此进入参数求值时在资源管理的意义上也在所在函数合并的同一个尾上下文(@4.4.8) 。 |
4882 | -另见 TCO 实现(@7.11.5) 。 | |
5134 | +另见 TCO 实现(@7.11.6) 。 | |
4883 | 5135 | **注释** |
4884 | 5136 | PTC 实现对应 klisp 的 ktail_call 实现 Kernel 的默认求值算法(包括 [RnRK] 的 eval 应用子的底层的操作子);但和后者依赖 GC 不同,TCO 动作可对环境有所有权(@7.10.6) 。 |
4885 | 5137 | klisp 还使用 ktail_call 提供本机实现。NPLA1 也可使用类似的实现。 |
4886 | 5138 | 其它更一般的情况下,在求值上下文(@4.4.8) 中隐含已构造 TCO 动作为前提,通过直接或间接调用 A1::ReduceOnce(@7.4.4) 提供 PTC 保证。 |
4887 | 5139 | 这对应 klisp 的 ktail_eval ;但和后者不同,A1::ReduceOnce 调用确切地只对应不显式引入新的环境的操作。 |
4888 | 5140 | |
4889 | -@7.11.A 资源回收限制: | |
5141 | +@7.11.5 资源回收限制: | |
4890 | 5142 | 当前 TCO 操作压缩不保证回收(通过 #ignore 指定)被忽略外的动态环境(@4.6.1.2) 。 |
4891 | 5143 | 注意 [RnRK] §3.3 仅保证的应用子调用是尾上下文(@4.4.8) ,使用任意环境作为动态环境的操作子调用时不需要满足 PTC 。 |
4892 | 5144 | 这可能有误,因为造成应用子的底层合并子和不作为底层操作子的其它操作子不一致,并且和这里的操作压缩限制一样,实际无法总是保证具有动态环境的应用子在动态环境被用用时能满足的调用 PTC 。 |
@@ -4904,7 +5156,7 @@ | ||
4904 | 5156 | $sequence ($def! $f ($vau () e (() $f))) (() $f) |
4905 | 5157 | 以上 Kernel 程序使用 klisp 和 SINK 中(后者需替换 #ignore 为 %ignore ),效果一致。 |
4906 | 5158 | |
4907 | -@7.11.5 TCO 动作(@7.10.3) 实现和其它限制: | |
5159 | +@7.11.6 TCO 动作(@7.10.3) 实现和其它限制: | |
4908 | 5160 | 因为维护资源(@7.10.5) 的需要,NPLA1 规范求值算法(@7.8.2) 不处理 WHNF(@4.4.5.1) 以外的操作数且允许宿主类型的调用(@7.6.1.2) 处理 WHNF 的第一项。 |
4909 | 5161 | 当前在 TCO 动作中被分配的临时对象(@5.8.5) 的操作都针对作为 WHNF 的第一项的合并子(@4.5.3.2) ,其中合并子的相等关系由相等性(@8.4.5.5) 或不影响可观察行为的其它宿主实现提供的 == 操作确定。 |
4910 | 5162 | 由相等性(@8.4.5.5) ,捕获不同环境的 vau 合并子不相等;不论是否具有相等的外部表示(@1.2.4) 和来源,其右值不会被直接操作压缩(@7.10.4) 避免多次添加而被 TCO ;若其嵌套调用因为捕获不同的存在引用关系的环境,不支持其它形式的 TCO ;因此,不支持 PTC 。 |
@@ -4930,7 +5182,7 @@ | ||
4930 | 5182 | (begin (define f (lambda () ((f)))) ((f))) |
4931 | 5183 | (begin (define f (lambda () (f))) (f)) |
4932 | 5184 | |
4933 | -@7.11.6 续延名称: | |
5185 | +@7.11.7 续延名称: | |
4934 | 5186 | 模块 NPL::NPLA1 提供特定可由函数 A1::QueryContinuationName(@7.7.5) 查询得到的续延名称。 |
4935 | 5187 | 其中查询结果的名称和 A1::Continuation::Handler 或 Reducer 的特定目标类型关联。 |
4936 | 5188 | 除非另行指定,关联的类型是非公开的内部类型。 |
@@ -4947,7 +5199,7 @@ | ||
4947 | 5199 | 若特定对象语言操作使用非本机实现(@5.3) ,可使用其它续延。 |
4948 | 5200 | 另见其它 NPLA1 实现模块中引入的续延名称(@8.6.1) 。 |
4949 | 5201 | |
4950 | -@7.11.7 嵌套调用说明: | |
5202 | +@7.11.8 嵌套调用说明: | |
4951 | 5203 | 当前实现显式包含保证嵌套调用安全(@5.11) 机制的异步调用 API 包括: |
4952 | 5204 | NPL::SContext 中的递归遍历 API(@6.1.6.4) ; |
4953 | 5205 | 环境数据结构(@6.11.1) 的可重入 API ; |
@@ -4963,7 +5215,7 @@ | ||
4963 | 5215 | @8.1 语法形式支持: |
4964 | 5216 | 模块 NPL::NPLA1Forms 提供的 API 专用于实现语法形式对应的功能而不是 NPLA1 的核心语言(@9) 一般机制。 |
4965 | 5217 | 其中,主要操作的实现在命名空间 A1::Forms 中提供。 |
4966 | -作为对象语言中可用的语言特性(@2.1.2) ,一部分功能是基本的(primitive) 。其余功能是派生的(derived) ,可在对象语言源代码的形式通过组合这些已有的其它特性实现。 | |
5218 | +作为对象语言中可用的语言特性(@2.1.2) ,一部分功能是基本的(@1.5.5.1) 。其余功能是派生的(@1.5.5.1) ,可在对象语言源代码的形式通过组合这些已有的其它特性实现。 | |
4967 | 5219 | 功能以操作(@4.4.5.1) 提供,可作为对象语言中的合并子类型的被绑定对象(@7.6.1.1) 的本机实现(@5.3) 。 |
4968 | 5220 | 派生操作的功能对 NPLA1 实现并非严格地必要,但适合使用宿主语言实现,被 NPLA1 代码间接地调用。 |
4969 | 5221 | 因性能等原因,一些基本派生操作在此直接提供 API 。 |
@@ -5112,12 +5364,12 @@ | ||
5112 | 5364 | 过程使用 A1::FormContextHandler(@7.6) 实现。 |
5113 | 5365 | 后者包含的实际求值使用使用 A1::ReduceArguments(@7.4.6) 实现。 |
5114 | 5366 | **注释** 当前实现中使用从左到右的子项规约,和 [Shu10] 实际使用的不同。 |
5115 | -Forms 提供以下函数支持 lambda 特殊形式创建以过程为基础的 λ 抽象(@4.5.2.2) : | |
5367 | +Forms 提供以下函数支持 lambda 特殊形式创建以过程为基础的 λ 抽象(@4.5.2.4) : | |
5116 | 5368 | Forms::Lambda |
5117 | 5369 | Forms::LambdaRef |
5118 | 5370 | Forms::LambdaWithEnvironment |
5119 | 5371 | Forms::LambdaWithEnvironmentRef |
5120 | -Forms 提供以下函数支持更一般的 vau 抽象(@4.5.2.3) 。 | |
5372 | +Forms 提供以下函数支持更一般的 vau 抽象(@4.5.2.5) 。 | |
5121 | 5373 | Forms::Vau |
5122 | 5374 | Forms::VarRef |
5123 | 5375 | Forms::VauWithEnvironment |
@@ -5127,14 +5379,14 @@ | ||
5127 | 5379 | Vau 抽象的应用在的新创建的空的但以上述静态环境为父环境(@5.7) 的局部环境(local environment) 中对抽象中指定的表达式(函数体)的副本求值,以包含局部环境(@4.6.1.2) 的数据结构作为活动记录帧(@5.6) 。 |
5128 | 5380 | 记号值(@6.8.1) 可表示待替换的符号(@6.8.1) ,用于实现形式参数(@8.4.5) 。 |
5129 | 5381 | λ 抽象可通过 vau 抽象实现,但由于简单和性能原因,直接提供 API 支持。 |
5130 | -创建过程抽象的调用时递归检查形式参数树的叶节点是否为符号。当前递归检查符号非嵌套调用安全(参见 @6.1.3 )。 | |
5382 | +创建过程抽象的调用时递归检查形式参数树的叶节点是否为符号。当前递归检查符号非嵌套调用安全(@6.1.3) 。 | |
5131 | 5383 | |
5132 | 5384 | @8.4.5.1 捕获: |
5133 | 5385 | 环境中的变量被隐含地捕获。 |
5134 | 5386 | 捕获通过调用时设置在初始化保存的父环境指针实现。 |
5135 | 5387 | 因为实际指称的确定发生应用中对表达式副本的求值时的名称查找(@4.3.3) ,所以这是延迟到应用的按名称捕获(capture by name) 。这也在引入抽象时实现递归指称。 |
5136 | 5388 | 由于使用 NPL::EnvironmentReference 值保证有效性,无效的父环境访问会出错,但行为仍可预测;详见 @8.4.5.2 。 |
5137 | -被 A1::EvaluateIdentifier(@7.6.4) 求值的变量按引用捕获。 | |
5389 | +被 A1::EvaluateIdentifier(@7.6.4) 求值的变量按引用捕获(@4.5.2) 。 | |
5138 | 5390 | 若修改被捕获的变量对应的对象,可引起调用过程的可观察行为(@4.1.3) 的改变。 |
5139 | 5391 | |
5140 | 5392 | @8.4.5.2 内部数据所有权: |
@@ -5151,7 +5403,7 @@ | ||
5151 | 5403 | 形式参数树(@7.7.3) 作为数据成员同 vau 抽象对象被项或环境所有,可能被不同值类别的表达式标识,但它作为表达式和子表达式时不被直接求值,且子表达式中的符号被复制而不是以间接值引用(@6.4.6.2) ,因此是纯右值(@6.4.5) 。 |
5152 | 5404 | |
5153 | 5405 | @8.4.5.3 参数绑定: |
5154 | -通过 vau 抽象引入的函数调用(@4.5.3.1) 在求值时,以 TermNode 的项而不是 ValueObject 的值对象替换上下文中的形式参数(@4.5.2) 树,用于在调用时被操作数树(@7.7.4) 作为实际参数替换。 | |
5406 | +通过 vau 抽象引入的函数调用(@4.5.3.1) 在求值时,以 TermNode 的项而不是 ValueObject 的值对象替换上下文中的形式参数(@4.5.2.1) 树,用于在调用时被操作数树(@7.7.4) 作为实际参数替换。 | |
5155 | 5407 | 绑定参数通过调用 A1::BindParameter(@7.7.4) 实现,其中对参数和操作数进行匹配。 |
5156 | 5408 | 按值传递时需使用效果等价返回值转换(@6.4.6.4) 的方式确保在被保存(@8.4.5.2) 前不再引用其它项对象而维护内存安全。 |
5157 | 5409 | 注意提升项不是递归的,按值传递的操作数的项的只提升直接子项(同 @6.9.5.3 )。 |
@@ -5236,11 +5488,18 @@ | ||
5236 | 5488 | Forms::Provide |
5237 | 5489 | Forms::Import |
5238 | 5490 | Forms::ImportRef |
5491 | +Forms::Assq | |
5492 | +Forms::Assv | |
5239 | 5493 | **注释** |
5240 | 5494 | 部分实现在使用非一等对象的内部列表表示。 |
5241 | 5495 | Forms::FoldR1 和 Forms::Map1 当前可使用不在对象语言中的右值的表示中支持的标签(@6.2.2.1) 。 |
5242 | 5496 | |
5243 | -@8.4.10 外部调用: | |
5497 | +@8.4.10 一等续延支持: | |
5498 | +Forms 提供以下在对象语言中实现一等续延(@4.5.3.3) 相关操作: | |
5499 | +Forms::Call1CC | |
5500 | +Forms::ContinuationToApplicative | |
5501 | + | |
5502 | +@8.4.11 外部调用: | |
5244 | 5503 | Forms 提供以下在对象语言中调用实现环境外部功能的函数: |
5245 | 5504 | Forms::CallSystem 实现到 YFramework 提供的 usystem 函数的调用转发,用于调用外部命令。 |
5246 | 5505 |
@@ -5267,12 +5526,13 @@ | ||
5267 | 5526 | 模块 NPL::Dependency 还提供其它一些接口,调用函数 Forms::LoadGroundContext 初始化上下文后继续调用,初始化特定操作的环境: |
5268 | 5527 | 函数模板 Forms::GetModuleFor 加载代码作为模块(@1.5.6.4) 以提供特定集合的对象语言实体的定义。 |
5269 | 5528 | 函数模板 Forms::LoadModule 和 Forms::LoadModuleChecked 加载模块为变量。后者在指定名称的绑定已存在时抛出异常。 |
5270 | -函数 Forms::LoadModule_std_promises 提供延迟求值等操作(@12.1) ; | |
5271 | -函数 Forms::LoadModule_std_math 提供数学功能相关操作(@12.2) ; | |
5272 | -函数 Forms::LoadModule_std_strings 提供字符串操作(@12.3) ; | |
5273 | -函数 Forms::LoadModule_std_io 提供输入/输出操作(@12.4) ; | |
5274 | -函数 Forms::LoadModule_std_modules 提供模块管理操作(@12.6) ; | |
5275 | -函数 Forms::LoadModule_std_system 提供系统操作(@12.5) ; | |
5529 | +函数 Forms::LoadModule_std_continuations 提供一等续延(@4.5.3.3) 相关操作(@12.1) ; | |
5530 | +函数 Forms::LoadModule_std_promises 提供延迟求值等操作(@12.2) ; | |
5531 | +函数 Forms::LoadModule_std_math 提供数学功能相关操作(@12.3) ; | |
5532 | +函数 Forms::LoadModule_std_strings 提供字符串操作(@12.4) ; | |
5533 | +函数 Forms::LoadModule_std_io 提供输入/输出操作(@12.5) ; | |
5534 | +函数 Forms::LoadModule_std_modules 提供模块管理操作(@12.7) ; | |
5535 | +函数 Forms::LoadModule_std_system 提供系统操作(@12.6) ; | |
5276 | 5536 | 函数 Forms::LoadModule_SHBuild 提供其它一些供 SHBuild 间接调用的操作(@13.1.1) 。 |
5277 | 5537 | 函数 Forms::LoadStandardContext 调用 Forms::LoadGroundContext 并加载基础上下文中提供的库模块(@10.2) ;另见 NPLA1 参考实现扩展环境(@12) 。 |
5278 | 5538 |
@@ -5280,9 +5540,10 @@ | ||
5280 | 5540 | 本节指定当前实现的一些非公开接口性质。 |
5281 | 5541 | |
5282 | 5542 | @8.6.1 NPLA1 实现模块的续延名称: |
5283 | -在模块 NPL::NPLA1Forms 中引入的续延名称(@7.11.6) 包括: | |
5543 | +在模块 NPL::NPLA1Forms 中引入的续延名称(@7.11.7) 包括: | |
5284 | 5544 | as-environment-return |
5285 | 5545 | bindings-to-env |
5546 | +captured-one-shot-continuation | |
5286 | 5547 | combine-let-return |
5287 | 5548 | conditional-eval-sequence |
5288 | 5549 | equal-subterm |
@@ -5320,13 +5581,14 @@ | ||
5320 | 5581 | 在模块 NPL::Dependency 中,若特定对象语言操作使用非本机实现(@5.3) ,可使用不同的续延名称。 |
5321 | 5582 | |
5322 | 5583 | @8.6.2 和 klisp 续延名称的关系: |
5323 | -NPLA1 续延名称(@7.11.6) 和上述续延名称(@8.6.1) 中,部分名称和 klisp 0.3 中作用近似的续延的名称一致,功能近似。 | |
5584 | +NPLA1 续延名称(@7.11.7) 和上述续延名称(@8.6.1) 中,部分名称和 klisp 0.3 中作用近似的续延的名称一致,功能近似。 | |
5324 | 5585 | 一致但存在附加不同用法的名称包括: |
5325 | 5586 | eval-let :同时用于 Forms::Provide 和 Forms::ProvideLet(@8.4.9) 。 |
5326 | 5587 | set-eval-obj :同时用于 Forms::SetWithNoRecursion 和 Forms::SetWithRecursion(@8.4.4.3) 。 |
5327 | 5588 | import-bindings :同时用于 Forms::Import 和 Forms::ImportRef(@8.4.9) ,但不用于 Forms::Provide 。 |
5328 | 5589 | 不一致的名称包括: |
5329 | 5590 | as-environment-return :Forms::AsEnvironment(@8.4.9) 返回环境。 |
5591 | +captured-one-shot-continuation :通过 Forms::Call1CC(@8.4.10) 捕获的一次调用续延(@4.5.3.3) 。 | |
5330 | 5592 | combine-let-return :非 TCO 的 Forms::Let 、Forms::LetRef 、Forms::LetWithEnvironment 、Forms::LetWithEnvironmentRef 、Forms::LetAsterisk 、Forms::LetAsteriskRef 、Forms::LetRec 、Forms::LetRecRef 和 Forms::AsEnvironment(@8.4.9) 的实现中设置规约合并最后的返回结果。 |
5331 | 5593 | combine-return :非 TCO 的规约合并求值(@7.6.4.2) 实现中设置规约合并最后的返回结果。 |
5332 | 5594 | equal-subterm :Forms::EqualTermValue(@8.4.1) 比较直接子项。 |
@@ -5342,7 +5604,7 @@ | ||
5342 | 5604 | eval-foldr1-kons :Forms::FoldR1(@8.4.9) 应用子调用。 |
5343 | 5605 | eval-guard :A1::MakeKeptGuard(@7.7.6) 引入的守卫操作,用于: |
5344 | 5606 | 非 TCO 的求值实现重置守卫的附加动作帧(@6.12.6) ; |
5345 | -异步规约(@7.9.2) 的 LoadModule_std_io(@8.5.2) 实现 get-module(@12.4) 和 LoadModule_std_module(@8.5.2) 实现 require(@12.5) 加载翻译单元时以守卫保护当前环境。 | |
5607 | +异步规约(@7.9.2) 的 LoadModule_std_io(@8.5.2) 实现 get-module(@12.5) 和 LoadModule_std_module(@8.5.2) 实现 require(@12.6) 加载翻译单元时以守卫保护当前环境。 | |
5346 | 5608 | eval-lazy-parent :惰性求值指定父环境求值。 |
5347 | 5609 | eval-let-parent :Forms::LetWithEnvironment 和 Forms::LetWithEnvironmentRef 中求值过程抽象(@8.4.5) 的父环境的表达式。 |
5348 | 5610 | eval-letrec-bind :Forms::LetRec 和 Forms::LetRecRef 中的变量绑定。 |
@@ -5361,11 +5623,15 @@ | ||
5361 | 5623 | klisp 的 equal? 直接使用自动机实现,在比较操作中没有对应的续延。NPLA1 支持互操作(@5.3) 允许相等的子对象以异步规约实现,不能使用这种方式而排除续延。 |
5362 | 5624 | klisp 的 $vau 不具有应保证被求值的实际参数,不需要异步调用,没有 eval-vau-parent 对应的续延。 |
5363 | 5625 | import-bindings 和 Forms::Provide 使用的 provide-let-return 共用部分本机实现。 |
5626 | +klisp 的 call/cc 直接捕获当前续延而不进行其它操作。控制作用(@4.1) 对续延的调用在本机函数 kcall_cont 实现,并被 apply-continuation 的本机实现调用,其中可调用本机函数 do_interception ,但没有指派具体的续延名称。 | |
5627 | +和 klisp 不同,Forms::Call1CC 需检查一次续延调用的约束,在不修改宿主类型 A1::Continuation(@7.4.2) 时,不能单独把控制作用的实现放在调用处,而是和 Scheme 这样直接把续延具现为过程的方式,在内部的 A1::ContextHandler(@7.4.2) 中指定其实现。 | |
5628 | +这样的实现方式在捕获时单独指定不同的特性(包括续延名称)而不是在其它位置(如应用或扩展续延)时指定信息,同时不需要修改 A1::Continuation 的实现。这些信息原则上是可选的,减少 A1::Continuation 的实现意味着减小实现开销(但 TCO 实现当前仍然无法避免类似的开销)。 | |
5364 | 5629 | |
5365 | 5630 | @9 NPLA1 核心语言设计: |
5366 | 5631 | NPL 是独立设计的,但其派生语言和其它一些语言有类似之处;这些语言和 NPL 方言之间并不具有派生关系。但为简化描述,部分地引用这些现有语言规范(@1.2.1.2) 中的描述,仅强调其中的不同。 |
5367 | 5632 | NPLA1 符合 NPL 和 NPLA 的语言规则,其实现环境(@1.2.4) 还应提供本章起的其它程序接口。 |
5368 | -NPLA1 和 Kernel 语言(即 [RnRK] )的特性设计(如 vau(@4.5.2.3) 和作为一等对象(@4.1) 的环境表达过程抽象(@8.4.5) )有很多类似之处,因此许多概念是通用的;但从设计哲学(@1.5) 到本章介绍的各个细节(如默认求值规则(@9.7.1) )都存在深刻的差异。 | |
5633 | +NPLA1 支持和宿主语言的互操作,一些接口处理的值可约定宿主类型(@5.5) 。但这些类型不一定在对象语言层次上稳定,可能在之后的版本变化。稳定性由具体实现提供的附加规则(若存在)保证。 | |
5634 | +NPLA1 和 Kernel 语言(即 [RnRK] )的特性设计(如 vau(@4.5.2.5) 和作为一等对象(@4.1) 的环境表达过程抽象(@8.4.5) )有很多类似之处,因此许多概念是通用的;但从设计哲学(@1.5) 到本章介绍的各个细节(如默认求值规则(@9.7.1) )都存在深刻的差异。 | |
5369 | 5635 | 部分名称指定的操作和 [RnRS] 或 klisp (http://klisp.org/docs/index.html) 指定的类似。 |
5370 | 5636 | 以下章节主要介绍和 Kernel 约定不同的设计。各节的通用约定不再在之后的各个接口单独说明。 |
5371 | 5637 |
@@ -5478,10 +5744,11 @@ | ||
5478 | 5744 | 求值得到的操作数的文法约定如下: |
5479 | 5745 | <object> :一般对象,包括引用对象的引用值(@9.9.1) 。 |
5480 | 5746 | <reference> :对象引用值。 |
5481 | -<list> :列表。参见 @9.9.4 。 | |
5747 | +<list> :列表。 | |
5748 | +**注释** 关于对列表类型的值的具体要求,参见列表(@9.9.4) 。 | |
5482 | 5749 | <lists> :元素都是列表的列表。 |
5483 | 5750 | <boolean> :布尔值(@4.1),即取值为 #t 或 #f 的集合,是 NPLA 类型映射(@5.5) 指定的用于条件判断的单一值的类型。 |
5484 | -推论:<boolean> 对应的宿主类型(@5.5) 是 bool 。 | |
5751 | +推论:<boolean> 对应的宿主类型是 bool 。 | |
5485 | 5752 | <test> :类似 <object> ,通常预期为 <boolean> ,作为条件。 |
5486 | 5753 | 和 Scheme 类似但和 Kenerl 不同,非 #t 的值在决定分支时视同 #f ,以允许在 <boolean> 外自然扩展的逻辑代数操作。 |
5487 | 5754 | 和 Common Lisp 不同,不使用空列表(或符号 nil )代替 #f ,以避免需要特设的规则以特定的其它类型的值(如 Common Lisp 的符号 t )表示逻辑真(这在逻辑非操作中不可避免)。 |
@@ -5496,7 +5763,7 @@ | ||
5496 | 5763 | 此外,支持的数值操作数参见 NPLA 数值类型(@6.14.1) 。 |
5497 | 5764 | **原理** |
5498 | 5765 | <object> 等求值得到的操作数不保证是语法意义上连续的词法组合,不能由多个表达式构成,因此即便出现在元素末尾,也不能如 <body> 一样减少括号。 |
5499 | -<object> 表示类型全集(@4.7.5) ,其元素可被断言在论域(@1.5.3.6) 内,即任何其它类型都是 <object> 的子类型(@9.5.4.1) 。 | |
5766 | +<object> 表示类型全集(@4.7.5) ,其元素可被断言在论域(@1.5.3.6) 内,即任何其它类型都是 <object> 的子类型(@4.7.7) 。类型检查(@9.5.4.1) 可对此进行验证。 | |
5500 | 5767 | 和 [RnRK] 的理由不同,允许布尔代数以外扩展的比较判断在此不认为是易错的,而是有意的设计(by design) 。这避免预设地假定类型的名义语用作用(“角色(role)” ),也避免限制语言和派生语言的类型全集(@4.7.5) 的设计。 |
5501 | 5768 | |
5502 | 5769 | @9.2.2.4 文法形式补充约定: |
@@ -5558,7 +5825,7 @@ | ||
5558 | 5825 | 另见基本对象(@11.3.1) 。 |
5559 | 5826 | #ignore :类似 Kernel 的 #ignore 字面量。 |
5560 | 5827 | **原理** |
5561 | -从表达上,#inert 和 #ignore 仍都可以被视为特定单元类型的值:等价的类型判断谓词可以直接使用值的相等关系确定。 | |
5828 | +从表达上,#inert 和 #ignore 仍都可以被视为特定单元类型(@4.7) 的值:等价的类型判断谓词可以直接使用值的相等关系确定。 | |
5562 | 5829 | #inert 和 #ignore 的值是 ValueToken 宿主类型唯一直接在对象语言中提供的宿主值。 |
5563 | 5830 | 和 [RnRS] 及 klisp 不同,不需要因兼容性支持扩展字面量中不同的大小写变体,特别是 [R6RS] 的 #T 和 #F 。 |
5564 | 5831 | **注释** |
@@ -5814,24 +6081,29 @@ | ||
5814 | 6081 | 当前实现一般支持使用自由存储分配,以尽可能地避免宿主语言的未定义行为(@5.4) : |
5815 | 6082 | 其中,异步规约(@7.9) 实现使分配以环境(@6.11.1) 表示的活动记录失败时满足常规宿主资源分配要求(@5.4.1) 。 |
5816 | 6083 | 另见语法分析(@6.1.6) 和绑定支持 API(@7.7.4) 的类似的资源分配失败的约定。 |
5817 | -具体实现方式参见一般实现策略(@7.10.1) 和部分实现限制(@7.11.5) 。 | |
6084 | +具体实现方式参见一般实现策略(@7.10.1) 和部分实现限制(@7.11.6) 。 | |
5818 | 6085 | 当前实现的规约(如 A1::Reduce(@7.4.4) )直接递归规约子表达式支持的尾上下文(@4.4.8) 较少(详见 @9.7.4.1) ,和 Scheme 对大量特定的形式提供明确的尾上下文上的 PTC 保证不同。 |
5819 | 6086 | 和 Kernel 类似,这是由于缺少针对特定的特殊形式(@5.2) 的特设规则。 |
5820 | -因为程序实现(@9.1) 等限制,当前设计和实现支持的合并子调用以外的上下文(@9.7.4.3) 较 Kernel 也更少,但实际涵盖范围却可能更大(@7.11.5) 。 | |
6087 | +因为程序实现(@9.1) 等限制,当前设计和实现支持的合并子调用以外的上下文(@9.7.4.3) 较 Kernel 也更少,但实际涵盖范围却可能更大(@7.11.6) 。 | |
5821 | 6088 | |
5822 | 6089 | @9.7.4.1 支持 PTC 的尾调用尾上下文: |
5823 | 6090 | 当允许忽略非平凡析构(@7.10.4) 时,可以在尾上下文求值中进行 TCO 并支持 PTC 。 |
6091 | +**注释** TCO 实现依赖 NPL_Impl_NPLA1_Enable_TCO(@7.10) 。 | |
5824 | 6092 | 注意尾上下文中被求值的子表达式中的对象生存期可被调整(@5.10) ,是否调整和具体调整的方式未指定,不同的实现可能不同。 |
5825 | 6093 | 提供 PTC 保证的尾上下文和 Kernel 类似,当前仅包括 PTC 不改变可观察行为(@4.1.3) 时的: |
5826 | -函数调用(@4.5.3.1) 中,(使用 $vau 和 $lambda 等合并子抽象引入的)合并子(@9.9.5) 的调用(注意不包括其它途径引入的外部函数的调用); | |
5827 | -对 <body>(@9.2.2.1) 的求值; | |
5828 | -对 <expression-sequence>(@9.2.2.1) 中最后一个子表达式的求值; | |
5829 | -由 <test>(@9.2.2.1) 控制或操作子中连续求值多个表达式中最后被求值的子表达式求值。 | |
5830 | -**注释** 这包括 $if(@11.3.3) 、$sequence(@11.4.1) 、$and(@11.4.1) 和 $or(@11.4.1) 的最后的参数(若存在)的求值。应用子对参数的求值明确非 PTC 。 | |
5831 | -函数 eval(@11.3.7) 、eval%(@11.3.7) 、$quote(@11.4.1) 、id(@11.4.1) 、idv(@11.4.1) 、eval-string(@12.1) 和 eval-string%(@12.1) 的调用中蕴含的求值或替换; | |
5832 | -**注释** 这不是作为参数的 <expression> 的求值。 | |
5833 | -函数 apply(@11.4.1) 对 <applicative> 和 <object> 在 <environment> 或默认的动态环境中的应用; | |
5834 | -函数 accl(@11.4.1) 、map-reverse(@11.4.3) 和 for-each-ltr(@11.4.3) 蕴含的尾上下文的递归调用。 | |
6094 | + **注释** 应用子对参数的求值明确非 PTC 。 | |
6095 | + 函数调用(@4.5.3.1) 中,以对象语言中的合并子抽象引入的合并子(@9.9.5) 的调用。 | |
6096 | + **注释** 合并子抽象包括 $vau(@11.4.1) 和 $lambda(@11.4.1) 等。 | |
6097 | + **注释** 不保证互操作(@5.3) 引入的外部函数的调用。 | |
6098 | + 对 <body>(@9.2.2.1) 的求值。 | |
6099 | + 对 <expression-sequence>(@9.2.2.1) 中最后一个子表达式(若存在)的求值。 | |
6100 | + 由 <test>(@9.2.2.1) 控制或 <test>... 中最后一个被求值的子表达式(若存在)求值。 | |
6101 | + **注释** 这包括 $if(@11.3.3) 、$and(@11.4.1) 和 $or(@11.4.1) 的最后的参数(若存在)的求值。 | |
6102 | + 函数 eval(@11.3.7) 、eval%(@11.3.7) 、$quote(@11.4.1) 、id(@11.4.1) 、idv(@11.4.1) 、eval-string(@12.2) 和 eval-string%(@12.2) 的调用中蕴含的求值或替换。 | |
6103 | + **注释** 这不是作为参数的 <expression> 的求值。 | |
6104 | + 函数 apply(@11.4.1) 对 <applicative> 和 <object> 在 <environment> 或默认的动态环境中的应用(@4.5.3) 。 | |
6105 | + 函数 accl(@11.4.1) 、map-reverse(@11.4.3) 和 for-each-ltr(@11.4.3) 蕴含的在尾上下文中的递归调用。 | |
6106 | + 其它具体操作定义的求值。 | |
5835 | 6107 | |
5836 | 6108 | @9.7.4.2 支持 PTC 的非调用尾上下文: |
5837 | 6109 | 通过环境(@9.9.3) 支持的名称解析(@6.11.1) 的实现在符合嵌套调用安全的约定(@6.1.3) 。 |
@@ -5905,6 +6177,7 @@ | ||
5905 | 6177 | 除非另行指定,NPLA1 不限制任意对象不可修改。 |
5906 | 6178 | 等价关系和限制不可修改性的方法的方式不唯一,因此不可修改性也不唯一。 |
5907 | 6179 | 因为外部表示不唯一(@9.6) ,不需要基于此定义一种正规的关于外部表示的等价判断形式。 |
6180 | +**原理** | |
5908 | 6181 | 开放类型映射(@5.5) 不保证非特定对象之间的不可修改性具有唯一的定义。 |
5909 | 6182 | **注释** |
5910 | 6183 | 对象的子对象作为可变管理状态,使不可变对象具有允许这些状态改变的内部可变性(@4.1.4.2) 。 |
@@ -5943,7 +6216,7 @@ | ||
5943 | 6216 | |
5944 | 6217 | @9.8.5 等价比较: |
5945 | 6218 | 和 Kernel 不同,一些类型的对象若可修改(@9.8.3)(如环境(@9.9.3) ),不保证 eq? 和其它等价谓词(@11.3.2) 的比较结果等价。 |
5946 | -但是,基于 @6.11.1.1 已约定宿主值的类型,在对象语言中传递环境引用(@6.11.1) 不改变环境的这些等价性。 | |
6219 | +但是,基于环境类(@6.11.1.1) 已约定宿主值的类型,在对象语言中传递环境引用(@6.11.1) 不改变环境的这些等价性。 | |
5947 | 6220 | 为了满足不依赖引用的一等对象(@4.1) ,区分引用和被引用的对象都作为一等实体,这是必要的。 |
5948 | 6221 | 一般地,谓词 eq? 用于判断两个参数对象的同一性(@4.1) 。 |
5949 | 6222 |
@@ -6025,7 +6298,9 @@ | ||
6025 | 6298 | @9.9.3.3 环境的宿主值和所有权: |
6026 | 6299 | 和 Kernel 不同,环境可使用环境强引用和环境弱引用(@6.11.1) 蕴含不同的所有权。 |
6027 | 6300 | 环境对应多个宿主值类型(@6.11.1.3) 。 |
6028 | -除非另行指定,使用不同宿主值类型的环境引用被视为相同类型的一等环境。(另见开放类型映射(@5.5) 和相关的其它数据结构(@6.11.1) 。) | |
6301 | +除非另行指定,使用不同宿主值类型的环境引用被视为相同类型的一等环境。 | |
6302 | +**注释** | |
6303 | +另见开放类型映射(@5.5) 和相关的其它数据结构(@6.11.1) 。 | |
6029 | 6304 | |
6030 | 6305 | @9.9.3.4 环境的稳定性: |
6031 | 6306 | 和 Kernel 不同,环境对象符合默认的等价比较规则(@9.8.5) ,不提供不同等价谓词的结果一致性。 |
@@ -6165,17 +6440,17 @@ | ||
6165 | 6440 | @9.10 实现注记: |
6166 | 6441 | NPLA1 中提供的一些名称以及实现架构和 klisp 相似,且都为 AST 解释器,但实现方式略不同。 |
6167 | 6442 | 作为比较实现的备注,以下是 klisp 中(除当前不支持的 [RnRK] 特性外)相较 NPLA1 的一些主要实现差异: |
6168 | -使用跟踪 GC ,被 GC 管理的对象都支持跟踪回收,不特别处理 TCO(另见 TCO 实现(@7.11.5) ); | |
6443 | +使用跟踪 GC ,被 GC 管理的对象都支持跟踪回收,不特别处理 TCO(另见 TCO 实现(@7.11.6) ); | |
6169 | 6444 | 使用紧凑的(类似 lua )的对象布局,依赖体系结构的数据模型假设(只实现了 32 位); |
6170 | 6445 | 使用 C 函数且支持 FFI ; |
6171 | 6446 | 使用 setjmp/longjmp 恢复跳板中的上下文; |
6172 | 6447 | 使用 C 函数调用产生对象语言错误,不直接使用和支持本机的其它机制(如 setjmp/longjmp 和 C++ 异常)的交互; |
6173 | 6448 | 在全局上下文中支持名称表; |
6174 | 6449 | 在上下文中支持当前续延(和 NPLA 当前动作对应的数据结构被拆分为 next_function 等 C 数据结构); |
6175 | -分离续延应用和合并子调用操作(而非组合到当前动作的调用中)。 | |
6450 | +分离实现内部的当前续延的应用(kapply_cc ,非对象语言的 apply-continuation )和合并子调用操作(而非组合到当前动作的调用中)。 | |
6176 | 6451 | 此外 klisp 还有一些在 NPLA1 参考实现中不存在的实现缺陷: |
6177 | 6452 | 不支持多重包装调用(@7.11.4) ; |
6178 | -特定上下文的 PTC 失败(@7.11.5) 。 | |
6453 | +特定上下文的 PTC 失败(@7.11.6) 。 | |
6179 | 6454 | |
6180 | 6455 | @10 NPLA1 参考实现环境: |
6181 | 6456 | NPLA1 提供参考实现环境。其实现可在内部使用 NPLA1 库特性(@9.1) ,提供给 NPLA1 用户程序(@9.1) 。 |
@@ -6204,7 +6479,7 @@ | ||
6204 | 6479 | 特性设计注记: |
6205 | 6480 | 为避免依赖逻辑上复杂的形式,一些特性当前在当前设计中排除。 |
6206 | 6481 | 例如,依赖一阶算术的操作、其硬件加速形式的 ISA 表示的整数操作及依赖这些操作实现的数值塔(numerical tower) 被整体忽略。 |
6207 | - 上述忽略的特性可由派生实现补充(如以类似 @11.4 的形式),在派生根环境后按需进行 AOT(ahead-of-time) 优化(如 Kernel 的 $let-safe! 中包含的内容,其中引用基础环境的符号不再可变),然后组成类似基础环境。 | |
6482 | + 上述忽略的特性可由派生实现补充(如以类似特性描述(@11.4) 的形式),在派生根环境后按需进行 AOT(ahead-of-time) 优化(如 Kernel 的 $let-safe! 中包含的内容,其中引用基础环境的符号不再可变),然后组成类似基础环境。 | |
6208 | 6483 | 通过派生实现定义的方式一般依赖本机实现。 |
6209 | 6484 | |
6210 | 6485 | @10.1.1 初始化: |
@@ -6314,7 +6589,7 @@ | ||
6314 | 6589 | 引入函数时需注意避免未绑定对象(@9.7.4.3) 等关于环境生存期的问题,以维护内存安全(@9.4) ;另见绑定操作(@7.7.3) 。 |
6315 | 6590 | |
6316 | 6591 | @10.4.1 作为实际参数: |
6317 | -除非另行指定(如 @10.5.2 和 @11.2.2 ),一般地,函数接受左值引用操作数,使用引用的对象的值和直接使用右值作用相同,但不会修改被左值引用的对象。 | |
6592 | +除非另行指定(如函数参数转发(@10.5.2) 和 @11.2.2 ),一般地,函数接受左值引用操作数,使用引用的对象的值和直接使用右值作用相同,但不会修改被左值引用的对象。 | |
6318 | 6593 | 此处的左值引用和宿主语言中的( const 非 volatile )左值作用类似。 |
6319 | 6594 | 这等价隐含无副作用的左值到右值转换(@5.8.4) 。 |
6320 | 6595 | **注释** |
@@ -6366,11 +6641,12 @@ | ||
6366 | 6641 | 一些其它保留引用值的操作中,引用值来自参数,且难以通过自身的逻辑单独决定可否安全地直接返回引用值。 |
6367 | 6642 | 此时,在返回之前根据特定参数是否为引用值,可选地转换结果以确定是否保留引用值,即进行转发。 |
6368 | 6643 | 特定的显式转发操作转发临时对象(@5.8.5) 的引用值(@6.3.4) 使临时对象被转移,以转发的值作为返回值,可不同于使用返回值转换(@6.4.6.4) : |
6369 | -同返回值转换,转发转移右值,复制左值;但当转发临时对象可确定唯一使用时,也转移临时对象(实现参见 @5.8.3.6 )。 | |
6644 | +同返回值转换,转发转移右值,复制左值;但当转发临时对象可确定唯一使用时,也转移临时对象。 | |
6370 | 6645 | 确定是否保留引用值的机制类似 ISO C++14 中从没有括号的 id-expression 上推断返回 decltype(auto) 类型是否为引用类型。 |
6371 | 6646 | 函数值转发使某些操作在默认情况下满足间接值生存期规则而保持内存安全,符合适用性原则(@1.5.5.2) 。 |
6372 | 6647 | 函数值转发的实现可通过判断是否需要转发引用而按需决定返回引用值或非引用值(@10.4.2) ,或使用标准库的相关函数(@11.2.2.4) 。前者支持本机实现。 |
6373 | 6648 | **注释** |
6649 | +另见对象的可转移条件(@5.8.3.6) 。 | |
6374 | 6650 | 显式转发操作把右值、消亡值和带有临时对象标签(@6.2.2) 的左值引用视为被转发的目标。 |
6375 | 6651 | 转发列表对象的子对象可能转移子对象。 |
6376 | 6652 |
@@ -6378,7 +6654,7 @@ | ||
6378 | 6654 | 构造器(constructor) 是用于创建对象的函数。 |
6379 | 6655 | 除非显式指定创建的对象具有引用值类型,构造器是典型的(typical) ,返回非引用值(@10.5.3) 。 |
6380 | 6656 | 部分操作涉及对其它对象具有所有权的对象。 |
6381 | -一部分对象的构造器(@10.5.3) 创建的对象完全通过其它对象的引用或对象的值作为构造器的参数而决定,且创建的对象对这些参数具有所有权,这样的对象称为容器(container) 。 | |
6657 | +一部分对象的构造器创建的对象完全通过其它对象的引用或对象的值作为构造器的参数而决定,且创建的对象对这些参数具有所有权,这样的对象称为容器(container) 。 | |
6382 | 6658 | 容器构造器的参数作为容器的子对象(@9.8.2) ,是容器的元素。 |
6383 | 6659 | **注释** 这扩展了列表的元素(@9.9.4) 的概念。 |
6384 | 6660 | 一些不是容器的对象(如真合并子(@9.9.5) )可通过非容器形式的构造器创建。 |
@@ -6386,7 +6662,7 @@ | ||
6386 | 6662 | 标准库提供一些属于构造器和访问器的操作。除非另行指定,标准库的访问器符合子对象访问的约定(@9.8.2.1) 。 |
6387 | 6663 | |
6388 | 6664 | @10.5.6 转发参数或返回值的实现: |
6389 | -可转发参数或返回值(@10.5) 的函数可包含以下实现方式: | |
6665 | +可转发参数(@10.5.2) 或返回值(@10.5.4) 的函数可包含以下实现方式: | |
6390 | 6666 | 使用特定的操作,以需被转发的表达式作为其操作数; |
6391 | 6667 | (仅对参数转发)使用标记字符 % 的参数绑定(@7.7.3.5) 的变量。 |
6392 | 6668 | 上述特定的操作可在被求值的表达式中构造显式的转发。 |
@@ -6420,11 +6696,10 @@ | ||
6420 | 6696 | 被错误处理和检查的函数不修改参数或者函数调用外创建的对象。 |
6421 | 6697 | |
6422 | 6698 | @10.6.5 非常规函数: |
6423 | -不符合常规函数约定的例外的一个例子是续延(@4.5.2.1) 。 | |
6699 | +不符合常规函数约定的例外的一个例子是续延(@4.5.2.2) 。 | |
6424 | 6700 | 非常规函数归类为对象而非操作(@10.3.1) ,但调用时候错误处理同常规函数(@10.6.4) 。 |
6425 | 6701 | **注释** |
6426 | 6702 | 类似 [RnRK] 而和 [RnRS] 不同,作为一等对象的续延和控制参数是否求值无关,因此不是合并子,且默认求值算法(@9.7.1) 不支持续延作为函数合并被求值;但续延可通过特定的操作转换为应用子。 |
6427 | -(当前未提供作为一等续延的正式支持;续延仅在非一等对象的情形(@4.5.3.3) 调用。) | |
6428 | 6703 | |
6429 | 6704 | @10.7 函数名称约定: |
6430 | 6705 | 除非另行指定,以指定名称的函数表示具有名称的操作时,其命名由本节的规则约定。 |
@@ -6463,7 +6738,7 @@ | ||
6463 | 6738 | 类似 Kernel ,可变管理状态(@9.8.3) 的改变不需要指示可修改;此外,类似地,不改变可观察行为(@4.1.3) 的隐藏状态的修改不属于上述修改。 |
6464 | 6739 | 类似 [RnRS] 和 [RnRK] ,这类操作同时是改变操作(@9.8.3) 。 |
6465 | 6740 | 一些其它情形下,特定的函数不被视为改变操作但其调用仍可能产生副作用,这样的函数名不要求带有 ! 后缀: |
6466 | -和 [RnRK] 类似,直接求值操作数、其子表达式关联的对象可能产生副作用,如 load(@12.4) ; | |
6741 | +和 [RnRK] 类似,直接求值操作数、其子表达式关联的对象可能产生副作用,如 load(@12.5) ; | |
6467 | 6742 | 和 Kernel 不同,一些操作包含使操作数指称或引用的对象的值有效但未指定(@5.6.2) 的修改,如隐式的转移操作(@9.8.3.2) 。 |
6468 | 6743 | 带有 ! 后缀的函数的调用的求值可能因为不同的操作数的影响,不一定总是具有能视为修改的副作用,可能不改变任何对象: |
6469 | 6744 | 当通过操作数指定的条件确定是否修改时; |
@@ -6593,7 +6868,7 @@ | ||
6593 | 6868 | 类似保留引用值的容器构造器,这些操作可在结果中保留参数的引用值(@10.7.3.3) 。 |
6594 | 6869 | |
6595 | 6870 | @10.7.5.3 直接参数转发操作: |
6596 | -部分不带有引用标记字符的直接参数转发操作是可能直接保留引用值的操作(@10.4.3) 。 | |
6871 | +部分不带有引用标记字符的参数转发操作(@10.5.2) 是可能直接保留引用值的操作(@10.4.3) ,称为直接参数转发操作。 | |
6597 | 6872 | 函数名不使用引用标记字符,和可直接保留引用值的函数名(@10.7.4) 使用引用标记字符不一致: |
6598 | 6873 | 本节约定的函数和可直接保留引用值的函数名中带有 % 结尾的函数同属参数转发操作,但后者同时有不带有引用标记字符的变体; |
6599 | 6874 | 本节不约定和可直接保留引用值的函数名中不带有引用标记字符结尾的函数对应的操作。 |
@@ -6603,13 +6878,14 @@ | ||
6603 | 6878 | 这些操作并非用于取子对象(@9.8.2) ,返回值不一定是引用值,和具体操作相关,不适合使用引用标记字符区分; |
6604 | 6879 | 为简化接口及满足其它分类(如不以引用值作为结果的操作(@10.7.5.2) ),不适合提供不保留引用值的操作。 |
6605 | 6880 | 本节约定的函数对引用标记字符的使用和可提供以引用标记字符结尾变体的操作(@10.7.4) 的函数名的使用不一致,含义相当于前者的结尾的 % 。 |
6606 | -以下函数值转发操作(@10.7.5.4) 同时也是直接参数转发操作。 | |
6881 | +以下的函数值转发操作(@10.7.5.4) 同时也是直接参数转发操作。 | |
6607 | 6882 | 其它函数的参数传递的一般规则参见引用值作为实际参数(@10.4.1) 、函数参数和函数值传递约定(@10.5) 和实际参数约定(@10.6.3) 。 |
6608 | 6883 | |
6609 | 6884 | @10.7.5.4 函数值转发操作: |
6610 | 6885 | 若其它情形确需非转发操作取得引用值,可使用带有 % 或 & 结尾的操作(@10.7.4) 及可直接以引用值作为结果的操作(@10.7.5.1) 替代实现。 |
6611 | 6886 | 本节约定的函数不使用引用标记字符,和容器元素访问器(@10.7.4.2) 的函数名不使用引用标记字符一致: |
6612 | 6887 | 本节约定的函数和上述容器元素访问器的函数名中不带有引用标记字符结尾的函数同属函数值转发操作,但后者同时有带有引用标记字符的变体。 |
6888 | +**原理** | |
6613 | 6889 | 和容器构造器(@10.7.4.1) 引入引用值的情形不同,不带有后缀 % 相对不容易引起误用,因为返回值保留的引用可以继续被返回值转换(@10.4.2) 影响。 |
6614 | 6890 | 例如,使用保证返回非引用值的涉及环境中求值的操作(@10.7.4.4) ,引用值会在引用的对象生存期结束前被返回值转换而不影响内存安全。 |
6615 | 6891 |
@@ -6617,6 +6893,15 @@ | ||
6617 | 6893 | 类似可提供以引用标记字符结尾变体的对应操作(@10.7.4.4) ,部分不带有引用标记字符的操作可能间接保留引用值(@10.4.3) 。 |
6618 | 6894 | 这包括由类型为合并子的参数(而非 <body> 或 <expressions> )决定是否保留引用值同时对其它参数进行转发的操作。 |
6619 | 6895 | |
6896 | +@10.7.6 中缀: | |
6897 | +中缀 -> 在函数名中可能出现一次,表示其(移除前缀和后缀之后的函数名中的)左边为源类型名的值到右边为目标类型名的值的转换操作。 | |
6898 | +转换得到的值是纯右值。 | |
6899 | +除非另行指定,若被转换的值的复制可能影响可观察行为,被转换的值初始化返回值(@10.4.2) ,明确转移而不是复制其中的右值。 | |
6900 | +**原理** | |
6901 | +如有可能,被转换的值一般应避免被复制。在接口上要求转发右值避免不必要的复制。 | |
6902 | +**注释** | |
6903 | +一般地,转换操作是源类型的值作为单一实际参数的转换目标类型的构造器(@10.5.5) 。 | |
6904 | + | |
6620 | 6905 | @10.8 不安全操作约定: |
6621 | 6906 | 除非另行指定,执行时蕴含以下操作的操作是不安全操作(@9.4.5) : |
6622 | 6907 | 以下不具有内存安全保证(@9.4) 的操作: |
@@ -6708,17 +6993,30 @@ | ||
6708 | 6993 | 满足运行时错误条件(@9.5.3) 时,按要求诊断之后的操作内部分配的资源的状态; |
6709 | 6994 | 变量的绑定(@4.1) 或其它可选地由派生实现定义的可调试实体的引入是否由本机实现(@5.3) 的机制提供; |
6710 | 6995 | 是否存在不改变操作语义的附加的可被捕获的续延和这些续延的操作数; |
6711 | -操作的实现使用的续延名称(@7.11.6) 。 | |
6996 | +操作的实现使用的续延名称(@7.11.7) 。 | |
6712 | 6997 | 以上未指定行为允许同一操作的接口约定和实现之间及不同实现之间允许存在引起调用操作时的可观察行为不同的次要差异。 |
6713 | 6998 | |
6714 | 6999 | @10.9.3 实体实现(@1.2.1.2) 约定: |
6715 | -因不预期通过派生实现,当前实现中,基本特性总是通过本机实现(@5.3) 提供。(这不被接口设计(@10.3.2) 严格要求。) | |
6716 | -类似 [RnRK] ,派生特性可通过基本特性及派生特性实现。 | |
6717 | -这些名称可能是本机实现(@5.3) ,即直接调用 NPLA 或 NPLA1 API(参见 @7 、@7 和 @8 )绑定定义;也可能是非本机的派生的(derive) 实现,即通过在当前的基础上求值特定的对象语言代码引入(其实现最终依赖本机实现)。 | |
7000 | +在实现意义上,库特性(@9.1) 提供的实体的方式分为两类: | |
7001 | +直接调用 NPLA 或 NPLA1 API(参见 @7 、@7 和 @8 )绑定定义,提供本机实现(@5.3) ; | |
7002 | +通过组合既有对象语言提供的接口实现,即派生(derivation) 。 | |
7003 | +因不预期通过派生实现,一些特性被设计为基本(primitive) 特性,总是通过直接调用本机 API 的本机实现(@5.3) 提供。 | |
7004 | +其它特性都是派生特性,通过派生(derived) 实现提供。 | |
7005 | +典型地,派生实现通常不依赖实现特定的互操作(@1.2.3) 接口的本机实现(@5.3) ,这类派生实现是非本机的(non-native) ;其它的派生是本机的。 | |
7006 | +直接调用本机 API 提供绑定和本机的派生实现都是本机实现。 | |
7007 | +类似 [RnRK] ,派生特性可通过基本特性及其它已提供实现的派生特性实现。 | |
6718 | 7008 | 以下的派生操作(@11.4) 应能以派生的方式提供。派生操作是否以派生的形式提供未指定。 |
6719 | 7009 | 派生的操作不依赖隐式求值风格(@9.7.1.1) ,除非语义明确要求避免操作数的求值(如 $deflazy!(@11.4.1) )且对实现可提供必要的简化,应避免依赖 $quote(@11.4.1) ;$quote 自身通过派生实现引入。 |
6720 | 7010 | 与引入绑定不同,实现内部不要求类似的优先使用,在需要明确实现需要符合的要求或减小实现开销时,可使用提供以引用标记字符结尾变体的操作(@10.7.4) 替代其它操作。 |
6721 | 7011 | 作为扩展,操作的非本机实现也允许假定纯右值(@5.8.1) 的项不包含不被项的结构(@7.1.2) 约定直接使用的标签(@6.2.2) 。 |
7012 | +**注释** | |
7013 | +接口的派生实现可作为派生语言实现提供新的库特性的方式。 | |
7014 | +派生语言也可以提供新的本机实现的库特性。 | |
7015 | +基本特性类似 [RnRK] ,但和 [RnRK] 不同(@9.1) ,也属于库特性。 | |
7016 | +基本特性通过本机实现提供的实现方式不被接口设计(@10.3.2) 严格要求。基于简化设计和平衡功能丰富性的实际考虑,在设计中蕴含的是否预期通过派生实现的决策并不排除理论上可能允许的其它替代方式。 | |
7017 | +本机的派生通常以使用宿主语言的本机 API 和组合现有派生特性的符号,通过本机 API 初始化构造求值绑定的方式引入。 | |
7018 | +非本机的派生实现通常以求值特定的对象语言源代码引入。 | |
7019 | +非本机的实现最终依赖本机实现。 | |
6722 | 7020 | |
6723 | 7021 | @10.9.3.1 函数约定: |
6724 | 7022 | 名称使用 $def 起始的函数用于在当前环境引入绑定。 |
@@ -6742,8 +7040,10 @@ | ||
6742 | 7040 | |
6743 | 7041 | @11 NPLA1 根环境特性(@10.2) : |
6744 | 7042 | 本章指定在根环境(@10.1) 提供的 NPLA1 标准库特性(@10.3.3) 。 |
6745 | -根环境基本特性(@11.3) 提供在基础上下文(@8.5.2) 初始化的根环境基本(primitive) 特性,作为单独的模块。 | |
7043 | +根环境基本特性(@11.3) 提供在基础上下文(@8.5.2) 初始化的根环境基本(@10.9.3) 特性,作为单独的模块。 | |
6746 | 7044 | 基础派生特性(@11.4) 起的各节提供其余要求 NPLA1 实现直接支持的各个模块的操作。 |
7045 | +**注释** | |
7046 | +一些特性可约定处理的值的宿主类型(@9) 。 | |
6747 | 7047 | |
6748 | 7048 | @11.1 不安全操作(@9.4.5) 索引: |
6749 | 7049 | 本节按不安全操作约定(@10.8) 的分类对根环境中的不安全操作进行归类。 |
@@ -6799,7 +7099,8 @@ | ||
6799 | 7099 | 在 NPLA1 参考实现环境提供的函数具体详见根环境基本特性和基础派生特性。 |
6800 | 7100 | 除非另行指定,本节约定的函数属于 NPLA1 参考实现环境。 |
6801 | 7101 | 本节约定的函数提供的部分操作属于转发(@10.5) 。 |
6802 | -转发的实现(@10.5.6) 中,可使用 forward!(@11.4.1) 作为其中的特定的操作。 | |
7102 | +**注释** | |
7103 | +转发参数或返回值的实现(@10.5.6) 中可使用 forward!(@11.4.1) 。 | |
6803 | 7104 | |
6804 | 7105 | @11.2.1 可提供以引用标记字符结尾(@10.7.4) 变体的函数: |
6805 | 7106 | 本节内的以下分类不相交,但部分分类中函数名不带有引用标记字符结尾的操作可能和 @11.2.2 中的操作相交。 |
@@ -6833,7 +7134,7 @@ | ||
6833 | 7134 | rest&(@11.4.1) |
6834 | 7135 | restv(@11.4.1) |
6835 | 7136 | 注意 restv 和 rest% 总是构造列表,并不直接返回子对象(@9.8.2) 的引用(另见引用值构造(@11.2.4) );其它访问器若带有引用标记字符,可直接返回引用值。 |
6836 | -此外,@11.2.2.4 中部分函数也符合容器元素访问器的要求,但当前不提供带有后缀标记字符的变体。这些函数包括: | |
7137 | +此外,标准库中的函数值转发操作(@11.2.2.4) 中部分函数也符合容器元素访问器的要求,但当前不提供带有后缀标记字符的变体。这些函数包括: | |
6837 | 7138 | unwrap(@11.3.8) |
6838 | 7139 | unbox(@11.4.3) |
6839 | 7140 |
@@ -6859,7 +7160,7 @@ | ||
6859 | 7160 | 以上操作中的求值符合词法闭包(@4.6.1.2) 规则,可使用 A1::RelayForEval 或 A1::RelayForCall(@7.7.2) 实现。 |
6860 | 7161 | |
6861 | 7162 | @11.2.2 不提供结尾 % 或 & 对应变体(@10.7.5) 的函数: |
6862 | -通常使用 forward(@11.4.1) 转发实现上述保证(@10.5.2) 。 | |
7163 | +通常使用 forward!(@11.4.1) 转发实现上述保证(@10.5.2) 。 | |
6863 | 7164 | |
6864 | 7165 | @11.2.2.1 可直接以引用值作为结果的操作(@10.7.5.1) : |
6865 | 7166 | 包括: |
@@ -6881,7 +7182,7 @@ | ||
6881 | 7182 | @11.2.2.3 直接参数转发操作(@10.7.5.3) : |
6882 | 7183 | 包括合并子基本操作(@11.3.8) : |
6883 | 7184 | unwrap |
6884 | -包括库函数(@11.4.1) : | |
7185 | +包括核心库函数(@11.4.1) : | |
6885 | 7186 | accl |
6886 | 7187 | accr |
6887 | 7188 | foldr1 |
@@ -6898,6 +7199,7 @@ | ||
6898 | 7199 | unwrap(@11.3.8) |
6899 | 7200 | id(@11.4.1) |
6900 | 7201 | forward(@11.4.1) |
7202 | +forward!(@11.4.1) | |
6901 | 7203 | unbox(@11.4.3) |
6902 | 7204 | 基本操作(@11.3) 的不具有名称的相关操作中参数和函数值原生支持的转发操作包括: |
6903 | 7205 | 使用 make-encapsulation-type(@11.3.10) 返回的访问器合并子。 |
@@ -6933,6 +7235,17 @@ | ||
6933 | 7235 | 特别地,由绑定使用引用标记字符的非递归绑定(@7.7.3.5) 的规则,绑定列表的子对象引用(@9.9.4.1) 不直接共享操作数列表对象,而共享元素是原容器元素的(经折叠的(@7.7.3.4) )引用值的列表。 |
6934 | 7236 | **注释** 这类似宿主语言的容器对象一般不能转换为共享容器部分元素的 C++ 对象引用。 |
6935 | 7237 | |
7238 | +@11.2.5 非引用值构造器: | |
7239 | +一些函数构造非引用值: | |
7240 | +包括核心库(@11.4.1) 转换函数: | |
7241 | +$bindings/p->environment | |
7242 | +$bindings->environment | |
7243 | +symbols->imports | |
7244 | +包括续延库(@12.1) 转换函数: | |
7245 | +continuation->applicative | |
7246 | +其中,明确转移被转换的右值(@10.7.6) 的函数有: | |
7247 | +continuation->applicative | |
7248 | + | |
6936 | 7249 | @11.3 根环境基本特性(@10.2) : |
6937 | 7250 | 和 [RnRK] 不同,为简化设计,NPLA1 不提供可选(optional) 的合并子,而通过预定义对象(@10.9.3.2) 的形式提供可选的模块(@12) 。 |
6938 | 7251 | 根环境基本特性是除了这些模块的以变量绑定形式提供的不要求可派生实现的特性。 |
@@ -6941,7 +7254,7 @@ | ||
6941 | 7254 | 若派生实现不提供在返回值中保留其它间接值的操作(@11.1.2) ,可以简化部分 @11.3.4 中与之关联的操作的实现。 |
6942 | 7255 | 部分可选的 Kernel 合并子被直接提供。 |
6943 | 7256 | 和 Kernel 不同,一些函数显式地操作引用值(@6.3.4) ,包括未折叠的引用值(@6.3.4) 。 |
6944 | -和 Kernel 不同,求值算法不直接处理对象的引用值(@6.10.6.3) 。 | |
7257 | +和 Kernel 不同,求值算法(@9.7.1) 不直接处理对象的引用值(@6.10.6.3) 。 | |
6945 | 7258 | 为简化实现,部分提供 % 等后缀的函数(@11.2.1) 不被派生。 |
6946 | 7259 | 因为设计原因,不提供以下 Kernel 合并子对应的操作: |
6947 | 7260 | copy-es-immutable |
@@ -6995,10 +7308,11 @@ | ||
6995 | 7308 | eql? 实际比较宿主值的相等。允许 eqv? 和 eql? 的不同可允许一对多的类型映射(@5.5.1) 下比较对象语言的值的相等。(而多对一的类型映射 eql? 和 eqv? 可一致地比较。) |
6996 | 7309 | 但是,当前实现中,大多数一对多映射的类型(如环境)都没有引起使 eql? 和 eqv? 不同的比较实现,因为不同宿主值类型的对象具有足够显著的差异,在大多数上下文不通过一些具有不可忽略开销的转换机制(如锁定环境弱引用(@6.11.1) 转换为环境强引用(@11.3.7) ),无法直接相互替换而保证行为差异可被忽略,因此逻辑上不适合定义为相等的。 |
6997 | 7310 | 而基于性能等理由,等其它一对多映射的类型(特别是可能基于宿主类型的值的子集的,如 NPLA 数值类型(@6.14.1) 中的 <integer> )的值的比较也没有特别的处理,而引起 eqv? 和 eql? 的不同。 |
6998 | -这些类型可能需要其它针对特定类型的等价谓词(如 =?(@12.2) )进行相等性的比较。 | |
7311 | +这些类型可能需要其它针对特定类型的等价谓词(如 =?(@12.3) )进行相等性的比较。 | |
6999 | 7312 | 类似 [RnRS] ,不同类型决定 eqv? 的结果是 #f ,但此处类型相同的含义不通过类型分区(@9.8.7) 定义。 |
7000 | 7313 | 类似 [RnRS] ,行为不等价的函数的 eqv? 结果原则上应为 #f ,但这种等价性一般不可证明而无法保证,特别在关于语言实现以外的调用上。 |
7001 | 7314 | 为支持互操作使用本机实现(@5.3) 及避免限制合并子的子类型的开放性(@1.5.3.6) ,允许这些实现另行指定规则,假定引起程序可观察行为差异的函数调用调用名义等价。 |
7315 | +宿主实现的 == 操作中不使用 NPLA1 异步规约(@7.1.5) 。 | |
7002 | 7316 | **注释** |
7003 | 7317 | 通常,等价谓词比较的求值应保证能终止且对非列表项和 n 个子项的列表分别具有 O(1) 和 O(n) 平摊复杂度。这是依赖数据结构实现的细节;语言不需要约束这个性质。 |
7004 | 7318 | 实现中,值数据成员的相等由 ValueObject 的 == 操作定义。这对应 C++ 意义上的对象相等。 |
@@ -7008,7 +7322,7 @@ | ||
7008 | 7322 | 列表和非列表之间的 eql? 结果总是为 #f ,列表之间的 eql? 的比较结果总是 #t 。 |
7009 | 7323 | 所有列表在 TermNode 的值数据成员中的表示都一致,在 eqr? 等价关系下视为相同的对象。 |
7010 | 7324 | 使用 eqr? 判断宿主值 TermNode 的值数据成员表示的左值(@5.8.1) 的标识,当且仅当为操作数比较同一个对象时比较结果是 #t 。 |
7011 | -关于合并子相等,另见 TCO 实现(@7.11.5) 中关于函数右值去重(@7.10.7.1) 的说明。 | |
7325 | +关于合并子相等,另见 TCO 实现(@7.11.6) 中关于函数右值去重(@7.10.7.1) 的说明。 | |
7012 | 7326 | 因为枝节点和其它节点不共享除公共超类型(@4.7.7) <object> 外具有相同类型的表示,任意一个被比较的值是枝节点时,eqv? 的结果也同 eq? 。 |
7013 | 7327 | |
7014 | 7328 | @11.3.3 控制: |
@@ -7031,7 +7345,7 @@ | ||
7031 | 7345 | 因此,仍然需要有 $when 以外的省略第三参数的基本控制操作子。基于同一性(@1.5.5.1) ,对应函数名仍然为 $if 。(尽管使用了相同的原则,这和 Kernel 的结论恰好相反。) |
7032 | 7346 | 与此类似,和 [Racket] 的理由(参见 https://stackoverflow.com/questions/10863192/why-is-one-armed-if-missing-from-racket )不同,不因为 $when 提供只强调副作用的操作而取消 $if 的 <alternative> 。 |
7033 | 7347 | NPLA1 不会如 Racket 一样在此避免遗漏 <alternate> 导致的非预期结果。这并不违反 @1.5.5.2 ,因为不使用 <alternate> 的结果非常显然,同时选择使用 $if 这样的基本控制操作而不是更特定派生控制操作或更高级的抽象已蕴含注意误用的必要性。 |
7034 | -一般地,NPLA1 不提供强调只存在副作用的操作。返回未指定(而不要求被使用)的求值结果的情形并不表示只有副作用(@10.9) ,因为副作用是否存在原则上依赖具体操作。这和 Kernel 的 #inert 以及 Racket 的 #<void> 值即便在实现上都一致,但含义不同。 | |
7348 | +一般地,NPLA1 不提供强调只存在副作用的操作。返回未指定(而不要求被使用)的求值结果的情形并不表示只有副作用(@10.9.1) ,因为副作用是否存在原则上依赖具体操作。这和 Kernel 的 #inert 以及 Racket 的 #<void> 值即便在实现上都一致,但含义不同。 | |
7035 | 7349 | 另见 $when 的说明。 |
7036 | 7350 | |
7037 | 7351 | @11.3.4 对象: |
@@ -7046,14 +7360,14 @@ | ||
7046 | 7360 | branchv? <object> :判断操作数是否为具有枝节点表示的纯右值。 |
7047 | 7361 | 同 branch? ,但不支持引用值。 |
7048 | 7362 | reference? <object> :判断操作数是否为引用值。 |
7049 | -uncollapsed? <object> :判断操作数是否为未折叠的引用值。 | |
7363 | +unique? <object> :判断操作数是否为唯一引用(@6.2.2) 。 | |
7364 | +modifiable? <object> :判断操作数是否为可修改对象或可修改对象的引用值。 | |
7365 | +temporary? <object> :判断操作数是否为临时对象(@5.8.5) 或临时对象的引用值。 | |
7050 | 7366 | bound-lvalue? <object> :判断操作数是否为被引用的被绑定对象左值。 |
7051 | -绑定临时对象(@5.8.5) 的引用类型的参数不被视为左值引用。 | |
7367 | +绑定临时对象的引用类型的参数不被视为左值引用。 | |
7052 | 7368 | 配合 $resolve-identifier(@11.3.7) 和 % 引用标记绑定(@7.7.3.5) 的变量,可确定实际参数是否为左值;参见 $lvalue-identifier?(@11.4.1) 。 |
7053 | 7369 | 使用 bound-lvalue? 和 & 引用标记字符绑定(@7.7.3.5) 的变量,可确定实际参数是否为引用。 |
7054 | -unique? <object> :判断操作数是否为唯一引用(@6.2.2) 。 | |
7055 | -modifiable? <object> :判断操作数是否为可修改对象或可修改对象的引用值。 | |
7056 | -temporary? <object> :判断操作数是否为临时对象或临时对象的引用值。 | |
7370 | +uncollapsed? <object> :判断操作数是否为未折叠的引用值。 | |
7057 | 7371 | deshare <object> :取指定对象取消共享的值。 |
7058 | 7372 | 同 idv(@11.4.1) ,但显式提升操作数中具有共享持有者的值数据成员(@6.2) ,且不转移宿主值。 |
7059 | 7373 | 因为提供在返回值中保留其它间接值的操作(@11.1.2) ,这个区别是必要的。否则,使用 idv 替代应不影响可观察行为(@4.1.3) 。 |
@@ -7062,7 +7376,8 @@ | ||
7062 | 7376 | 同 id(@11.4.1) ,但当参数是引用值时,结果是和参数引用相同对象的不可修改的引用值。 |
7063 | 7377 | expire <object> :取指定对象的消亡值(@5.8.1) 。 |
7064 | 7378 | 同 id(@11.4.1) ,但当参数是引用值时,结果是和参数引用相同对象的唯一引用。 |
7065 | -可用于显式地指定之后被转移(@9.8.3.2) 的对象,而不需要直接转移参数,因此这个操作也不是修改操作(@9.8.3) ,函数名不以 ! 结尾(@10.7) 。 | |
7379 | +可用于显式地指定之后被转移(@9.8.3.2) 的对象,而不需要直接转移参数。 | |
7380 | +**原理** 因此这个操作也不是修改操作(@9.8.3) ,函数名不以 ! 结尾(@10.7.2.3) 。 | |
7066 | 7381 | **注释** 这个函数类似宿主语言标准库中作用在对象类型实际参数的 std::move ,可能减少没有经过复制消除(@5.8.5.3) 的复制或转移(@5.8.2.3) 而改变使用这个函数的结果对象的副作用。 |
7067 | 7382 | 特别地,指定列表的引用值被转移时,不需要立即转移列表的每个元素,而允许之后通过绑定构造(@9.7.3) 等方式选择转移的子对象(@9.8.2) 。 |
7068 | 7383 | 可能包含立即转移的操作如 forward!(@11.4.1) 。 |
@@ -7168,18 +7483,18 @@ | ||
7168 | 7483 | 和 Scheme 及 Kernel 不同,<body> 可以是多个项,而不在派生另外的变体支持顺序求值。 |
7169 | 7484 | 引入合并子的操作子不求值 <body> ,后者在被调用时替换操作数以后被求值。这允许安全地使用 $def! 而不需要 $defrec! 进行递归绑定(@11.3.7) 。 |
7170 | 7485 | 检查失败的错误(@9.5.1) 是(可能依赖(@9.5.1) 类型错误(@9.5.4.1) 的)语法错误(@9.5.1) 。 |
7171 | -$vau/e <parent> <formals> <eformal> <body> :创建指定静态环境的 vau 抽象(@4.5.2.3) 。 | |
7486 | +$vau/e <parent> <formals> <eformal> <body> :创建指定静态环境的 vau 抽象(@4.5.2.5) 。 | |
7172 | 7487 | 创建的对象是操作子(@7.6.1.1) 。 |
7173 | 7488 | $vau/e% <parent> <formals> <eformal> <body> :同 $vau/e ,但保留引用值。 |
7174 | 7489 | wrap <combiner> :包装(@4.5.3.2) 合并子为应用子(@7.6.1.1) 。 |
7175 | 7490 | 包装应用子可能符合包装数溢出的错误条件(@9.9.5) 。 |
7176 | 7491 | wrap% <combiner> :同 wrap ,但参数不隐含左值到右值转换,在结果中保留引用值。 |
7177 | 7492 | unwrap <applicative> :解包装(@4.5.3.2) 应用子为底层的合并子(@7.6.1.1) 。 |
7178 | -和 Kernel 不同,参数是右值时子对象(@9.8.2) 被复制,由这些合并子创建的操作子当前仍不足以取代内置的一等操作子,因为不支持只能转移而不能复制的对象。传递这些对象作为操作数会引起构造失败的异常。 | |
7179 | 7493 | 左值参数解包装的结果是合并子的子对象引用(@11.2.4) 。 |
7180 | 7494 | **注释** |
7181 | 7495 | 和 Kernel 不同,因为支持保存环境的所有权,$vau/e 被设计为比 $vau 更基本的操作。 |
7182 | 7496 | 不考虑所有权时,eval(@11.3.7) 和 $vau 可派生 $vau/e 。 |
7497 | +和 Kernel 不同,参数是右值时解包装的子对象(@9.8.2) 被复制。由这些合并子创建的操作子当前仍不足以取代内置的一等操作子,因为不支持只能转移而不能复制的对象。传递这些对象作为操作数会引起构造失败的异常。 | |
7183 | 7498 | |
7184 | 7499 | @11.3.9 错误处理(@10.6.4) 和检查: |
7185 | 7500 | raise-error <string> :引发表示错误的异常。 |
@@ -7207,10 +7522,10 @@ | ||
7207 | 7522 | 另见等价谓词的设计用例(@4.1.4.3) 。 |
7208 | 7523 | 使用 Forms::MakeEncapsulationType(@8.4.8) 的实现中,递归相等的比较不依赖当前上下文,也不支持捕获比较相等的续延。 |
7209 | 7524 | |
7210 | -@11.4 基础派生特性(grounded derived feature) : | |
7211 | -根环境特性(@10.2) 中,除根环境基本特性(@11.3) 的剩余接口可通过既有对象语言提供的接口实现,或依赖其它实现特定的互操作(@1.2.3) 接口的本机实现(@5.3) 组合实现。 | |
7212 | -基本派生特性(@11.4.1) 可使用派生实现(可能需要使用 @11.3 或 @11.4.1 中的部分非派生实现),但因为性能等原因在 NPLA1 API 中提供直接支持的本机实现(@10) 。 | |
7213 | -这些操作的派生实现同时在模块 NPL::Dependency 内被给出作为替代实现。 | |
7525 | +@11.4 基础派生特性: | |
7526 | +根环境特性(@10.2) 中,除根环境基本特性(@11.3) 的剩余接口是派生特性(@10.9.3) 。其中在基础环境中提供的特性时基础派生特性(grounded derived feature) 。 | |
7527 | +因为性能等原因,基础派生特性可能同时提供本机实现和非本机的派生实现(@10.9.3) 。 | |
7528 | +其中,本机实现在 NPLA1 API 中提供直接支持(@10) 。这些操作的派生实现同时在模块 NPL::Dependency 内被给出作为替代实现。 | |
7214 | 7529 | 启用本机实现使用以下实现选项(@7.1) 控制: |
7215 | 7530 | NPL_Impl_NPLA1_Native_Forms |
7216 | 7531 | NPL_Impl_NPLA1_Native_EnvironmentPrimitives |
@@ -7220,16 +7535,23 @@ | ||
7220 | 7535 | 以下不同实现可引起不同的诊断: |
7221 | 7536 | 由合并子调用的参数绑定(@8.4.5.3) 引起(@9.5.1) 的静态语法错误(@9.5.1) 。 |
7222 | 7537 | 类型检查(@9.5.4.1) 可具有不同的顺序而引起不同的类型错误。 |
7538 | + 列表元素检查的错误中的诊断消息(@1.2.4) 。 | |
7223 | 7539 | 使用 TCO 实现 PTC(@9.7.4) ,可能具有不同的支持(@9.7.4.1) 和实现(如 TCO 动作消除(@7.10.9) )。 |
7224 | 7540 | 操作的语义允许时,操作的结果和调用操作的作用(@4.1) 引起改变的对象中可包含符合具体操作的约定的未指定值(但不含没有附加限制条件的未指定值(@10.7.2.3) )。 |
7225 | 7541 | 没有明确指定时,创建的对象的具体内部表示及其宿主类型。 |
7226 | - 续延名称(@7.11.6) 等在特定条件下具有的可观察行为的差异。 | |
7542 | + 续延名称(@7.11.7) 等在特定条件下具有的可观察行为的差异。 | |
7227 | 7543 | 其中,影响可观察行为的差异应为操作符合性(@10.9.2) 约定的实例。 |
7228 | -NPLA1 中的操作的替代实现的没有被以上例外或操作自身的语义指定的其它行为(如符合诊断(@9.5) 和接口要求的诊断消息(@1.2.4) 的内容)仍应和本机实现保持一致。 | |
7544 | +NPLA1 中的操作的替代实现的没有被以上例外或操作自身的语义指定的其它行为(如符合诊断(@9.5) 和接口要求的诊断消息的内容)仍应和本机实现保持一致。 | |
7545 | +**原理** | |
7546 | +因为本机实现可能提前判断空列表,所以能比访问元素的检查更加准确。 | |
7229 | 7547 | **注释** |
7230 | 7548 | 以上一致性要求和 [RnRK] 合并子的派生完全省略错误处理(@10.6.4) 而允许不同的诊断不同。 |
7231 | - | |
7232 | -@11.4.1 基本派生特性(basic derived feature) : | |
7549 | +当前已知和派生实现的诊断消息不同的本机实现有: | |
7550 | +Forms::Assq(@8.4.9) | |
7551 | +Forms::Assv(@8.4.9) | |
7552 | + | |
7553 | +@11.4.1 基本派生特性: | |
7554 | +基本派生特性可使用派生实现(可能需要使用 @11.3 或 @11.4.1 中的部分非派生实现)。 | |
7233 | 7555 | 模块约定: |
7234 | 7556 | 引入合并子的操作子对 <body> 的约定同 @11.3.8 。 |
7235 | 7557 | 因互相依赖,一些操作实现为派生操作时,不能用于直接派生特定一些其它操作。 |
@@ -7241,7 +7563,7 @@ | ||
7241 | 7563 | 结果具有宿主值类型 NPL::EnvironmentReference 。派生需要非派生实现的 vau/e 。 |
7242 | 7564 | () lock-current-environment :锁定当前环境:取当前环境的环境强引用。 |
7243 | 7565 | 结果具有宿主值类型 shared_ptr<Environment> 。 |
7244 | -$vau <formals> <eformal> <body> :创建 vau 抽象(@4.5.2.3) 。 | |
7566 | +$vau <formals> <eformal> <body> :创建 vau 抽象(@4.5.2.5) 。 | |
7245 | 7567 | 类似 $vau/e(@11.3.8) ,但以当前环境代替额外的求值环境作为静态环境。 |
7246 | 7568 | 和 Kernel 不同,可通过 $vau/e(@11.3.8) 和(非派生的)get-current-environment 派生,不是基本操作(@11.3) 。 |
7247 | 7569 | $vau% <formals> <eformal> <body> :同 $vau(@11.4.1) ,但保留引用值。 |
@@ -7275,13 +7597,13 @@ | ||
7275 | 7597 | 和 Kernel 不同而和 NPLA1 的 $def! 等类似,在修改绑定前对冻结环境进行检查。 |
7276 | 7598 | $setrec! <environment> <definiend> <body> :修改指定环境的绑定,绑定效果同使用 $defrec!(@11.3.7) 。 |
7277 | 7599 | 同 $set! ,但允许不同的递归操作,参见 $defrec! 。 |
7278 | -$wvau <formals> <eformal> <body> :创建包装的 vau 抽象(@4.5.2.2) 。 | |
7600 | +$wvau <formals> <eformal> <body> :创建包装的 vau 抽象(@4.5.2.4) 。 | |
7279 | 7601 | 同 $vau ,但创建的是调用时对操作数的元素求值一次的应用子(@7.6.1.1) 。 |
7280 | 7602 | 参数的作用同 $vau 的对应参数。 |
7281 | 7603 | $wvau% <formals> <eformal> <body> :同 $wvau ,但允许函数体求值返回引用值。 |
7282 | 7604 | $wvau/e <parent> <formals> <eformal> <body> :同 $wvau ,但支持显式指定求值环境参数作为静态环境。 |
7283 | 7605 | $wvau/e% <parent> <formals> <eformal> <body> :同 $wvau/e ,但保留引用值。 |
7284 | -$lambda <formals> <body> :创建 λ 抽象(@4.5.2.2) 。 | |
7606 | +$lambda <formals> <body> :创建 λ 抽象(@4.5.2.4) 。 | |
7285 | 7607 | 同 $vau ,但创建的是调用时对操作数的元素求值一次的应用子(@7.6.1.1) ,且忽略动态环境。 |
7286 | 7608 | **注释** 可通过 vau 抽象或 $lambda/e 和(非派生的)get-current-environment 派生。 |
7287 | 7609 | 除未提供的 <eformal> ,参数的作用同 $vau 的对应参数。 |
@@ -7400,7 +7722,7 @@ | ||
7400 | 7722 | 类似 SRFI-1 的 fold-right ,但只接受一个真列表(@9.9.4) 。 |
7401 | 7723 | map1 <applicative> <list> :单列表映射操作:使用指定应用子对列表中每个参数进行调用,结果是调用结果的列表。 |
7402 | 7724 | 参数 <applicative> 应接受一个参数,否则引起错误。 |
7403 | -操作中的应用子和列表构造的结果的确定满足过程调用的因果性(@4.5.2.1) ;其余任意 <applicative> 调用的求值、列表构造操作和销毁列表中的元素的操作的相对顺序未指定。 | |
7725 | +操作中的应用子和列表构造的结果的确定满足过程调用的因果性(@4.5.2.2) ;其余任意 <applicative> 调用的求值、列表构造操作和销毁列表中的元素的操作的相对顺序未指定。 | |
7404 | 7726 | **注释** foldr1 和 map1 名称中的 1 指 <list> 参数的个数。(更一般的其它形式可接受多个 <list> 。) |
7405 | 7727 | <applicative> 的调用不添加或移除列表元素,否则行为未指定。 |
7406 | 7728 | 类似 Kernel 的 map ,但只接受一个真列表(@9.9.4) 。 |
@@ -7490,11 +7812,24 @@ | ||
7490 | 7812 | nonfoldable? <list> :判断参数是否不可被继续折叠映射:存在空列表。 |
7491 | 7813 | 参数是同 [RnRK] 的 map 操作可接受的列表参数或空列表,但排除非真列表(@6.2.1) 。 |
7492 | 7814 | 若参数是空列表,结果是 #f 。 |
7815 | +assq <object> <lists> :取关联列表中和参数的引用相同的元素。 | |
7816 | +以 eq? 依次判断第二参数指定的列表中的第一个元素是否和第一参数指定的元素等价。 | |
7817 | +若不存在相等的元素,结果为空列表右值;否则是同 first% 访问得到的等价的列表的值。 | |
7818 | +**原理** 和 Kernel 不同,NPLA1 只支持真列表(@9.9.4) ,因此可以要求顺序,提供关于等价的元素的更强的保证。 | |
7819 | +**原理** 尽管和 Kernel 相同而和 Scheme 不同,<test> 支持非布尔类型的值(@11.3.3) ,不存在元素时的 #f 结果可以简化比较,但和 [RnRK] 的原理类似,这不利于提供清晰的类型错误,且没有如空列表值这样作为求值算法的特殊值的自然推论。使用空列表值和传统 Lisp 也一致。 | |
7820 | +**注释** 类似 Kernel 的 assq ,但保证顺序且转发参数。 | |
7821 | +**注释** 类似 Scheme 的 assq ,但失败的结果不是 #f 。 | |
7822 | +assv <object> <lists> :取关联列表中和参数的值相等的元素。 | |
7823 | +以 eqv? 依次判断第二参数指定的列表中的第一个元素是否和第一参数指定的元素等价。 | |
7824 | +若不存在相等的元素,结果为空列表右值;否则是同 first% 访问得到的等价的列表的值。 | |
7825 | +**原理** 参见 assq 。 | |
7826 | +**注释** 类似 Kernel 的 assoc ,但使用 eqv? 而不是 equal? ,保证顺序且转发参数。 | |
7827 | +**注释** 和 Scheme 的 assv 类似,但失败的结果不是 #f 。 | |
7493 | 7828 | box <object> :装箱:构造参数对象经左值到右值转换的箱(类型为 <box> 的对象)。 |
7494 | 7829 | box% <object> :同 box ,但参数不隐含左值到右值转换,在结果中保留参数的引用值(@10.7.3.3) 。 |
7495 | 7830 | box? <object> :<box> 的类型谓词(@10.7.2.1) 。 |
7496 | 7831 | unbox <box> :拆箱:从箱中还原对象。 |
7497 | -作为传递操作,保留引用值。 | |
7832 | +作为函数值转发操作(@11.2.2.4) ,保留引用值。 | |
7498 | 7833 | 以上 4 个函数除引用标记字符(@10.7) 对应处理引用值的差异外,功能和使用方式对应类似 SRFI-111 的 3 个过程 box 、box? 和 unbox 。 |
7499 | 7834 | 类型分区(@9.8.7) 使 box? 对 <list> 类型的参数的结果总是 #f 。若没有这个限制,用以下 <list> 的相关操作可整体替换进行功能等价的代替: |
7500 | 7835 | **原理** |
@@ -7518,11 +7853,6 @@ | ||
7518 | 7853 | |
7519 | 7854 | @11.4.3 核心库(core library) : |
7520 | 7855 | 核心库提供以下操作,即核心库函数: |
7521 | -assv <object> <lists> :访问关联元素。 | |
7522 | -顺序查找列表中的元素,若列表的元素的第一个值以 eqv? 判断和参数指定的对象相等,则结果是这个元素转发的值; | |
7523 | -否则,值为空列表。 | |
7524 | -类似 Kernel 的 assoc ,但使用 eqv? 而不是 equal? ,且转发参数。 | |
7525 | -**注释** 和 Scheme 的 assv 类似,但失败的值不返回 #f ,尽管和 Kernel 相同而和 Scheme 不同,<test> 支持非布尔类型的值(@11.3.3) 。 | |
7526 | 7856 | map-reverse <applicative> <list>... :映射并反转结果。 |
7527 | 7857 | 参数 <applicative> 应满足以下要求,否则引起错误(@9.5.1) : |
7528 | 7858 | <list>... 中的参数的元素数都相等; |
@@ -7534,14 +7864,43 @@ | ||
7534 | 7864 | @12 NPLA1 参考实现扩展环境: |
7535 | 7865 | 类似 NPLA1 根环境特性(@11) ,NPLA1 以根环境(@10.1) 的形式提供其它一些模块(@10.2) 的操作,但默认不以根环境中的绑定而是以其中的环境子对象(@10.2) 中的绑定提供。 |
7536 | 7866 | 这些模块和 @10 的提供的特性一同构成了标准库(@10.3.3) 。 |
7537 | -这些绑定不需要直接引入基础上下文(@8.5.2) 的根环境中,因为同时满足以下条件而不需要以基础环境(@10.1) 可访问的名称提供: | |
7538 | -它们不是使用作为环境的模块(@10.2) 时被依赖的主要操作(如 $import!(@11.4.1) ); | |
7539 | -它们不需要被根环境特性(@11) 的在接口意义上依赖(实现上可选依赖,参见标准派生特性(@11.4.2) )。 | |
7867 | +这些绑定不需要直接引入基础上下文(@8.5.2) 的根环境中,因为: | |
7868 | + 同时满足以下关于接口依赖的约束,而不必要以基础环境(@10.1) 可访问的名称提供: | |
7869 | + 它们不是使用作为环境的模块(@10.2) 时被依赖的主要操作。 | |
7870 | + **原理** 为了使用非根环境的模块,需要绑定在根环境的函数引入其中的绑定。这样的接口应在根环境中提供而保证默认可访问,避免引入绑定这样的功能的在逻辑上的循环依赖。 | |
7871 | + **注释** 这样的操作如 $import!(@11.4.1) 。 | |
7872 | + 它们不被根环境特性(@11) 的在接口意义上依赖。 | |
7873 | + **注释** 在实现上仍可选依赖,参见标准派生特性(@11.4.2) 。 | |
7874 | + 接口的功能不对一般的实体具有足够普遍性,而不需要以基础环境默认可访问的名称提供。 | |
7875 | +判定上述的足够普遍性的具体规则包括: | |
7876 | + 普遍性以实体类型体现为接口的功能对非特定类型的对象适用,最终不依赖具有更特定的类型特有的性质。 | |
7877 | + **注释** 一般的实体作为一等对象,即具有 <object>(@9.2.2.2) 的值。<object> 是足够普遍的类型。 | |
7878 | + **注释** <reference>(@9.2.2.2) 和 <box>(@11.4.1) 等由 <object> 构造的值最终依赖 <object> 的值,而非其它更特定类型特有的性质。 | |
7879 | + 在求值算法(@9.7.1) 中出现决定具体步骤的具体实体类型,被认为是足够普遍的。 | |
7880 | + **注释** 这包括 <symbol>(@9.2.2.1) 、<list>(@9.2.2.2) 和 <combiner>(@9.2.2.1) 及其子类型(@9.2.2.1) 。 | |
7881 | + **注释** <number>(@6.14.1) 或 <string>(@9.2.2.2) 等其它比 <object> 更具体类型的值不在此列。 | |
7882 | + 若接口的功能仅依赖比一般的实体更具体的特定类型的值,则不以基础环境默认可访问的名称提供。 | |
7883 | + **注释** 功能上的依赖包含区分这些特定类型的值,如类型谓词(@10.7.2.1) 。 | |
7884 | + 接口的功能描述涉及的类型的足够普遍性对以基础环境默认可访问的名称提供是必要非充分条件。 | |
7885 | + **原理** 这些类型仅决定接口功能的一部分。 | |
7886 | + **注释** 若接口的功能仅依赖足够普遍的类型,但功能不足以涵盖它的任何的子类型(@4.7.7) 或者值,也可在参考实现扩展环境中提供。 | |
7887 | + **注释** 一个主要特例:足够普遍的具体类型的类型谓词涵盖所有值,因此类型的足够普遍性可直接作为对应的类型谓词的足够普遍性的充分必要条件。 | |
7888 | +具有足够普遍性而应在根环境而非参考实现扩展环境提供的操作具体包括以下几类: | |
7889 | + 创建非特定的不同名义类型(@4.7.2) 的对象使用的普遍机制的主要操作。 | |
7890 | + **注释** 使用 make-encapsulation-type(@11.3.10) 可创建不同名义类型。 | |
7891 | + 不改变一般的实体可能蕴含的对象同一性(@4.2.1.1) 而同时附加非特定种类(@4.7.9) 的副作用(@4.1) 的操作。 | |
7892 | + **原理** 同一性是所有对象的属性。显示同一性不依赖具体副作用的种类,因此要求特定种类的接口削弱普遍性。另见可变状态和普遍性(@4.2.1.2) 。 | |
7893 | + **注释** 根环境中这样的操作如 box% 和 unbox(@11.4.3) 。 | |
7894 | + 不依赖特定对象类型,直接引入副作用的操作。 | |
7895 | + **原理** 因为引入副作用可能是接口的关键功能及主要目的,此处的普遍性不限制非特定种类。 | |
7896 | + **注释** 仅具有控制作用为副作用的操作仍被视为是普遍的。因此,可具有控制作用(@4.1) 的 $if(@11.3.3) 等函数仍在根环境中提供。 | |
7897 | + **注释** 依赖一等续延(@4.5.3.3) 的控制作用(@4.1) 因续延类型而不视为足够普遍,因此根环境不直接提供一等续延(@4.5.3.3) 关联的操作。 | |
7540 | 7898 | 在此基础上,这些绑定被设计为环境子对象提供的子模块,因为以下的一个或多个原因: |
7541 | -它们可能具有非全局含义的名称而更适合隔离在不同命名空间(@4.3.4) 中以避免使用时的歧义; | |
7542 | -它们可能关注(如作为操作的参数或返回类型)仅在求值得到的操作数(@9.2.2.2) 类型; | |
7543 | -它们中的每一个模块具有足够或内聚性而不和其它子模块耦合,且绑定提供的实体关注相同的功能集合,适合被派生实现直接配置为可选的(@11.3) 特性分组; | |
7544 | -允许实现使用特殊的环境子类型延迟加载(@10.2)(当前 NPLA1 没有提供支持)。 | |
7899 | + 它们可能具有非全局含义的名称而更适合隔离在不同命名空间(@4.3.4) 中以避免使用时的歧义。 | |
7900 | + 它们可能仅关注(如作为操作的参数或返回类型)特定的求值得到的操作数(@9.2.2.2) 类型。 | |
7901 | + 它们中的每一个模块具有足够或内聚性而不和其它子模块耦合,且绑定提供的实体关注相同的功能集合,适合被派生实现直接配置为可选的(@11.3) 特性分组。 | |
7902 | + 允许实现使用特殊的环境子类型延迟加载(@10.2) 。 | |
7903 | + **注释** 当前 NPLA1 没有提供支持。 | |
7545 | 7904 | 除非派生实现定义,每个标准库模块都不是可选的。否则,作为被加载的模块的环境的名称由派生实现定义。 |
7546 | 7905 | 修改(@9.8.3) 这些环境的程序行为未定义。 |
7547 | 7906 | 默认加载使用 . 分隔标识符得到的符号作为名称。 |
@@ -7550,16 +7909,40 @@ | ||
7550 | 7909 | 在调用其中的合并子时,可能求值符号引用依赖的环境。其中的环境可能在求值定义时不依赖而不作为对应的本机 API 的前置条件。 |
7551 | 7910 | 环境是否具有依赖的环境的绑定绑定是未指定的。 |
7552 | 7911 | 用户程序需保持加载为环境的模块具有适当的生存期(@9.9.3.5) ,以避免其中的合并子调用引起未定义行为。 |
7553 | -**注释** | |
7912 | +本章的特性可使用本机实现(@5.3) 或非本机的派生实现(@10.9.3) ,分别符合根环境基本特性(@11.3) 和基础派生特性(@11.4) 的规则。具体使用何种实现是未指定的。 | |
7913 | +派生实现可定义更具体的实现要求,以便互操作(@5.3) 的兼容性。 | |
7914 | +**注释** | |
7915 | +类似根环境特性(@11) ,一些特性可约定处理的值的宿主类型(@9) 。 | |
7916 | +当前实现中,本机实现的宿主类型和非本机的派生实现不同的实例有: | |
7917 | +<promise>(@12.2) 。 | |
7554 | 7918 | 使用 . 分隔标识符得到的符号类似 CHICKEN Scheme 的转换 R7RS 的标准模块名(参见 http://wiki.call-cc.org/eggref/4/r7rs#import )。 |
7555 | 7919 | |
7556 | -@12.1 代理求值: | |
7920 | +@12.1 续延(@4.1.7) : | |
7921 | +通过初始化基础上下文后调用 Forms::LoadModule_std_continuations(@8.5.2) 初始化,默认加载为根环境下的 std.continuations 环境。 | |
7922 | +模块约定: | |
7923 | +本节约定以下求值得到的操作数: | |
7924 | +<continuation> :一等续延(@4.5.3.3) 。 | |
7925 | +续延的宿主类型是 A1::Continuation(@7.4.2) 。 | |
7926 | +类似函数应用表达式(@4.5.3) ,续延应用(continuation application) 表达式是求值时蕴含续延调用(@4.5.3.3) 的表达式。 | |
7927 | +操作: | |
7928 | +call/1cc <combiner> :捕获一次续延(@4.5.3.3) ,具现(@1.2.4) 为一等续延作为参数调用合并子。 | |
7929 | +续延首先在尾上下文中(@9.7.4.1) 被按引用捕获(@4.5.2.1) ,再作为操作数,调用 <combiner> 。 | |
7930 | +来自同一具现的一次续延的任何副本只允许一次显式(续延应用)或者隐式(如被函数调用的返回蕴含)地成功调用; | |
7931 | +否则,调用被重入(@4.5.4) ,引起错误(@9.5.1) 。 | |
7932 | +捕获的续延之后允许被复制。 | |
7933 | +continuation->applicative <continuation> :转换续延为应用子。 | |
7934 | +结果是转换的 <applicative> 类型的值。 | |
7935 | +**注释** | |
7936 | +call1/cc 的名称来自 https://legacy.cs.indiana.edu/~dyb/pubs/call1cc.pdf ,同 Chez Scheme 。 | |
7937 | +类似 Kernel 而不同于 Scheme ,续延具有单独的类型,续延应用也不是蕴含过程调用的函数应用。 | |
7938 | +另见关于一次调用检查的实现(@7.4.2.1) 。 | |
7939 | + | |
7940 | +@12.2 代理求值: | |
7557 | 7941 | 通过初始化基础上下文后调用 Forms::LoadModule_std_promises(@8.5.2) 初始化,默认加载为根环境下的 std.promises 环境。 |
7558 | 7942 | 模块约定: |
7559 | 7943 | 本节约定以下求值得到的操作数: |
7560 | 7944 | <promise> :求值代理:表示可被求值取得结果的对象。 |
7561 | 7945 | 保存待求值的表达式和这个表达式求值时的动态环境(@4.6.1.2) ,或已求值的结果。 |
7562 | -**注释** 在 <promise> 上的并发访问并不具有特别的同步保证和要求。 | |
7563 | 7946 | 除 $lazy/d 外,同 [RnRK] 的 promises 模块。 |
7564 | 7947 | 操作: |
7565 | 7948 | promise? <object> :判断参数是否为 <promise> 类型的值。 |
@@ -7577,14 +7960,15 @@ | ||
7577 | 7960 | 由于当前语言不支持并发访问,即使对 <promise> 的修改操作(@9.8.3) 导致变化,在语言中其状态也不可见,没有要求支持这种同步;未来可能会附加要求以提供更完善的并发支持。 |
7578 | 7961 | 关于 API 的设计,参见 [RnRK] 第 9 章和 SRFI-45 。 |
7579 | 7962 | **注释** |
7963 | +在 <promise> 上的并发访问并不具有特别的同步保证和要求。 | |
7580 | 7964 | 和 [RnRK] 不同,通过 force 引起 <promise> 对象的求值可能修改这个对象自身而使其中的状态失效(如通过 assign!(@11.4.1) 对这个对象赋值)。 |
7581 | 7965 | 因此,实现中需要重新访问状态,而重新进行类型检查。 |
7582 | 7966 | |
7583 | -@12.2 数学功能: | |
7967 | +@12.3 数学功能: | |
7584 | 7968 | 通过初始化基础上下文后调用 Forms::LoadModule_std_math(@8.5.2) 初始化,默认加载为根环境下的 std.math 环境。 |
7585 | 7969 | 以下操作中: |
7586 | 7970 | 涉及数值操作数的操作符合数值操作约定(@6.14.2) ; |
7587 | -所有除法和取余数的计算符合除法约定(@12.2.1) 。 | |
7971 | +所有除法和取余数的计算符合除法约定(@12.3.1) 。 | |
7588 | 7972 | number? <object> :<number> 的类型谓词(@10.7.2.1) 。 |
7589 | 7973 | complex? <object> :<complex> 的类型谓词。 |
7590 | 7974 | **注释** 同 number? ,因为当前 <number> 都是 <complex> 值。 |
@@ -7640,7 +8024,7 @@ | ||
7640 | 8024 | 关于比较操作: |
7641 | 8025 | 同 [RnRK] 而不同于 [R5RS] 、[R6RS] 和 [R7RS] ,比较操作不明确要求传递性(但精确数仍然因真值的数学性质而保证传递性),以允许操作数存在不精确数时,转换不精确数 flonum 的简单实现。 |
7642 | 8026 | [R6RS] 继承了 [R5RS] 要求过程 = 具有传递性(注释指出传统类 Lisp 语言的实现不要求),而 [R7RS] 的对应注释指出这不能通过把所有操作数都转换为不精确数实现。 |
7643 | -不要求不精确数的传递性和除法约定(@12.2.1) 对除数为不精确数 0 值的处理兼容。 | |
8027 | +不要求不精确数的传递性和除法约定(@12.3.1) 对除数为不精确数 0 值的处理兼容。 | |
7644 | 8028 | 不同的语言在此可能有不同的规则,参见 https://codewords.recurse.com/issues/one/when-is-equality-transitive-and-other-floating-point-curiosities ,可见: |
7645 | 8029 | 一些现代 Lisp 语言可能满足(即便不是 Scheme 实现,如 SBCL )或不满足(即便是 Scheme 实现的派生,如 Racket )此要求; |
7646 | 8030 | 一些语言的不同版本的实现可能使用不同的规则(如 Ruby 1.8.7 和 Ruby 2.0 )。 |
@@ -7659,7 +8043,7 @@ | ||
7659 | 8043 | **注释** |
7660 | 8044 | 和数值操作约定不同,幂等操作要求超过一次应用时,结果和参数的宿主类型也相同。 |
7661 | 8045 | |
7662 | -@12.2.1 除法约定: | |
8046 | +@12.3.1 除法约定: | |
7663 | 8047 | 二元除法或者取余数的操作中,第一个参数是被除数,第二个参数是除数。 |
7664 | 8048 | 当除数是不精确数 0 时: |
7665 | 8049 | 若被除数是非零有限数值或无限大值,则商的符号同被除数的符号; |
@@ -7705,7 +8089,7 @@ | ||
7705 | 8089 | https://github.com/johnwcowan/r7rs-work/blob/master/WG1Ballot3Results.md#185-add-sixth-centered-division-operator |
7706 | 8090 | https://wiki.call-cc.org/eggref/5/srfi-141 |
7707 | 8091 | |
7708 | -@12.3 字符串: | |
8092 | +@12.4 字符串: | |
7709 | 8093 | 通过初始化基础上下文后调用 Forms::LoadModule_std_strings(@8.5.2) 初始化,默认加载为根环境下的 std.strings 环境。 |
7710 | 8094 | 模块约定: |
7711 | 8095 | 本节约定以下求值得到的操作数: |
@@ -7733,7 +8117,7 @@ | ||
7733 | 8117 | 在 <string1> 的副本中搜索正则表达式指定的模式串的所有匹配,替换为 <string2> 指定的格式字符串。 |
7734 | 8118 | 结果是替换后的字符串。 |
7735 | 8119 | |
7736 | -@12.4 输入/输出: | |
8120 | +@12.5 输入/输出: | |
7737 | 8121 | 通过初始化基础上下文后调用 Forms::LoadModule_std_io(@8.5.2) 初始化,默认加载为根环境下的 std.io 环境。 |
7738 | 8122 | readable-file? <string> :判断参数指定的文件名对应的文件是否存在且可读。 |
7739 | 8123 | writable-file? <string> :判断参数指定的文件名对应的文件是否存在且可写。 |
@@ -7759,7 +8143,7 @@ | ||
7759 | 8143 | 和 [R7RS] 不同,load 不支持指定环境,而总是使用当前环境。 |
7760 | 8144 | 类似 Kernel ,当前环境可通过不同机制改变,而不需由 load 提供特设的支持。例如,可使用 eval(@11.3.7) 指定蕴含 load 的调用的求值使用的环境。 |
7761 | 8145 | load 的语义隐含从外部来源取得求值构造后在当前环境求值,其中的求值明确允许隐含副作用。这和其它一些语言的类似命名的功能(如 Lua 的 loadfile )不同。在此,load 的求值被视为初始化加载的模块过程中的一部分。 |
7762 | -因为当前不提供取得求值构造的读取(read) 等函数,不要求 load 具有非本机派生实现。并且,取得求值构造可能有其它方式,如从二进制映像映射(map) 到内部表示等替代,这些实现通常不应被要求为总是具有本机派生实现而降低实现质量。 | |
8146 | +因为当前不提供取得求值构造的读取(read) 等函数,不要求 load 具有非本机的派生实现。并且,取得求值构造可能有其它方式,如从二进制映像映射(map) 到内部表示等替代,这些实现通常不应被要求为总是具有本机派生实现而降低实现质量。 | |
7763 | 8147 | **注释** |
7764 | 8148 | 关于 newline 、put 和 puts : |
7765 | 8149 | 实现使用 REPLContext::GetOutputStreamRef(@7.8.1) 。 |
@@ -7770,7 +8154,7 @@ | ||
7770 | 8154 | 按 [Shu09] 一致的描述和 klisp 的实际实现,调用 load 时应在当前环境求值,而不同于 [Shu09] 的 get-module 中描述的使用创建的新标准环境进行求值。 |
7771 | 8155 | 否则,使用 [Shu09] 的 get-module 的派生不能实现 klisp 和 [Shu09] 中描述的 get-module 的预期语义。 |
7772 | 8156 | |
7773 | -@12.5 系统: | |
8157 | +@12.6 系统: | |
7774 | 8158 | 通过初始化基础上下文后调用 Forms::LoadModule_std_system(@8.5.2) 初始化,默认加载为根环境下的 std.system 环境。 |
7775 | 8159 | () get-current-repl :取表示当前 REPL 环境的引用值。 |
7776 | 8160 | eval-string <string> <environment> :在参数指定的环境中求值作为外部表示的字符串。 |
@@ -7797,9 +8181,9 @@ | ||
7797 | 8181 | 带空格或水平制表符的字符串、以半角引号开始或结束的字符串和空字符串会被引用。 |
7798 | 8182 | remove-file <string> :移除参数指定的路径命名的文件。 |
7799 | 8183 | **注释** |
7800 | -当前实现依赖可用的 std.strings(@12.3) 环境。 | |
7801 | - | |
7802 | -@12.6 模块管理: | |
8184 | +当前实现依赖可用的 std.strings(@12.4) 环境。 | |
8185 | + | |
8186 | +@12.7 模块管理: | |
7803 | 8187 | 通过初始化基础上下文后调用 Forms::LoadModule_std_modules(@8.5.2) 初始化,默认加载为根环境下的 std.modules 环境。 |
7804 | 8188 | 模块约定: |
7805 | 8189 | 需求字符串(requirement string) 是具有 <string> 类型的非空字符串。 |
@@ -7828,9 +8212,9 @@ | ||
7828 | 8212 | 不提供访问创建的环境的操作,以避免污染外部的访问。若需公开其中的变量绑定,可使用返回或模块参数。 |
7829 | 8213 | 操作的设计同 klisp 的 ports 模块中的同名操作,但有以下不同: |
7830 | 8214 | 同时保存创建的环境,以避免因程序没有保存环境引用而无效化(@9.8.6) ,使访问变量绑定的程序具有未定义行为; |
7831 | -结果是加载结果(同 std.io 模块中的 load(@12.4) )而不是 #inert 。 | |
7832 | -**注释** | |
7833 | -当前实现依赖可用的 std.strings(@12.3) 、std.io(@12.4) 和 std.system(@12.5) 环境。 | |
8215 | +结果是加载结果(同 std.io 模块中的 load(@12.5) )而不是 #inert 。 | |
8216 | +**注释** | |
8217 | +当前实现依赖可用的 std.strings(@12.4) 、std.io(@12.5) 和 std.system(@12.6) 环境。 | |
7834 | 8218 | 通过不经过本模块的操作、重复字符串模板的重复项、符号链接和字符串大小写不敏感的文件名等可能绕过本模块的注册机制而重复加载同一个外部文件。本模块的操作不对这些情形进行任何检查。 |
7835 | 8219 | |
7836 | 8220 | @13 SHBuild 实现环境: |
@@ -7877,7 +8261,7 @@ | ||
7877 | 8261 | 前缀 SHBuild_ 的特性被设计为可被其它程序使用,但接口仍可能改变。部分特性参见外部用户文档 https://frankhb.github.io/YSLib-book/Scripts.zh-CN.html 。 |
7878 | 8262 | |
7879 | 8263 | @13.2.1 序(prelude) : |
7880 | -除 move!(@11.3.4) 及显式检查类型的赋值(如 string<-(@12.3) )外的不安全操作(@9.4.5) 被禁用,调用时引起错误(@9.5.1) : | |
8264 | +除 move!(@11.3.4) 及显式检查类型的赋值(如 string<-(@12.4) )外的不安全操作(@9.4.5) 被禁用,调用时引起错误(@9.5.1) : | |
7881 | 8265 | assign%!(@11.4.1) |
7882 | 8266 | assign@!(@11.4.1) |
7883 | 8267 | copy-environment(@11.3.7) |
@@ -7899,12 +8283,12 @@ | ||
7899 | 8283 | cons-cmd <string>... :串接命令。 |
7900 | 8284 | 参数指定构成命令的字符串。空字符串被忽略。结果是没有被忽略的字符串构成的串接,每个字符串附带一个后缀空格。 |
7901 | 8285 | rmatch? <string1> <string2> :简单正则匹配。 |
7902 | -使用 std.strings(@12.3) 的操作: | |
8286 | +使用 std.strings(@12.4) 的操作: | |
7903 | 8287 | 设参数列表 (&x &r) ,结果同求值 regex-match? x (string->regex r) 。 |
7904 | 8288 | putss <string>... :输出串接的字符串。 |
7905 | -以 puts(@12.4) 输出参数字符串的串接结果。 | |
8289 | +以 puts(@12.5) 输出参数字符串的串接结果。 | |
7906 | 8290 | system-ok <string> :调用外部命令并判断是否调用成功。 |
7907 | -使用 system(@12.5) 调用外部命令。 | |
8291 | +使用 system(@12.6) 调用外部命令。 | |
7908 | 8292 | 若 system 调用的值等于 0 ,结果是 #t ;否则,结果是 #f 。 |
7909 | 8293 | win32? <string> :判断字符串是否等于 "Win32" 。 |
7910 | 8294 |
@@ -7916,7 +8300,7 @@ | ||
7916 | 8300 | 若调用失败,则使用 cmd-fail 抛出异常。 |
7917 | 8301 | $set-system-var! <variable> <string> :设置变量绑定的值为命令结果。 |
7918 | 8302 | 若第一参数指定的变量在当前环境中不存在绑定,则修改绑定为调用作为外部命令的第二参数的值的结果。 |
7919 | -使用 system-get(@12.5) 调用命令。 | |
8303 | +使用 system-get(@12.6) 调用命令。 | |
7920 | 8304 | 判断错误的逻辑同 system-ok(@13.2.2) 。 |
7921 | 8305 | 若调用失败,则使用 cmd-fail 抛出异常。 |
7922 | 8306 | $assert <variable> <predicate> <string> :断言第一参数指定的变量在当前环境中存在绑定,且其值符合第二参数指定的谓词。 |
@@ -7926,17 +8310,17 @@ | ||
7926 | 8310 | $assert-absolute-path <variable> :断言参数指定的变量在当前环境中存在绑定,且其值是表示文件系统绝对路径的字符串。 |
7927 | 8311 | |
7928 | 8312 | @13.2.4 环境变量缓存: |
7929 | -脚本缓存环境变量并在访问缓存时可能按外部环境的要求以未指定的方式跟踪环境变量的访问操作。缓存的环境变量的值在第一次访问时确定。 | |
7930 | -对环境变量的直接访问是使用系统模块(@12.5) 的 env-get、env-set 和 env-empty? 的访问,忽略缓存的环境变量。直接设置环境变量不更新缓存的值。 | |
8313 | +脚本缓存环境变量并在访问缓存时可能按外部环境的要求以未指定的方式跟踪环境变量的访问操作。 | |
8314 | +缓存的环境变量的值在使用缓存的函数第一次访问时确定。若环境变量未设置,则缓存的值为空;否则,缓存的值被初始化为环境变量的值。 | |
8315 | +对环境变量的直接访问是使用系统模块(@12.6) 的 env-get、env-set 和 env-empty? 的访问,忽略缓存的环境变量。直接设置环境变量不更新缓存的值。 | |
7931 | 8316 | 自本节起,除非另行指定,本章的特性访问环境变量时,使用缓存的环境变量。 |
7932 | 8317 | 带缓存环境变量的访问接口如下: |
7933 | 8318 | safeenv-get <string> :同 env-get ,但使用缓存。 |
7934 | -初始化缓存的值为第一次调用时的环境变量的值。若环境变量未设置,则值为空。 | |
7935 | 8319 | safeenv-set <string1> <string2> :同 env-set ,但使用缓存。 |
7936 | -ss-verbose-puts <string> :若初始化时环境变量 SS_Verbose 的值非空字符串非空则调用 puts(@12.4) 输出参数。 | |
8320 | +ss-verbose-puts <string> :若初始化时环境变量 SS_Verbose 的值非空字符串非空则调用 puts(@12.5) 输出参数。 | |
7937 | 8321 | safeenv-empty? <string> :同 env-empty? ,但使用缓存。 |
7938 | 8322 | safeenv-restore <string> :重置参数指定名称的环境变量为缓存的值。 |
7939 | -$lazy-env-val <string> <body> :创建初始化参数指定名称的环境变量的值的求值代理对象(@12.1) 。 | |
8323 | +$lazy-env-val <string> <body> :创建初始化参数指定名称的环境变量的值的求值代理对象(@12.2) 。 | |
7940 | 8324 | 若第一参数指定名称的环境变量非空,则结果被求值时,求值 <body> 取得求值结果。 |
7941 | 8325 | $env-de! <variable> <body> :按环境变量定义变量的值。 |
7942 | 8326 | 使用 $def!(@11.3.7) 定义第一参数指定的变量绑定。 |