<a id="to-github" href="https://github.com/snowmantw/Fluorine"><img style="position: fixed; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png" alt="Fork me on GitHub"></a> <div id="logo"> ![Fluorine Logo](Fluorine-logo.png) <div>a context based Javascript eDSL</div> </div> - [Fluorine](#fluorine) - [Main Concepts](#main-concepts) - [Pure and Impure](#pure-impure-concepts) - [Contexts](#contexts-concepts) - [Features](#features) - [Isolating Impure Parts in Javascript Applications](#isolating-impure-parts-in-javascript-applications) - [Break the Glass in Emergency](#break-the-glass-in-emergency) - [Flow-Control](#flow-control) - [Laziness ( well, sort of )](#laziness) - [Installation](#installation) - [Dependencies](#dependencies) - [Recommends](#recommends) - [How to Use It](#how-to-use-it) - [Initialize](#initialize) - [Build a Runnable Program](#build-a-runnable-program) - [Guides and Common Mistakes](#guides-N-common-mistakes) - [0. Wrap all computation with side-effects](#wrap-all-computation-with-side-effects) - [1. Every context chain should be enclosed](#every-context-chain-should-be-enclosed) - [2. Don't forget the period](#dont-forget-the-period) - [3. Turn on the debug option](#turn-on-the-debug-option) - [4. Execute only when it's necessary](#execute-only-when-its-necessary) - [APIs](#apis) - [Use with Other DSLs](#use-with-other-dsls) - [License](#license) - [Contacts](#contacts) # <a id="fluorine"></a>Fluorine [Fluorine](https://github.com/snowmantw/Fluorine) is a Javascript eDSL and library, want to help developers constructing their application with more functional features, and build large programs from small functions. It currently has these features: * [Isolating impure parts in the program.](#isolate-impure-parts-in-javascript-applications) * [Allow to mix pure/impure when necessary.](#break-the-glass-in-emergency) * [Flow-Control](#flow-control), to avoid callback hell. * [Laziness](#laziness) (well, sort of) . It's first inspired by the [Arrowlets](http://www.cs.umd.edu/projects/PL/arrowlets/api-arrowlets.xhtml) library, which brought the functional structure, Arrowlet, into Javascript world. Of course, the great [jQuery](http://jquery.com/) and [Underscore.js](http://underscorejs.org/) also shown how amazing Javascript could be. Futhurmore, this library also want to experiment the possibility of constructing reasonable Javascript programs **without (too much) class, object and other OOP things**. Because Javascript, at least according to Douglas Crockford, ["has more in common with functional languages like Lisp or Scheme than with C or Java".](http://www.crockford.com/javascript/javascript.html) This is a good point in this OOP-overflowed age, especailly most of libraries are all eager to provide class, inheritance and other OOP stuffs. --- ## <a id="main-concepts"></a>Main Concepts ### <a id="pure-impure-concepts"></a>Pure and Impure Considering we have two string values, they have only one difference: <p style="text-align:center"> ![Impure Values and Pure Values](wrapped-pure-values.png) </p> <p style="text-align:center"> _Fig.1: Impure values differ from pure values._ </p> String "Foo" comes from some DOM in the document, and the "Bar" is a Javascript string variable spawed from some literal. The ways to generate them shows the difference between pure and impure value in Fluorine: **Impure value** means it come from IO, UI and other computations may be failed, like extract string from a removed DOM, or fetch data from a no longer existing URL. **Pure value**, on the other hand, should be never failed in it's scope. The most important concept of Fluorine is that these two kinds of values and computations **should never be mixed arbitrarily**. This doesn't mean we can live without any impure things, but just want to apply them reasonably. ### <a id="contexts-concepts"></a>Contexts Fluorine use a structure named **context** to wrap those computations with side-effects. The name "context" means it's different from other purer and stronger structures like [Arrowlet](http://www.haskell.org/haskellwiki/Typeclassopedia#Arrow), [Monad](http://www.haskell.org/haskellwiki/Typeclassopedia#Monad) and [MonadTransformer](http://www.haskell.org/haskellwiki/Typeclassopedia#Monad_transformers). These structures are delicate enough but also hard to implement well in this little eDSL (at least, in current stage) . <p style="text-align:center"> ![Context Wrapped Computing](context-wrapped-computing.png) </p> <p style="text-align:center"> _Fig.2: All computation with side-effects should be wrapped by contexts._ </p> The _Fig.2_ shows that all computation, include fetching data from server, rendering DOMs in document, and any other comes with side-effects, should be wrapped by contexts. It also shows the result generated by those computations is wrapped, too. In such a way that we can distinguish pure values and computations, like applying math functions or creating but not appending DOMs, from those impure parts mentioned earlier. Contexts in Fluorine are just predefined functions like the "jQuery" function, and the main difference is that the later don't provide mechanisms to execute lazily, so we can't combine all jQuery chains togeter to build a large program. Other differences between jQuery and Fluorine's contexts are that jQuery didn't care about side-effects, and it's combinators mainly provide UI manipulations. These differences don't mean it isn't a great Javascript library, but only show there're some holes need to be filled. So Fluorine actually depends on `jQuery` and `underscore.js`, which can easily build important UI and other computations. This library just provide some contexts to orgranize them in proper ways. Currently, Fluorine has these important contexts: * **Context**: the basic context, others are inherited from it; extend more new context is possible * **IO**: fetching data from server, or from local storage (not implement yet) * **UI**: wrapped jQuery, to seduce it become lazy and greedy (never leaks any impure value) * **Event**: providing a basically usable event mechanism * **Socket**: wrapped WebSocket, experimental See the [APIs](#apis) section to get more information. --- ## <a id="features"></a>Features ### <a id="isolating-impure-parts-in-javascript-applications"></a>Isolating Impure Parts in Javascript Applications Fluorine's contexts can help Javascript programmers isolate impure parts in their applications, so that errors resulted from side-effects can be reduced as much as possible: // Infect whole context to omit prefix namespaces. // You can restore this by call `fluorine.heal()`. fluorine.infect() // Impure function: manipulate DOMs in the document. // All impure computing should be wrapped with contexts. // // :: String -> UI $DOM drawMyName = function(name) { // $() means apply jquery: `a -> UI $DOM` return UI('#name-user').$().text(name).done() } // Extract string from DOM in document is impure. // // :: UI String getName = function() { return UI('#name-user').text().done() } // Pure function: return a plain old string. // // :: String giveName = function() { return "foobar" } The result of `getName` means **"a String under the UI context"**. On the contrary, function `giveName` will generate **a pure String**. Because they have different types, users can't arbitrarily mix them like normal strings in Javascript: ui_name = getName() name = giveName() error = ui_name + name In fact, programmers can only mix them in some proper ways, like in a generator function returing yet another larger context: // Generator means this function will return another context wrapped value, rather than a pure value. // // :: UI $DOM drawApp = function() { return UI(). let(giveName).as('name'). // Allow to define some local variables in the scope of chain. tie( function() // Allow to tie with other chains to build a large chain. { // Use local variables just like "let" in other languages. var slc_ex1 = '#ex1' // Subchain's result will become next step's input. // In this case, it will return `UI $DOM`, and the next `tie` will receive that. return UI("<div id='name-user'></div>").$().appendTo(slc_ex1).done() }). tie( function($dom) { // Apply the value pre-defined by `let` and `as`. return drawMyName(this.name) }). done() // Don't forget to close this chain. } ### <a id="break-the-glass-in-emergency"></a>Break the Glass in Emergency Basically, pure functions and values can't arbitrarily mix with impure values: // :: String pureString = function() { return "pure" } // :: UI String impureString = function() { return UI("impure").done() } illegal_string = pureString() + impureString() // Error. But in some special cases, we still can do that if we jump into the underlay implementation: illegal_string = pureString() + impureString()().extract() // extract:: Process String -> String // The "Process" is a underlay component used to control contexts' flow. // Every pure value in contexts are actually double-wrapped, by the context and the Process. // // impureString() :: UI (Process String) // impureString()() :: Process String // extract :: Process String -> String Also, we can do some dirty works in the theoretically pure combinator, like the `let` or `_`: UI('#name-user').$(). _(function(e) // Call it "lambda" if you like. { // Should keep it pure, not violenting like this: $(e).text("Name on the document was changed !") }). done() This is useful because sometimes we may want to embed 3rd libraries in our contexts. Nevertheless, these tricks should be exceptions, not normalities. ### <a id="flow-control"></a>Flow-Control Fluorine use a hidden and stack based process to manage each step in context chains. The most classical example is the `IO` context: IO(). get('/ajax/hello').as('hello'). // Delegate to `jQuery.get` . get('/ajax/world').as('world'). _(function(){ return this.hello + ' ' + this.world }). tie(function(msg) { return UI('<div id="io-msg"></div>').$().text(msg).appendTo('body').done() }) done() Here, we get a serialized execution of multiple Ajax requests. The second `get` will not execute unless the first one got successfully executed. In this way, we can "flatten" nested Ajax callbacks such like: function requests() { var responses = {} $.get('/one', function(one) { responses["one"] = one $.get('/two', function(two){ responses["two"] = two $.get('/three', function(three){ responses["three"] = three $('#io-msg').text(responses["one"]+responses["two"]+responses["three"]) }) }) }) } Into this: function requests() { return IO(). get('/one').as('one'). get('/two').as('two'). get('/three').as('three'). tie(function() { return UI('#io-msg').$().text(this["one"]+this["two"]+this["three"]).done() }). done() } Of course there're many flow-control libraries promising this, but Fluorine hope this feature can naturally become a part of context-based computing, not a standalone feature. Concurrent IOs like initialize multiple requests in the same time is not implemented yet. It will be a important feature in the near future. ### <a id="laziness"></a>Laziness ( well, sort of ) Context chains in Fluorine will not execute immediately, unless it's required. // Normal jQuery chain will execute immediately: act = $('#io-msg').hide().css('background-color', 'cyan').text('New message').fadeIn() // Fluorine chain: nothing happened. act = UI('#io-msg').$().hide().css('background-color', 'cyan').text("New message").fadeIn().done() So we can storage the chain to embed and apply it in anywhere we want // Can return entire chain because it will not execute in this function. // :: UI () function newMessage() { return UI('#io-msg').$().hide().css('background-color', 'cyan').text("New message").fadeIn().done() } // When the program initialize, execute it: newMessage()() The first application of `newMessage` function is for generating the `UI ()` value: // :: UI () newMessage() And the next is for extract (execute) pure value from it: // :: UI () -> () newMessage()() Because the `UI () -> ()` will return nothing, `newMessage()()` will return nothing. The second `()` is like runner functions in Haskell. For instance, the `runST:: (forall s. ST s a) -> a` can "execute" a ST Monad. If anonymous functions annoys you, we also provide `idGen` function. Using it to put a chain in another chain directly can avoid the annoying anonymous function syntax: // No need to use anonymous functions anymore. Event('initialize'). tie(idGen( UI('#io-msg').$().hide().css('background-color', 'cyan').text("New message").fadeIn().done() )). done() The "lazyness" and the way we controlling processes are exactly why users should never expect to extract values from IOs: we can't guarrantee the IO chain will execute and return the value as users wish. In fact, if someone do this: // Try to extract the result of IO context: var hello = IO(). get('/ajax/hello'). done()().extract() // And use it in another context: UI('#io-msg').$().text(hello).done()() The `hello` shall be null because the asynchronous `get` still waiting the response. If values from IO is needed by other contexts, embedding them in the chain is the standard way to satisfy the request: IO(). get('/ajax/hello'). tie( function( msg ) { // Instead of try to get the IO handled value outside, // it's better to embed the receiver context in. return UI('#io-msg').$().text(msg).done() }). done()() --- ## <a id="installation"></a>Installation Clone it from Github, then locate the merged source file in `build/fluorine/fluorine.js`. git clone https://github.com/snowmantw/Fluorine.git Or, you can install it via [bower](https://github.com/twitter/bower) : bower install Fluorine Lasted tagged version in GitHub: https://github.com/snowmantw/Fluorine/archive/v0.2.2.zip ### <a id="dependencies"></a>Dependencies * [jQuery](http://jquery.com) : UI context. * [Underscore.js](http://underscorejs.org) : use it for your good ! ### <a id="recommends"></a>Recommends * [bacon.js](https://github.com/raimohanska/bacon.js) : FRP programming. --- ## <a id="how-to-use-it"></a>How to Use It ### <a id="initialize"></a>Initialize Normally, use `fluorine.infect()` first, to omit prefix namespace and do some initialization works: fluorine.infect() The notifier, designed as a individual part, need initialization too. fluorine.Notifier.init() The `infect()` will embed Fluorine utils functions and contexts in `window` object, so you can call them without prefixes: fluorine.infect() IO(). get('/ajax/hello'). tie(function(hello) { return UI('#io-msg').text(hello).done() }). done()() You can use `fluorine.heal()` to remove the infection. ### <a id="build-a-runnable-program"></a>Build a Runnable Program It will generate a Todo list in HTML. This example is also runnable in this page (see below). In the repo, the example is also in `/demo/todo`. Run `npm install` in the server directory, and `node ./server.js` to run it. First we need to prepare our library: fluorine.infect() fluorine.debug(true) fluorine.Notifier.init() We should define our main function, even though it's not complete yet: // Parse settings in JSON format. // // :: String -> Object function parseSettings(str) { return JSON.parse(str) } // Standard main function. // // :: IO () function main() { return IO(). get('/static/todo-settings.json'). let(parseSettings).as('settings'). tie(/*TODO*/). // Do other actions under the settings. done() } Then we need to define some movable parts of our program: // Generate a frame of this Todo example. // // :: UI $DOM function frame_todo() { // Define pure DOM don't violent rules of purity. var tpl = [ '<div id="todo-frame"><input id="todo-new" size="18" placeholder="New Todo..." type="text"/>' , '<ul id="todo-list"></ul>' , '<div id="todo-footer">Fluorine Todo List Demo</div></div>'].join("") var frame = $(tpl).get(0) // This is how event works: forward to global scope then handle it. // Fluorine's event mechanism hasn't been comprehensively implemented, // so user can use other libraries like `bacon.js` to get more close to real FRP. return UI(). tie(idGen(UI(frame).$().appendTo('body').done())). tie(idGen(UI('#todo-frame'). select('#todo-new'). forward('keypress', function(e) { if( 13 == e.which ) { return 'todo-item-new' } else { return "_" } }). done()) ). done() } // When a new todo created, append it. // // :: String -> UI $DOM function new_todo(str_todo) { // Generate the new item and append to the list. var item = $(_.template("<li><%= _todo_ %></li>")({_todo_: str_todo})) return UI(item).$(). appendTo('#todo-list'). forward('click', function() { return 'todo-item-remove' }). done() } // Remove todo under a event context. // // The event object, because it's forwarded from jQuery's event, // so will bring all infomation we need. // // :: EventObject -> UI $DOM function remove_todo(e) { return UI(e.target).$().remove().done() } // Generate some pre-defined Todos from settings. // // :: [Environment] -> UI $DOM function predefine_todos(settings) { var uis = _.chain(settings.todos). reduce(function(mem, str_todo) { // Compose the new generated UI chain with a growing one. var result = mem.tie(idGen(new_todo(str_todo))).done() return result }, UI().done()). value() return uis } Events need to initialize: // Register event handler of 'todo-item-remove'. // // :: Event () function on_todo_item_remove() { return Event('todo-item-remove').tie(remove_todo).done() } // Register event handler of 'todo-item-new'. // // :: Event () function on_todo_item_new() { return Event('todo-item-new'). _(function(e){ return $(e.target).val() }). tie(function(str_todo){ return new_todo(str_todo) }). done() } Finally, we can combine everything together, and complete our main function: // The entry point of this program. // // :: IO () function main() { return IO(). get('/static/todo-settings.json'). let(parseSettings).as('settings'). tie( frame_todo ). tie( function() { return predefine_todos(this.settings) }). fork(on_todo_item_remove). fork(on_todo_item_new). done() } Using `fork` here because the default `tie` in Event will not execute the next step until the binding notification triggered, we need to fork these contexts to concurrently bind and handle events Ex1: UI().tie(Event('a').done()).tie(Event('b').done()).done() The event chain of notification 'b' will NOT execute (binding on 'b') until event 'a' triggered. Ex2: UI().fork(Event('a').done()).fork(Event('b').done()).done() The event chain of notification 'a' and 'b' will simutaneously bind to the notifications. Now we can run the program. Ideally, this is the only one executable entry of the program. main()() <a id="run-todo" href="#todo-wrapper" onclick="eval(window.script_todo); $('#run-todo').remove()">Run it !</a> <div id="todo-wrapper"></div> --- ### <a id="guides-N-common-mistakes"></a>Guides and Common Mistakes #### <a id="wrap-all-computation-with-side-effects"></a>0. Wrap all computation with side-effects Contexts can wrap a pure value to lift it: a = "#msg" // String UI(a).done() // UI String Only wrapped or locally defined values can join computations in the chain: UI(a).$(). // Use the wrapped ID to select elements: `a -> UI $DOM` hide(). tie(function($dom) { // Like "let" in other languages var msg = "Some Message" // This subchain will return `UI $DOM` to next step, the `fadeIn`. return UI($dom).$().text(msg).done() }). fadeIn(). done() However this is Javascript, Fluorine will keep slient if you still decide to do some dirty works, like to read/write a non-const global variable inside the context. #### <a id="every-context-chain-should-be-enclosed"></a>1. Every context chain should be enclosed This is resulted from the limitation of our eDSL, which need to do some post-action after user defined a new chain, but it's impossible to know that without the step because we don't have a compiler. So, if you `tie` an undone chain in another context, it's an error: fluorine.infect() IO(). get('/ajax/hello'). tie(function(hello) { return UI('#io-msg').$().text(hello) // Undone context }). done()() When this IO chain got executed, it will throw: "Tying an undone context or just not a context." #### <a id="dont-forget-the-period"></a>2. Don't forget the period Just like jQuery and underscore.js, you will get strange results and errors if you forget the peroid: fluorine.infect() IO(). get('/ajax/hello') // Missing the period tie(function(hello) { return UI('#io-msg').$().text(hello) // Undone context }). done()() When this IO chain got executed, it will throw an error: "ReferenceError: tie is not defined Because "ReferenceError" is a general error, you will find it's hard to debug. So before you typing anything, it is a good practice to insert a period after the chain. Of course, you can choose period-prefix style to make the problem more significant: fluorine.infect() IO() get('/ajax/hello') // Missing the peroid; more apt to discover it .tie(function(hello) { return UI('#io-msg').$().text(hello) // Undone context }) .done()() But some DSL like Coco and CoffeeScript may not accept such style, and will give compiling errors while you want to mix this eDSL and the DSL. #### <a id="turn-on-the-debug-option"></a>3. Turn on the debug option Fluorine can output every steps it executed, to help you debug the program: fluorine.infect() fluorine.debug(true) // Turn the debug mode on IO(). get('/ajax/hello'). tie(function(hello) { return UI('#io-msg').$().text(hello) // Undone context }). done()() fluorine.debug(false) // Turn off the debug mode When you turn on the mode, Fluorine will print out every steps and errors in your console: [DEBUG] Process executing step #0, step name(if any): Context::initialize ( call with ), [] fluorine.js:100 [DEBUG] Process executing step #1, step name(if any): Context::tie, next level --> ( call with ), [undefined] fluorine.js:100 [ERROR] Process terminated at step #1, step name(if any): Context::tie, next level --> Tying an undone context or just not a context. fluorine.js:100 [ERROR] Process terminated at step #1, step name(if any): Context::initialize Tying an undone context or just not a context. fluorine.js:100 Here you can see the undone context stop the execution, just in the `tie` function. This debugging mode is still in very early stage, so it's messages sometimes are hard to understand. Making it better if you can do some contributes ! #### <a id="execute-only-when-its-necessary"></a>4. Execute only when it's necessary Fluorine encourage programmers to create small funcions named generators, which return context chains rather than normal values: // :: UI () function createMsgBox() { return UI('body').$().append('<div id="#io-msg"></div>').done() } // :: String -> UI () function drawMsg(msg) { return UI('#io-msg').$().text(msg).done() } // :: IO () function main() { return IO(). tie( createMsgBox ). get('/ajax/hello'). tie( drawMsg ). done() } // Not execute any chains, until the last moment: main()() These functions, except the last one, will not be executed in entire program. They just provide some subchains to the main context. In fact because these "chains" are just context wrapped values, they're unsurprisedly can be stored and passed to another function as arguments. This feature also means **it's possible to create Javascript programs with a "compile time" phase**, which can find errors before we run it. You may also notice that the two subchains are defined in some named functions, not in anonymous functions in the last IO chain. It's more clear and important when users want to mix 3 or more contexts in one main context. Please also note that anonymous generators are easy to write but also has over-nested problem, unless we can change the syntax with other DSLs like CoffeeScript or Coco. --- ## <a id="apis"></a>APIs There're no individual API documents yet. Annotated sources is here: <a href="../process/process.html">process/process</a> <a href="../notifier/notifier.html">notifier/notifier</a> <a href="../context/context.html">context/context</a> <a href="../context/event.html">context/event</a> <a href="../context/io.html">context/io</a> <a href="../context/ui.html">context/ui</a> <a href="../context/socket.html">context/socket</a> <a href="../utils/utils.html">utils/utils</a> <a href="../export/export.html">export/export</a> Note: because comments in source codes not perfectly fit the need of Docco, some codes become hard to read after converted. These may not be fixed soon. So [reading source codes](https://github.com/snowmantw/Fluorine/tree/master/source/fluorine) from GitHub is still recommended. --- ## <a id="use-with-other-dsls"></a>Use with Other DSLs Although Fluorine want to provide functional features **without** any compiler, it can perform more sweet syntax if you can use it with other DSLs like CoffeeScript or CoCo : // :: UI () createMsgBox = -> UI('body').$().append('<div id="#io-msg"></div>').done() // :: String -> UI () drawMsg = -> return UI('#io-msg').$().text(msg).done() // :: IO () main = -> IO(). tie( createMsgBox ). get('/ajax/hello'). tie( drawMsg ). done() // Not execute any chains, until the last moment: main()() The original version with plain Javascript: // :: UI () function createMsgBox() { return UI('body').$().append('<div id="#io-msg"></div>').done() } // :: String -> UI () function drawMsg(msg) { return UI('#io-msg').$().text(msg).done() } // :: IO () function main() { return IO(). tie( createMsgBox ). get('/ajax/hello'). tie( drawMsg ). done() } // Not execute any chains, until the last moment: main()() And we can get even close to real curry functions ( not directly related to Fluorine, but it's a very important advantage ): add3 = a -> b -> c-> a+b+c If we implement that with plain Javascript, it will become a nightmare: add3 = function(a) { return function(b){ return function(c){ return a+b+c // Nested, WTF } } } --- ## <a id="license"></a>License Fluorine: a context-based Javascript eDSL Copyright (C) 2012 Greg Weng, snowmantw@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. --- ## <a id="contacts"></a>Contacts Greg Weng, snowmantw at gmail dot com - mail, [Google+](https://plus.google.com/117594021220065986125/about), [GitHub](https://github.com/snowmantw) --- <div id="footer"> Page generated by [Strapdown.js](http://strapdownjs.com/) ; static contents powered by [GitHub](https://github.com) </div>