Types 返回类型是什么(显示8)?/什么是空值表达式?

Types 返回类型是什么(显示8)?/什么是空值表达式?,types,scheme,lisp,kawa,Types,Scheme,Lisp,Kawa,在方案的实施中,表达式 (空?()) 显然是回报 #t 但是如果我打字 (null?(显示8)) 在解释器中,输出是 8#f 因此,似乎display是一个确实有副作用的函数,即打印值和某种非空返回值。嗯。可能(显示8)返回8?毕竟,8和(显示8)都会在交互式口译员中显示8 所以我打了进去 (=8(显示8)) 反应是 /dev/stdin:5:6: warning - void-valued expression where value is needed java.lang.NullPoint

在方案的实施中,表达式

(空?())

显然是回报

#t

但是如果我打字

(null?(显示8))

在解释器中,输出是

8#f

因此,似乎
display
是一个确实有副作用的函数,即打印值和某种非空返回值。嗯。可能
(显示8)
返回
8
?毕竟,
8
(显示8)
都会在交互式口译员中显示
8

所以我打了进去

(=8(显示8))

反应是

/dev/stdin:5:6: warning - void-valued expression where value is needed
java.lang.NullPointerException
    at gnu.math.IntNum.compare(IntNum.java:181)
    at atInteractiveLevel-5.run(stdin:5)
    at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:293)
    at gnu.expr.ModuleExp.evalModule(ModuleExp.java:212)
    at kawa.Shell.run(Shell.java:283)
    at kawa.Shell.run(Shell.java:196)
    at kawa.Shell.run(Shell.java:183)
    at kawa.repl.processArgs(repl.java:714)
    at kawa.repl.main(repl.java:820)
8
所以,
(显示8)
不是null,而是“void value”?这是什么意思?我可以像在Scheme中检查空值一样检查空值吗


另外,为什么
8
出现在错误消息之后?

您的推断是正确的,即
显示
是一个返回值的函数(除了具有打印到当前输出端口的副作用)。然而,
display
的这一特定调用返回的值是read-eval-print循环在计算表达式时自行选择不打印的值

川有一些特殊的功能;其中之一就是
#!void
,相当于计算表达式
(值)
(表示“完全没有值”)的结果。如果您得到值
#!void
从读取评估打印循环中,它不会打印:

#|kawa:1|# #!void
#|kawa:2|# (values)
#|kawa:3|# 
这是因为Kawa的read-eval-print循环用于打印表达式计算结果的值,而
display
在给定
#时将选择不打印任何内容!作废


在比较
8
(display 8)
行为的具体实验中,实际发生的情况有着至关重要的区别。当您向解释器提供任何输入时,它:

  • 读取(并编译)输入
  • 将编译后的表达式求值为一个值,然后
  • 打印出结果值
  • 因此,当您输入
    8
    时,打印将在步骤3中进行。当您输入
    (显示8)
    时,打印在步骤2中进行,然后步骤3中的打印不会打印任何内容(因为
    (显示8)
    返回的值是解释器选择不打印的值)

    观察这种区别的一种方法是:根据兴趣表达构建一个列表

    #|kawa:1|# (list (display 7) 8 (display 9))
    /dev/stdin:1:7: warning - void-valued expression where value is needed
    /dev/stdin:1:21: warning - void-valued expression where value is needed
    7 9 (#!null 8 #!null)
    #|kawa:2|# 
    
    这里我们看到,在求值步骤中,解释器先显示
    7
    ,然后显示
    9
    ,然后构建了一个包含三个元素的列表:
    #!空
    8
    ,然后是
    #!再次为空


    Kawa解释器还警告我们,我们的代码似乎有问题:Kawa解释器在读取和编译步骤(发生在评估和打印步骤之前)期间足够聪明,可以分析代码是否存在潜在问题。在这里,它表示“调用
    显示的结果
    并不意味着它是一个正常值”(与数字或字符串相比)

    因此,这就解释了为什么会看到错误消息(因为它认为调用
    display
    的结果是无效值),并且它知道对这些值的处理可能与用户的期望不符。(它还解释了为什么在错误消息之后打印示例中的数字
    8
    :因为错误消息是在“读取”步骤中生成的,但显示是在“评估”步骤中进行的,如上所述


    为什么我要说“这些值的处理可能不符合用户的期望”?好吧,从上面我们运行
    (列表(显示7)8(显示9))
    的实验中,您可能会推断评估
    (显示7)
    的结果是
    #!null
    。但事实并非如此

    在Kawa中,
    #!null
    是一个特殊常数,它不同于
    #!void
    。出于某种原因,Kawa解释器决定,当您将
    (display 7)
    插入列表构造表达式(或者更一般地说,我认为任何需要非void值的上下文)时,它可以丢弃
    的返回值(显示7)
    并将
    #!null
    插入其中

    为什么我要这么说呢?还有另一种方法可以在Scheme中打印值:您可以使用
    写入
    过程。
    显示
    过程通常用于“人类(或最终用户)可读”输出,而
    write
    过程旨在显示更多关于给定数据结构的信息(如果可用)。例如:

    #|kawa:1|# (display "Hello World")
    Hello World
    #|kawa:2|# (write "Hello World")
    "Hello World"
    #|kawa:3|# 
    
    在上面的例子中,
    display
    丢弃了我有一个字符串的信息,只关注该字符串的内容,而
    write
    告诉我“我们这里有一个字符串,它有11个字符长。下面是它的内容。”

    那么,如果我们
    编写调用
    display
    的结果会发生什么呢

    #|kawa:1|# (write (display 8))
    8#!void
    #|kawa:2|# 
    
    在这里,我们没有从读取和编译步骤得到任何警告。相反,它保留了
    (display 8)
    的值。因此它首先计算
    (display 8)
    (将
    8
    打印到输出中),然后将从
    (#void)
    生成的值输入到
    写入
    调用中


    (我不会说这是有史以来最清晰的语义。但我的推断是,Kawa's告诉我们,编译器被允许插入一个
    #!null
    来代替
    #!void

    你的推断是正确的,即
    display
    是一个返回值的函数(除了具有打印到当前输出端口的副作用外)。但是,此特定调用
    显示
    返回的值是read eval print loop simp返回的值
    
    (list (display 8)) ; ==> (#!void) (and prints 8 on the terminal as side effect)