- Easy to get started for mainstream programmers (JavaScript, Java, Python, Ruby), don't need to know monads to print a string, no enforcement of pure/impure in the type system, no Lisp-syntax (use Clojure if you want that), strict evaluation
- Readable programs: one true way to layout code, significant whitespace,
minimal noise in code, no custom operators like
.&&.
- Rich set of base data types: int, float, decimal, unicode strings, lists, tuples, maps, sets, date/times, function expressions, unicode strings
- Rich set of language constructs: iterators/generators, channels, variable interpolation in strings
- Functions as equations, pattern matching like Haskell or Erlang
- Statically typed with type inference for building and maintaining large programs
- Opinionated, like Golang (e.g. one true way to format source code, enforced
naming conventions
fooBar
notfoo_bar
, really long functions, too many arguments, cyclomatic complexity is too high are compiler errors?) - Batteries-included like https://golang.org/pkg/, easy to do common tasks like read and write files, manipulate filepaths, build HTTP clients and servers, access databases, parse URLs, parse/generate common encodings like HTML, XML, JSON, CSV, MIME-encoded decs like emails, generate text with templates, sort, do regexes, parse command line arguments, write logs, run tests
- Expressive with functional gymnastics like Haskell and Clojure, collection and iterator functions like map, filter, any, all, currying, maybe/option
- Simple to build general data structures like JSON objects
- REPL with readline, tab completion, pretty colours
- Complete toolchain like Go: build, friendly, complete online docs, package
manager and repository like NPM (with isolated versions like
node_modules
and a global cache like Maven), Glide or Cabal, install code from GitHub - Side effects in expressions, designed for writing "real world" programs like Clojure, Scala or Erlang
- Fast compiler emits fast code with fast startup times, predictable memory use and minimally obtrusive GC behaviour (like Golang)
- Interfaces/type classes/protocols like Smalltalk, Golang, Scala, Clojure, Haskell
- Safe concurrency: STM like Haskell, channel concurrency like Go and Clojure
- Easy to deploy, build static binaries and lightweight Docker images with one command
- FFI
- Simple error handling - no exceptions
- Tail call optimisation
- DSLs with
do
(monads are nice for this, e.g. StreamEditor.hs) - Multiline strings
- Trust the type system to infer the best (most general) type, type annotations are documentation, avoid them otherwise
main =
print "Hello, World!"
-- Simple HTTP service which periodically generates a block of content and
-- serves it from memory
import http (listen, get, post, request)
import stm
import time
type Greeting =
greeting string,
subject string
defaultState: Greeting = {greeting: "Hello", subject: "World"}
-- update state with a random greeting every 10 seconds
updatePeriodically state: STMVar Greeting :: None =
fork () ->
newSubject = random.choose ["City", "Planet", "Universe", "Solar system"]
stm.update state (greeting) -> {greeting | subject: newSubject}
print "Subject is now ${newSubject}"
sleep (time.seconds 10)
main = do
state: STMVar Greeting = stm.var defaultState
updatePeriodically state
listen 3000 do
get "/" (req, res) ->
"${state.greeting} ${state.content}!"
post "/greeting" (req, res) ->
stm.update state (greeting) -> {greeting | greeting: request.body.greeting}
-- Hello World gRPC service
--
-- See Python gRPC example at https://github.com/grpc/grpc/blob/v1.0.0/examples/python/helloworld/greeter_server.py for comparison.
import grpc (listen)
import helloworld (GreeterService) -- generated by protobuf compiler
instance GreeterService Greeter where
sayHello name =
{message: "Hello ${name}!"}
main =
listen 5051 Greeter
- Golang
- Haskell
- Scala
- Clojure
- CoffeeScript
- Erlang
- Elm
- Purescript
- LLVM tutoral
- Flex/Bison quickstart
- Writing your own toy compiler (Flex, Bison, LLVM)
- Code for toy compiler article
- Python grammar definition
- Language Implementation Patterns: Create Your Own Domain-Specific and General Programming Languages
- Custom lexers with lex/flex - inc lexing Python significant whitespace
- LLVM language reference
- The Architecture of Open Source Applications: LLVM
- Paper: Basic Polymorphic Typechecking (Cardelli) (free simple applicative language!)
- LLVM tutorial in Haskell
- Write you a Haskell - Haskell from first principals
- 4 MLs and a Python - comparison of various MLs
- Hue - A functional language that compiles to LLVM! A dead project, but there's a lot of great code in here, including parsing, AST, type inference of simple expressions and codegen for lots of things including calls and structures
- A Hindley-Milner type inference implementation in Python
- Learn X in Y Minutes - "Scenic programming language tours"
- Baby's First Garbage Collector - implementation of a mark-and-sweep garbage collector in C by the magnificent Munificent
- on-the-fly-gc - an on-the-fly (not stop-the-world) GC
- Of All The Garbage In The World - Writing a tri-color (actually 4-color) incremental generational garbage collector in C
- libmill - Go-style concurrency in C, by one of the contributors to ZeroMQ
- stevedekorte/coroutine - minimal coroutine implementation in C by the author of the Io programming language
- stevedekorte/garbagecollector - incremental tricolor tracing collector garbage collector in C by the author of the Io programming language
Haskell's type annotations are noisy, require the programmer to duplicate the method name and separate the type name from the parameter name (compare f(int a)
and f :: Int; f a
). However, pattern matching in the function signature means that multiple declarations are needed (e.g. map
below). Erlang and Haskell both opt to separate the type information from the declaration. This has the benefit that the actual declaration is cleaner, more like an equation (see . Many annotations are documentation - they show the type information to other programmers, rather than changing the behaviour of the program. Perhaps there's a better way to get type documentation, e.g. read the docs, or a source browser that adds annotations as tooltips?
-- Haskell
plus :: Int -> Int -> Int
plus = (+)
plus a b = a + b
map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = f x : map f xs
-- Erlang
plus(int(), int()) -> int().
-- Golang
func Foo(a int, b int) int {
}
func MapInts(f func(int), xs []{int}) {
}
-- Scala
def plus(a: int, b: int): int {
}
-- Scala lambda
val plus: (Int, Int) => Int
-- Clojure
(t/ann plus [t/Int :-> t/Int :-> t/Int])
-- SML
fun listenAndServe (address: string) (port: int) (router: Router.t) : unit =
...
-- TypeScript
function plus(x: number, y: number): number {
return x + y;
}
-- Proposal 1
plus a b :: Int -> Int -> Int =
a + b
map f [] :: (a -> b) -> [a] -> [b] = []
f (x:xs) = f x : map f xs
-- Proposal 2
-- Type info is next to parameter declarations, but this is noisy if a function has multiple declarations
-- with pattern matches (which declaration would you add a signature to?)
map f: (a -> b) []: [a] => [b] = []
map f: (a -> b) (x:xs): [a] => [b] = f x : map f xs
-- Proposal 3
:: Int -> Int -> Int
plus a b =
a + b
:: (a -> b) -> [a] -> [b]
map f [] = []
f (x:xs) = f x : map f xs
-- Haskell
square = \x -> x * x
-- Haskell with type signature
square = \x -> x * x :: Int -> Int
-- Coffeescript
square = (x) -> x * x
-- ES6 arrow functions
var square = (x) => x * x
-- Scala
val square = (x: Int) => x * x
-- Clojure
(fn [x] (* x x))