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 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 suite.
Heavily inspired by Haskell , F# , Coffeescript. , (_)(S)(OCA)ML , Clojure , JMacro , Ruby
(tested on Snow Leopard, Lion, Ubuntu). Note that Forml also requires Closure for optimizations and either Phantom.js or Node.js
Install the Haskell Platform, 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 if you're into EMACS.
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
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.
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 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, 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
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]
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
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
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
This example will compile to a for
loop, as it is tail recursive.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
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 "I am executed conditionally" 'whenever (6 < 5) == {}
Arguments to an inline can be repeated, nested in yields, ifs or anonymous functions, even removed entirely from the code. Inlines can be used inside let bindings, too!
let inline comment _ = true
in comment <: error "I'm compiled away"
Forml is strong, statically typed, and types are inferred and checked at compile 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<a> -> Bool
has_value {just: _} = true
has_value _ = false
has_value {just: 4}
not (has_value {nothing})
0.2
if
expressions.where
expressions.0.1.3
isnt
, accessor ordering.sum = reduce (+)
-o
option.0.1.2
x.map f == x |> .map f
)_
as valid type variable for throwaway unique types.open prelude.list as list
).array
module.0.1.1
open prelude
- the compiler will include the code automatically. Currently weighs in at ~11k, if you care about that sort of thing.A special thanks to everyone who has contributed to forml!