If data structures are completely unknown, then you are in troubles.
If your data structures are known, then there is a way., If the same parameter to function can take absolutely different shapes, then you can actual make it type proof.
I had similar situation: in production we have to convert ‘old’ API calls into ‘new’ API calls. Old input had absolutely different input structures.
At first, you have to define all possible type of data structures. Then write transform functions, which based on input, will emit exact data type you have.
For example:
/* first data structure */
type metricCount = {type_: string};
/* another data structure, but it may appear in the same input parameter */
type metricWithLabel = {
type_: string,
label: string
};
let createCountWithLabel = (label: string) : metricWithLabel => {type_: "COUNT", label};
let countMetric: metricCount = {type_: "COUNT"};
/* oldJson might be your unknown data structure */
let transformToCount20 = (oldJson) => {
/* here I used Json.Decode and, by checking oldJson's label propery I know 100% that
oldJson is of type metricWithLabel. So all next calls to createCountWIthLabe, MetricEncoder...
are type proofed during compilation time. Json.Decode actually also provides verification at runtime
*/
let label = Json.Decode.(oldJson |> optional(field("label", string)));
switch label {
/* if label property is present, then compiler knows 100% we deal with metricWithLabel */
| Some(v) => createCountWithLabel(v) |> MetricEncoder.encodeCountMetricWithLabel
/* if label is absent, compiler knows, it deals with countMetric type */
| None => countMetric |> MetricEncoder.encodeCountMetric
}
};
I provided a simple source listing, but it comes from real production code. I have more 300 lines of code, which investigate ‘unknown’ data structure input and make it type proof during compilation and runtime times.
It might require some time to specify all possible types, variants, constructors for variants. But once you do it, you are 100% sure you covered all cases.
You can read my post on medium, how I dealt with wild input parameters here: