Skip to content

Instantly share code, notes, and snippets.

@bobbbay
Last active July 29, 2021 21:27

Revisions

  1. bobbbay revised this gist Jul 27, 2021. 1 changed file with 6 additions and 6 deletions.
    12 changes: 6 additions & 6 deletions salo.org
    Original file line number Diff line number Diff line change
    @@ -97,11 +97,11 @@
    #+end_src

    Cool, right?
    **** TODO Pattern matching

    **** Pattern matching

    Salo supports pattern matching, e.g.:

    #+begin_src
    name : Bool -> String
    name true = "Bob"
    @@ -127,7 +127,7 @@

    With the ~_~ character, Salo can match every other variant.

    **** TODO Generic parameters
    **** Generic parameters

    Functions don't have to have strict types - with polymorphism, we're able to allow any type to pass into our program, as long as it can validly compile (more on this later).

    @@ -158,7 +158,7 @@

    Anyways, note the ~return~ keyword here. This indicates to Salo that this value should be returned, i.e. this file evaluates to ~true~.

    *** TODO Imports
    *** Imports

    Salo is also able to import other files using the ~import~ keyword. Imports can either bring a library file or a local file into scope. For example:

    @@ -182,7 +182,7 @@
    #+end_src

    Assuming =./git.sa= exists and returns an Attrset, the ~git~ value will contain that value. If any Salo rules are violated during the import - the file does not exist or the returned value isn't an Attrset - a compile-time error will be thrown.

    ** Example configuration

    #+begin_src salo
  2. bobbbay revised this gist Jul 26, 2021. 1 changed file with 95 additions and 21 deletions.
    116 changes: 95 additions & 21 deletions salo.org
    Original file line number Diff line number Diff line change
    @@ -8,7 +8,7 @@

    * Salo is a "wrapper language" to Nix
    * It is statically typed (and type inferred), has an ML-inspired syntax, and provides macros
    * Each Salo configuration should provide at least one return value by the end of the file
    * Each top-level Salo configuration must return an Attrset by the end of the file

    ** Goals

    @@ -20,9 +20,9 @@

    ** The basics

    This section outlines the basics of Salo's syntax.
    This section outlines the basics of Salo's syntax. If you'd like to skip the basics and check out an actual example, scroll down to the [[Example configuration]].

    ** The REPL
    *** The REPL

    Before starting, let's get familiar with Salo's REPL. Run ~salo~ in the command line once Salo is installed to open up the prompt:

    @@ -35,7 +35,7 @@

    * ~:t x~: get the type of ~x~
    * ~:q~: quit
    * [ ] More to come!
    * [TODO] More to come!

    Now that we're familiar with the REPL, we can continue to learning Salo's configuration language. From this point on, every code block that begins with ~> ~ is run in the REPL.

    @@ -99,48 +99,122 @@
    Cool, right?

    **** TODO Pattern matching

    Salo supports pattern matching, e.g.:

    #+begin_src
    name : Bool -> String
    name true = "Bob"
    name false = "Jeffrey"
    #+end_src

    In this case, if the Bool given to ~name~ is true, it will evaluate to "Bob". If it is given false, then it will evaluate to "Jeffrey".

    Salo pattern matches /must/ be exhaustive. Meaning, this won't work:

    #+begin_src
    isOne : Int -> Bool
    isOne 1 = true
    #+end_src

    Salo will complain /during compile time/ that this match does not cover every variant. What if we pass on 5, 6, or 7? Salo has no idea what to evaluate to. This, however, will work:

    #+begin_src
    isOne : Int -> Bool
    isOne 1 = true
    isOne _ = false
    #+end_src

    With the ~_~ character, Salo can match every other variant.

    **** TODO Generic parameters

    Functions can also have generic parameters, such as ~Array<T>~. Similar to Rust, generic parameters are kept in angle brackets upon declaration:
    Functions don't have to have strict types - with polymorphism, we're able to allow any type to pass into our program, as long as it can validly compile (more on this later).

    Again, similar to Haskell:

    #+begin_src
    generic : a -> a -> a
    generic x y = x + y
    #+end_src

    This function will have a different type signature per call. For example, if we run:

    #+begin_src
    generic "A" "B"
    #+end_src

    The type signature will be ~generic : String -> String -> String~. Salo knows the very second it sees that first argument ~"A"~ that the other two values in the type signature must also be a String.

    *** Returning

    Earlier in this document, we mentioned that each top-level Salo configuration file *must* return an Attrset. Now, let's examine /how/ this is done.

    #+begin_src salo
    return true
    #+end_src

    This is a minimal, valid Salo file. Crazy, right? Just kidding.

    Anyways, note the ~return~ keyword here. This indicates to Salo that this value should be returned, i.e. this file evaluates to ~true~.

    *** TODO Imports

    *** NixOS

    To generate a NixOS configuration, an Attrset should be returned.
    Salo is also able to import other files using the ~import~ keyword. Imports can either bring a library file or a local file into scope. For example:

    #+begin_src
    import std::prelude::*;
    #+end_src

    Will import everything in the ~prelude~ module of the standard library. This line is actually automatically inserted into every Salo file for ease-of-use. Note that glob imports are not recommended, but are possible.

    #+begin_src
    import ./emacs.sa::backgroundColor
    #+end_src

    Will search for =./emacs.sa=. If not found, Salo will throw a compile-time error. If found, it will import the ~backgroundColor~ value in emacs.sa.

    Finally, we have the ability to import the returned value of a file, e.g.

    #+begin_src
    git : Attrset
    git = import ./git.sa
    #+end_src

    Assuming =./git.sa= exists and returns an Attrset, the ~git~ value will contain that value. If any Salo rules are violated during the import - the file does not exist or the returned value isn't an Attrset - a compile-time error will be thrown.

    ** Example configuration

    #+begin_src salo
    description : String; // type is string
    description = "A system flake for my x86_64 server"; // set value
    description : String; -- type is string
    description = "A system flake for my x86_64 server"; -- set value
    -- Note that `description` is not specifically used in the result

    // Type is inferred : Array<Derivation>
    -- Type is inferred : Array<Derivation>
    packages = [
    pkgs.git // type is Derivation
    pkgs.git -- type is Derivation
    ];

    hardware.pulseaudio = { // an Attrset
    enable = true; // Booleans
    extraModules = [ pkgs.pulseaudio-modules-bt ]; // guess what type this is :P
    hardware.pulseaudio = { -- an Attrset
    enable = true; -- Booleans
    extraModules = [ pkgs.pulseaudio-modules-bt ]; -- guess what type this is :P
    package = pkgs.pulseaudioFull;
    support32Bit = true;
    extraConfig = "
    load-module module-bluetooth-policy auto_switch=2
    "; // multiline Strings also work
    }; // end of Attrset
    "; -- multiline Strings also work
    }; -- end of Attrset

    {
    networking.hostName = "MyServer", // can inline value
    networking.hostName = "MyServer", -- can inline value

    environment.systemPackages = packages, // can use variable's value as long as the type checks
    environment.systemPackages = packages, -- can use variable's value as long as the type checks

    hardware, /* desugars into `hardware = hardware`
    hardware is an Attrset which contains
    Attrset, `pulseaudio`. */
    } // Note that the semicolon is omitted here, because this is what will be returned
    // If we placed a semicolon here, Salo would complain that nothing is returned
    } -- Note that the semicolon is omitted here, because this is what will be returned
    -- If we placed a semicolon here, Salo would complain that nothing is returned
    #+end_src

    Evaluates to:
  3. bobbbay revised this gist Jul 26, 2021. 1 changed file with 63 additions and 12 deletions.
    75 changes: 63 additions & 12 deletions salo.org
    Original file line number Diff line number Diff line change
    @@ -1,25 +1,53 @@
    * Salo MVP

    Welcome to the Salo MVP document. If you're here, you're probably interested in what Salo is, what it hopes to accomplish, and what advantages it provides. Without further ado, let's get started!

    ** Overview

    In short...

    * Salo is a "wrapper language" to Nix
    * It is statically typed (and type inferred), has an ML-inspired syntax, and provides macros
    * Each Salo configuration should provide at least one return value by the end of the file

    ** Goals

    * To use a static type system to check code at compile-time
    * To provide a familiar, ML-like syntax
    * To give extra functionality to configuration with macros
    Salo aims to:

    * Use a static type system to check code at compile-time
    * Provide a familiar, ML-like syntax
    * Give extra functionality to configurations with macros

    ** Overview
    ** The basics

    * Salo is a "wrapper language" to Nix
    * It is statically typed (and type inferred), has a slightly-altered syntax, and provides macros
    * Each Salo configuration should provide at least one return value, using the `return` keyword or by omitting a semicolon at the final line (similar to Rust's synax)
    This section outlines the basics of Salo's syntax.

    ** The REPL

    Before starting, let's get familiar with Salo's REPL. Run ~salo~ in the command line once Salo is installed to open up the prompt:

    #+begin_src salo-repl
    > Welcome to the Salo REPL!
    >
    #+end_src

    The REPL supports a few commands:

    * ~:t x~: get the type of ~x~
    * ~:q~: quit
    * [ ] More to come!

    Now that we're familiar with the REPL, we can continue to learning Salo's configuration language. From this point on, every code block that begins with ~> ~ is run in the REPL.

    *** Types

    Salo has many types, mostly springing off of Nix's types. These include:

    * Bool
    * Int
    * String
    * Attrset
    * Array\<T>
    * Array<T>
    * Derivation
    * Function

    @@ -44,15 +72,38 @@
    Functions are defined in a slightly different syntax:

    #+begin_src salo
    f : String -> String
    f x = x
    > f : String -> String
    > f x = x
    #+end_src

    Very Haskell-esque, indeed!

    If you're unfamiliar with ML syntax, this defines a function that takes a String and returns a String. In the implementation, ~f~ takes ~x~ and returns ~x~ without modifications.

    *** TODO Currying
    **** Currying

    Salo types curry by default. Take the following code example (note the REPL prompt):

    #+begin_src salo-repl
    > :t f
    f : String -> String

    > g : String -> String -> String
    > g x y = x + y
    > :t g
    g : String -> String -> String
    > :t g "Hello, "
    g "Hello, " : String -> String
    #+end_src

    Cool, right?

    **** TODO Pattern matching

    **** TODO Generic parameters

    Functions can also have generic parameters, such as ~Array<T>~. Similar to Rust, generic parameters are kept in angle brackets upon declaration:

    *** TODO Imports

    *** NixOS
    @@ -108,4 +159,4 @@ Evaluates to:
    extraConfig = "load-module module-bluetooth-policy auto_switch=2";
    };
    }
    #+end_src
    #+end_src
  4. bobbbay created this gist Jul 26, 2021.
    111 changes: 111 additions & 0 deletions salo.org
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,111 @@
    * Salo MVP

    ** Goals

    * To use a static type system to check code at compile-time
    * To provide a familiar, ML-like syntax
    * To give extra functionality to configuration with macros

    ** Overview

    * Salo is a "wrapper language" to Nix
    * It is statically typed (and type inferred), has a slightly-altered syntax, and provides macros
    * Each Salo configuration should provide at least one return value, using the `return` keyword or by omitting a semicolon at the final line (similar to Rust's synax)

    *** Types

    Salo has many types, mostly springing off of Nix's types. These include:
    * Bool
    * Int
    * String
    * Attrset
    * Array\<T>
    * Derivation
    * Function

    Values are created with a (mostly optional) type signature and value, as such:
    #+begin_src salo
    a : String
    a = "Hello, world!"
    #+end_src

    This is very similar to ML syntax.

    In the example above, the definition of ~a~ could have been rewritten as:

    #+begin_src salo
    a = "Hello, world!"
    #+end_src

    Because Salo is smart enough to infer that ~a~'s type is String.

    *** Functions

    Functions are defined in a slightly different syntax:

    #+begin_src salo
    f : String -> String
    f x = x
    #+end_src

    Very Haskell-esque, indeed!

    If you're unfamiliar with ML syntax, this defines a function that takes a String and returns a String. In the implementation, ~f~ takes ~x~ and returns ~x~ without modifications.

    *** TODO Currying
    *** TODO Imports

    *** NixOS

    To generate a NixOS configuration, an Attrset should be returned.

    ** Example configuration

    #+begin_src salo
    description : String; // type is string
    description = "A system flake for my x86_64 server"; // set value

    // Type is inferred : Array<Derivation>
    packages = [
    pkgs.git // type is Derivation
    ];

    hardware.pulseaudio = { // an Attrset
    enable = true; // Booleans
    extraModules = [ pkgs.pulseaudio-modules-bt ]; // guess what type this is :P
    package = pkgs.pulseaudioFull;
    support32Bit = true;
    extraConfig = "
    load-module module-bluetooth-policy auto_switch=2
    "; // multiline Strings also work
    }; // end of Attrset

    {
    networking.hostName = "MyServer", // can inline value

    environment.systemPackages = packages, // can use variable's value as long as the type checks

    hardware, /* desugars into `hardware = hardware`
    hardware is an Attrset which contains
    Attrset, `pulseaudio`. */
    } // Note that the semicolon is omitted here, because this is what will be returned
    // If we placed a semicolon here, Salo would complain that nothing is returned
    #+end_src

    Evaluates to:

    #+begin_src nix
    { config, pkgs, ... }:

    {
    networking.hostName = "MyServer";
    environment.systemPackages = [ pkgs.git ];
    hardware.pulseaudio = {
    enable = true;
    extraModules = [ pkgs.pulseaudio-modules-bt ];
    package = pkgs.pulseaudioFull;
    support32Bit = true;
    extraConfig = "load-module module-bluetooth-policy auto_switch=2";
    };
    }
    #+end_src