Skip to content

Instantly share code, notes, and snippets.

@afternoon
Last active March 13, 2017 20:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save afternoon/96bd84c5bab13bda0779b8fc30efa060 to your computer and use it in GitHub Desktop.
Save afternoon/96bd84c5bab13bda0779b8fc30efa060 to your computer and use it in GitHub Desktop.

A functional programming language for low-latency microservices

  • 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 not foo_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

Hello World

main =
  print "Hello, World!"

HTTP service

-- 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}

gRPC service

-- 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

Influences

  • Golang
  • Haskell
  • Scala
  • Clojure
  • CoffeeScript
  • Erlang
  • Elm
  • Purescript

Reading

Type annotations

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

Lambda Expressions

-- 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))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment