Process variant data
In this tutorial, you will perform a calculation based on VaraintType
data. You will
- define a writeable
VaraintType
data source, and - define a function with
FunctionBuilder
to perform calculations based on variant data using theMatch
expression, and - launch a solution, interact with and validate your calculations.
This lesson will assume that you have an empty project and asset which you can to deploy to a workspace named 06_04_03_use_varrianttype_data
with the following command:
edk template deploy -ycw 06_04_03_use_varrianttype_data
The area calculation
This tutorial extends
the previous by performing a calculation on the variant data. The source data will be a variant type representing different geometric objects. There are three cases describing three kinds of geometry: points, circles and rectangles.const GeometryType = VariantType({
point: NullType,
circle: FloatType,
rectangle: StructType({
width: FloatType,
height: FloatType,
}),
})
The goal is to perform a calculation of the area of each type of geometry. The calculation will depend on which shape it is, and the dimensions of the shape:
- A point by definition has zero area.
- The area of a circle is the mathematical constant pi (3.14159...) multiplied by the radius squared.
- The area of a rectangle is the width multiplied by the height.
Variants types are ideal for representing various cases that might require different logic.
The Match
expression is the primary way of performing such logic on a case-by-case basis.
To perform the logic described above on an a value geometry
of type GeometryType
, we can use the following East expression:
Match(
geometry,
{
point: _ => Const(0.0),
circle: radius => Multiply(Math.PI, Multiply(radius, radius)),
rectangle: dimensions => Multiply(
GetField(dimensions, "width"),
GetField(dimensions, "height"),
)
}
)
The Match
expression returns a result that depends on the case of geometry
.
Each expression is a function of the data associated with that case.
For the point
case we return the constant 0.0
(we ignore the null
associated data by naming it _
, which is convention for a variable we don't care about).
For the circle
case we multiply pi with the radius times the radius.
For the rectangle
case we multiply the width and the height of the rectangle, which were stored in a struct associated with this case.
Next you'll create a project that performs this calculation in a function.
Preparation
Create a directory in the project root called data
, and within the directory place a file called point.json
to write into the data stream. Place the following content into the file:
{
"type": "point",
"value": null
}
Also create a file called circle.json
with this data.
{
"type": "circle",
"value": 10
}
Finally create a file called rectangle.json
with this data.
{
"type": "rectangle",
"value": { "width": 5, "height": 4 }
}
Define the data source
Start where you left off in the previous tutorial, with a writeable data source for GeometryType
data.
import { VariantType, FloatType, NullType, SourceBuilder, StructType, Template } from "@elaraai/core"
const GeometryType = VariantType({
point: NullType,
circle: FloatType,
rectangle: StructType({
width: FloatType,
height: FloatType,
}),
})
const my_source = new SourceBuilder("My Source")
.writeable(GeometryType);
export default Template(my_source)
Add the function to calculate area
Now we add a function using FunctionBuilder
.
If necessary, refresh your memory by revising the
In this function we'll import the data source as an input named "geometry"
.
Next we use .let
to create a variable containing the calculated area, using the expression devised earlier.
Finally we return an output called "area"
based on the result.
The full sollutions is shown below:
import { Const, FloatType, FunctionBuilder, GetField, Match, Multiply, NullType, SourceBuilder, StructType, Template, VariantType } from "@elaraai/core"
const GeometryType = VariantType({
point: NullType,
circle: FloatType,
rectangle: StructType({
width: FloatType,
height: FloatType,
})
})
const my_source = new SourceBuilder("My Source")
.writeable(GeometryType);
const area = new FunctionBuilder("Area")
.input("geometry", my_source.outputStream())
.body(block => block
.let("area", vars => Match(vars.geometry, {
point: _ => Const(0.0),
circle: radius => Multiply(Math.PI, Multiply(radius, radius)),
rectangle: dimensions => Multiply(
GetField(dimensions, "width"),
GetField(dimensions, "height"),
)
}))
.return({
area: vars => vars.area,
})
)
export default Template(my_source, area);
Observe the resulting area calculation
First build the template, and then deploy it with:
edk template deploy -ycw 06_04_03_use_varianttype_data
Points
Write the contents of data/point.json
to your data stream with the following command:
edk stream replace "Writeable.My Source" --file data/point.json -w 06_04_03_use_varianttype_data
You can validate to contents of the area calculation by using the edk stream get
command:
edk stream get Function.Area.area -w 06_04_03_use_varianttype_data
Which will result area in std out:
▹▹▹▹▹ Attempting to stream Function.Area.area to stdout
0
✔ Download complete
As expected, the area of a point is zero.
Circles
Write the contents of data/circle.json
to your data stream with the following command:
edk stream replace "Writeable.My Source" --file data/circle.json -w 06_04_03_use_varianttype_data
Now fetch the result of the area calculation by using the edk stream get
command:
edk stream get Function.Area.area -w 06_04_03_use_varianttype_data
The result is:
▹▹▹▹▹ Attempting to stream Function.Area.area to stdout
314.1592653589793
✔ Download complete
The result is pi times 100
, which is what we expect for a circle of radius 10
.
Rectangles
Write the contents of data/rectangle.json
to your data stream with the following command:
edk stream replace "Writeable.My Source" --file data/rectangle.json -w 06_04_03_use_varianttype_data
Now fetch the result of the area calculation by using the edk stream get
command:
edk stream get Function.Area.area -w 06_04_03_use_varianttype_data
The result is:
▹▹▹▹▹ Attempting to stream Function.Area.area to stdout
20
✔ Download complete
The result is the width 5
multiplied by the height 4
, which is 20
.
Additional exercises
Suppose we wanted to add a new geometry type for a square
.
How would you modify the type?
How would you modify the area calculations?
Optionally, make this change to your project and try to write a square
to the data stream and confirm the results of the area calculation are correct.
Next suppose we wanted to calculate the perimeter as well as the area of the geometries in our function.
How would you modify the FunctionBuilder
in order to calculate and return the perimeter?
Optionally, make this change to your project and confirm the results of the perimeter calculations are correct with the various geometry cases.
Example Solution
The final solution for this tutorial is available below:
Next Steps
In this tutorial, you learned how to transform variants in an expression as a part of a function. This concludes the module on variants and the topic of working with complex data.