25
$\begingroup$

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)?

$\endgroup$
14
  • 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$ Dec 7, 2012 at 21:48
  • $\begingroup$ Look also at SubValues[FittedModel] for those properties you were missing. $\endgroup$ Dec 7, 2012 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, 2012 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$ Dec 7, 2012 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, 2012 at 22:11

2 Answers 2

18
$\begingroup$

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"} *)
$\endgroup$
4
  • $\begingroup$ This is really awesome, and motivating. $\endgroup$
    – Eric Brown
    Dec 9, 2012 at 2:26
  • $\begingroup$ Happy it helps! Spent a night figuring this out ... simple once you see it all together :-) $\endgroup$
    – Gabriel
    Dec 9, 2012 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, 2013 at 0:33
  • $\begingroup$ @Matariki thanks! Fixed $\endgroup$
    – Gabriel
    Nov 1, 2013 at 1:01
13
+50
$\begingroup$

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}]},
    myHead[{
        "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.

$\endgroup$
11
  • $\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, 2014 at 5:29
  • $\begingroup$ @kale So you don't want delayed calcs? $\endgroup$ Feb 22, 2014 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, 2014 at 5:37
  • $\begingroup$ @kale Isn't that just memoizing them? $\endgroup$ Feb 22, 2014 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, 2014 at 5:56

Your Answer

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

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