25

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

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
Gabriel
  • 2,225
  • 18
  • 19
  • 1
    I'm sure we've had a question on this before, perhaps on StackOverflow, but can't find it at the moment. – Sjoerd C. de Vries Dec 07 '12 at 21:48
  • Look also at SubValues[FittedModel] for those properties you were missing. – Leonid Shifrin Dec 07 '12 at 21:59
  • 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. – Mr.Wizard Dec 07 '12 at 21:59
  • @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. – Leonid Shifrin Dec 07 '12 at 22:01
  • @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. – Mr.Wizard Dec 07 '12 at 22:11
  • @Mr.Wizard I don't have a strong opinion on the matter. I would go with whatever Gabriel decides. – Leonid Shifrin Dec 07 '12 at 22:17
  • nah the close is fine. I tried searching as I had vague memories of this issue. The SubValues solves the issue well enough for me. Thanks – Gabriel Dec 08 '12 at 02:07
  • @LeonidShifrin a FittedModel doesn't seem to have SubValues either ... – Gabriel Dec 08 '12 at 02:41
  • @Gabriel It did for me. Perhaps, you have to use it first, it may be getting those defs dynamically, like many Mathematica symbols do. Execute first some example from Help on say LinearModelFit, and try then - this is what I did. – Leonid Shifrin Dec 08 '12 at 02:43
  • @LeonidShifrin thanks ... I was doing it on a variable that contained a FittedModel not on FittedModel itself. Confusion is slowly clearing. Thanks! – Gabriel Dec 08 '12 at 02:47
  • @Gabriel Yes, of course you should always check ..Values on the symbols themselves (FittedModel here). – Leonid Shifrin Dec 08 '12 at 03:11
  • Okay, in the end @LeonidShifrin was right ... I do really want to understand how to use DownValues/SubValues to actually implement the type of behavior something like FittedModel has. Now I am going to try and do this on my own for a bit, but when I want to ask it ... 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)? – Gabriel Dec 08 '12 at 05:14
  • Question reopened. I added a portion of your last comment to the question, but please change the wording as you see fit. – Mr.Wizard Dec 08 '12 at 15:48
  • Possible duplicate of http://mathematica.stackexchange.com/q/2590/57 – Sjoerd C. de Vries Apr 16 '14 at 06:05

2 Answers2

18

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"} *)
Gabriel
  • 2,225
  • 18
  • 19
13

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.

rm -rf
  • 88,781
  • 21
  • 293
  • 472
  • 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? – kale Feb 22 '14 at 05:29
  • @kale So you don't want delayed calcs? – Dr. belisarius Feb 22 '14 at 05:35
  • @belisarius Ideally, all the subvalues would be calculated on the first function call and stored for later use. – kale Feb 22 '14 at 05:37
  • @kale Isn't that just memoizing them? – Dr. belisarius Feb 22 '14 at 05:45
  • @belisarius Not sure how you'd construct that here. Maybe a better example: Let's say my function imports a data table from a website. That website changes based on the function argument. That data has properties that I'd like to capture. I think rm-rf's method would require me to import that data at least once per property retrieved. Or do all subvalues get calculated at first function call?? – kale Feb 22 '14 at 05:54
  • 1
    @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). – rm -rf Feb 22 '14 at 05:56
  • Probably true. My first example hinted at it with assigning the different downvalues within a function but additional clarity is needed. – kale Feb 22 '14 at 06:04
  • @kale See my edit. – rm -rf Feb 22 '14 at 06:24
  • @rm-rf Perfect. Thanks. – kale Feb 22 '14 at 14:49
  • Very nice. Just in case:Have you done something similar for lists that resembles the behavior of Excel's Range? I was in need of that a few months ago but haven't had the time and inspiration to try to make the boilerplate – Dr. belisarius Feb 22 '14 at 15:54
  • @belisarius I haven't used Excel since MS Office '97 and I don't remember/know what Range does there. Is it the function where you do something like B1:B10 and it gives you those cells (and you can apply a function to them)? – rm -rf Feb 22 '14 at 16:27