C++的void类型并非无人居住.问题在于,虽然它只有一个居民,非常像ML类语言中的Unit类型(又名()),居民不能被命名或传递为普通价值.例如,以下代码无法编译:
void foo(void a) { return; }
void bar() { foo(foo()); }
Run Code Online (Sandbox Code Playgroud)
而等效的(比方说)Rust代码编译得很好:
fn foo(a : ()) { return; }
fn bar() { foo(foo(())); }
Run Code Online (Sandbox Code Playgroud)
实际上,void就像一种unit类型,但只是半心半意.为什么会这样?
C++标准是否明确声明无法创建类型的值void?如果是,这个决定背后的理由是什么?如果没有,为什么上面的代码不能编译?
如果是一些向后兼容性相关的原因,请举一个代码示例.
要清楚,我不是在寻找解决问题的方法(例如使用空结构/类).我想知道现状背后的历史原因.
编辑:我稍微改变了代码示例中的语法,以明确我没有试图劫持现有的语法void foo(void)(因此,一些注释可能已过时).这个问题背后的主要动机是"为什么类型系统不像X",而不是 "为什么这一部分语法不像我喜欢的那样".如果你正在写一个关于打破向后兼容性的答案,请记住这一点.
通过单态化(仅单态化)在语言中实现多态性的一个限制是你失去了支持多态递归的能力(例如参见rust-lang #4287).
有哪些引人注目的用例支持编程语言中的多态递归?我一直在努力寻找使用它的库/概念,到目前为止我遇到过一个例子:
为了防止这个问题过于宽泛,我正在寻找其他程序/图书馆/研究论文,它们将多态递归的应用呈现给传统的计算机科学问题,例如那些编写编译器的问题.
我不想要的事情的例子:
回答显示如何使用多态递归从类别理论中编码X,除非它们演示了编码X如何有利于解决Y符合上述标准的问题.
小玩具示例,表明你可以用多态递归做X,但你不能没有它.
Rust的std::process::exit类型
pub fn exit(code: i32) -> !
Run Code Online (Sandbox Code Playgroud)
哪里!是"从不" 原始类型.
为什么Rust需要特殊类型呢?
将其与Haskell进行比较,其类型System.Exit.exitWith为
exitWith :: forall a. Int -> a
Run Code Online (Sandbox Code Playgroud)
相应的Rust签名将是
pub fn exit<T>(code: i32) -> T
Run Code Online (Sandbox Code Playgroud)
没有必要将此函数单独化为不同T的函数,因为a T从未实现,因此编译仍然可以工作.
这一章在现实世界中OCaml中描述了不同数据类型的运行时内存布局。但是,没有关于惰性值的讨论。
lazy_t实现,即它的运行时表示形式是什么以及编译器内置的主要操作是什么?链接到源代码将不胜感激。我查看了CamlinternalLazy模块,但是仅基于对Obj模块中函数的调用,似乎很难解读实际的表示形式。注意:此问题与 OCaml编译器/运行时有关。据我所知,有如何偷懒值应该由没有实施标准规范的 ocaml的编译器/运行。
在很多情况下,我看到两者在看似可以互换使用 - 它们是相同还是不同?这似乎也取决于语言是在谈论UTF-8(例如Rust)还是UTF-16(例如Java/Haskell).代码点/标量的区别是否依赖于编码方案?
我一直在试用绑定包 - 你可以尝试使用的一个玩具示例是System F.与包文档中的示例不同,它包含一个由lambda绑定的变量的类型参数,System F将有两个类型参数,一个用于普通变量(由普通的lambda抽象绑定),一个用于类型变量(由类型抽象绑定).
我不太明白如何使用这个包,但是看一下这些例子,我得到的印象是我应该从编写Monad表达式类型的实例开始.然而,我遇到了麻烦,因为我无法想出具体的东西并且"显然是正确的"(即通过检查看起来直观正确).到目前为止我有
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE LambdaCase #-}
module SystemF where
import Bound
import Control.Monad
import Data.Bifunctor
-- e ::= x | ?x : ?. e | e1 e2 | ?X. e | e [?]
-- t denotes type variables, e denotes expression variables
data Exp t e
= Var e
| Lam (Scope () (Exp t) e)
| App (Exp t …Run Code Online (Sandbox Code Playgroud) 考虑以下示例(?>= ghci,$= shell):
?> writeFile "d" $ show "d"
$ cat d
"d"
?> writeFile "d" "d"
$ cat d
d
?> writeFile "backslash" $ show "\\"
$ cat backslash
"\\"
?> writeFile "backslash" "\\"
$ cat backslash
\
?> writeFile "cat" $ show "" -- U+1F408
$ cat cat
"\128008"
?> writeFile "cat" ""
$ cat cat
Run Code Online (Sandbox Code Playgroud)
我理解另一种方式"\128008"只是""在Haskell源代码中表示的另一种方式
.我的问题是:为什么""示例的行为类似于反斜杠而不是像"d"?既然它是一个可打印的角色,它不应该像一个字母吗?
更一般地说,确定字符是显示为可打印字符还是转义码的规则是什么?我查看 了Haskell 2010语言报告中的第6.3节,但没有指定确切的行为.
在64位平台上,int由于指针标记,OCaml的类型为63位.这允许整数被打开,并且仍然可以在运行时与指针区分开来,从而实现精确的GC.IIRC,GHC RTS中的GC也是精确的,但GHC的Int是64位,它们可以是未装箱的.如果是这种情况,那么运行时系统如何区分Ints和指针?看来,区分其他未装箱的字大小值和指针也会产生同样的问题.
我有一个使用的结构Arena:
struct Foo<'f> {
a: Arena<u64>, // from the typed-arena crate
v: Vec<&'f u64>,
}
Run Code Online (Sandbox Code Playgroud)
只要引用的生命周期受主结构体生命周期的约束,延长竞技场引用的生命周期是否安全?
impl<'f> Foo<'f> {
pub fn bar(&mut self, n: u64) -> Option<&'f u64> {
if n == 0 {
None
} else {
let n_ref = unsafe { std::mem::transmute(self.a.alloc(n)) };
Some(n_ref)
}
}
}
Run Code Online (Sandbox Code Playgroud)
有关更多背景信息,请参阅此Reddit 评论。
据我所知,Rust ABI不稳定.但是,Rust编译器当前执行一些优化以将字段压缩到标记中.例如:
use std::mem::size_of;
enum Node {
N1_1 {
is_good: bool,
stuff: u32,
},
N1_2 {
is_good: bool,
left: Box<Node>,
right: Box<Node>,
},
}
enum Node2 {
N2_1 { stuff: u32 },
N2_2,
}
fn main() {
println!("{:?} {:?}", size_of::<Node>(), size_of::<Node2>());
}
Run Code Online (Sandbox Code Playgroud)
这打印24 8.显然,正在发生的是字段被折叠到构造函数标记中.这种行为有保证吗?我不是在询问特定的内存表示是否保持不变,而是否承诺未来某个时间点的大小不会增加.
我想不出一个很好的理由,为什么他们可能想在某些时候改变代表性以增加尺寸,但也许这只是我缺乏想象力,所以我正在寻找一个"官方"答案.
与GitHub问题/ RFC的链接会很有帮助.我尝试浏览问题跟踪器,但无法找到任何内容.我能找到的最接近的东西bool是1字节大小,这是无法保证的.