Skip to content

Commit

Permalink
Add support for Clojurescript
Browse files Browse the repository at this point in the history
Many changes were done in order to accomplish this, however the core library
still intact. The following changes were done:

* Rename monads.test.core to monads.test.core-tests to be
  easily identified on open buffers (not having to explore the path
* Move the do macro to monads.macro namespace
* Add dev-dependencies and plugins:
  - lein-cljsbuild
  - lein-dalap
  - buster-cljs
* Test suites run successfuly both in JVM and in Javascript (including optimized version)
* Add .travis.yml automated test
* Remove reify usage because of strange bugs in cljs
  • Loading branch information
roman committed Jan 29, 2013
1 parent 999d448 commit 224a151
Show file tree
Hide file tree
Showing 12 changed files with 1,129 additions and 709 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.lein-*
node_modules/
resources/js/protocol_monads_dev.js
resources/js/protocol_monads_browser_test.js
resources/js/protocol_monads_browser_optimized_test.js
resources/js/protocol_monads_node_test.js
target/
22 changes: 22 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
language:
- node_js
- clojure
lein: lein2
jdk:
- oraclejdk7
node_js:
- "0.8"
before_script:
- "export DISPLAY=:99:0"
- "echo 'Compile to javascript'"
- "lein2 cljsbuild once"
- "echo 'Starting buster server'"
- "./node_modules/buster/bin/buster-server &"
- "sleep 3"
- "echo 'Done.'"
- "echo 'Starting phantomjs server'"
- "phantomjs ./node_modules/buster/script/phantom.js &"
- "sleep 3"
- "echo 'Done.'"
script:
- lein2 test && npm test
14 changes: 14 additions & 0 deletions buster.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var config = exports;

config['Browser Tests'] = {
environment: 'browser',
sources: [],
tests: [ "resources/js/protocol_monads_browser_test.js"
, "resources/js/protocol_monads_browser_optimized_test.js" ]
};

config['Node Tests'] = {
environment: 'node',
sources: [],
tests: ["resources/js/protocol_monads_node_test.js"]
};
14 changes: 14 additions & 0 deletions dalap_rules.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
["src/clj/monads/core.clj" "src/cljs/monads/core.cljs"]
['clojure.lang.PersistentList 'List
'clojure.lang.PersistentList$EmptyList 'EmptyList
'clojure.lang.PersistentVector 'PersistentVector
'clojure.lang.PersistentHashSet 'PersistentHashSet
'clojure.lang.LazySeq 'LazySeq
'clojure.lang.IFn 'IFn
'clojure.lang.IDeref 'IDeref
'deref '-deref
'invoke '-invoke]
["test/clj/monads/test/core_test.clj" "test/cljs/monads/test/core_test.cljs"]
[]
}
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "protocol-monads",
"description": "",
"version": "1.0.0",
"author": "",
"dependencies" : { "buster" : "0.6.12" },
"scripts": { "test": "buster-test -e node && buster-test -e browser" }
}
44 changes: 42 additions & 2 deletions project.clj
Original file line number Diff line number Diff line change
@@ -1,3 +1,43 @@
(defproject net.clojure/monads "1.0.0"
(defproject net.clojure/monads "1.0.1-SNAPSHOT"
:description "A protocol based implementation of monads"
:dependencies [[org.clojure/clojure "1.4.0"]])
:dependencies [[org.clojure/clojure "1.4.0"]]
:source-paths ["src/clj"]
:test-paths ["test/clj"]
:profiles {:dev {:dependencies [[com.birdseye-sw/buster-cljs "0.1.0"]]
:plugins [[lein-cljsbuild "0.2.9"]
[com.birdseye-sw/lein-dalap "0.1.0"]]}}
:hooks [leiningen.dalap]

:cljsbuild
{:builds
[{:id :dev
:source-path "src/cljs"
:compiler
{:output-to "resources/js/protocol_monads_dev.js"
:optimizations :simple
:pretty-print true}}
{:id :browser-test
:source-path "test/cljs"
:compiler
{:output-to "resources/js/protocol_monads_browser_test.js"
:libraries ["resources/js/protocol_monads_dev.js"]
:externs ["externs/buster.js"]
:optimizations :simple
:pretty-print true}}
{:id :browser-test-optimized
:source-path "test/cljs"
:compiler
{:output-to "resources/js/protocol_monads_browser_optimized_test.js"
:libraries ["resources/js/protocol_monads_dev.js"]
:externs ["externs/buster.js"]
:optimizations :advanced}}
{:id :node-test
:source-path "test/cljs"
:compiler
{:output-to "resources/js/protocol_monads_node_test.js"
:libraries ["resources/js/protocol_monads_dev.js"]
:externs ["externs/buster.js"]
:optimizations :simple
:pretty-print true}}]}

)
73 changes: 35 additions & 38 deletions src/monads/core.clj → src/clj/monads/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
; the terms of this license.
; You must not remove this notice, or any other, from this software.


(ns monads.core
(:refer-clojure :exclude [do seq map])
(:require [clojure.set :as set]))
Expand All @@ -21,33 +22,6 @@
(defn plus [[mv & mvs]]
(plus-step mv mvs))

(defmacro do
"Monad comprehension. Takes the name of a monad (like vector, hash-set),
a vector of steps given as binding-form/monadic-expression pairs, and
a result value specified by expr. The monadic-expression terms can use
the binding variables of the previous steps.
If the monad contains a definition of m-zero, the step list can also
contain conditions of the form :when p, where the predicate p can
contain the binding variables from all previous steps.
A clause of the form :let [binding-form expr ...], where the bindings
are given as a vector as for the use in let, establishes additional
bindings that can be used in the following steps."
[result bindings expr]
(let [steps (partition 2 bindings)]
`(monads.core/bind (~result nil)
(fn [_#]
~(reduce (fn [expr [sym mv]]
(cond
(= :when sym) `(if ~mv
~expr
(monads.core/zero (~result nil)))
(= :let sym) `(let ~mv
~expr)
:else `(monads.core/bind ~mv (fn [~sym]
~expr))))
`(monads.core/do-result (~result nil) ~expr)
(reverse steps))))))

(defn- comprehend [f mvs]
(let [mv (first mvs)
rest-steps (reduce (fn [steps mv]
Expand Down Expand Up @@ -138,7 +112,8 @@
(writer-m-combine [c1 c2] (concat c1 c2)))

;; Monads describing multi-valued computations, i.e. computations
;; that can yield multiple values.
;; that can yield multiple values.

(extend-type clojure.lang.PersistentList$EmptyList
Monad
(do-result [_ v]
Expand Down Expand Up @@ -185,6 +160,27 @@
(clojure.core/seq ls) (lazy-concat (first l) (rest ls))
:else (list)))))


#_(:cljs
(extend-type Range
Monad
(do-result [_ v]
(list v))
(bind [mv f]
(mapcat f mv))

MonadZero
(zero [_]
[])
(plus-step [mv mvs]
(lazy-concat mv mvs))

MonadWriter
(writer-m-empty [_] (list))
(writer-m-add [c v] (conj c v))
(writer-m-combine [c1 c2] (concat c1 c2))))


(extend-type clojure.lang.LazySeq
Monad
(do-result [_ v]
Expand Down Expand Up @@ -281,20 +277,21 @@
[v]
(state-monad. v nil nil))

(deftype StateMonadFn [f]
clojure.lang.IFn
(invoke [_ s]
[s (f s)])
Monad
(do-result [_ v]
(state-monad. v nil nil))
(bind [mv f1]
(state-monad. nil mv f1)))

(defn update-state
"Return a state-monad value that replaces the current state by the
result of f applied to the current state and that returns the old state."
[f]
(reify
clojure.lang.IFn
(invoke [_ s]
[s (f s)])

Monad
(do-result [_ v]
(state-monad. v nil nil))
(bind [mv f]
(state-monad. nil mv f))))
(StateMonadFn. f))

(defn set-state
"Return a state-monad value that replaces the current state by s and
Expand Down
35 changes: 35 additions & 0 deletions src/clj/monads/macros.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
; Copyright (c) Jim Duey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns monads.macros)

(defmacro do
"Monad comprehension. Takes the name of a monad (like vector, hash-set),
a vector of steps given as binding-form/monadic-expression pairs, and
a result value specified by expr. The monadic-expression terms can use
the binding variables of the previous steps.
If the monad contains a definition of m-zero, the step list can also
contain conditions of the form :when p, where the predicate p can
contain the binding variables from all previous steps.
A clause of the form :let [binding-form expr ...], where the bindings
are given as a vector as for the use in let, establishes additional
bindings that can be used in the following steps."
[result bindings expr]
(let [steps (partition 2 bindings)]
`(monads.core/bind (~result nil)
(fn [_#]
~(reduce (fn [expr [sym mv]]
(cond
(= :when sym) `(if ~mv
~expr
(monads.core/zero (~result nil)))
(= :let sym) `(let ~mv
~expr)
:else `(monads.core/bind ~mv (fn [~sym]
~expr))))
`(monads.core/do-result (~result nil) ~expr)
(reverse steps))))))
Loading

0 comments on commit 224a151

Please sign in to comment.