4[](http://travis-ci.org/paf31/purescript)
6A small strongly, statically typed compile-to-JS language with basic extensible records and type-safe blocks
11- Higher Kinded Polymorphism
12- Support for basic Javascript types
14- Type-safe blocks with for/while/assignment etc.
15- Functions with multiple arguments
21Try PureScript in the browser [here](http://tryps.functorial.com/).
29Contributions are always encouraged! Please see `CONTRIBUTING.md` for guidelines.
33Install the Haskell Plaform, then `cabal install`
37The `psc` executable takes a list of PureScript source files as arguments and by default writes out its errors or output to the console.
39The following options are supported:
42 Write the generated Javascript to the specified file
45 Write a list of externs declarations to the specified file in addition to generating Javascript output
49I was looking for a simple functional language which would compile to JavaScript and have the following characteristics:
51- Generate simple readable Javascript
52- Provide the ability to compile to tight loops if necessary
53- Reasonable type system
54- Ideally, written in a programming language I would enjoy working in
55- Provides a simple interface to existing Javascript code
57I didn't find exactly what I was looking for, so I wrote PureScript. It doesn't have everything right now, but it should serve as a simple core on which to develop new ideas.
59PureScript is *not* designed to be a general-purpose programming language. The primary use case is as a generator for purely-functional core libraries, with the main application code written in another language.
61PureScript can also be seen as a trade-off between a theoretically ideal language and one which generates reasonably high performance code.
65The following code defines a `Person` data type and a function to generate a string representation for a `Person`:
68data Person = Person { name :: String, age :: Number }
70extern numberToString :: Number -> String
72showPerson :: Person -> String
73showPerson = \p -> case p of
74 Person o -> o.name ++ ", aged " ++ numberToString(o.age)
77Line by line, this reads as follows:
79- `Person` is a data type with one constructor, also called `Person`
80 - The `Person` constructor takes an object with two properties, `name` which is a `String`, and `age` which is a `Number`
81- The `numberToString` function is written in Javascript, and converts a `Number` to its `String` representation
82- The `showPerson` function takes a `Person` and returns a `String`
83- `showPerson` works by case analysis on its argument `p`, first matching the constructor `Person` and then using string concatenation and object accessors to return its result.
85The generated Javascript looks like this:
88var Person = function (value) {
89 return { ctor: 'Person', value: value };
92function showPerson(p) {
93 if (p.ctor === "Person") {
94 return p.value.name + ", aged " + itoa(p.value.age)
96 throw "Failed pattern match"
103The type system defines the following types:
110 - E.g. `[String]`, `[[Number]]`
112 - E.g. `{ foo :: String, bar :: Number }`
114 - E.g. `data Foo a = Foo | Bar String`
116 - E.g. `Number -> String`
117 - E.g. `(Number, Number) -> Number`
118 - Functions can have zero or more arguments
119- Polymorphic types (for top level declarations only)
120 - E.g. `forall a. a -> a`
124The three primitive types `String`, `Number` and `Boolean` correspond to their Javascript equivalents at runtime.
126PureScript supports the same binary and unary operations on primitive types as Javascript, with the following exceptions:
128- String concatenation is denoted `++` to differentiate it from numeric addition, `+`
129- PureScript's `==` and `!=` correspond to Javascript's strong equality tests `===` and `!==`
135str = "Hello, " ++ "World!"
141PureScript arrays correspond to Javascript arrays at runtime, but all elements must have the same type.
143Array literals look like Javascript array literals: `[1, 2, 3]`
145Array elements can be read using array index notation `arr[0]`
149PureScript records correspond to Javascript objects.
151Record literals look like Javascript object literals: `{ foo: "Foo" }`
153Properties can be read by using dot notation: `o.foo`
157Tagged unions consist of one or more constructors, each of which takes zero or one arguments.
159Tagged unions can only be created using their constructors, and deconstructed through pattern matching (see later).
164data Foo a = Foo | Bar String
166runFoo = \foo -> case foo of
168Bar s -> "It's a Bar. The string is " ++ s
170runFoo Foo ++ runFoo (Bar "Test")
173In the example, Foo is a tagged union type which has two constructors. It's first constructor `Foo` takes no argument, and it's second `Bar` takes one, which must be a String.
175`runFoo` is an example of pattern matching on a tagged union type to discover its constructor, and the last line shows how `Foo`s are constructed.
179Functions in PureScript can have zero or more arguments in general, just like in Javascript.
181Functions are introduced by using a backslash followed by a comma separated list of argument names:
184test1 = \a, b, c -> a + b + c
187which would correspond to the Javascript `function test1(a, b, c) { return a + b + c; }`
189A function taking no arguments would look like this:
195which would correspond to the Javascript `function test2() { return 100; }`
197Functions are applied by providing their arguments inside parentheses:
204A special case is made in the case of functions with one argument. These functions can be applied without parentheses, and function application associates to the left:
207-- has type Number -> Number -> Number -> Number
208addThree = \a -> \b -> \c -> a + b + c
210-- has type Number -> Number -> Number
213-- has type Number -> Number
222Expressions defined at the top level may have polymorphic types.
230`identity` is inferred to have (polymorphic) type `forall t0. t0 -> t0`. This means that for any type `t0`, `identity` can be given a value of type `t0` and will give back a value of the same type.
232A type annotation can also be provided:
235identity :: forall a. a -> a
239Functions may also be polymorphic in row types or type variables with other kinds (see "Kind System"):
242addProps = \o -> o.foo + o.bar
245Here, `addProps` is inferred to have type `forall r. { foo :: Number, bar :: Number | r } -> Number`. That is, it can take any type which has properties `Foo` and `Bar`, and *any other record properties*.
247So, the following compiles:
250addProps { foo: 1, bar: 2, baz: 3 }
253but the following does not:
259since the `bar` property is missing.
261Again, a type annotation can be provided if necessary.
265All types can be inferred, but annotations can optionally be provided.
269There are two primitive kinds, the kind `*` of types and the kind `#` of rows. Higher kinded types are also supported. That is, a type variable can refer to not only a type or a row, but a type constructor, or row constructor etc.
273Syntax is whitespace sensitive. The general rule of thumb is that declarations which span multiple lines should be indented past the column on which they were first defined on their subsequent lines.
275That is, the following is valid:
291There is a keyword `do`, which does not have the same purpose as the `do` keyword in Haskell.
293`do` introduces a block. Blocks consist of statements, and must return a value of the same type on every branch of execution.
295The following types of statement are supported:
297- Variable introduction
302- If-Then-Else statements
304Here is an example of a power function defined using a block:
314Blocks enable local mutation of their variables, but mutation is not allowed in general. The type system prevents mutable variables from escaping their scope.
316That is, while the example above is valid, the following does not compile:
324The variable `n` is not mutable, and so the assignment in the first line of the `do` block is not allowed.
326This function can be rewritten as follows:
337For loops look like this:
347The bounds `0` and `10` are inclusive and exclusive respectively.
351For each loops loop over the elements in an array using the `Object.forEach` method. A polyfill may be required for some browsers:
363The syntax of a while loop is similar to a foreach loop:
375## If-Then-Else Statements
377Else branches are optional, and may contain further `if` statements, just as in Javascript:
392## If-Then-Else Expressions
394The `if`, `then` and `else` keywords can also be used to create conditional expressions. In this case, the `else` block is always required.
399conditional = if 2 > 1 then "ok" else "oops"
404Pattern matching deconstructs a value to bring zero or more expressions into scope. Pattern matches are introduced with the `case` keyword.
406Pattern matches have the following general form
415The following pattern types are supported:
425Patterns need not be exhaustive. A pattern match failed exception will be thrown at runtime if no pattern matches the input.
429The wilcard `_` matches any input and brings nothing into scope:
438Literal patterns are provided to match on primitives:
456A variable pattern matches any input and binds that input to its name:
460 result -> bar(result)
465Array patterns match an input which is an array, and bring its elements into scope.
470f = \arr -> case arr of
472 [x, y : xs] -> x * y + f(xs)
476Here, the first pattern only matches arrays of length one, and brings the first element of the array into scope.
478The second pattern matches arrays with two or more elements, and brings the first and second elements into scope, along with the remainder of the array as the variable `xs`.
482Record patterns match an input which is a record, and bring its properties into scope.
486 { foo = "Foo" } -> o.bar
492The patterns above can be combined to create larger patterns.
498 { arr = [x:_], take = "car" } -> x
499 { arr = [_,x:_], take = "cadr" } -> x
505Named patterns bring additional names into scope when using nested patterns. Any pattern can be named by using the `@` symbol:
508f = \arr -> case arr of
513Here, in the first pattern, any array with two or more elements will be matched and bound to the variable `a`.
517Guards are used to impose additional constraints inside a pattern using boolean-valued expressions, and are introduced with a pipe after the pattern:
520evens = \arr -> case arr of
522 [x:xs] | x % 2 == 0 -> 1 + evens xs
528For convenience, it is possible to declare a synonym for a type using the `type` keyword. Type synonyms can include type arguments.
533type Foo = { foo :: Number, bar Number }
535addFoo :: Foo -> Number
536addFoo = \o -> o.foo + o.bar
540Properties on records can be updated using the following syntax: `o { key = value, ..., key = value }`
542For example, the following function increments the `foo` property on its argument:
545incr = \o -> o { foo = o.foo + 1 }
548The generated Javascript assumes the existence of a method called `Object.extend` such that `Object.extend(o, p)` takes an object `o` and generates a shallow copy of `o` including the properties of `p`. A simple JQuery implementation of this specification is
551Object.prototype.extend = function(o, p) {
552 return $.extend({}, o, p);
558In addition to the standard operators, user-defined infix operators can be created by enclosing names in parentheses:
560E.g. to create a synonym for string concatenation:
563(<>) = \s1 -> \s2 -> s1 ++ s2
565greeting = "Hello" <> "World!"
568Regular functions can be used as operators by enclosing their names in backticks:
571foo = \x -> \y -> x * y + y
576Fixity declarations can associate a precedence level, which is a natural number, to a user-defined operator, and specify which way it associates:
583## Foreign Function Interface
585The `extern` keyword declares a value which is defined in Javascript, and its type:
588extern pow :: (Number, Number) -> Number
591To declare a new type with no constructors, use `extern data` and provide the kind:
594extern data IO :: * -> *
597 log :: String -> IO {}
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list