Gleam, a statically typed language for the Erlang VM

Congrats on the 0.1.0 release! Gleam is really looking like it has potential for future projects.

I have two questions. One: what plans do you have for metaprogramming facilities? And, two: how easy will it be to drop a Gleam program into an Elixir program (and vice versa)?

Thanks, and keep up the awesome work!!

1 Like

Maybe a rebar project I slide an umbrella would “just work”? :slightly_smiling_face:

1 Like

what plans do you have for metaprogramming facilities?

I’m interested in adding some kind of metaprogramming system, though it will likely not be for some time. Currently an implicit module system inspired by OCaml looks like a good option to investigate, and a macro system could also be useful.

how easy will it be to drop a Gleam program into an Elixir program (and vice versa)?

Using Gleam modules in Elixir should be as simple as calling an Erlang module, and libraries could be distributed in their compiler Erlang form so the Elixir user doesn’t need the Gleam compiler installed.

Calling Elixir or Erlang functions from Gleam is a little more work as they will need to be annotated as Gleam cannot infer their types. This can be done once and then distributed as a library for other Gleam programs to use.

Having both in one mix project would be possible not but the tooling is not made yet, I am supporting rebar3 first as it is the official build tool of the Erlang ecosystem. What @mbuhot has suggested may be the way to go.

2 Likes

OCaml has a couple of macro systems as libraries for it. The most elixir-like is called ppx_stage or something like that, a simple staged macro system. It uses OCaml attributes so I’ll use it’s syntax here, but you could easily come up with language built-in’s instead!

Here is the jist of how it works:

Quote code

# let greeting = [%code print_string "Hello!\n"];;
val greeting : unit Ppx_stage.code = {Ppx_stage.compute = <fun>; source = <fun>}

The code attribute takes an expression argument and returns it as a code expresesion with the type of the return value of the expression (unit in this case, I.E. {} in Elixir terms).

Converting quoted code at compile-time to an actual expression

The returned code can be ‘run’ at compile-time:

# Ppx_stage.run greeting;;
Hello!
- : unit = ()

The PPX looks for Ppx_stage.run calls (or run scoped within Ppx_stage to be specific, so you can open it for example) taking a 'a Ppx_stage.code type and rewrites it to be the expression represented, so the above is quite literally compiled and run as:

# (fun () -> print_string "Hello!\n") ()
- : unit = ()

Altering quoted code

You can build and splice and alter quoted code of course as well:

# let two = [%code 2];;
val two : int Ppx_stage.code = 2
# let three = [%code 1 + [%e two]];;
val three : int Ppx_stage.code = 1 + 2

The e attribute inside a code attribute takes the code that two represents, so the AST value of the literal 2, and puts it in place of itself, so three becomes [%code 1 + 2].

User quoting

And the big part that lets you make an elixir-like macro is being able to accept user quotes:

let map f = [%code
  let rec go = function
    | [] -> []
    | x :: xs -> [%e f [%code x]] :: go xs in
  go]

So this makes a perfectly optimized function for the type of what is being passed in on demand, so it uses the f from outside the code attribute, and thus this whole map function gets this nasty type of:

val map :
  ('a Ppx_stage.code -> 'b Ppx_stage.code) ->
  ('a list -> 'b list) Ppx_stage.code = <fun>

However, this means you have to pass quoted code ‘to’ this function, so calling it like:

map [%code [%e x] + 1]

Passing code to function calls is annoying though, so can at least make it more like ‘usual’ code, so it gives this:

map (fun%staged x -> x + 1)

So now it looks mostly like a normal function call.

Elixir does the same thing automatically by detecting if the map function is actually a MACRO-map function on the module first, and if so then it just calls that passing in the AST instead of compiling a call to map, so with actual compiler support it is pretty trivial to do kind of the same thing, but instead just check type that it takes and returns and if it is a macro then just use it as such, or follows Elixir as a naming convention, whichever. :slight_smile:

Hi everyone!

With Gleam’s nest release it will be possible to namespaced modules, similar to Elixir (i.e. MyApp.Controller.Login).

I’ve a couple of small design questions that I’m interested in getting second opinions about:

Import syntax

Which of these should be the syntax for importing a nested module?

// #1
import my_app/controller/login

// #2
import myapp:controller:login

// #3
import myapp.controller.login

#1: / is similar to the unix path separator, which is nice as in Gleam the module my_app/user will live in src/my_app/user.gleam.

#2: : is the operator for accessing a module’s field in Gleam list:map(xs, fun)

#3: . is the operator for accessing a Map’s field in Gleam current_user.name. This would match other languages such as Java, Python, Elixir, etc.

stdlib namespace

I intend to move the stdlib into a namespace to avoid clashing with Erlang modules.

Which namespace should we use?

import std/string
import gleam/string
import gleam/std/string
import $SOMETHING_ELSE/string

Thanks!

1 Like

I’d go with / unless . is usable to call functions, like myapp.controller.login:some_fun(). So depends on the semantics of an import which isn’t clear from just the syntax.

2 Likes
import my_app/controller/login

Creates a variable login that contains the module, and functions can be called on it using a similar syntax to Erlang login:some_function().

What’s your plan for namespace conflict?

Example if you have namespaces
import my_app/controller/login
import my_app/controller2/login

Do you plan to support namespace alias? Something like

import my_app/controller/login
import my_app/controller2/login as login2

That’s the plan! And I intend to use the syntax you’ve used there :slight_smile:

4 Likes

I personally think that the : is the preferred character for separating Beam modules. Since you intend to allow lowercase module names w/o explicitly making them atoms, I think it will be less ambiguous.

1 Like

It seems very hard to add compile-time features to a language that’s meant to run on the BEAM but which has a compiler without access to the BEAM runtime.

It’s more difficult as you can’t use the regular runtime using eval, but it’s possible. Other languages I’ve looked at have an interpreter that implements a subset of the language which can be used to resolve macros (and dependant types) at compile time. We’re a long way away from any such features :slight_smile:

You should make it self-hosting then. ^.^

v0.2.0 is out! :tada:

The changelog can be found here -> https://github.com/lpil/gleam/blob/master/CHANGELOG.md

Please build the latest version of the compiler and upgrade the rebar_gleam plugin with rebar3 as global plugins upgrade rebar_gleam :slight_smile:

3 Likes

Could you please create a release on github, or a tag at least, such that I can update the AUR package for Arch Linux?

PS: As a temporary measure, I’ll bump to 0.1.2, as I have missed that release :wink:

1 Like

Oh! I forgot to push the tags. Thanks for the reminder :slight_smile:

1 Like

I’ll bump as soon as I am in the office and were able to verify the buildstatus.

1 Like

I’ve added precompiled binaries for macOS and Linux to the GitHub releases page, so it’s now much easier to get started with Gleam.

There’s also a docker image on DockerHub, if that’s your preference :slight_smile:

https://cloud.docker.com/u/lpil/repository/docker/lpil/gleam

2 Likes

We can’t access that page, it’s private to you, you probably meant this one:

https://hub.docker.com/r/lpil/gleam

2 Likes

Oops, thank you. :slight_smile: