4
$\begingroup$

The below code defines a couple "objects" in the Mathematica sense using a convention consisting of so-called Q-functions for creating types and data validation, MakeBoxes for formatting, Association's for data, and SubValues for properties and methods. (It's a more of a convention than a framework and one I use often because it is not fussy and embraces Mathematica's symbolic paradigm.)

However, recently I have run into display issues of my custom formats within Dataset. Whenever I have an object like SubObject below it displays fine but when I have an on object like Object below which itself can contain a list of SubObject's, the displaying of even a list of such Object objects results in ... being displayed and notably this only seems to happen with an Object object contains a list of more than one SubObject objects.

Thank you ahead of time for any insights or fixes.

To test this out, plese just copy the below code and then run the subsequent tests to get the weird Dataset display issues. Here's my code to define the objects:

(*Utility q-function defining allowed rule pattern.*)
ClearAll[validRuleQ];
validRuleQ[(Rule | RuleDelayed)[_String, _]] := True;
validRuleQ[__] := False;

(*SubObject Data Q-Function*)
ClearAll[SubObjectDataQ];
SubObjectDataQ[
   Association["Name" -> _String, 
    "Date" -> _?DateObjectQ, ___?validRuleQ]] := True;
SubObjectDataQ[__] := False;

(*SubObject Q-Function*)
ClearAll[SubObjectQ];
SubObjectQ[SubObject[_?SubObjectDataQ]] := True;
SubObjectQ[__] := False;

(*SubObject*)
ClearAll[SubObject];

(*SubObject Format*)
SubObject /: 
  MakeBoxes[obj : SubObject[_?SubObjectDataQ], form : StandardForm] :=
   FrameBox[
   RowBox[{StyleBox[obj["Name"], Bold], " ", 
     StyleBox["(subobject)", Bold, Gray]}], RoundingRadius -> 5, 
   Background -> LightBlue];

(*SubObject Constructor*)
SubObject[name_String, date_?DateObjectQ, rules___?validRuleQ] := 
  SubObject[Association["Name" -> name, "Date" -> date, rules]];

(*SubObject Properties*)
SubObject[assoc_?SubObjectDataQ]["Association"] := assoc;
SubObject[assoc_?SubObjectDataQ]["Properties"] := Keys[assoc];
SubObject[assoc_?SubObjectDataQ][key_String] := Lookup[assoc, key];



(*Object Data Q-Function*)
ClearAll[ObjectDataQ];
ObjectDataQ[
   Association["Name" -> _String, "Date" -> _?DateObjectQ, 
    "Objects" -> {___?SubObjectQ}, ___?validRuleQ]] := True;
ObjectDataQ[__] := False;

(*Object Q-Function*)
ClearAll[ObjectQ];
ObjectQ[Object[_?ObjectDataQ]] := True;
ObjectQ[__] := False;

(*Object*)
ClearAll[Object];

(*Object  Format*)
Object /: 
  MakeBoxes[obj : Object[_?ObjectDataQ], form : StandardForm] := 
  FrameBox[RowBox[{StyleBox[obj["Name"], Bold], " ", 
     StyleBox["(object)", Bold, Gray]}], RoundingRadius -> 5, 
   Background -> LightBlue];

(*Object Constructor*)
Object[name_String, date_?DateObjectQ, objects : {___?SubObjectQ}, 
   rules___?validRuleQ] := 
  Object[Association["Name" -> name, "Date" -> date, 
    "Objects" -> objects, rules]];

(*Object Properties*)
Object[assoc_?ObjectDataQ]["Association"] := assoc;
Object[assoc_?ObjectDataQ]["Properties"] := Keys[assoc];
Object[assoc_?ObjectDataQ][key_String] := Lookup[assoc, key];

Here's the code to test the object definitions and see the output in a Dataset.

First, create some SubObject objects and their list variable with this code:

(*Create some SubObject objects and then put them in a list for later.*)
so1=SubObject["SubObject1",DateObject[]];
so2=SubObject["SubObject2",DateObject[]];
so3=SubObject["SubObject3",DateObject[]];
sobjs={so1,so2,so3}

This should display like this:

enter image description here

Now create the Object objects and their list with this code:

(*Create some Object objects and then put them in a list for later.*)
o1=Object["Object1",DateObject[],sobjs[[1;;1]]];
o2=Object["Object2",DateObject[],sobjs[[1;;2]]];
o3=Object["Object3",DateObject[],sobjs[[1;;3]]];
objs={o1,o2,o3}

This should display like this:

enter image description here

Now, simply display the Dataset for the SubObject object list:

(*Display SubObject Dataset*)
Dataset[sobjs]

Which displays as expected like so:

enter image description here

Problem:

And finally, the unexpected problem is here where the ... displays instead of an Object object for any Object objects that contain more than one SubObject in a list, which in our case are the variables o2 and o3. Here's the problematic code:

(*Problematic code for display of Object in Dataset*)
Dataset[objs]

Which displays as

enter image description here

How do I fix this so the Object objects display in Dataset correctly?

(Note: I have tried using HoldAll as an attribute on the constructor functions symbols, but this is not a desirable solution because then it's not convenient to script with the objects as any input variables to the constructors, such as a list of SubObjects into the constructor for Object won't be evaluated before insertion to the Association data.)

$\endgroup$

1 Answer 1

4
$\begingroup$

The issue comes from TypeSystem`NestedGrid`PackagePrivate`smallQ, which is used to decide whether to display an item in full or whether to elide it with :

TypeSystem`NestedGrid`PackagePrivate`smallQ[e_] := 
  Or[SameQ[TypeSystem`PackageScope`$ElisionEnabled, False],
    TypeSystem`AtomicDataQ[e],
    And[Length@e <= TypeSystem`PackageScope`$ElisionLengthLimit,
        ByteCount@e <= TypeSystem`PackageScope`$ElisionByteLimit,
        LeafCount@e <= TypeSystem`PackageScope`$ElisionLeafLimit
    ]
   ];

TypeSystem`AtomicDataQ[x_] := Or[And[AtomQ[x], ! AssociationQ[x]],
    MemberQ[TypeSystem`$AtomicHeads, Head @ x],
    System`Private`ValidQ[x]
   ];

As you can see, the second and third object are determined to not be small:

TypeSystem`NestedGrid`PackagePrivate`smallQ /@ objs
(* {True, False, False} *)

The definition of smallQ and AtomicDataQ also show a good solution for the issue: Simply add Object to TypeSystem`$AtomicHeads to tell the dataset rendering to treat Object[...] expressions similarly to DateObject etc:

AppendTo[TypeSystem`$AtomicHeads, Object];

TypeSystem`NestedGrid`PackagePrivate`smallQ /@ objs
(* {True, True, True} *)

Dataset@objs

enter image description here

$\endgroup$

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.