DocsGetting started

Your first program.

Run a small program, then separate its pure calculation from its output.

Let’s write some Fango. You can follow along in the playground without installing anything. If you’d rather run the examples on your own machine, install Fango first.

We’ll start with the classic. Create hello.fango with this program, or paste it into the playground:

main() = print "Hello, Fango!"

On your machine, run it with:

fango run hello.fango

You should see:

Hello, Fango!

That one line has a few parts worth knowing. main is where every program starts. The () after it means it takes a Unit argument, a value that carries no information, which is how Fango says “nothing to pass in”. print writes a value followed by a newline, and the space between print and the text is all it takes to call it.

You didn’t import anything to get print. Fango’s prelude provides it, along with a handful of other everyday names, so a small program stays small.

Name a calculation.

Now let’s make the program calculate something. Replace it with:

square : Int -> Int
square number = number * number

main() = print (square 7)

It prints 49. A definition is a name, its parameters, an =, and an expression. There’s no return: whatever the expression evaluates to is the result.

The line above square is a type annotation. Int -> Int reads as “takes an integer, gives back an integer”. You could leave it out here, but it’s good practice to write it: it tells the reader what the function is for.

The parentheses in print (square 7) group the call, so square 7 is worked out first and its result is handed to print. Without them, print would be given square and 7 as two separate arguments.

Build a small block.

Functions can do more than one thing. Give main a few steps:

square : Int -> Int
square number = number * number

main() =
    answer = square 7
    print "Working it out..."
    print "The square of 7 is #{answer}."

This prints:

Working it out...
The square of 7 is 49.

The body is indented with spaces. Here answer is a name for a value, not a variable: once it’s set, it stays that way, and you can’t assign to it again. A block ends with its result expression, just like a one-line definition. Any statements before it, like the first print, have to return Unit, since nothing would use their result.

The second string uses interpolation: #{answer} puts the value of answer into the text. Any expression can go between the braces, so "#{square 8}" works too.

Read an effect annotation.

One more annotation, this time on main:

square : Int -> Int
square number = number * number

main : () ->{Console} ()
main() = print (square 7)

Compare the two types. square is pure: given the same number, it always gives the same answer and touches nothing else. main has {Console} between its arrow and its result type, which says that calling it uses the console: here, it writes output. Its result is Unit because there’s nothing useful to return; the point of calling it is the printing.

Console is a service, one of a few small effects for things outside the program, alongside files, the network, and the clock. When main names one, Fango connects it to the real thing when the program starts. Tests can connect the same code to a fake console instead, as Files, console, and sockets shows.

This is the idea that gives the language its tagline. A function’s type tells you what it might do, so you can tell at a glance which parts of a program calculate and which parts act. Effects aren’t commands that get queued up and run later. Calling the function is what performs the work. You’ll see how to define your own effects, and decide what they mean, in Effects and handlers.

Keep going.

Next, Syntax and operators covers values and blocks in more detail, and Functions shows how to take several arguments, pass callbacks, and write recursion. When you want to check or build a program, Setup and commands has what you need.