shapeOf function
Infer the structural Shape of value.
Recurses through lists and maps. Lists are sampled rather than fully walked: if the first few elements agree on shape, the returned SList carries that element shape; otherwise the element is SAny. Sampling keeps inference cost bounded by structure depth rather than by total element count.
Heterogeneity detection is local to the sampling window. A list whose first elements share a shape but whose later elements differ may still report the first element's shape. Callers that require exact per-row shape information should walk the list themselves.
Implementation
Shape shapeOf(Object? value) {
if (value == null) return const SNull();
if (value is bool) return const SBool();
if (value is num) return const SNum();
if (value is String) return const SString();
if (value is List<Object?>) return _listShape(value);
if (value is Map<String, Object?>) return _mapShape(value);
return const SAny();
}