-- Forml -- ===== -- -- Forml is a contemporary programming language for the discriminating -- programmer, intended to fill a niche -- somewhere between [Haskell](http://www.haskell.org) and -- [Ruby](http://www.ruby.org), should such a niche turn out to exist. -- Philosophy -- ========== -- 1) Writing software is unnecessarily hard, because syntax is sufficiently -- abstracted from thought process to lose something in the translation. -- A modern programming language should be designed for reading - an -- [API exposing the programmer](http://www.artima.com/intv/ruby4.html). -- 2) Writing software is unnecessarily hard, because making mistakes is too -- easy. A modern programming language should prevent the programmer from -- writing incorrect code, in so far as it doesn't conflict with 1. -- Features -- ======== -- * Buzzwords: functional, strict, expressive, pure(ish), static(y), -- inferred, fast, fun. -- Strong like a gorilla, yet soft and yielding like a Nerf ball. -- * Targets Javascript, but please regard this as an implementation detail - -- forml is not intended as an answer to "the Javascript problem." Simple -- foreign function interface, which allows the -- introduction of untyped values for quick & dirty -- code integration, which can later be restricted via explicit typing. -- * Type system which is designed to be simple and catch obvious errors, -- not entirely exhaustive, dictatorial and somewhat combative (I'm looking -- at you, Haskell). Inferred, strucural types, partials records, -- ability to introduce unrestricted types via FFI. -- * Fast. Automatic tail call optimization, -- inline functions, designed for use with -- [Google Closure Compiler](https://developers.google.com/closure/) -- advanced optimizations mode. See -- [Tests]((http://texodus.github.com/forml/prelude.html) for some -- simple benchmarks. -- * Flexible, expressive syntax. Lots of sugar for obvious tasks. -- * Inline testing, compiles to a -- [Jasmine](http://pivotal.github.com/jasmine) suite. -- * Heavily inspired by -- [Haskell](http://www.haskell.org/haskellwiki/Haskell) -- , [F#](http://msdn.microsoft.com/en-us/vstudio/hh388569.aspx) -- , [Coffeescript](http://coffeescript.org/). -- , ([_](http://en.wikipedia.org/wiki/Category:ML_programming_language_family))([S](http://www.smlnj.org/))([OCA](http://caml.inria.fr/))ML -- , [Clojure](http://clojure.org/) -- , [JMacro](http://www.haskell.org/haskellwiki/Jmacro) -- , [Ruby](http://www.ruby-lang.org/en/) -- Examples -- ======== -- * [Tetris](http://texodus.github.com/forml/tetris.html) -- * [Prelude](http://texodus.github.com/forml/prelude.html) -- * [Parsec](http://texodus.github.com/forml/parsec.html) -- Installation -- ============ -- (tested on Snow Leopard, Lion, Ubuntu). Note that Forml also requires -- [Closure](https://developers.google.com/closure/) for optimizations and -- either [Phantom.js](http://phantomjs.org) or [Node.js](http://nodejs.org) -- Install the -- [Haskell Platform](http://hackage.haskell.org/platform/index.html), then --
$ cabal install forml
-- To compile some forml files:
-- $ forml -o app test.forml test2.forml
-- will create an app.js and app.spec.js with the compiled code and
-- test suite respectively.
-- Forml will by default try to minify/optimize your code with Closure, via the
-- $CLOSURE environment variable, which should point to the closure jar. Failing this,
-- forml will attempt to post your code to the Closure web service.
-- Additionally, forml will attempt to run the test suite with the phantomjs binary,
-- which it expects to find on your PATH. You may optionally specifiy to run your suite
-- via node.js with
-- $ forml -node-test test.forml
-- To compile literate
-- forml (eg, Forml code embedded in Markdown):
-- $ forml test.lforml
-- To see the inferred types:
-- $ forml -t test.forml
-- To turn off optimizing (eg, Closure) or testing:
-- $ forml -no-test -no-opt test.forml
-- To watch a file for changes and incrementally compile:
-- $ forml -w test.forml
-- To generate documentation and test runner (like this file):
-- $ forml -docs test.forml
-- Be sure to check out [forml-mode](https://github.com/jvshahid/forml-mode) if you're into EMACS.
-- Tutorial
-- ========
-- This is unfortunately not comprehensive, and presumes some working knowledge of
-- ML or Haskell. Forml supports a flexible, forgiving syntax that supports many synonymous forms.
-- This will be illustrated by adherring to an entirely random, arbitrary style throughout.
-- The basic unit of code organization in forml is the `module`, which is simply
-- a collection of definitions in a namespace.
module readme
-- Within a module, the compiler recognizes strictly ordered logical sections divided by
-- `open` statements and sub modules; within a section, however, declarations
-- can be in any order. `open` statements create local aliases for their own public definitions, which will shadow previously defined symbols.
open prelude
open prelude.string
-- Definitions
-- -----------
-- Simple functions. Forml allows function application via spaces as in
-- ML, or via `()`'s in a more traditional c style -
square x = x * x
add(x, y) = x + y
-- With pattern matching
fib 0 = 0 | 1 = 1 | n = fib (n - 1) + fib (n - 2)
-- Patterns can be separated with `|`, or by repeating the definition's
-- name ala Haskell. Definitions can have optional type annotations, which
-- may restrict the inferred type of the definition, but must not be
-- more general
private
fib' : Num -> Num
fib' 0 = 0
fib' 1 = 1
fib' n = fib' (n - 1) + fib' (n - 2)
-- Operators can be defined much like in Haskell. Precedence is currently
-- fixed,
-- though you can declare right associative operators by ending them with
-- a `:` character.
-- Testing
-- -------
-- Tests are a first class concept in forml - any unbound in a module (or in other words,
-- any expression which isn't part of a definition), which is inferred as type `Bool`,
-- is treated as a test, and is compiled
-- to a [Jasmine](http://pivotal.github.com/jasmine) suite in a separate file from
-- your definitions.
fib' 7 == 13
fib' 0 == 0
-- For example, this file is the result of running the forml compiler with the `-docs`
-- flag for [readme.forml](https://github.com/texodus/forml/blob/master/src/forml/readme.forml),
-- and incorporates both the compiled output and the Jasmine suite. You can execute
-- this suite by clicking the `RUN TESTS` button, which will highlight the test
-- results in this document
-- Modules
-- -------
-- Namespaces are not symbols, so this won't work:
-- prelude.log "Hello, World!" -- Won't compile!
-- Instead, you must qualify the import and supply
-- a symbol name to bind to. The alias will be typed
-- to a record whose fields are the first-class
-- definitions in the module.
open prelude.array as array
array.map fib [3,4,5] == [2,3,5]
-- Notice this means aliased modules can be passed as arguments
mmap dict f xs = dict.map f xs
mmap array fib [3,4,5] == [2,3,5]
-- Records
-- -------
-- Forml has the basic primative types from Javascript: Num, String,
-- Bool; plus
-- a record, which is structurally typed (and implemented as a simple
-- Javascript object, for the curious).
person name address = {
name = name
address = address
message = "`name` lives at `address`"
say msg = "`name` says '`msg`'"
}
person("Andrew", "123 Fake St.").message is "Andrew lives at 123 Fake St."
point = {x: 10, y: 10}
20 == point.x + point.y
-- The sugared syntax `.field` represents an anonymous accessor function
-- for convenient piping.
person "Wilfred" "couch"
|> .say "I'm lazy"
== "Wilfred says 'I'm lazy'"
var people = [
person "Josh" "Jersey"
person "John" "Egypt"
] in
people 'map (.name) == [ "Josh", "John" ]
-- Records can be destructured in function argumentsn and can partially match
-- with the `_` character. This type of function will apply to
-- any record with at least the keys in the partial match.
magnitude {x: x, y: y, _} = sqrt (square x + square y)
magnitude {x: 3, y: 4, other: "test"} == 5
magnitude {x: 4, y: 3, test: "other"} == 5
-- Functions
-- ---------
-- Anonymous functions also follow Haskell, can be written with `\` or
-- `λ`, and allows pattern seperation via `|`
map: (a -> b) -> Array a -> Array b
map f xs = do! `xs.map(f)`
let fil =
λ x when x > 5 = x
| 5 = 0
| x = 5
map fil [2, 6, 3, 7, 5] is [5, 6, 5, 7, 0]
-- All functions are curried, and can be partiall applied - even
-- operators.
x +* y = (2 * x) + (2 * y)
[1, 2, 3] 'map ((+*) 2) == [6, 8, 10]
add_twelve x y = x + y + 12
let f = add_twelve 5
f 10 == 27
-- Inlines
-- -------
-- Function inlining allows for macro like behavior, like lazy
-- & conditional evaluation (the `'` operator here is equivalent
-- is "left-pipe" application, `x |> f == x 'f`).
inline whenever x f =
if x
then f
else {}
error "FAILURE" 'whenever (6 < 5) == {}
-- Tail Call Optimization
-- ----------------------
-- This example will compile to a `for` loop, as it is
-- [tail recursive](http://en.wikipedia.org/wiki/Tail_call).
-- `var` is a synonym for `let`, and `in` is an optional binding
-- separator.
(**):
String -> Num -> String
text ** n =
var f(_, 0, acc) = acc
f(text, n, acc) =
f(text, n - 1, acc +++ text)
in f(text, n, "")
"hello" ** 3 == "hellohellohello"
length ("a" ** 10000) == 10000
-- Interop & Side Effects
-- ----------------------
-- Forml technically allows unrestricted side effects, but by default
-- wraps them in a `JS a` type, which can be composed with the
-- `>>=` and `>>` operators, or a variation of Haskell's `do` notation.
hello_world = do
`console.log("Hello World")` -- Calls to Javascript always return type `JS a`
x <- `Math.sqrt(9)` -- `x` is inferred to be the unrestricted type `a`
let z = x + 1 -- `x` is now restricted to type `Num`
return (z + 1) -- type of `hello_world` is inferred to be `JS Num`
8 == do! hello_world >>= λx = `x + 3`
-- Though this function is inferred to be `a -> b`, you can restrict it with
-- a signature.
inline
sqrt: Num -> Num
sqrt x = do! `Math.sqrt(x)` -- `do!` realizes its argument immediately
-- Forml also supports additional keywords `lazy` and `yield`. Both take
-- expressions as arguments (as opposed to `do` syntax), but return an
-- unrealized type `JS a`, the difference being that `lazy` will only
-- evaluate it's arguments once, then cache the result.
let x = 0
test = lazy do! `x = x + 1; x`
in 1 == do! test >> test >> test >> test
-- Types, Aliases & Unions
-- -----------------------
-- Forml is strong, statically typed, and types are inferred and
-- checked at copmile time. Unlike in traditional Hindley Milner style
-- inferrence, forml allows you to break the rules with some explicit
-- type annotations.
num_or_string:
(Num | String) -> String
| x when num? x = "Num"
| _ = "String"
-- Structural types look just like the records they represent.
unwrap: {box: a} -> a
unwrap {box: x} = x
-- Algebraic data types and type aliases are declared the same way
-- (where the `type` keyword is optional). `{nothing}` here is
-- shorthand for the record type {nothing: {}}, useful for enum types.
Maybe a = {just: a} | {nothing}
-- Notice there are no explicit type constructors - in forml, types
-- constructor functions are inferred from the fields of a record
-- and applied automatically.
-- For example, when `Maybe a` is in scope, any record type with the `just`
-- or `nothing` keys will be inferred to be a type `Maybe a`.
maybe x {just: y} = y
maybe x {nothing} = x
maybe 3 {just: 4} == 4
maybe 3 {nothing} == 3
type Tree a =
{left_tree: Tree a, right_tree: Tree a}
| {leaf: a}
type List a = { head: a, tail: List a } | { nil }
sum: List Num -> Num
sum { head: x, tail: xs } = x + sum xs
sum { nil } = 0
sum { head: 2
tail: { head: 3
tail: { nil } } }
== 5
-- Lists have a syntax sugar as well.
sum [: 3, 4, 5 ] == 12
-- In case this sort of things floats your boat, you can also declare
-- polymorphic types in "java" style, with `< >`.
has_value: Maybe -> Bool
has_value {just: _} = true
has_value _ = false
has_value {just: 4}
not (has_value {nothing})
-- Changes
-- =======
-- 0.2
-- * Incremental compilation, increased compilation speed
-- * Made inlines inline their arguments as well when fully applied,
-- like a macro. Code generator works better with Closure, resulting
-- in smaller, more optimized JS.
-- * One-sided `if` expressions.
-- * `where` expressions.
-- * Silent mode
-- * Fixed namespace rendering error.
-- * Fixed definition ordering in let bindings.
-- * Fixed indentation error
-- 0.1.3
-- * Fixed many parsing bugs, including negative numbers, indentation
-- on anonymous functions & do blocks, string-escaping, `isnt`,
-- accessor ordering.
-- * Operator partials ala Haskell, `sum = reduce (+)`
-- * Fixed type checking of mutually recursive definitions.
-- * Fixed Node.js test suite to work nearly-identically to Phantom.js
-- * Fixed output to use sensible filenames, and respect the `-o` option.
-- * [Tetris!](http://texodus.github.com/forml/tetris.html)
-- 0.1.2
-- * Added accessor function syntax for treating record fields
-- as functions (eg `x.map f == x |> .map f`)
-- * Added `_` as valid type variable for throwaway unique types.
-- * Module aliases allow binding a module to
-- a structurally typed symbol (eg `open prelude.list as list`).
-- * Prelude expanded, with special attention to the `array` module.
-- * Embedded prelude tests are skipped.
-- * Bug fixes.
-- 0.1.1
-- * Documentation generation has been greatly improved. Better styling, generates individual pages for each file.
-- * The prelude is now embedded in the compiler. Simply import it via `open prelude` - the compiler will include
-- the code automatically. Currently weighs in at ~11k, if you care about that sort of thing.
-- * Command line interface is more pleasant to work with
-- Contributors
-- ============
-- A special thanks to everyone who has contributed to forml!
-- * [jvshahid](https://github.com/jvshahid)
-- * [jhawk](https://github.com/JHawk)
-- * [mightybyte](https://github.com/mightybyte)
-- * [taku0](https://github.com/taku0)
-- * [brow](https://github.com/brow)
-- * [dignati](https://github.com/dignati)