• This repository has been archived on 20/Sep/2022
  • Stars
    star
    1,237
  • Rank 37,964 (Top 0.8 %)
  • Language
    Go
  • License
    Other
  • Created over 6 years ago
  • Updated about 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An experimental language which adds functional programming features to Go.

Fo

Fo is an experimental language which adds functional programming features to Go. The name is short for "Functional Go".

Table of Contents

Current Status

Fo is no longer being actively worked on or maintained.

When this project was first created, Go did not support generics. However, as of Go version 1.18, that has changed. For more information about how to use generics in the latest version of Go, see this official tutorial.

Since Go supports generics, there is much less reason to continue working on Fo. It is sort of true that Fo supports some things that Go does not (like generic methods or omitting the type parameters on a receiver type when it is not used). But overall Fo is still fairly unstable and has the major limitation of being restricted to compiling a single file. This likely makes it unsuitable for any real world applications.

I have decided to leave this repository and the playground up for. It might be helpful for anyone who wants to hack on the Go compiler or work with ASTs.

Playground

If you want to give Fo a try without installing anything, you can visit The Fo Playground.

Installation

The Fo compiler is written in Go, so you can install it like any other Go program:

go get -u github.com/albrow/fo

Command Line Usage

For now, the CLI for Fo is extremely simple and only works on one file at a time. There is only one command, run, and it works like this:

fo run <filename>

<filename> should be a source file ending in .fo which contains a main function.

Examples

You can see some example programs showing off various features of the language in the examples directory of this repository.

Language Features

In terms of syntax and semantics, Fo is a superset of Go. That means that any valid Go program is also a valid Fo program (provided you change the file extension from ".go" to ".fo").

Generic Named Types

Declaration

Fo extends the Go grammar for type declarations to allow the declaration of generic types. Generic types expect one or more "type parameters", which are placeholders for arbitrary types to be specified later.

The extended grammar looks like this (some definitions omitted/simplified):

TypeDecl   = "type" identifier [ TypeParams ] Type .
TypeParams = "[" identifier { "," identifier } "]" .

In other words, type parameters should follow the type name and are surrounded by square brackets. Multiple type parameters are separated by a comma (e.g., type A[T, U, V]).

Here's the syntax for a generic Box which can hold a value of any arbitrary type:

type Box[T] struct {
	v T
}

Type parameters are scoped to the type definition and can be used in place of any type. The following are all examples of valid type declarations:

type A[T] []T

type B[T, U] map[T]U

type C[T, U] func(T) U

type D[T] struct{
	a T
	b A[T]
}

In general, any named type can be made generic. The only exception is that Fo does not currently allow generic interface types.

Usage

When using a generic type, you must supply a number of "type arguments", which are specific types that will take the place of the type parameters in the type definition. The combination of a generic type name and its corresponding type arguments is called a "type argument expression". The grammar looks like this (some definitions omitted/simplified):

TypeArgExpr = Type TypeArgs .
TypeArgs    = "[" Type { "," Type } "]" .

Like type parameters, type arguments follow the type name and are surrounded by square brackets, and multiple type arguments are separated by a comma (e.g. A[string, int, bool]). In general, type argument expressions can be used anywhere you would normally use a type.

Here's how we would use the Box type we declared above to initialize a Box which holds a string value:

x := Box[string]{ v: "foo" }

Fo does not currently support inference of type arguments, so they must always be specified.

Generic Functions

Declaration

The syntax for declaring a generic function is similar to named types. The grammar looks like this:

FunctionDecl = "func" FunctionName [ TypeParams ] Signature [ FunctionBody ] .
TypeParams   = "[" identifier { "," identifier } "]"

As you might expect, type parameters follow the function name. Both the function signature and body can make use of the given type parameters, and the type parameters will be replaced with type arguments when the function is used.

Here's how you would declare a MapSlice function which applies a given function f to each element of list and returns the results.

func MapSlice[T](f func(T) T, list []T) []T {
	result := make([]T, len(list))
	for i, val := range list {
		result[i] = f(val)
	}
	return result
}

Usage

Just like generic named types, to use a generic function, you must supply the type arguments. If you actually want to call the function, just add the function arguments after the type arguments. The grammar looks like this (some definitions omitted/simplified):

CallExpr = FunctionName ( TypeArgs ) Arguments .
TypeArgs = "[" Type { "," Type } "]" .

Here's how you would call the MapSlice function we defined above:

func incr(n int) int {
	return n+1
}

// ...

MapSlice[int](incr, []int{1, 2, 3})

Generic Methods

Declaration

Fo supports a special syntax for methods with a generic receiver type. You can optionally include the type parameters of the receiver type and those type parameters can be used in the function signature and body. Whenever a generic method of this form is called, the type arguments of the receiver type are passed through to the method definition.

The grammar for methods with generic receiver types looks like this (some definitions omitted/simplified):

MethodDecl = "func" Receiver MethodName [ TypeParams ] Signature [ FunctionBody ] .
Receiver   = "(" [ ReceiverName ] Type [ TypeParams ] ")" .
TypeParams = "[" identifier { "," identifier } "]" .

Here's how we would define a method on the Box type defined above which makes use of receiver type parameters:

type Box[T] struct {
  v T
}

func (b Box[T]) Val() T {
  return b.v
}

You can also omit the type parameters of the receiver type if they are not needed. For example, here's how we would define a String method which does not depend on the type parameters of the Box:

func (b Box) String() string {
  return fmt.Sprint(b.v)
}

A method with a generic receiver can define additional type parameters, just like a function. Here's an example of a method on Box which requires additional type parameters.

func (b Box[T]) Map[U] (f func(T) U) Box[U] {
  return Box[U]{
    v: f(b.v),
  }
}

Usage

When calling methods with a generic receiver type, you do not need to specify the type arguments of the receiver. Here's an example of calling the Val method we defined above:

x := Box[string]{ v: "foo" }
x.Val()

However, if the method declaration includes additional type parameters, you still need to specify them. Here's how we would call the Map function defined above to convert a Box[int] to a Box[string].

y := Box[int] { v: 42 }
z := y.Map[string](strconv.Itoa)

More Repositories

1

jobs

A persistent and flexible background jobs library for go.
Go
494
star
2

zoom

A blazing-fast datastore and querying engine for Go built on Redis.
Go
309
star
3

forms

A lightweight go library for parsing form data or json from an http.Request.
Go
137
star
4

vdom

A virtual dom implementation written in go which is compatible with gopherjs
Go
90
star
5

elara

Work-in-progress educational programming game
TypeScript
71
star
6

people

A simple HTTP/JSON API written in Go using the Negroni middleware library and Zoom as a datastore.
Go
13
star
7

negroni-json-recovery

Middleware for negroni which catches panics and wraps them into a JSON response.
Go
11
star
8

liquid_time_ago_in_words

A rails-style time_ago_in_words filter for the liquid templating language
Ruby
8
star
9

stringset

A simple and space-effecient Go implementation of a set of strings.
Go
6
star
10

blog-old

The old version of my blog, built with jekyll + octopress
Ruby
6
star
11

fipple

A testing utility for go which lets you easily record and test http responses from any url
Go
4
star
12

doc_parser

A docx parser written in Ruby
Ruby
4
star
13

scribble

A tiny static blog generator written in go.
Go
3
star
14

jekyll-bootstrap-boilerplate

A boilerplate jekyll setup that includes a sass port of bootstrap. I use this as a starting point when designing static websites.
Ruby
3
star
15

prtty

A small go library for logging things with color.
Go
2
star
16

gopherjs-router

A client-side router written in go that compiles to javascript via gopherjs
JavaScript
2
star
17

confirm

A tiny but useful utility for confirming actions, typically used in user-executable scripts or makefiles.
Go
2
star
18

editor

A minimal command line text editor written in go
Go
2
star
19

todo-backend

A simple REST backend for a todoMVC application. Written in go.
Go
1
star
20

timelord

A Go library which allows manipulating time by monkey-patching time.Now
Go
1
star
21

gopherjs-watch

A demo/prototype of two-way data binding with gopherjs
JavaScript
1
star
22

gopherchat

A peer-to-peer command line chat application built using golang. For science!
Go
1
star
23

bullhorn

An easy-to-use SMS and voice broadcast system. You can use it to keep clients who might not have internet access up-to-date.
Go
1
star
24

wemo

Go
1
star
25

go-benchmark-example

An example of how to benchmark and optimize go programs, in particular computing binomial coefficients.
Go
1
star
26

go-linkedlist

A sorted, doubly-linked list implementation written in Go
Go
1
star
27

sids

Bracket website for fundraising for SIDS
Ruby
1
star
28

sample_app

Ruby on Rails Tutorial sample application
Ruby
1
star
29

dependency-linearization

An experiment in finding a fast dependency linearization algorithm for go.
Go
1
star
30

gqlgen-todos

An example of using gqlgen to generate a simple GraphQL API
Go
1
star
31

zoom_example

An example of how to use my go+redis ORM library called zoom
Go
1
star
32

gopherjs-live

Automatic watching and recompiling for gopherjs
Go
1
star
33

calc

A simple command-line calculator program written in go. Good practice for interpreters/compilers.
Go
1
star
34

Pedia

A simple command line tool for querying wikipedia using bash and dig.
Shell
1
star
35

golearn-digit-recognition

Handwritten digit recognition in go using golearn
Go
1
star
36

demo_app

Ruby on Rails Tutorial demo application
Ruby
1
star
37

martini-json-recovery

Middleware for martini which catches panics and wraps them into a JSON response.
Go
1
star
38

xml_format

A simple command line tool for auto-formatting xml documents. Written in Ruby, and uses the ultra-fast libxml-ruby gem.
Ruby
1
star
39

first_app

The first app created for the Ruby on Rails Tutorial
Ruby
1
star