35
$\begingroup$

"Everything is an expression" is a popular citation from many Mathematica guidebooks. So, what is type in Mathematica? How does it relate to common types from Haskell, for example?

I did some simulation of dependent types:

DependentType::illegal= "Value is illegal for dependent types with constraints";
FixedSizedVector[n_?(Positive[#]&)] :=
Module[{type = Symbol["Vector" <> ToString[n]]},
    type[dat_] := Message[DependentType::illegal] /; VectorQ[dat] && Length[dat] != n;
    type
];
types =
Map[
    FixedSizedVector[#]&,
    Range[1, 25]
];

I have received an array types of symbols that are constrained with pattern checking rules. Is it a family of types?

Another point is Head replacing. For example, for list Range[1, 5] I can just type Plus @@ Range[1, 5] and get Integer[15]. From the point of view of type, what is Apply?

$\endgroup$
2
  • 3
    $\begingroup$ "Mathematica ... gets great flexibility precisely from avoiding the use of types." -Stephen Wolfram $\endgroup$
    – Andrew
    Aug 18, 2012 at 19:51
  • $\begingroup$ I rather say that Mathematica introduces a new "dimension" called "Expression" in the flat model "types -> values". Expressions make that model to commute: one can get from types to values and vice versa. So Mathematica's approach is a flexible way to do "types <-> values" model. $\endgroup$ Nov 20, 2012 at 11:12

4 Answers 4

23
$\begingroup$

The nearest Mathematica has to "types" are Heads of expressions that are Atoms. For example:

Through[{AtomQ, Head}[2]]

{True, Integer}

Through[{AtomQ, Head}[2 + I]]

{True, Complex}

Through[{AtomQ, Head}["cat"]]

{True, String}

and so on...

There are also somewhat different "types" in the context of Compile.

$\endgroup$
5
  • $\begingroup$ Thank you for response! So, is it correct to claim that Mathematica has not types as they are in strict-typed languages replacing them with a more generalized notion of expression? Types and values are expressions, so programmer has an ability to switch between them (as I modeled with some kind of dependent types in question body). $\endgroup$ Mar 25, 2012 at 9:29
  • $\begingroup$ I think this is correct. $\endgroup$ Mar 25, 2012 at 9:49
  • $\begingroup$ Thanks. Just a single extra detail: not only Symbol can be interpret as a type, but any unevaluated/evaluated expression. So my last question for this topic is: does Mathematica provide a language that cannot be explained by traditional theory of types? Are there existed any papers about Mathematica programming language semantics? $\endgroup$ Mar 25, 2012 at 13:44
  • $\begingroup$ Mathematica is a symbolic programming language see this post stackoverflow.com/questions/4430998/…. Also the book "Structure and interpretation of computer programs" by Abelson et al. deals with concepts relevant to Mathematica. $\endgroup$
    – faysou
    Mar 26, 2012 at 7:11
  • $\begingroup$ Let me add to the above. In programming language theory a Type is not just a set of values, but also the operations available to them. In Mathematica a value with a particular head can be considered of that 'type' but regarding the operations available to such head, Mathematica allows you to add and remove rules for those values, so types are defined at runtime and can change. In addition, upvalues and downvalues enrich the picture quite a bit. $\endgroup$
    – carlosayam
    Dec 31, 2013 at 11:06
18
$\begingroup$

In Mathematica, the type of a built-in object is represented by the Head. For example, Head[3] is Integer, Head[1.5] is Real and Head[a] is Symbol (assuming that a hasn't been assigned a value, of course, because in that case, you'll get the Head of that value).

Note that for expressions of the form foo[bar,baz], the head is foo. Most expressions are internally represented in such a form, as can be seen by applying FullForm, for example:

{1, 2, 3}//FullForm
(*
==> List[1, 2, 3]
*)
a + b //FullForm
(*
==> Plus[a, b]
*)

and consequently the Head gives List in the first case, and Plus in the second case.

Edit

Note that Mathematica evaluates the expression given to Head if possible, so the result of Head[1+2] is not Plus but Integer because Head is replaced only after evaluating 1+2 which gives the value 3 of type Integer. On the other hand, Head[a+b] gives Plus (assuming neither a nor b have any applicable definition), because a+b is "inert" under evaluation: There are no evaluation rules which could applied to it, therefore it is self-evaluating. Note that the same is true for head replacing (Apply, @@). Also note in that context that Mathematica, unlike the typical functional language, continues evaluation until it reaches an "inert" expression which cannot be further evaluated. This is best demonstrated by the following Mathematica code:

foo := bar;
bar := 3;
foo
(*
==> 3
*)

Here in the first step, Mathematica evaluates foo, which evaluates to bar. But it doesn't stop there, but continues to evaluate bar which gives the result 3. Now that result cannot be further evaluated, and therefore is given as the result. On the other hand, the seemingly equivalent Lisp code

(progn
  (setq foo 'bar)
  (setq bar 'baz)
  foo)

will evaluate to bar because after evaluating foo to bar, Lisp doesn't try to re-evaluate.

To see how this interacts with head replacing, consider the following code:

Plus@@Range[1,5]
(*
==> 15
*)

In the first step, Range[1,5] is evaluated, giving {1,2,3,4,5}, that is List[1,2,3,4,5] with head List. Now Apply replaces the head of that expression with Plus, resulting in Plus[1,2,3,4,5], that is 1+2+3+4+5. Now that is again an evaluatable expression, evaluating to 15 which as an "inert" expression is the final result.

Note that you can prevent Mathematica from evaluating an argument before calling a function by using Unevaluated. For example, Head[Unevaluated[Range[1,5]]] gives Range, and Plus@@Unevaluated[Range[1,5]] gives 6 (because the Range got replaced by Plus, and then the resulting expression is evaluated). In this respect, Unevaluated is similar to Lisp's quote. Note however that due to Mathematica's continuing evaluation, the effects can be drastically different. For example, consider the code

f[x_]:=x
f[Unevaluated[1+1]]
(*
==> 2
*)

You might expect this to return 1+1 unevaluated, just like the corresponding Lisp code with quote does. However while Unevaluated indeed causes 1+1 to be passed to f without further evaluation, applying the definition of f results in 1+1 (note: without the Unevaluated!), which Mathematica continues to evaluate, resulting in 2.

$\endgroup$
5
  • $\begingroup$ This also applies to types of numbers, like Head/@ {0.5, 1, I, 3/2} == {Real, Integer, Complex, Rational}. Although they also considered atomic (AtomQ/@ {0.5, 1, I, 3/2} == {True, True, True, True}) and are treated subtly differently by some functions, like Map primarily operates on atoms, yet Apply does not. $\endgroup$
    – rcollyer
    Mar 25, 2012 at 1:50
  • $\begingroup$ Thank you for responses! Head is the case, but I have some problems with that. In Mathematica, all is an expression such as functions and values are. So how should I interpret head replacing? For example, Range[1, 5] returns list but it has Head Range. And it becomes just a Integer if we replace its Head with Plus: Plus @@ Range[1, 5] is just Integer[15] $\endgroup$ Mar 25, 2012 at 8:52
  • $\begingroup$ @spk: While the expression Range[1,5] has head Range, it evaluates to {1,2,3,4,5} with head List. Similarly, Plus@@Range[1,5] first evaluates Range[1,5] to {1,2,3,4,5}, then replaces the head (List) with Plus, giving Plus[1,2,3,4,5], i.e. 1+2+3+4+5, which evaluates to 15. Note that a difference between M'a and typical functional languages is that M'a continues evaluating until there's nothing left which could be evaluated. For example, in M'a foo:=bar;bar:=3;foo evaluates to 3, while e.g. in Lisp (progn (setq foo ´bar) (setq bar 3) foo) evaluates to bar. $\endgroup$
    – celtschk
    Mar 25, 2012 at 16:10
  • 1
    $\begingroup$ @celtschk That comment is probably worth working into your answer. $\endgroup$ Mar 26, 2012 at 4:18
  • $\begingroup$ @BrettChampion: Done. $\endgroup$
    – celtschk
    Mar 26, 2012 at 11:48
8
$\begingroup$

Since Mathematica 10, there is the TypeSystem` Context, that is nearly what you might be looking for. It is just a wrapper around patterns.

TypeSystem`ConformsQ[
  {1, 2, 3}, 
  TypeSystem`Vector[TypeSystem`IntegerT, 3]
]  (* --> True *)

It is the thing being used internally by Dataset-related functions. (Maybe one should say something like Dataset[{}] first to beable to use the TypeSystem; I’m too lazy to check again whether it is necessary.)

Unfortunately, it is not documented but you can figure it out looking at names in ?TypeSystem`* and at GeneralUtilities`PrintDefinitions @ TypeSystem`Validation`PackagePrivate`vtor.

TypeSystem`Either is (was?) not working as expected, but see my patch.

$\endgroup$
4
$\begingroup$

In Mathematica, values are expressions.

Types in computer science are collections of values.

Perhaps a type in Mathematica could be viewed as a pattern which matches a certain class of expressions.

Programming language designers often classify values according to the different roles that different types of values are allowed to play in that language. For example, in functional languages, functions are often described as "first-class citizens" because they can be passed as parameters to functions and returned as values from functions, much like atomic data types such as integers and strings.

Mathematica is very flexible in that (like S-expressions in LISP) values can play any role for which meaningful transformation rules are defined that match those values (perhaps as part of a larger expression). Viewing types as patterns that describe the class of expressions that the pattern matches, one could ask what does it mean then to "instantiate" a value of a certain type in Mathematica ... in analogy to instantiating an object (i.e. a value) in say Java of the abstract data type described by the class definition?

When the kernel reads in any expression that matches that pattern (even if no explicit pattern matching is requested) one could say that a value of the pattern type has been instantiated (through the act of parsing the expression). So here is one sense in which the notion of types as patterns may be a little more general than how this term is used in other programming languages like Java. The pattern-as-type notion of type allows one to test for type membership, which is useful for verifying program correctness, making internal optimizations, etc. But it is also possible to instantiate a value of a given type (actually of an infinite number of different types) without ever defining the type (i.e. specifying a pattern that matches the type) explicitly as in a class definition.

So in Mathematica, all conceivable types already exist implicitly (which perhaps helps clarify the Wolfram quote in the response to the OP). The patterns are then like names that explicitly describe types when one wants to use them for testing say membership in a certain class of expressions defined by the type pattern ... perhaps much like lambda expressions name the anonymous functions that they define at time and place of use.

Or maybe not ... anyone else have any thoughts here?

$\endgroup$
3
  • $\begingroup$ I suppose, types aka patterns is a very nice interpretation. I also see that this is a more general notion than type from classical programming languages. $\endgroup$ Dec 3, 2012 at 9:03
  • $\begingroup$ It's worth pointing out here that patterns themselves are valid expressions in Mathematica. Clearly Mathematica is just one of many programming languages today that treats types as values. Part of what makes Mathematica so appealing however is the diversity of complex data types that can be easily created on the fly using its rich pattern-matching syntax. $\endgroup$ Dec 3, 2012 at 21:23
  • $\begingroup$ Your point describes the Mathematica world only partially. Mathematica does not introduces the notion of the type at all. It uses only List with special treatment of the first element called Head. This concept allows anybody to emulate any known type system. Also it makes Mathematica to have the most flexible programming language I have ever seen. However, it must be noted that Mathematica is an interpretator. So it is impossible to split the program life-cycle into the compile-time and run-time. $\endgroup$ Dec 4, 2012 at 7:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.