introduction to liquidsoap's programming language

big balls of mud killer
, in 20 November 2014

I’d like to write a post to try to understand liquidsoap’s own scripting language. Liquidsoap is a functional language, which you may find confusing if you have not programmed much before. In functional languages you can’t do things you might be used to doing in other languages like mutating values, changing state, etc.

types

The basic types in liquidsoap are integers, floats, strings and booleans.

You probably are quite familar with these if you have programmed before. There are other types specific to liquidosoap, these are

and a few others.

no changing variables

Since liquidsoap is a functional language, there are no variables, only definitions. So in this example, I’m not really changing the value of the source variable, I’m simply creating new ones and using the same name.

source = fallback(track_sensitive=false,
                  [live_dj,backup_playlist,on_fail])
source = on_metadata(pub_metadata, source)

source = server.rms(source)

no unused variables

You aren’t allowed to declare a variable and then not use it, liquidsoap will complain:

At line 6, character 15: The variable some_variable defined here is not used
  anywhere in its scope. Use ignore(...) instead of some_variable = ... if
  you meant to not use it. Otherwise, this may be a typo or a sign that
  your script does not do what you intend.

static typing

Liquidsoap is statically typed, but the types are inferred. This means you won’t have to write types like in C or Java.

function notation in the API docs

The function notation used in the liquidsoap api documentation may be a bit daunting at first. For example here is the documentation for input.harbor:

(?id:string,?auth:((string,string)->bool),?buffer:float,
 ?debug:bool,?dumpfile:string,?icy:bool,
 ?icy_metadata_charset:string,?logfile:string,?max:float,
 ?metadata_charset:string,
 ?on_connect:(([(string*string)])->unit),
 ?on_disconnect:(()->unit),?password:string,?port:int,
 ?timeout:float,?user:string,string)->source('a)

All the question marks are simply optional named arguments and usually described in the function’s documentation. Required arguments are marked with a ~ instead of a question mark.

?id:string

This is a optional parameter called id that is a type string.

?on_disconnect:(()->unit)

This is an optional parameter called on_disconnect that takes a function that is of type ()->unit, which is a function that takes no arguments and returns unit. What’s a unit, you ask? From the wikipedia page

In the area of mathematical logic and computer science known as type theory, a
unit type is a type that allows only one value (and thus can hold no
information).

In simple terms, a unit is returned when you don’t care about the return value. In other languages, you could simply not return anything. However, in functional languages, all functions must return a value, so we denote the return value as unit when we don’t care about the return value.

[t] are lists and (t*t) are ‘pairs’ (or ‘tuples’ if you come from another language like Python).

([(string*string)])->unit

This is a function that takes a list of (string*string) (such as ("a","b")) tuples and returns a unit.

Finally you can see what this function returns by looking at the arrow(->) at the end.

->source('a)

This means the function returns a source type.

defining functions

We can define a function with def and end to mark the end of a function.

  def function_name(arguments) =
    function_body
  end

like so:

def get_user(user,password) =
  if user == "source" then
    x = string.split(separator=';',password)
    list.nth(x,0)
  else
    user
  end
end

The return type of the function is simply the last line of the body of the function. So in the case of the function above, it would be string.

refs

Liquidsoap is a functional language, however it is not a pure functional language. You can use mutatable variables if you want to. Liquidsoap borrows a concept from ocaml known as ref:

When assigning values to a ref, you use := instead of =.

title := ref ""
current_dj_name := ref ""

If you want to print a ref, you should use the ! operator.

title = ref ""
log("title: #{!title}")

Check out the official liquidsoap language reference for more.

Modern Online Radio with Liquidsoap Book - Free Sample

Need more help with liquidsoap? Can’t get your script to work?

I wrote a book to help you learn Liquidsoap. The book covers all aspects of liquidsoap, from getting started, to making dynamic streams, audio processing, video, customizing metadata, authentication, and more. The book is available for purchase now here!

You can get a free sample chapter of my book! Just enter your email address to subscribe to my mailing list and I'll send you a free PDF sample of the book in return.