Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

In a program I'm writing, I create a list of lists, that looks something like this:

{{100, {1,2,3,4,5}},
 {105, {2,4,6,8}},
 {42, {42,39,56}}}

I then pass this list of lists around to other functions. All well and good -- the problem is that this program has been developed iteratively, and this representation has changed, and so now I'd like to take more care in checking that the function is receiving the right kind of input.

In another language I could make this list of lists into its own type, and the compiler would enforce things. I know Mathematica has some facility for doing type checking, at least with primitive types, such as:

myFunc[a_String, b_Integer] := ...

But are there facilities available for creating (and enforcing, or at least checking) one's own ADTs?

share|improve this question
1  

4 Answers 4

up vote 23 down vote accepted

In practice, enforcing strong types in Mathematica seldom pays off, just because, as mentioned by @belisarius, Mathematica is untyped (and perhaps more so than most other langauges, since it is really a term-rewriting system). So, most of the time, the suggestion of @Mr.Wizard describes what I'd also do.

The way to define ADT-s (strong types) was described in depth by Roman Maeder, in his books on Mathematica programming. This requires something more than what you provided in your question - namely, a more formal definition of what is in your data structure (so that we can form constructors, selectors and mutators). I will give here a very simple example to show how ADT can be implemented in Mathematica. The key points are using UpValues and (mostly inert) symbols to serve as heads of new types. Consider a simple "pair" type:

ClearAll[pair];
pair /: getFirst[pair[fst_, sec_]] := fst;
pair /: setFirst[pair[_, sec_], fst_] := pair[fst, sec];
pair /: getSecond[pair[fst_, sec_]] := sec;
pair /: setSecond[pair[fst_, _], sec_] := pair[fst, sec];

We can now define some function on this new type:

Clear[sortPairsByFirstElement];
sortPairsByFirstElement[pairs : {__pair}, f_] :=
     Sort[pairs, f[getFirst[#1], getFirst[#2]] &];

And here is an example of use:

pairs = Table[pair[RandomInteger[10],RandomInteger[10]],{10}]

{pair[0,10],pair[4,7],pair[5,3],pair[10,9],pair[9,2],pair[6,10],pair[3,7], pair[4,2],pair[0,4],pair[3,9]}

 sortPairsByFirstElement[pairs,Less]

{pair[0,4],pair[0,10],pair[3,9],pair[3,7],pair[4,2],pair[4,7],pair[5,3], pair[6,10],pair[9,2],pair[10,9]}

You can enforce stronger typing on what can go into a pair. One thing I've done is to enforce that in the "constructor":

pair[args__] /; ! MatchQ[{args}, {_Integer, _Integer}] :=
    Throw[$Failed, pair];

The technique just described produces truly strong types, in contrast to the pattern-based typing. Both are useful and complementary to each other. One reason why such strong typing as described above is rarely used in Mathematica is that all the rest of the infrastructure usual for the strongly-typed languages (compiler, type system, smart IDE-s, type-inference) is missing here (so you'd need to construct that yourself), plus often this will induce at least some overhead. For example, we may wish to represent an array of pairs as a 2-dimensional packed array for efficiency, but here the pair type will get in the way, and we'd have to write extra conversion functions (which will induce an overhead, not to mention the memory-efficiency). This is not to discourage this type of things, but just to note that over-using them, you may lose some advantages that Mathematica offers.

share|improve this answer

As you mentioned in your question and belisarius illustrates above, you can check arguments with arbitrary pattern matching.

When I need to do checks of this kind I often use a couple of methods; I will define the pattern once and then reference it by name:

p1 = {{_Integer, {_Integer ...}} ...};

dat = {{100, {1, 2, 3, 4, 5}}, {105, {2, 4, 6, 8}}, {42, {42, 39, 56}}};

f[x : p1] := First[x]

f[dat]
{100, {1, 2, 3, 4, 5}}

I also will make this check only once in a Module that calls other functions so that this check is not wastefully made a number of times. This may be common sense but I mention it anyway.

share|improve this answer
2  
This is exactly what I was going to suggest. Damned sleep interrupting my attempts to garner reputation. :) –  rcollyer Apr 23 '12 at 12:25

You can effectively create your own types by using the feature that Mathematica expressions have a Head, the head can be used to define a type. Functions can then use the Head value to apply only to arguments matching the defined type.

A version with loose format checking, format checked only upon creation,can be implemented as simply as this:

(* Define your type *)
    ValidMyTypeQ[data_List] :=(* check data format here *) 

    CreateMyType[data_List] := If[ValidMyTypeQ@data, MyType @@ data,Print@"Incorrect format"]

(* Utilise your type *)
    myFunction[data_MyType] := (* do your funky stuff *)

A stiffer version could format check each time MyType is passed to a function:

(* Define your type *)
 ValidMyTypeQ[data_MyType] :=(* check data format here *)  

 CreateMyType[data_List] := If[ValidMyTypeQ[MyType@@data], MyType@@data,
                            Print@"Incorrect format"]

(* Utilise your type *)    
    myFunction[data_MyType?ValidMyTypeQ] := (* do your funky stuff *)
share|improve this answer
    
I was remiss not to mention PatternTest which I also use when appropriate (see this for explanation). +1 –  Mr.Wizard Apr 23 '12 at 11:54
1  
+1 A practical example of this approach, showing how simple and readable it is, appears in a reply at mathematica.stackexchange.com/questions/1900 (the confetto type and associated methods). The reply by @Leonid Shifrin shows what is involved in a complete implementation. How far you go down this road depends on how complicated the data structures are and how reliable you need the code to be. –  whuber Apr 23 '12 at 15:03
    
@whuber thanks, very elegant example both visually and software design wise –  image_doctor Apr 23 '12 at 16:41

No ADT in Mma (natively at least) ... but in your case you could use pattern matching:

yours = {{100, {1, 2, 3, 4, 5}}, {105, {2, 4, 6, 8}}, {42, {42, 39, 56}}}; 
f[x_] := 1 /; MatchQ[x, List[List[_Integer, List[_Integer ...]] ...]]
f[yours]
f["mySymbol"]

(*->
1
f["mySymbol"]
*)
share|improve this answer
    
+1 -- any reason for the explicit long form List pattern? Given that not everyone is familiar with FullForm this seems to potentially obscure your answer rather than strengthen it, unless you are also making some point about FullForm that I'm missing. –  Mr.Wizard Apr 23 '12 at 6:21
    
@Mr.Wizard I am sure you know that no everyone matches the pattern "obscurity" with the same expressions. In particular, when I match some Heads (List being one of them), I prefer to see them explicitly in the pattern. Just taste. –  belisarius Apr 23 '12 at 11:38
    
Interesting. What other heads to you prefer to use the long form for? –  Mr.Wizard Apr 23 '12 at 11:50
1  
@Mr.Wizard For example, when my match comes from inspecting a FullForm[], I prefer to keep the FullForm output format, with explicit heads. –  belisarius Apr 23 '12 at 12:07
1  
Is there a reason why you used MatchQ instead of using the pattern directly, as in f[x:{{_Integer,{___Integer}} ...}] := 1? –  celtschk Apr 23 '12 at 14:32

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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