Hello World, Part 3: Intro to Ludwig

This is Hello World, Part 3, an introduction to the Ludwig language and a line-by-line explanation of HelloWorld.lw, the composition in Hello World, Part 2. If you haven’t read Part 1 or Part 2 yet, we recommend that you start there and come back here when you’re done.

If you want a preview of this walkthrough take a look at our video here.

Hello World, Line By Line

Ready to examine HelloWorld.lw? Let’s start from the top.

Get editor plug-ins here.

Composition keyword


Files ending in .lw are Ludwig files. The composition keyword denotes a special type of Ludwig file that is executable with fugue run. All other Ludwig files are modules, and like you’d expect from a module, they can be imported into compositions, but they are not runnable on their own. Groups of modules are libraries, and there are many examples of Ludwig modules (and libraries) in the Standard Library Reference.

It’s a best practice to put the composition keyword at the very beginning of your composition.

Importing Modules

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

Modules are another name for importable Ludwig code, groups of modules are libraries. Our Hello World composition makes use of two modules: Fugue.AWS and Fugue.AWS.EC2. If you have Fugue, you have these modules; they are part of the Fugue Standard Library, which is bundled with the Fugue Client Tools. In addition to Fugue.AWS.* modules, the Fugue Standard Library includes Fugue.Core.AWS.* and Fugue.AWS.Pattern.* modules. You can read more about the different categories in the Ludwig Programming Guide.

We need to import Fugue.AWS and Fugue.AWS.EC2 because our composition uses types defined in each module – which we’ll get to in just a few lines.

We can give each module an alias, so we don’t have to type out the whole module name when we refer to one of its types. We do this by using as, like so: import <Module Name> as <Alias>.



Need to document your code? Write a TODO? Make a note to yourself? Any line beginning with a # is a comment, which Fugue ignores.


hello-world-vpc: EC2.Vpc.new {
  cidrBlock: "",
  tags: [hello-world-tag],
  region: AWS.Us-west-2,

Now we’re getting to the good stuff! This chunk of code is a binding. A binding is similar to a variable, but bindings are immutable, meaning once the value is assigned, it can’t be changed. In Ludwig, the colon (:) acts the same way as an equals sign (=) does in other languages: to indicate assignment.

Here, you’re assigning the hello-world-vpc binding to a value consisting of the results of the EC2.Vpc.new function, which is imported from the Fugue.AWS.EC2 module of the Fugue Standard Library. This function creates a new value of the type EC2.Vpc, specified in a multiline argument inside curly braces.

Punctuation and spacing are important: There’s a space between the function and its curly-braced argument, and each line inside the argument is indented two spaces. Trailing commas are allowed, so we’ve used them here. The binding is followed by a blank line.

Now, let’s take a look at each line in the EC2.Vpc.new function. There are a bunch of fields here, but don’t worry, you don’t have to memorize them! Just consult the Standard Library Reference; there, you’ll find the list of fields for types in every supported AWS service.

The EC2.Vpc.new function allows you to create a new binding of the type EC2.Vpc. As you can see in the Standard Library Reference, the type Vpc has the following fields:

  • cidrBlock
  • tags
  • instanceTenancy
  • region
  • dhcpOptions

The Standard Library Reference even tells us what type of value each field expects. Note: The order of fields within a type does not matter.

For instance, the cidrBlock field is explained in the Standard Library Reference, which states that the required value is a string written in CIDR notation. In Ludwig, strings are enclosed in single or double quotes. The reference helpfully points out that this field is validated at compile time. Our composition uses the string “”, because that’s the range of IP addresses we want our VPC to have. See Wikipedia for more information about CIDR notation.

tags is an optional list of the type Tag, which we have opted to use in our composition. We’ll explain this in the next section, but you can see that we are referring to another binding here, called hello-world-tag. List items are enclosed in brackets – even if there’s only one item, like in this particular list.

instanceTenancy is an optional field that takes a value of the type Tenancy; valid values for this type are enumerated in the Standard Library Reference. We don’t need the tenancy of instances launched into this VPC set to “dedicated” or “host,” so we’ve left this optional field out to simplify our composition. See the AWS documentation for more information about instance tenancy.

The region field requires a value of the type Region; valid values for this type are enumerated in the Standard Library Reference. Our composition uses AWS.Us-west-2, because we want to build our composition in the U.S. West (Oregon) region. See the AWS documentation for more information about available regions.

dhcpOptions is an optional field that takes a value of the type DhcpOptions; the Standard Library Reference explains how to create your own DHCP options sets. We don’t want to create a DHCP options set for this VPC, so we’ve left this optional field out to simplify our composition. See the AWS documentation for more information about DHCP options sets.

Adding Another Binding

hello-world-tag: AWS.tag("Application", "Hello World")

Look familiar? This part of the composition defines the binding you saw earlier, hello-world-tag. This binding uses the AWS.tag() helper function. The AWS.tag() function is a handy way to create a new binding with a value of the type AWS.Tag. As you can see from the AWS.Tag documentation, the AWS.Tag type has two fields, key and value. Each value must be a string: key is limited to 128 characters and cannot start with “aws:”, and value is limited to 255 characters. AWS.tag() allows us to define our key-value pair between the parentheses of the function, so we’ve used the string “Application” for key and the string “Hello World” for value. And as before, the binding is followed by a blank line.

You’re Done With Part 3!

Congratulations! You’ve learned the basics of Ludwig and writing a composition. You’ve completed the Hello World tutorial. Now you’re ready for some of our other examples!

Next Steps

Now that you’ve gotten a taste of how Ludwig works, you can find more information about Ludwig in the Advanced Ludwig. Or, if you want to jump into building more with Fugue, try the next example, Building A Network. You may also return to Examples to see our other examples. And if you’re stuck and you just want a hand, reach out to support@fugue.co.