In the course of making some RLink wrappers I want to have some richer containers like Mathematica does with its FittedModel code. I thought I had a good idea of how this might be done, i.e make a custom Format specification that hides some arguments and use DownValues to give different parts of the code.

In looking at actual FittedModel objects this does not seem to be what is being done, as it has no DownValues. Also when you look at the FullForm it doesn't seem to have enough data to give back all the "Properties" available.

My question is, is their documentation for making rich data objects like Mathematica is commonly doing these days?

I do really want to understand how to use DownValues/SubValues to actually implement the type of behavior something like FittedModel has. ... Is there a way to make it clear that this is not covered by the linked to question (which just deals with the Format/Boxes issue)?

  • 1
    $\begingroup$ I'm sure we've had a question on this before, perhaps on StackOverflow, but can't find it at the moment. $\endgroup$ – Sjoerd C. de Vries Dec 7 '12 at 21:48
  • $\begingroup$ Look also at SubValues[FittedModel] for those properties you were missing. $\endgroup$ – Leonid Shifrin Dec 7 '12 at 21:59
  • $\begingroup$ I have closed this question as it appears to cover similar ground. If your question is more conceptual than pragmatic, and you are not looking for a method (e.g. Format or MakeBoxes) but rather information about an underlying design, I will reopen it. EDIT question reopened. Related but not duplicate here. $\endgroup$ – Mr.Wizard Dec 7 '12 at 21:59
  • $\begingroup$ @Mr.Wizard I think this question may have this more conceptual component too, e.g. how DownValues and SubValues are used to implement some properties of such object. $\endgroup$ – Leonid Shifrin Dec 7 '12 at 22:01
  • $\begingroup$ @Leonid Vote to reopen if it suits you. I closed it because it's easier for me to close it now and let the community reopen (or Gabriel ask me to) than to try to remember to come back later and close it. I believe in quick closes when possible as it keeps good answers form ending up under closed questions later. $\endgroup$ – Mr.Wizard Dec 7 '12 at 22:11

After some work and clarification from Leonid it becomes clear this is a case where SubValues is the exact solution. As this answer points out SubValues are patterns of the form

food[d][f] := a;

which is the correct form for accessing parts of an "data-like" object since the sub value has access to the containing expression parts.

Now to build on a similar answer we have to small extension of instead of just using accessor functions, we can actually build SubValues so that we can do this on the symbol itself like Mathematica data objects do. From the previous answer we have:

makeMyData[d1_, d2_] := MyData[d1, d2]
Format[MyData[d1_, d2_]] := "MyData[<" <> ToString[Length[d1] + Length[d2]] <> ">]"

Now we just add some SubValues to MyData

MyData[d1_, d2_]["D1"] := d1
MyData[d1_, d2_]["D2"] := d2
MyData[d1_, d2_]["Properties"] := {"D1", "D2"}

and then we get the expected behavior as follows

dat = makeMyData[Range[1, 10], b]
dat["D1"] (* returns {1, ..., 10} *)
dat["D2"] (* returns b *)
dat["Properties"] (* returns {"D1", "D2"} *)
  • $\begingroup$ This is really awesome, and motivating. $\endgroup$ – Eric Brown Dec 9 '12 at 2:26
  • $\begingroup$ Happy it helps! Spent a night figuring this out ... simple once you see it all together :-) $\endgroup$ – Gabriel Dec 9 '12 at 3:32
  • $\begingroup$ There is a mistake in the definition MyData[d1_,d2_]:=... it must read makeMyData[d1_,... You also forgot the closing *) on the comment in the last line $\endgroup$ – Matariki Nov 1 '13 at 0:33
  • $\begingroup$ @Matariki thanks! Fixed $\endgroup$ – Gabriel Nov 1 '13 at 1:01

I like to use properties like those in SparseArray and I find subvalues very useful for defining and accessing them. This is best used with a dummy head. The following is some code pulled out from one of my packages and modified. I've defined func here to be a minimal example of what your actual function might look like.

Clear[func, myHead]
func[str_] := With[{img = ExampleData[{"TestImage", str}]},
        "Image" -> img, "ImageDimensions" -> ImageDimensions@img, 
        "ImageColorSpace" -> ImageColorSpace@img

myHead[list_][field_] := field /. list
myHead[list_]["Properties"] := list /. Rule[field_, _] :> field
myHead /: ReplaceAll[fields_, myHead[list_]] := fields /. list
Format[myHead[list_], StandardForm] := HoldForm[myHead]["<" <> ToString@Length@list <> ">"]

The following is how it works:

I like using Format to control the display and provide just a short summary (I just show the length of the list here, but you can change it to whatever you want), so that accidentally displaying it will not make the FE hang if it happens to contain large lists. You can also use it as a deliberate way to get a quick summary.

  • $\begingroup$ Okay, but what if the "properties" assigned aren't simple calculations, but rely on a larger and more expensive block of code? It wouldn't make sense to define all of that multiple times, correct? $\endgroup$ – kale Feb 22 '14 at 5:29
  • $\begingroup$ @kale So you don't want delayed calcs? $\endgroup$ – Dr. belisarius Feb 22 '14 at 5:35
  • $\begingroup$ @belisarius Ideally, all the subvalues would be calculated on the first function call and stored for later use. $\endgroup$ – kale Feb 22 '14 at 5:37
  • $\begingroup$ @kale Isn't that just memoizing them? $\endgroup$ – Dr. belisarius Feb 22 '14 at 5:45
  • 1
    $\begingroup$ @kale You need a more representative example then... none of this is reflected in your question or the example. I'm quite certain that you can do it with just 1 import. I've certainly done it that way many times (I like the subvalue approach to define and access properties). $\endgroup$ – rm -rf Feb 22 '14 at 5:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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