Mathematica是一种无类型语言吗?

14

与大多数编程语言不同,Mathematica 中的每个值都是一个表达式。对任何表达式应用任何操作都会产生另一个表达式。因此,Mathematica 实际上只有一种类型。Mathematica 不进行静态类型检查,甚至可以说它在运行时也不会检查类型。

例如,在 Mathematica 中将整数表达式 1 加到字符串表达式 "foo" 中会导致(荒谬的)表达式 1+"foo",但不会出现错误。在其他情况下,Mathematica 会提供关于荒谬输入的反馈,但生成此反馈的检查必须由程序员显式执行。

因此,描述 Mathematica 为弱类型语言与静态或动态类型语言相对而言是否公正呢?


1
@Yaroslav Bulatov:你所引用的帖子是关于Mathematica的性能的。这个Stack Overflow问题与Mathematica的性能毫无关系。这是关于分类Mathematica的类型系统的。 - J D
3
这个问题似乎是主观的。 - Yaroslav Bulatov
你并不是真的在问基于符号项重写的计算机代数系统是否是一种类型化语言,对吧?为什么在克林贡代数中 1 + "foo" 是无意义的?顺便说一下,在 Mathematica 中,“无意义”并不总是被计算,因此它的含义比较弱,只是表示“没有相应的规则”。 - gwr
我认为(口语化地说),Mathematica是一种非常强类型的语言:实际上,所有东西都是字符串。 :) - gwr
5个回答

18

与“类型”不同,Mathematica拥有“头”的概念,其中任何Mathematica表达式都具有一个。这符合他们的“一切皆为表达式”范例

可以通过函数FullForm[]Head[]查看Mathematica表达式的结构。例如,Head[3]返回IntegerHead[2/3]返回RationalHead[I]返回ComplexHead[.3]返回RealHead[a]返回Symbol(假设您还没有将a赋值给任何内容),Head["a"]返回StringHead[{2}]返回List...我相信你已经明白了。

这样做的美妙之处在于,可以编写函数,使它们仅接受具有特定头的参数。例如:

f[x_Real] := x^2

f[3]
f[3]

f[3.]
9.

这个关于模式的讨论应该会给你一些思路,如何设置函数,使它们仅适用于具有特定头部或头部集合的对象。


你认为这与将Mathematica分类为静态、动态或无类型有何关联? - J D
3
@OP和downvoter:没有“类型”,只有“头”。我个人并不明白为什么你们如此在意将其归类为“类型化”、“非类型化”或其他无聊的东西。 - user414706
Plus[] 为例,Mathematica 中的例程/函数/过程会查看其输入的头部(或更一般地,匹配的模式),并相应地进行操作。1+1/2 的结果是带有 Rational 头部的 3/2,因为 Plus[] 知道在给定 RationalInteger 输入时该怎么做。相比之下,1+Sqrt[2] 不会给出“有用”的结果,因为 Plus[] 不知道如何处理带有 Sqrt[] 头部的输入。 - user414706
3
我因为这个回答和我的问题关于Mathematica类型系统的分类关系不大而对其进行了负评。其他人可能也对其他回答进行了负评。 - J D

7
如果我们将“静态类型”和“动态类型”这些短语视为术语,指的是语言在检查操作是否有效时检查类型的时间,那么我认为可以用术语“未经类型检查”的方式来描述Mathematica——因为它“从不”检查操作是否适用于某种类型。
然而,我喜欢Belisarius使用“类型不可知”的术语。我之所以这么说,是因为虽然语言中几乎所有的类型检查都是惯用的(即由程序员实现,而不是语言实现),但应用运算符到有类型操作数的概念也是惯用的!
考虑“无意义”的例子1 + "foo"。我认为可以公正地说,在学习该语言时,几乎所有Mathematica用户都会遇到此类情况。当以C语言的风格编写代码时,问题尤为明显。在Mathematica圈子里,如何处理这种情况是一个广泛讨论的话题。
另一方面,这种弱点也是Mathematica的最大优势。Mathematica针对创建新符号进行了优化。许多符号具有与小学算术中的加法非常相似的+概念。在构建这样的符号时,如果Mathematica插手并抱怨+的操作数不是数字,那将非常不方便。在这样更高级的Mathematica应用程序中,“荒谬”的例子不仅是“有意义的”,而且实际上是至关重要的。
因此,考虑到这一点,类型问题经常是无关紧要的。因此,我喜欢Belisarius的“类型不可知”表述。我赞成他的观点;)
编辑
我将尝试澄清我在区分“无类型”和“类型不可知”时所想的内容。
阅读各种答案和评论后,我试图弄清楚Mathematica和LISP之间的区别。后者通常被视为“动态类型”的示例,尽管核心LISP求值器非常像Mathematica,几乎没有任何类型检查。我们在LISP程序中看到的类型错误大多是由(通常是内置的)函数中的硬编码检查发出的。例如,+将仅接受数字参数,即使求值器本身无论如何都不关心。话虽如此,用LISP编程的“感觉”与用Mathematica编程的“感觉”大相径庭(至少对我来说是这样)。1 +“foo”的例子真正捕捉了这种差异。
虽然我大体上同意将Mathematica描述为“未类型化”,但我仍感觉缺少某些东西。对我来说,汇编语言是未类型化的,早期的FORTRAN和ANSI C也是如此。在这些情况下,参数的位模式是唯一重要的,如果我传递一个字符串参数而需要整数,则程序将继续执行。Mathematica当然也共享了这种未类型化的行为。但有一个区别:在汇编语言、FORTRAN和C中,缺乏类型检查很少会带来好结果。正如我上面提到的,在Mathematica中,依赖这种行为有时甚至很常见。

进入“类型不可知”。我喜欢它不偏不倚的态度,听起来比“未类型化”更不激烈。我感觉它反映了Mathematica本质上未类型化的特性,但留给那些支持LISP动态风格中惯用的类型检查(即“head”惯用法和支持功能)的语言特性一些余地。

因此,简而言之,我认为Mathematica在完全未类型化和动态类型之间徘徊。“类型不可知”对我来说捕捉了这种情感。你的看法可能不同 :)

我坦率地承认,仅从“untyped”和“type-agnostic”这两个短语来检查我的回答,没有人可能重构出任何东西。再次强调,我认为“untyped”是对Mathematica的公正描述,但我也喜欢“type-agnostic”的事实,因为它引发了各种回应这个SO问题的问题。

如果有人投票支持我的答案,你就可以成为我参议员竞选的竞选经理:D - Dr. belisarius
+1 尽管我仍然不理解“类型无关”究竟是什么意思。例如,汇编语言是否是类型无关的? - J D
我无法理解为什么当使用1 + "foo"时,+不会生成错误。如果用户希望在最初生成错误时使其有效,他只需重载+即可。在我看来,在这种情况下+不会生成错误,仅仅是因为像1 + f[x]这样的符号表达式应该被允许,而1 + String[<hidden bits>]看起来非常像它。不幸的是,在Mathematica中,符号变量/函数(不会计算任何值)的概念并不是一流的。 - masterxilo

5

从实际应用角度来看,我认为可以说Mathematica比无类型更加不受类型限制。

此外,您可以使用类似以下的东西轻松构建一个有类型子语言(以下是非常基本的示例):

Unprotect[Set]; 

Set[a, x_] := If[Element[x, Integers], x, 0, 0];  

然后尝试:

a = 1; (*runs ok*) 

并且

a = "caca"  (*assigns zero to a*)

编辑

此外,您可以将用户定义类型构建为命名模式,并在重新定义Set时使用它们,而不是整数。
类型组合应该以相同的方式工作。


1
你能详细说明无类型和“类型不可知”的区别吗? - J D
+1是因为我喜欢“类型不可知”,但如果你想避免宗教战争,你可以称其为“可选类型”。只要你不指望它绝对不会出错,就可以采用适合问题的任何模型。例如,静态: 'f[a_List]';动态: 'Head[a]';未命名: 'g[a_]'。实际上,MMA既不是鱼也不是禽,这可能与它作为计算系统(而非通用语言)的起源有关。你还可以问:“它是一种函数式语言吗?如果不是,那么它是什么?”然后会得到更多矛盾的回复。 - Tim
@Tim 我不喜欢圣战,所以我决定让答案保持原样,不再发表评论。我认为你的评论与我的想法一致,也许比我的回答更清晰。 - Dr. belisarius

5

Mathematica具有一些类型,并且它是动态的。您可以使用StringIntegerRealComplexListSymbol这些类型。您可以通过执行以下操作之一创建仅针对一个类型操作的函数:

f[x_Integer]:=x+1

以创建仅在整数上操作的函数。

Mathematica基于模式和替换机制;对我来说,类型似乎是帮助您开发模式的另一种方式。在1 + "foo"的情况下,没有模式可以计算添加到字符串的数字,因此结果就是表达式本身。在1 + 2的情况下,有一个模式可以将数字相加并进行评估。Mathematica的模式和替换规则可以更加复杂,如果您感兴趣,最好阅读一本书。


7
使用Integer等作为类型只是一种惯例:模式x_Integer匹配当且仅当x的头部是Integer。因此,对于With[{x=Integer["hello"]},f[x]],将会得到Integer["hello"]+1... - Janus
@Janus:这显然与动态语言实现中使用的运行时类型标签有些相似,但不同之处在于“函数”f实际上并不是一个函数,而是一条重写规则。 - J D
1
@Jon Harrop。完全正确。我只是试图表达一个观点,即所谓的类型系统可能只是类型系统的粗略近似 -- 没有任何契约。 - Janus
@Janus:是的,如果那些运行时类型标签没有被自动测试,那么它就不是真正的动态类型,当然也不是静态类型... - J D
1
请注意,“仅对整数操作的函数”实际上是运行时分派:如果x的头部是Integer,则应用此规则,否则查找另一个规则。如果这是动态类型,则语言将要求其他参数导致运行时类型错误,但Mathematica不会这样做。 - J D
版本12新增了Typed - Alan

4
短答案:未分类或无类型。这是 Wolfram Research 描述其产品的方式。请参见此处。 长答案: Jon,我认为你的问题实际上取决于你所谓的未分类是什么意思。为了吸引权威资源 维基百科 的注意,“相比之下,未分类语言(如大多数汇编语言)允许对任何数据执行任何操作,这些数据通常被认为是各种长度的位序列。”
阅读之前的回答,似乎争论的核心是当类型检查器遇到错误时应该做什么。通常的答案是停止评估并报告某种错误。从Stackoverflow上的几个早期问题 (1)(2) 中可以看出,在Mathematica中没有内置的优雅方式来处理这个问题。(我要补充一点,随着版本8中更多地强调编译为C,编写经过类型检查的代码是可能的,但我不确定这是否应被视为主语言的一部分。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接