Functions Tutorial, Part 6

Download this tutorial’s module, composition, and exercise.

A Complete Module with User-Defined Types

Now that we’ve explored a few basic functions, let’s examine a complete module. This time, we’ll write a function involving a user-defined type. This function creates a pair of public subnets with given CIDR blocks and attaches them to a given VPC. The result is similar to the function we examined in Part 5, but it’s presented in a cleaner, easier-to-understand format, and it implements user types. Let’s go!

Import Statements

import Fugue.AWS.EC2 as EC2
import Fugue.AWS as AWS

We’ll begin with the import statements. The Subnet type is part of the Fugue.AWS.EC2 library, so we’ll import it as EC2. We’ll also need to import Fugue.AWS as AWS so we can use the Region type.

You may have noticed there’s no composition keyword at the top. That’s because this won’t be a composition – we aren’t going to fugue run it. It’s just a module we’ll reference from an actual composition.

User-Defined Type

type PublicSubnetPair:
  subnet1: EC2.Subnet
  subnet2: EC2.Subnet

Next, we’re going to create a user-defined type. User-defined types are constructed from the types you find in the Fugue Standard Library. Here, the type PublicSubnetPair is made of two subnets, which are each of the type EC2.Subnet.

The Function

fun makePublicSubnetPair {subnet1cidr: String, subnet2cidr: String, sameVpc: EC2.Vpc, az1: AWS.AvailabilityZone, az2: AWS.AvailabilityZone} -> PublicSubnetPair:
  {
    subnet1: EC2.Subnet.new {
      cidrBlock: subnet1cidr,
      vpc: sameVpc,
      availabilityZone: az1
    },
    subnet2: EC2.Subnet.new {
      cidrBlock: subnet2cidr,
      vpc: sameVpc,
      availabilityZone: az2
    }
  }

Let’s break down this function bit by bit. First, it’s called makePublicSubnetPair. It takes five parameters: subnet1cidr, which is of the type String; subnet2cidr, which is also of the type String; sameVpc, which is of the type EC2.Vpc; az1, which is of the type AWS.AvailabilityZone; and az2, which is also of the type AWS.AvailabilityZone. The function returns a PublicSubnetPair.

Note how the parameters are enclosed in braces rather than parentheses. This is because we’ve used named arguments rather than positional arguments. The primary difference in syntax is that named arguments use braces, and positional arguments use parentheses. For examples of positional arguments, see the previous functions we’ve discussed.

In the body of the function, we create two bindings, subnet1 and subnet2. (If you’re wondering where we got the names from, check out the user-defined PublicSubnetPair type above.) Each one calls the EC2.Subnet.new constructor. The cidrBlock value is the subnet1cidr or subnet2cidr parameter, the vpc value is the sameVpc parameter, and the availabilityZone value is the az1 or az2 parameter.

And that’s the module! We’ll call it FunctionTutorial6SubnetFunction.lw. Overall, when you supply this function with two CIDR block strings, the name of a VPC binding, and two availability zones, you’ll end up with two public subnets attached to that VPC. It may sound complicated, but it’s quite simple, as you’ll see when we call the function shortly.

Let’s look at the composition.

Composition Statement and Imports

composition

import Fugue.AWS.EC2 as EC2
import Fugue.AWS as AWS
import FunctionTutorial6SubnetFunction as .

As usual, we begin our composition with a composition keyword. Same imports as before, with the addition of the line import FunctionTutorial6SubnetFunction as . – which is how we import our module.

A VPC

exampleVpc: EC2.Vpc.new {
  cidrBlock: "10.0.0.0/16",
  region: AWS.Us-west-2,
}

You can’t have a subnet without a VPC, so we’re going to make a VPC here. We’ll give it a /16 CIDR block and deploy it in the Oregon region.

Calling the Function

twoSubnets: makePublicSubnetPair {
  subnet1cidr: "10.0.1.0/24",
  subnet2cidr: "10.0.2.0/24",
  sameVpc: exampleVpc,
  az1: AWS.A,
  az2: AWS.B
}

This is how we call the makePublicSubnetPair function. We’ve given the binding the name twoSubnets and offered the five required arguments: subnet1cidr is the string "10.0.1.0/24", subnet2cidr is the string "10.0.2.0/24", sameVpc points to the exampleVpc binding we created just above, az1 is the availability zone AWS.A, and az2 is the availability zone AWS.B. This will create two /24 subnets and attach them to exampleVpc, which you can see in the figure below.

Two subnets attached to exampleVpc in the AWS Management Console.

Two subnets attached to exampleVpc in the AWS Management Console.

Exercise 6

Ready for your last exercise? Below is a composition with a function, but there’s something wrong with it. Find the mistake and fix it, so that the composition compiles.

When fixed, the makeDDBTablePair function takes two AWS.Region values and returns a pair of DynamoDB tables, each in a different region.

import Fugue.AWS as AWS
import Fugue.AWS.DynamoDB as DynamoDB

type DDBTablePair:
  ddbTable1: DynamoDB.Table
  ddbTable2: DynamoDB.Table

fun makeDDBTablePair {region1: AWS.Region, region2: AWS.Region} -> DDBTablePair:
  {
    ddbTable: DynamoDB.Table.new {
      name: "Table1",
      region: region1,
      attributes: {"PropertyName": DynamoDB.S},
      schema: {"PropertyName": DynamoDB.HASH},
      provisionedThroughput: {
        read: 10,
        write: 10
      },
    },
    ddbTable2: DynamoDB.Table.new {
      name: "Table2",
      region: region2,
      attributes: {"PropertyName": DynamoDB.S},
      schema: {"PropertyName": DynamoDB.HASH},
      provisionedThroughput: {
        read: 10,
        write: 10
      },
    }
  }

twoDDBTables: makeDDBTablePair {
  region1: AWS.Us-west-2,
  region2: AWS.Us-east-1
}

Hint: If you try to compile the composition as is, the compiler will give you a tip about what’s wrong.

You can download the file here. Compile it by executing the following:

lwc FunctionTutorial6Exercise.lw

You’re Done!

Congratulations! You’ve finished the Functions Tutorial, and you’re ready to make your own functions. We suggest browsing the Examples for ideas. Or, check out the Standard Library Reference to see more specific examples of Ludwig. You can also visit Advanced Ludwig Syntax to learn about advanced topics in Ludwig. As always, feel free to reach out to support@fugue.co with any questions.