Skip to content

Instantly share code, notes, and snippets.

@LyricLy
Last active August 9, 2023 00:21
Show Gist options
  • Save LyricLy/b8f0397b1481734effc4e8d88d094718 to your computer and use it in GitHub Desktop.
Save LyricLy/b8f0397b1481734effc4e8d88d094718 to your computer and use it in GitHub Desktop.
Docs for Foopy.

Welcome to the Foopy tutorial!

Foopy is a functional object-oriented programming language, which means it has many of the properties from both FP and OOP.

These include:

  • No side-effects
  • Everything is an object (except functions, macros and parsers)
  • Expressions are memoized
  • Functions and classes cannot be redefined
  • Objects cannot be mutated
  • Expressions are evaluated lazily

Let's begin with the basics.

Basic types

Foopy's basic types are Integer, Float, String, List, Tuple, Bool and Character. List, Float, Tuple and Integer are the only predefined types in the language.

All of these basic types have shortcuts to instantiate them:

  • Integers like 123
  • Floats like 17.135
  • Characters like 'a'
  • Strings like "aye?"
  • Lists like [1, 2, 3]
  • Bools like true

Strings subclass List, so they act in many ways like lists of Characters.

Characters subclass Integer, so can be treated much like Integers. You can use Character to turn an Integer into a Character.

Lists are homogeneous, meaning they are only able to contain one type. They are singly linked lists, where it is easy to take off the front of the list and nothing else. Thusly, performing operations at the end of a list is not very efficient and not recommended.

Note that functions are not a basic type. In fact, they are not types at all; functions are not objects in Foopy, although they are first-class.

Functions

Functions are an integral part of Foopy, as with any other language.

As in Haskell, function definition in Foopy looks like tuple x y = (x, y). Foopy also supports pattern-matching such as sumtuple (x, y) = x + y and first x;xs = x

Foopy supports anonymous functions of the form \xy. x + y.

Functions are curried in Foopy, meaning the above example is actually equivalent to tuple x = \y. x + y.

OOP

Due to Foopy being partially OOP, most things in the language are objects, which are dissimilar to objects in most languages as they are all immutable entirely.

You can get an attribute from a type with type:attribute. You can also use instance:attribute to get an attribute from an instance of a type. Getting an attribute that uses an instance variable from an object will raise an exception. instance.method is syntactic sugar for instance:method instance.

To create your own types, use the class statement.

To begin with, define the signature like class Name x y z, where Name is the name of the new type, and x, y and z are the instance variables that the constructor takes.

Afterwards, put a where on the end of the signature and define the class's methods in an indented block:

class Name x y z where
    sum = x + y + z

Note that the class's instance variables, x, y and z can be accessed from everywhere inside the class with only their names, but cannot be accessed with Name:x. To make the instance variables public, use:

class Name x y z where
    x = x
    y = y
    z = z
    sum = x + y + z

Directly using instance variables in a function is not recommended, as it doesn't allow for Name:sum <Name instance>, which can at times be quite useful, or Name.sum. Thusly, our end code becomes:

class Name x y z where
    x = x
    y = y
    z = z
    sum us = us:x + us:y + us:z

Macros

Macros are a mix between a tool to add new syntax to the language easily and a counterpart to functions that most non-functional programmings have.

IO mode

IO mode is the way macros and parsers are interpreted. Instead of just a long chain of different functions being called that eventually culminates in an answer, IO mode is interpreted line-by-line, greedily. That is, whenever a function is called in IO mode, it is immediately evaluated instead of being evaluated whenever the result is needed. In addition, IO mode has an operator, <-, that allows you to bind rebindable variables.

a <- 0
print a  # 0
a <- 1
print a  # 1

Parsers

A parser is a special system used in the preprocessing system to be able to have macros. A parser takes two strings and returns an object representing the parsed value.

To create a parser, we first have to start with the signature, parser name before after where before represents everything before the macro (explained later), backwards, and after represents everything after, forwards. Afterwards, we have an indented block in IO mode to parse the text. Let's write a simple parser that returns everything in front until a space is reached, as a string.

parser nextinfront _ after do # _ is a name that can never be bound to. Whenever you try, the binding is simply ignored. Useful for ignoring arguments that are not needed.
    return until (!= ' ') after  # until is a function that takes from a list until the filter function returns false, and `(!= ' ')` is equivalent to `\x. x != ' '`

When we use this parser on abc 123 ee e eeee, it will consume abc from the code and return abc. Parsers consume any data that they take from the two lazy strings. Note that, quite importantly, parsers exist in their own environment. Besides other parsers, parsers don't have any access to outside imported things or defined functions, although you can define functions inside a parser that will be usable outside.

Macros

Finally, we get to macros. Macros have a trigger, a parser and a binding. The trigger is what will activate the trigger, the parser is the parser to parse the text around the trigger, and the binding is what the result from the parser will be bound to. This all looks like macro ! nextinfront infront. Then, we have an indented block in IO mode to interpret the parsed data. Finally, we either insert or return to make the macro place its result into the code.

macro ! nextinfront infront do
    return infront

Now, if we enter !string, it will become "string"! If we had used insert infront instead, it would have literally inserted the code string and re-parsed the program, which would cause an error in this case but can be quite useful.

Much of the language itself is macros! ., +, and != are some examples of this.

@OliveIsAWord
Copy link

h

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment