I really miss having something like a struct in Mathematica. I know of (and regularly use) a couple of programming techniques which feel like a struct (e.g., using downvalues), but are ultimately unsatisfactory (perhaps I'm using downvalues incorrectly). What programming approaches are available which provide similar functionality to a struct?

Here's an abbreviated (and hopefully not too obtuse) example of how I use downvalues to emulate a struct. In this case, I'm distinguishing between TLC and TEC (these are sets of parameters for two different phases of a Moon mission, trans-lunar cruise and trans-earth cruise):

deadBandWidth[X][TLC] ^= 10. \[Degree];
deadBandWidth[Y][TLC] ^= 10. \[Degree];
deadBandWidth[Z][TLC] ^= 20. \[Degree];
sunSearchAngle[Z][TLC] ^= 230. \[Degree];
sunSearchRate[Z][TLC] ^= 1. \[Degree]/Second;
sunSearchAngle[X][TLC] ^= 75. \[Degree];
sunSearchRate[X][TLC] ^= 1. \[Degree]/Second;
safingSpinRate[TLC] ^= (360. \[Degree])/Day;
sunVector[TLC] ^= {-Cos[45. \[Degree]], 0., Sin[45. \[Degree]]};
safingSpinAxis[TLC] ^= sunVector[TLC];

deadBandWidth[X][TEC] ^= 20. \[Degree];
deadBandWidth[Y][TEC] ^= 20. \[Degree];
deadBandWidth[Z][TEC] ^= 20. \[Degree];
sunSearchAngle[Z][TEC] ^= 230. \[Degree];
sunSearchRate[Z][TEC] ^= 1. \[Degree]/Second;
sunSearchAngle[X][TEC] ^= 75. \[Degree];
sunSearchRate[X][TEC] ^= 1. \[Degree]/Second;
safingSpinRate[TEC] ^= (360. \[Degree])/Hour;
sunVector[TEC] ^= {0., 0., +1.};
safingSpinAxis[TEC] ^= sunVector[TEC];

?TLC
Global`TLC
safingSpinAxis[TLC]^={-0.707107,0.,0.707107}
safingSpinRate[TLC]^=6.28319/Day
sunVector[TLC]^={-0.707107,0.,0.707107}
deadBandWidth[X][TLC]^=0.174533
deadBandWidth[Y][TLC]^=0.174533
deadBandWidth[Z][TLC]^=0.349066
sunSearchAngle[X][TLC]^=1.309
sunSearchAngle[Z][TLC]^=4.01426
sunSearchRate[X][TLC]^=0.0174533/Second
sunSearchRate[Z][TLC]^=0.0174533/Second
link|improve this question
5  
I could search around for what a struct is, but it'd be nice if you can link to a description of it... :) – J. M. Jan 30 at 14:47
1  
Can you explain what you want to do with this "struct"? Instead of pointing to a C feature, please explain what kind of things you want to do with this object. I know C, but I'm still not sure what feature of structs you are looking for in Mathematica. Also note that Mathematica shines when using immutable structures while I have the impression that you are looking for something mutable here. – Szabolcs Jan 30 at 14:58
5  
Your example contains many UpValues, not DownValues. Also, it could you explain why you are using these assignments, why do you think they're advantageous? Again, the question is: what is the advantage of structs for you for this application? Then we can think about how to reproduce this advantage in Mathematica. – Szabolcs Jan 30 at 15:06
1  
Also consider datatype[field1, field2, ...]. More MMA style, easier to manipulate, you can check for data type as _datatype, etc. You can always define a couple of constants if you want to access the fields like instance[[field1]]. Anyway, tere are many approaches, each with its advantages and disadvantages I guess – Rojo Jan 30 at 15:13
5  
@Cassini So you just need something like a structured parameter list? That does not need to be a mutable variable. How about a list of rules, like tlc = {deadBandWidth -> {10,10,20}, sunSearchAngle -> 230}? If you just need to pass data around, then it is best not do make any definitions at all. – Szabolcs Jan 30 at 15:21
show 7 more comments
feedback

5 Answers

up vote 18 down vote accepted

Quoting the OP's comment:

Most of the work I do involves constructing mathematical models and then testing various scenarios against those models. I'd like to be able to populate a particular scenario and then pass that scenario to a model. I'd also like to be able to copy that scenario, modify one or more parameters, and then pass the new scenario to the model.

The requirement, as I understand, is to be able to pass many parameter values around in a structured way. Lists of rules are convenient for this:

params = {par1 -> 1, par2 -> 2, par3 -> {x,y,z}}

They can be extracted like this:

par1 /. params

(* ==> 1 *)

Once I wrote a function for substituting such parameter lists into bigger pieces of code:

ClearAll[withRules]
SetAttributes[withRules, HoldAll]
withRules[rules_, expr_] :=
  Internal`InheritedBlock[
    {Rule, RuleDelayed},
    SetAttributes[{Rule, RuleDelayed}, HoldFirst];
    Unevaluated[expr] /. rules
]

It can be used like this:

withRules[params,
  par1 + par2
]

(* ==> 3 *)

withRules can contain complex code inside, and all occurrences of par1, par2, etc. will be substituted with the values from the parameter list.

We can also write a function for easily modifying only a single parameter (from the whole list), and returning a new parameter list. Here's a simple implementation:

setParam[paramList_, newRules_] :=
 DeleteDuplicates[Join[newRules, paramList], 
  First[#1] === First[#2] &]

Example usage:

setParam[params, {par2 -> 10}]

(* ==> {par2 -> 10, par1 -> 1, par3 -> {x, y, z}} *)

Another list which has a different value for par2 is returned.


If needed, this could be extended to support more complex, structured lists such as { par1 -> 1, group1 -> {par2x -> 10, par2y -> 20}}, much how like the built-in option-handling works.

link|improve this answer
I think as soon as you start thinking about modifying a single element (setParam), your approach looses expressiveness, because within it this operation is not natural. If you go further down that road (I was actually doing this too, for a while), you end up with another emulation of mutable structures, and not the best one, because you are copying stuff (e.g. you have copy - internally - and traverse the entire parameter list to modify a single element - so your assignemnt is strictly speaking not constant time). – Leonid Shifrin Jan 30 at 15:50
@Leonid, well, but these lists tend to be rather small, so performance is not an issue. At least not for the things I was using them for. This does not become slow if we have a large parameter value, only if we have a large number of parameters. It's like Append. – Szabolcs Jan 30 at 15:53
@Leonid I actually use something similar in practice, so if you have a better (more convenient) solution for this particular problem (not for emulating structs, but actually for this problem), I'd like to hear it. – Szabolcs Jan 30 at 15:54
Sure, I understand that (although, once you start using that in huge quantities, this will become an issue even for a not very large number of parameters). I just think it is conceptually inelegant to use immutable structures to emulate mutability, for cases which don't warrant that. Which these cases are is debatable. For example, for configuring options, I take essentially the same approach with my option configurator stackoverflow.com/questions/7192599/…, - emulate the mutable state ... – Leonid Shifrin Jan 30 at 15:59
...with immutable expressions, and in that case I think it is well-justified. To summarize - I think, if the nature of your problem demands lots of mutable state (e.g. you write something like a virtual file system), I don't think emulating it with immutable constructs is appropriate. I'd rather isolate that module to make sure that all mutations happen in a well-defined place. But for just a few mutable variables, I think that's ok and may even be good. I certainly consider it good for options. – Leonid Shifrin Jan 30 at 16:02
show 6 more comments
feedback

This answer may be unacceptable right from the outset because it uses undocumented functions. However, it has advantages over some of the approaches suggested so far which might be redeeming enough in certain scenarios to recommend it in practice. In particular, it provides totally encapsulated state (unlike, e.g., DownValues or Temporary symbols) and O(1) access and updates (unlike, e.g., a list of rules).

I would suggest a System`Utilities`HashTable object, which exists in at least Mathematica 7 and 8 (but not in 5.2, and I didn't check 6). This is manipulated using a relatively small number of simple functions:

  • System`Utilities`HashTable[]: creates a new hash table.
  • System`Utilities`HashTableAdd[ht, key, val]: adds a key-value pair {key, val} to the hash table ht.
  • System`Utilities`HashTableGet[ht, key]: given a hash table ht and a key key, retrieves the value corresponding to key.
  • System`Utilities`HashTableRemove[ht, key]: given a hash table ht and a key key, removes key from ht.
  • System`Utilities`HashTableContainsQ[ht, key]: given a hash table ht and a key key which may or may not exist in ht, determines whether or not key does in fact exist in ht. (This is useful since adding a key that already exists or querying/removing a nonexistent key produces an ugly message.)

I trust that this is all quite self-explanatory, but the following is a brief usage example for reference:

h = System`Utilities`HashTable[]
 (* -> System`Utilities`HashTable[<0>] *)

(* Setting properties for an "account" *)
System`Utilities`HashTableAdd[h, accountID, 47];
System`Utilities`HashTableAdd[h, balance, 1632.40];

(* Querying a property *)
accid = System`Utilities`HashTableGet[h, accountID]
 (* -> 47 *)

(* Updating a property *)
bal = System`Utilities`HashTableGet[h, balance];
System`Utilities`HashTableRemove[h, balance];
System`Utilities`HashTableAdd[h, balance, bal + 506.31];

System`Utilities`HashTableGet[h, balance]
 (* -> 2138.71 *)

If you aren't completely put off by the fact that all of this is undocumented, the System`Utilities`HashTable looks it could offer a passable alternative to a struct for many applications.

link|improve this answer
that is really nice. I see I can pass a hash table to a function, and the function can update the hash table and on return the hash table is updated, and I did not have to pass it by reference at all. Here is an example: (I wish this forum have better way to reply<START CODE> Remove["Global`*"] update[h_] := Module[{}, System`Utilities`HashTableRemove[h, "accountID"]; System`Utilities`HashTableAdd[h, "accountID", 55] ]; h = System`Utilities`HashTable[] System`Utilities`HashTableAdd[h, "accountID", 47] update[h]; System`Utilities`HashTableGet[h, "accountID"]<END CODE> – Nasser M. Abbasi Jan 31 at 3:18
Well, I was getting a little too excited by this, only to to be disappointed to find that one can't use it in a demo. I get an error when I use it in a CDF and try to upload the file: This Demonstration could not be processed in its present form. Please edit the notebook to remove the following illegal symbols: System`Utilities`HashTable, System`Utilities`HashTableAdd, System`Utilities`HashTableGet. Too bad, because I wanted to use this. But the CDF plugin does not seem to support these. oh well. – Nasser M. Abbasi Jan 31 at 5:01
It's not the CDF format or plugin that forbids the use of these functions, it is just the check of the demonstrations site. You could very well use these functions (unsupported as they probably are...) in a CDF document, it it will be running in the CDF Player as well as the Player Pro. – Albert Retey Jan 31 at 9:06
Nice answer! Hash tables are not quite structs, but this is a valuable piece of information. And great that you joined. +1. – Leonid Shifrin Jan 31 at 9:34
1  
Is it possible to update a value without removing it first? – Ajasja Apr 12 at 13:06
show 1 more comment
feedback

There were several attempts to emulate structs in Mathematica. Emphasis on emulate, since AFAIK there is no built - in support for it yet. One reason for that may be that structs are inherently mutable, while idiomatic Mathematica gravitates towards immutability. You may find these discussions interesting:

Struct-data-type-in-mathematica

Object-oriented-mathematica-programming

Question-on-setting-up-a-struct-in-mathematica-safely

Mathematica-oo-system-or-alternatives

My own take on it is in this answer:

Tree-data-structure-in-mathematica

where I describe one possible emulation of structs, which I use every now and then when I need something like a struct (this is, of course, a personal preference. There are many ways to do this). It looks to be somewhat similar to your method. For a recent use case where I put similar approach to heavy use and where it really pays off (because structs are not the bottleneck there), see this answer, where I use this as an encapsulation mechanism for file-backed lists.

That said, a built-in support for mutable structures would be, I think, very desirable. Three major reasons I could think of, why various emulation approaches did not really take off:

  • Performance. Structs are the work-horse of data structures, and their performance is critical. OTOH, all emulations which are to be general, are bound to use the top-level code, and that is slow.
  • Garbage collection. The available ways to create encapsulated mutable state almost always involve creating definitions for symbols, and those definitions are frequently not automatically amenable to garbage collection
  • (The lack of) standardization. If there were a single emulation which would accumulate a significant code base, tools and practices of using it, that may have been different.
link|improve this answer
I've read about mutable and immutable several times on your posts and I get it only half of the way. I didn't study computer science, but googling tells me that mutable is when it can be changed after its creation, which Part can do it seems. On the other hand, you can change a downvalue without changing the others if you consider the pack of that symbols' definitoins as your structure. Also, wouldn't something like a tree built with node[left, data, right] with HoldAll be mutable? – Rojo Jan 30 at 15:22
I guess it's all about knowing when MMA does an internal copy of the expressions, which I don't know, nor know how to check... This reminds me that I've been confused with this issue also in the context of reading that Bags were so special because they could emulate a pointer, and a few things about linked lists (these I understand a little bit more than half of the way) – Rojo Jan 30 at 15:23
1  
@Rojo What I mean is that if I have, say in C, a struct like typedef struct{ int x, int y} mystruct;, then, if s is of this type, I can say s.x = 10. If I have an expression like node[left, data, right], and it is stored in some variable (say expr), then I also can say expr[[1]] = 10, but I can not nest this deeper. For example, if left is also a node, I can not say left = expr[[1]]; left[[1]] = 10 and expect this change to also affect the original left node inside expr - it only affects a copy. Basically, it boils down to the fact that we lack here pointer semantics. – Leonid Shifrin Jan 30 at 15:36
My guess is that Part on the lhs is the only way to change only part of an expression without making a copy. Also, that MMA only makes a copy of the data whe it has no other choice in its built-in functions that modify an expression. But how to know if, e.g, h=ReplacePart[h, 2->8] does a full copy or if its smart enough? (h being unpacked array) – Rojo Jan 30 at 15:46
@Rojo my point is that even Part is not enough, because elements of an expression are expressions, not pointers. When you extract them, their copy is made. When you assign these parts to variables and modify those, that assigned copy is modified, not the original part within a larger expression. As for ReplacePart, it sure does make a copy, as most other functions returning expressions. – Leonid Shifrin Jan 30 at 15:53
feedback

Using symbols to store data and object-like functions

Here are interesting functions to use symbols like objects. (I originally posted these thoughts in What is in your Mathematica tool bag?)

It is already well known that you can store data in symbols and quickly access them using DownValues.

(*Write/Update*)
mysymbol["property"]=2;
(*Access*)
mysymbol["property"]
(*Delete*)
Unset[mysymbol["property"]]

It is similar to a hashtable, new rules are added for each property to DownValues[mysymbol]. But internally, from what I understood, rules of a symbol are stored as a hashtable so that Mathematica can quickly find which one to use. The key ("property" in the example) doesn't need to be a string, it can be any expression (which can be used to cache expressions, as also shown in the post quoted above).

Keys

You can access the list of keys (or properties) of a symbol using these functions based on what dreeves once submitted (I was quite lucky to have found his post early in my Mathematica learning curve, because it allowed me to work on functions working with lots of different arguments, as you can pass the symbol containing the stored properties to a function and see which keys this symbol contains using Keys):

SetAttributes[RemoveHead, {HoldAll}];
RemoveHead[h_[args___]] := {args};
NKeys[_[symbol_Symbol]]:=NKeys[symbol]; (*for the head[object] case*)
NKeys[symbol_] := RemoveHead @@@ DownValues[symbol(*,Sort->False*)][[All,1]];
Keys[symbol_] := Replace[NKeys[symbol], {x_} :> x, {1}];

Usage example of Keys

a["b"]=2;
a["d"]=3;
Keys[a]

(*getting the values associated with the keys of the a symbol*)
a /@ Keys[a]

If you use multiple keys for indexing a value

b["b",1]=2;
b["d",2]=3;
Keys[b]

(*getting the values associated with the keys of the b symbol*)
b @@@ Keys[b]

PrintSymbol

I use this function a lot to display all infos contained in the DownValues of a symbol (which uses one key per value):

PrintSymbol[symbol_] :=
  Module[{symbolKeys=Keys[symbol]},
    TableForm@Transpose[{symbolKeys, symbol /@ symbolKeys}]
  ];

PrintSymbol[a]

Replacing a part of a list stored in a symbol

The following would produce an error

mysymbol["x"]={1,2};
mysymbol["x"][[1]]=2

One way to do this would be either to introduce a temporary variable for the list stored in mysymbol["x"], modify it and put it back in mysymbol["x"] or, if possible, use a syntax like

mysymbol["x"] = ReplacePart[mysymbol["x"], 1 -> 2]

Creation of objects with integrated functions

Finally here is a simple way to create a symbol that behaves like an object in object oriented programming, different function syntaxes are shown :

Options[NewObject]={y->2};
NewObject[OptionsPattern[]]:=
  Module[{newObject,aPrivate = 0},
    (*Stored in DownValues[newObject]*)
    newObject["y"]=OptionValue[y];
    newObject["list"] = {3, 2, 1};

    (*Stored in UpValues[newObject]*)
    function[newObject,x_] ^:= newObject["y"]+x;
    newObject /: newObject.function2[x_] := 2 newObject["y"]+x;

    (* "Redefining the LessEqual operator" *)
    LessEqual[newObject,object2_]^:=newObject["y"]<=object2["y"];

    (* "Redefining the Part operator" *)
    Part[newObject, part__] ^:= newObject["list"][[part]];

    (*Syntax stored in DownValues[newObject], could cause problems by 
      being considered as a property with Keys*)
    newObject@function3[x_] := 3 newObject["y"]+x;

    (*function accessing a "private" variable*)
    functionPrivate[newObject] ^:= aPrivate++;

    (* "Redefining the [ ] operator" *)
    newObject[x_] := x newObject["list"];

    (*Format*)
    Format[newObject,StandardForm]:="newObject with value y = "~~ToString[object["y"]];

    newObject
  ];

Properties are stored as DownValues and methods as delayed Upvalues (except for the [ ] redefinition also stored as DownValues) in the symbol created by Module that is returned. I found the syntax for function2 that is the usual OO-syntax for functions in Tree data structure in Mathematica.

Private variable

The variable aPrivate can be seen as a private variable as it is only seen by the functions of each newObject (you wouldn't see it using Keys). Such a function could be used to frequently update a list and avoid the issue of the previous paragraph.

If you wanted to DumpSave newObject you could know which aPrivate$xxx variable to also save by using the depends function of Leonid Shifrin described in the post Automatically generating a dependency graph of an arbitrary Mathematica function?.

depends[NewObject[]]

Note that xxx is equal to $ModuleNumber - 1 when this expression is evaluted inside Module so this information could be stored in newObject for later use.

Other way for storing functions in a different symbol

You could also store the function definition not in newObject but in a type symbol, so if NewObject returned type[newObject] instead of newObject you could define function and function2 like this outside of NewObject (and not inside) and have the same usage as before. See the second part of the post below for more on this.

(*Stored in UpValues[type]*)
function[type[object_], x_] ^:= object["y"] + x;
type /: type[object_].function2[x_] := 2 object["y"]+x;

(*Stored in SubValues[type]*)
type[object_]@function3[x_] := 3 object["y"]+x;

Usage example

x = NewObject[y -> 3]
x // FullForm

x["y"]=4
x@"y"

function[x, 4]
x.function2[5]
x@function3[6]

(*LessEqual redefinition test with Sort*)
z = NewObject[]
{x["y"],z["y"]}
l = Sort[{x,z}, LessEqual]
{l[[1]]["y"],l[[2]]["y"]}

(*Part redefinition test*)
x[[3]]

(*function accessing a "private" variable*)
functionPrivate[x]

(*[ ] redefinition test*)
x[4]

Reference/Extension

For a list of existing types of values each symbol has, see http://reference.wolfram.com/mathematica/tutorial/PatternsAndTransformationRules.html and http://www.verbeia.com/mathematica/tips/HTMLLinks/Tricks_Misc_4.html.

You can go further if you want to emulate object inheritance using a package called InheritRules available here http://library.wolfram.com/infocenter/MathSource/671/

Further ideas when storing functions in a head symbol

The idea is to use DownValues for storing properties in different symbols corresponding to objects and UpValues for storing methods in a unique head symbol (MyObject in the example below). We then use expressions of the form MyObject[object].

Here is a summary of what I currently use.

Constructor

Options[MyObject]={y->2};
MyObject[OptionsPattern[]]:=
   Module[{newObject,aPrivate = 0},
      newObject["y"]=OptionValue[y];
      newObject["list"] = {3, 2, 1};

      (*function accessing a "private" variable*)
      functionPrivate[MyObject[newObject]] ^:= aPrivate++;

      MyObject[newObject]
   ];

MyObject is used as "constructor" and as head of the returned object (for example MyObject[newObject$23]). This can be useful for writing functions that take into account the head of an object. For example

f[x_MyObject] := ...

Properties (like the value corresponding to the key "y") are stored as DownValues in a newObject symbol created by Module whereas functions will be stored in the MyObject symbol as UpValues.

Private variable

functionPrivate in the constructor definition has access to the "private" variable aPrivate. It is stored as UpValues of MyObject but with a dependency on each newObject symbol created in Module (which breaks the indendance between data stored in newObject and methods stored in MyObject that other function syntaxes described below share). It must be defined in Module contrary to other kind of functions described below, so that the symbols newObject and aPrivate generated in the same Module are stored in a same rule and thus can "see" each other.

Some methods stored as UpValues in MyObject (different syntaxes are shown)

(*Stored in UpValues[MyObject]*)
function[MyObject[object_], x_] ^:= object["y"] + x;
MyObject/: MyObject[object_].function2[x_] := 2 object["y"]+x;

(* "Redefining the LessEqual operator" *)
LessEqual[MyObject[object1_],MyObject[object2_]]^:=object1["y"]<=object2["y"];

(* "Redefining the Part operator" *)
Part[MyObject[object_], part__] ^:= object["list"][[part]];

myGet[MyObject[object_], key_] ^:= object[key];
mySet[MyObject[object_], key_, value_] ^:= (object[key]=value);    

Methods stored as SubValues in MyObject

A method stored to easily access the properties of an object. We restrict here key to be a string in order not to interfere with other functions defined as SubValues.

MyObject[object_Symbol][key_String] := object[key];

Another function stored in SubValues[MyObject]

MyObject[object_]@function3[x_] := 3 object["y"]+x;

Redefinition of the [ ] operator

MyObject[object_][x_] := x object["list"];

"Static" variable

Similarly to what is used for a private variable, a variable can be shared among all the objects of a similar class using a following definition for the function that accesses it. (Such variables use the keyword static in C++-like languages)

Module[{staticVariable=0},
   staticFunction[MyObject[object_]]^:=(staticVariable+=object["y"]);
]

Format

You can format the way the object is displayed with something like this:

Format[MyObject[object_Symbol],StandardForm]:="MyObject with value y = "~~ToString[object["y"]];

Creating an object

x = MyObject[y->3]

Test of the different functions

x // FullForm

function[x, 2]
x.function2[3]
x@function3[4]

x["y"]
x@"y"

(*LessEqual redefinition test with Sort*)
z = MyObject[]
{x["y"],z["y"]}
l = Sort[{x,z}, LessEqual]
{l[[1]]["y"],l[[2]]["y"]}

(*Part redefinition test*)
x[[3]]

(*function accessing a "private" variable*)
functionPrivate[x]

(*[ ] redefinition test*)
x[4]

(*static function example*)
staticFunction[x]
staticFunction[z]

Update properties

To update the "y" property of z you can use this (or use a setter function like mySet defined above)

ObjectSet[(_[symbol_Symbol]|symbol_),key_,value_]:=symbol[key]=value;
ObjectSet[z,"y",3]

If an object is of the kind MyObject[object] then value will be assigned to object[key] (DownValues of object) instead of being assigned to MyObject[object][key] (SubValues of MyObject whereas I want functions to be in general stored as UpValues of MyObject and properties as DownValues of object).

Another way that doesn't involve another function is to do

z[[1]]["y"] = 4

Update properties using Set

You can automate ObjectSet by overloading Set. This is what I use, as this makes the syntax more convenient for modifying the property of an object of the kind MyObject[object], but thouching to a function as important as Set is dangerous, especially if you're not the only user of your code, in this case use one of the method of the previous paragraph.

See this post for alternatives Alternative to overloading Set

Unprotect[Set];
Set[symbol_[key_],value_]:=
   Block[{$inObjectSet=True},
      ObjectSet[symbol,key,value]
   ]/;!TrueQ[$inObjectSet];
Protect[Set];

So that you can do

z["y"] = 4
function[z, 2]

This syntax works also for sub-objects

z["u"]=MyObject[]
z["u"]["i"]=2
PrintSymbol[z["u"]]
link|improve this answer
I'd like to suggest that you migrate the info from your toolbag answer here, as the toolbag question has been closed and its not likely to be re-opened. So, eventually the info will be deleted. – rcollyer Jan 30 at 16:50
5  
@Faysal Please, for the sake of salvation of all souls who will be lost after reading this part, don't overload Set! – Leonid Shifrin Feb 2 at 1:14
@Leonid I've warned people that it can be dangerous. I don't have any side effect using this though. Someone who wants to avoid any pitfall if they choose to use the ideas I shared can use other possibilities for setting values of such objects. It's a matter of free will ... – Faysal Aberkane Feb 2 at 9:47
4  
@Faysal The problem is, at least in my view, that we, as advanced users, have increased responsibility towards less experienced ones, and community as a whole. The technique you described is dangerous, and not only to those who decide to use it. As soon as more user-contributed code (by us say) becomes widely used, this may become a real problem. People may start using this stuff in packages, and this may lead to all sorts of problems. Also, what I first saw was the code, and I saw your warning only upon the second read. Adding rules to Set is a very appealing idea, but IMO it is just wrong. – Leonid Shifrin Feb 2 at 9:58
@Faysal One way to make this more bearable is through local environments, the approach I am pushing forward in many of my answers. You could create dynamic environments (say, with Iternal`InheritedBlock localizing Set), or, better yet, lexical environments. There are many problems with overloading Set via DownValues, but the major one IMO is that it is a non-local, system- wide change for something very frequently used. Environments help localize changes, and are a great tool for that. – Leonid Shifrin Feb 2 at 10:20
show 1 more comment
feedback

So the naive way to set up a data structure like struct is, as the OP suggested, to simply used DownValues and/or SubValues. In the below, I use SubValues.

Copying the Wikipedia C language struct example

struct account {
   int account_number;
   char *first_name;
   char *last_name;
   float balance;
};

struct account s; // Create new account labelled s
s.account_number // access the account number

In Mathematica, we can talk about an "instance" of account as

account["s"]

set and access its properties using SubValues

account["s"]["account_number"] = 12345

account["s"]["account_number"]
(* Returns: 12345 *)

To make this a bit more robust, you should probably have a gentleman's agreement with your code to only access the "objects" using type checked instantiation and setting methods. Also, code for deletion of "objects" is easy to write by using DeleteCases on the SubValues of account. That said, I've written largish applications for my own use that do not bother with such niceties.

link|improve this answer
feedback

Your Answer

 
or
required, but never shown

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