├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── callback-hell.jpg
├── clojure_code_survival.jpeg
├── doc
└── intro.md
├── hickey_meme.jpg
├── presentation.html
├── presentation.org
├── project.clj
├── quick_demo.gif
├── resources
└── public
│ └── index.html
├── src
├── clj
│ └── chat42
│ │ └── core.clj
└── cljs
│ └── chat42
│ └── core.cljs
└── test
└── chat42
└── core_test.clj
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | .hgignore
11 | .hg/
12 | /resources/public/js
13 | /resources/public/css
14 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
3 |
4 | ## [Unreleased]
5 | ### Changed
6 | - Add a new arity to `make-widget-async` to provide a different widget shape.
7 |
8 | ## [0.1.1] - 2016-12-09
9 | ### Changed
10 | - Documentation on how to make the widgets.
11 |
12 | ### Removed
13 | - `make-widget-sync` - we're all async, all the time.
14 |
15 | ### Fixed
16 | - Fixed widget maker to keep working when daylight savings switches over.
17 |
18 | ## 0.1.0 - 2016-12-09
19 | ### Added
20 | - Files from the new template.
21 | - Widget maker public API - `make-widget-sync`.
22 |
23 | [Unreleased]: https://github.com/your-name/chat42/compare/0.1.1...HEAD
24 | [0.1.1]: https://github.com/your-name/chat42/compare/0.1.0...0.1.1
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
4 |
5 | 1. DEFINITIONS
6 |
7 | "Contribution" means:
8 |
9 | a) in the case of the initial Contributor, the initial code and
10 | documentation distributed under this Agreement, and
11 |
12 | b) in the case of each subsequent Contributor:
13 |
14 | i) changes to the Program, and
15 |
16 | ii) additions to the Program;
17 |
18 | where such changes and/or additions to the Program originate from and are
19 | distributed by that particular Contributor. A Contribution 'originates' from
20 | a Contributor if it was added to the Program by such Contributor itself or
21 | anyone acting on such Contributor's behalf. Contributions do not include
22 | additions to the Program which: (i) are separate modules of software
23 | distributed in conjunction with the Program under their own license
24 | agreement, and (ii) are not derivative works of the Program.
25 |
26 | "Contributor" means any person or entity that distributes the Program.
27 |
28 | "Licensed Patents" mean patent claims licensable by a Contributor which are
29 | necessarily infringed by the use or sale of its Contribution alone or when
30 | combined with the Program.
31 |
32 | "Program" means the Contributions distributed in accordance with this
33 | Agreement.
34 |
35 | "Recipient" means anyone who receives the Program under this Agreement,
36 | including all Contributors.
37 |
38 | 2. GRANT OF RIGHTS
39 |
40 | a) Subject to the terms of this Agreement, each Contributor hereby grants
41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to
42 | reproduce, prepare derivative works of, publicly display, publicly perform,
43 | distribute and sublicense the Contribution of such Contributor, if any, and
44 | such derivative works, in source code and object code form.
45 |
46 | b) Subject to the terms of this Agreement, each Contributor hereby grants
47 | Recipient a non-exclusive, worldwide, royalty-free patent license under
48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise
49 | transfer the Contribution of such Contributor, if any, in source code and
50 | object code form. This patent license shall apply to the combination of the
51 | Contribution and the Program if, at the time the Contribution is added by the
52 | Contributor, such addition of the Contribution causes such combination to be
53 | covered by the Licensed Patents. The patent license shall not apply to any
54 | other combinations which include the Contribution. No hardware per se is
55 | licensed hereunder.
56 |
57 | c) Recipient understands that although each Contributor grants the licenses
58 | to its Contributions set forth herein, no assurances are provided by any
59 | Contributor that the Program does not infringe the patent or other
60 | intellectual property rights of any other entity. Each Contributor disclaims
61 | any liability to Recipient for claims brought by any other entity based on
62 | infringement of intellectual property rights or otherwise. As a condition to
63 | exercising the rights and licenses granted hereunder, each Recipient hereby
64 | assumes sole responsibility to secure any other intellectual property rights
65 | needed, if any. For example, if a third party patent license is required to
66 | allow Recipient to distribute the Program, it is Recipient's responsibility
67 | to acquire that license before distributing the Program.
68 |
69 | d) Each Contributor represents that to its knowledge it has sufficient
70 | copyright rights in its Contribution, if any, to grant the copyright license
71 | set forth in this Agreement.
72 |
73 | 3. REQUIREMENTS
74 |
75 | A Contributor may choose to distribute the Program in object code form under
76 | its own license agreement, provided that:
77 |
78 | a) it complies with the terms and conditions of this Agreement; and
79 |
80 | b) its license agreement:
81 |
82 | i) effectively disclaims on behalf of all Contributors all warranties and
83 | conditions, express and implied, including warranties or conditions of title
84 | and non-infringement, and implied warranties or conditions of merchantability
85 | and fitness for a particular purpose;
86 |
87 | ii) effectively excludes on behalf of all Contributors all liability for
88 | damages, including direct, indirect, special, incidental and consequential
89 | damages, such as lost profits;
90 |
91 | iii) states that any provisions which differ from this Agreement are offered
92 | by that Contributor alone and not by any other party; and
93 |
94 | iv) states that source code for the Program is available from such
95 | Contributor, and informs licensees how to obtain it in a reasonable manner on
96 | or through a medium customarily used for software exchange.
97 |
98 | When the Program is made available in source code form:
99 |
100 | a) it must be made available under this Agreement; and
101 |
102 | b) a copy of this Agreement must be included with each copy of the Program.
103 |
104 | Contributors may not remove or alter any copyright notices contained within
105 | the Program.
106 |
107 | Each Contributor must identify itself as the originator of its Contribution,
108 | if any, in a manner that reasonably allows subsequent Recipients to identify
109 | the originator of the Contribution.
110 |
111 | 4. COMMERCIAL DISTRIBUTION
112 |
113 | Commercial distributors of software may accept certain responsibilities with
114 | respect to end users, business partners and the like. While this license is
115 | intended to facilitate the commercial use of the Program, the Contributor who
116 | includes the Program in a commercial product offering should do so in a
117 | manner which does not create potential liability for other Contributors.
118 | Therefore, if a Contributor includes the Program in a commercial product
119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend
120 | and indemnify every other Contributor ("Indemnified Contributor") against any
121 | losses, damages and costs (collectively "Losses") arising from claims,
122 | lawsuits and other legal actions brought by a third party against the
123 | Indemnified Contributor to the extent caused by the acts or omissions of such
124 | Commercial Contributor in connection with its distribution of the Program in
125 | a commercial product offering. The obligations in this section do not apply
126 | to any claims or Losses relating to any actual or alleged intellectual
127 | property infringement. In order to qualify, an Indemnified Contributor must:
128 | a) promptly notify the Commercial Contributor in writing of such claim, and
129 | b) allow the Commercial Contributor to control, and cooperate with the
130 | Commercial Contributor in, the defense and any related settlement
131 | negotiations. The Indemnified Contributor may participate in any such claim
132 | at its own expense.
133 |
134 | For example, a Contributor might include the Program in a commercial product
135 | offering, Product X. That Contributor is then a Commercial Contributor. If
136 | that Commercial Contributor then makes performance claims, or offers
137 | warranties related to Product X, those performance claims and warranties are
138 | such Commercial Contributor's responsibility alone. Under this section, the
139 | Commercial Contributor would have to defend claims against the other
140 | Contributors related to those performance claims and warranties, and if a
141 | court requires any other Contributor to pay any damages as a result, the
142 | Commercial Contributor must pay those damages.
143 |
144 | 5. NO WARRANTY
145 |
146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON
147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR
149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the
151 | appropriateness of using and distributing the Program and assumes all risks
152 | associated with its exercise of rights under this Agreement , including but
153 | not limited to the risks and costs of program errors, compliance with
154 | applicable laws, damage to or loss of data, programs or equipment, and
155 | unavailability or interruption of operations.
156 |
157 | 6. DISCLAIMER OF LIABILITY
158 |
159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
166 | OF SUCH DAMAGES.
167 |
168 | 7. GENERAL
169 |
170 | If any provision of this Agreement is invalid or unenforceable under
171 | applicable law, it shall not affect the validity or enforceability of the
172 | remainder of the terms of this Agreement, and without further action by the
173 | parties hereto, such provision shall be reformed to the minimum extent
174 | necessary to make such provision valid and enforceable.
175 |
176 | If Recipient institutes patent litigation against any entity (including a
177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself
178 | (excluding combinations of the Program with other software or hardware)
179 | infringes such Recipient's patent(s), then such Recipient's rights granted
180 | under Section 2(b) shall terminate as of the date such litigation is filed.
181 |
182 | All Recipient's rights under this Agreement shall terminate if it fails to
183 | comply with any of the material terms or conditions of this Agreement and
184 | does not cure such failure in a reasonable period of time after becoming
185 | aware of such noncompliance. If all Recipient's rights under this Agreement
186 | terminate, Recipient agrees to cease use and distribution of the Program as
187 | soon as reasonably practicable. However, Recipient's obligations under this
188 | Agreement and any licenses granted by Recipient relating to the Program shall
189 | continue and survive.
190 |
191 | Everyone is permitted to copy and distribute copies of this Agreement, but in
192 | order to avoid inconsistency the Agreement is copyrighted and may only be
193 | modified in the following manner. The Agreement Steward reserves the right to
194 | publish new versions (including revisions) of this Agreement from time to
195 | time. No one other than the Agreement Steward has the right to modify this
196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The
197 | Eclipse Foundation may assign the responsibility to serve as the Agreement
198 | Steward to a suitable separate entity. Each new version of the Agreement will
199 | be given a distinguishing version number. The Program (including
200 | Contributions) may always be distributed subject to the version of the
201 | Agreement under which it was received. In addition, after a new version of
202 | the Agreement is published, Contributor may elect to distribute the Program
203 | (including its Contributions) under the new version. Except as expressly
204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
205 | licenses to the intellectual property of any Contributor under this
206 | Agreement, whether expressly, by implication, estoppel or otherwise. All
207 | rights in the Program not expressly granted under this Agreement are
208 | reserved.
209 |
210 | This Agreement is governed by the laws of the State of New York and the
211 | intellectual property laws of the United States of America. No party to this
212 | Agreement will bring a legal action under this Agreement more than one year
213 | after the cause of action arose. Each party waives its rights to a jury trial
214 | in any resulting litigation.
215 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # chat42
2 |
3 | We have
4 | a
5 | [demo deployed on IPFS](https://ipfs.io/ipfs/Qmc2fKMZf6o3N7bafPKMkXvhcfBaoVS5uTRXTBUGpeVzFr/).
6 | It uses a websocket connection to our test server on topiq.es. This is a simple
7 | web-chat application which leverages [replikativ](http://replikativ.io) for its
8 | state management.
9 |
10 |
11 |
12 |
13 |
14 | chat42 consists of two parts, a client written
15 | with [ClojureScript](https://clojurescript.org/)
16 | and [om-next](https://github.com/omcljs/om/wiki/Quick-Start-(om.next)) (react)
17 | that compiles into efficient Javascript, and a server written in Clojure that
18 | brokers communication between peers over a websocket. The server will be
19 | available for node.js soon and is only necessary to ensure a communication
20 | channel.
21 |
22 | There is also a [react native client](https://github.com/replikativ/chat42app).
23 |
24 | It was initially created as
25 | a
26 | [presentation for ClojureScript](https://github.com/replikativ/chat42/blob/master/presentation.org) in
27 | our local JavaScript meetup.
28 |
29 |
30 | ## Usage
31 |
32 | You need to have [Leiningen](https://leiningen.org/) installed.
33 |
34 |
35 | ### Client development
36 |
37 | Just run figwheel and edit `core.cljs` as needed. If the replikativ parts are un clear, also have a look at the [API walkthrough](http://replikativ.io/tut/get-started.html).
38 |
39 | ~~~clojure
40 | lein figwheel
41 | ~~~
42 |
43 | This allows you to develop the client against our test server.
44 |
45 | ### Server peer
46 |
47 | If you want to persist and distribute your own state, run the server with:
48 |
49 | ~~~clojure
50 | lein run
51 | ~~~
52 |
53 | The server will automatically connect to our new test net and dump potentially a
54 | few gigabytes to your disk. If you don't want this, comment out:
55 |
56 | ~~~clojure
57 | (connect! stage "ws://replikativ.io")
58 | ~~~
59 |
60 | to
61 |
62 | ~~~clojure
63 | #_(connect! stage "ws://replikativ.io")
64 | ~~~
65 |
66 | in [core.clj](https://github.com/replikativ/chat42/blob/master/src/clj/chat42/core.clj#L14)
67 |
68 | You also have to change the uri in `core.cljs` to make the client connect to
69 | your server and not the test server.
70 |
71 | If you have any problems, questions or suggestions, please
72 | join our gitter chat.
73 |
74 | ## License
75 |
76 | Copyright © 2016-2017 Konrad Kühne, Christian Weilbach
77 |
78 | Distributed under the Eclipse Public License either version 1.0 or (at
79 | your option) any later version.
80 |
--------------------------------------------------------------------------------
/callback-hell.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replikativ/chat42/b626e76e3e87ed0ed98d3747e6c16758c10596bf/callback-hell.jpg
--------------------------------------------------------------------------------
/clojure_code_survival.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replikativ/chat42/b626e76e3e87ed0ed98d3747e6c16758c10596bf/clojure_code_survival.jpeg
--------------------------------------------------------------------------------
/doc/intro.md:
--------------------------------------------------------------------------------
1 | # Introduction to chat42
2 |
3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/)
4 |
--------------------------------------------------------------------------------
/hickey_meme.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replikativ/chat42/b626e76e3e87ed0ed98d3747e6c16758c10596bf/hickey_meme.jpg
--------------------------------------------------------------------------------
/presentation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Back to the future: Cljs
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
25 |
26 |
27 |
28 |
29 |
30 |
Back to the future: Cljs Konrad Kühne & Christian Weilbach Created: 2016-12-14 Wed 18:01
31 |
32 |
33 |
34 |
35 |
36 | About us
37 |
38 | professional frontend (app and web) and backend (JVM) developers with
39 | LivelyCode
40 | core devs of isomorphic http://replikativ.io/
41 | regular experience with JavaScript
42 | introduction to FP with underscore.js and JSON data flow from web services
43 | functional programming zealots
44 | pragmatic: Making more money with less work now.
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | Why?
55 |
56 |
60 |
61 |
62 |
63 |
64 |
65 | Language design
66 |
67 | LISP was invented/discovered in 1958
68 | Brandon Eich's design objective: Java-like script language
69 | Time to design: 2 weeks !!!!
70 | \(\Rightarrow\) Some JavaScript core language problems: http://destroyallsoftware.com/talks/wat
71 | comparison design effort JavaScript vs. Clojure:
72 | 2 weeks vs. 2 years
73 | not to speak of all the history in Lisp language design which has since been
74 | ported to Clojure (for example MetaObject protocol as the "best" OO
75 | approach)
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | The original JavaScript
86 |
87 |
88 | JavaScript can be a nice language ("JavaScript the Good Parts")
89 |
90 |
91 |
92 |
93 |
94 | Lisp (Scheme dialect) was intended as the original JavaScript, before Java
95 | became so popular
96 | JS similar to imperative Lisps like Emacs-Lisp
97 | composition : dynamic extension of small core language, like Scheme
98 | Dynamic languages (e.g. Python, Ruby, JavaScript) subset of features of
99 | Lisp: The hundred year language . Paul Graham.
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | Language limitations
109 |
110 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | List Processing
121 |
122 | "Most important idea in Computer Science." Alan Kay
123 | Remove as much syntax as possible
124 | interpreter of itself can be expressed on half a page, (chapter 4 of SICP)
125 | All code is simplest data structure: List (homoiconic)
126 | adapt language to problem
127 | \(\Rightarrow\) Lisp Macros : Languages as libraries
128 |
129 | go-lang as core.async: no callback hell
130 | prolog as core.logic: relational programming
131 | statistics as anglican: probabilistic programming
132 | TypeScript as with core.typed: gradual typing
133 | OCaml style pattern matching as core.match: terse control flow
134 | …
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | Functional programming
146 |
147 | not just first class functions (closures)
148 | but pure functions: functions that do not mutate their parameters or the
149 | environment, but just return a value
150 | value semantics : true equality
151 | Bret Victor about Haskell: "Language for 21st century."
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | Functional programming in JS
160 |
161 | JavaScript core datastructures (JSON) are mutable
162 | \(\Rightarrow\) no real equality, parameters mutated by functions
163 | FP theoretically possible, but practically impossible on top of imperative
164 | programming
165 | JS libraries do not support FP in general
166 | current trend with TypeScript / EcmaScript class syntax rather in imperative
167 | object-oriented direction like Java
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 | ClojureScript and Clojure
177 |
178 | Clojure is a Lisp dialect for the JVM
179 | persistent datastructures for FP
180 | ClojureScript is a port of Clojure to JavaScript
181 | both are hosted languages , exposing host primitives and abstractions instead
182 | of introducing their own: ClojureScript \(\neq\) Clojure
183 | Clojure brings live coding and strong concurrent programming to the JVM and
184 | CLR
185 | Clojure and ClojureScript can be very nicely integrated, e.g. replikativ
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 | Cljs 💘 JS
196 |
197 | First class interop:
198 |
199 | All primitives are JavaScript primitives
200 | all ClojureScript functions are normal JavaScript functions
201 | data structures can be easily converted from and to JSON
202 | No overhead for large standard library or your code thanks to heavily optimizing
203 | Google Closure compiler (used for GMail, …)
204 |
205 |
206 | ClojureScript with functional programming strengthens the core value of
207 | JavaScript: Interactivity and Live Coding
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 | Relax
217 |
218 |
222 |
223 |
224 |
225 |
226 |
227 |
228 | Live Coding
229 |
230 | redux, immutable: react state management inspired by Om
231 | React through material-ui
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 | References
240 |
241 | Source code and presentation: https://github.com/replikativ/chat42
242 | Boston Clojure - learning resources
243 | Figwheel Quick Start (look at the FlappyBird Demo ;) )
244 | Background to Cljs webdev
245 | Some company web sites using cljs : Capital One, CircleCI, Cisco, Rackspace,
246 | Thomson Reuters, …
247 | Many more use Clojure now : Facebook, Deutsche Bank, Daily Mail, Heroku,
248 | Netflix, Oracle, Salesforce, SoundCloud, Walmart Labs, …
249 | ClojureScript + React Native
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
294 |
295 |
296 |
--------------------------------------------------------------------------------
/presentation.org:
--------------------------------------------------------------------------------
1 | #+Title: Back to the future: Cljs
2 | #+Author: Konrad Kühne & Christian Weilbach
3 | #+Email: ch_weil@topiq.es
4 |
5 | #+REVEAL_ROOT: http://cdn.jsdelivr.net/reveal.js/3.0.0/
6 | #+OPTIONS: reveal_center:t reveal_progress:t reveal_history:t reveal_control:t
7 | #+OPTIONS: reveal_mathjax:t reveal_rolling_Links:t reveal_keyboard:t reveal_overview:t num:nil
8 | #+OPTIONS: reveal_slide_number:t
9 | # +OPTIONS: reveal_width:1420 reveal_height:1080
10 | #+OPTIONS: toc:nil
11 | #+REVEAL_MARGIN: 0.1
12 | #+REVEAL_MIN_SCALE: 0.6
13 | #+REVEAL_MAX_SCALE: 1.2
14 | #+REVEAL_TRANS: linear
15 | #+REVEAL_THEME: sky
16 | #+REVEAL_HLEVEL: 1
17 |
18 |
19 | * About us
20 | - professional frontend (app and web) and backend (JVM) developers with
21 | LivelyCode
22 | - core devs of /isomorphic/ http://replikativ.io
23 | - regular experience with JavaScript
24 | - introduction to FP with underscore.js and JSON data flow from web services
25 | - functional programming zealots
26 | - pragmatic: Making /more/ money with /less/ work now.
27 |
28 |
29 |
30 | * Why?
31 | [[./hickey_meme.jpg]]
32 |
33 | * Language design
34 | - LISP was invented/discovered in *1958*
35 | - Brandon Eich's design objective: Java-like script language
36 | - Time to design: *2 weeks !!!!*
37 | - $\Rightarrow$ Some JavaScript core language problems: http://destroyallsoftware.com/talks/wat
38 | - comparison design effort JavaScript vs. Clojure: \\
39 | *2 weeks* vs. *2 years*
40 | - not to speak of all the history in Lisp language design which has since been
41 | ported to Clojure (for example MetaObject protocol as the "best" OO
42 | approach)
43 |
44 |
45 |
46 | * The original JavaScript
47 | #+BEGIN_NOTES
48 | - JavaScript can be a *nice* language ("JavaScript the Good Parts")
49 | #+END_NOTES
50 | - Lisp (Scheme dialect) was intended as the /original/ JavaScript, before Java
51 | became so popular
52 | - JS similar to imperative Lisps like Emacs-Lisp
53 | - *composition*: dynamic extension of small core language, like Scheme
54 | - Dynamic languages (e.g. Python, Ruby, JavaScript) subset of features of
55 | Lisp: [[http://paulgraham.com/hundred.html][The hundred year language]]. Paul Graham.
56 |
57 |
58 | * Language limitations
59 | [[./callback-hell.jpg]]
60 |
61 |
62 | * List Processing
63 | - "Most important idea in Computer Science." Alan Kay
64 | - *Remove as much syntax as possible*
65 | - *interpreter of itself* can be expressed on half a page, (chapter 4 of SICP)
66 | - All code is simplest data structure: List (homoiconic)
67 | - adapt language to problem \\
68 | $\Rightarrow$ *Lisp Macros*: Languages as libraries
69 | * *go-lang* as core.async: *no callback hell*
70 | * *prolog* as core.logic: *relational programming*
71 | * *statistics* as anglican: *probabilistic programming*
72 | * *TypeScript* as with core.typed: *gradual typing*
73 | * *OCaml style pattern matching* as core.match: *terse control flow*
74 | ...
75 |
76 |
77 | * Functional programming
78 | - not just /first class/ functions (closures)
79 | - but *pure* functions: functions that do not mutate their parameters or the
80 | environment, but just return a value
81 | - *value semantics*: true equality
82 | - Bret Victor about Haskell: "Language for 21st century."
83 |
84 | * Functional programming in JS
85 | - JavaScript core datastructures (JSON) are mutable \\
86 | $\Rightarrow$ no /real/ equality, parameters mutated by functions
87 | - FP theoretically possible, but practically impossible on top of imperative
88 | programming
89 | - JS libraries do not support FP in general
90 | - current trend with TypeScript / EcmaScript class syntax rather in /imperative
91 | object-oriented/ direction like Java
92 |
93 |
94 | * ClojureScript and Clojure
95 | - Clojure is a Lisp dialect for the JVM
96 | - *persistent datastructures* for FP
97 | - ClojureScript is a port of Clojure to JavaScript
98 | - both are /hosted languages/, exposing host primitives and abstractions instead
99 | of introducing their own: ClojureScript $\neq$ Clojure
100 | - Clojure brings live coding and strong concurrent programming to the JVM and
101 | CLR
102 | - Clojure and ClojureScript can be very nicely integrated, e.g. replikativ
103 |
104 |
105 |
106 | * Cljs 💘 JS
107 | - First class interop:
108 | * *All primitives* are JavaScript primitives
109 | * all ClojureScript functions are *normal JavaScript functions*
110 | * data structures can be easily converted from and to JSON
111 | * No overhead for large standard library or your code thanks to heavily optimizing
112 | *Google Closure compiler* (used for GMail, ...)
113 | - ClojureScript with functional programming strengthens the core value of
114 | JavaScript: *Interactivity* and *Live Coding*
115 |
116 |
117 | * Relax
118 | [[./clojure_code_survival.jpeg]]
119 |
120 |
121 | * Live Coding
122 | - redux, immutable: react state management /inspired by/ [[https://github.com/omcljs/om/][Om]]
123 | - React through [[http://www.material-ui.com/][material-ui]]
124 |
125 | * References
126 | - Source code and presentation: https://github.com/replikativ/chat42
127 | - [[https://github.com/boston-clojure/learning-clojure][Boston Clojure - learning resources]]
128 | - [[https://github.com/bhauman/lein-figwheel/wiki/Quick-Start][Figwheel Quick Start]] (look at the FlappyBird Demo ;) )
129 | - [[https://www.railslove.com/stories/my-way-into-clojure-building-a-card-game-with-om-part-1][Background to Cljs webdev]]
130 | - [[https://github.com/clojure/clojurescript/wiki/Companies-Using-ClojureScript][Some company web sites using cljs]]: Capital One, CircleCI, Cisco, Rackspace,
131 | Thomson Reuters, ...
132 | - [[http://clojure.org/community/companies][Many more use Clojure now]]: Facebook, Deutsche Bank, Daily Mail, Heroku,
133 | Netflix, Oracle, Salesforce, SoundCloud, Walmart Labs, ...
134 | - [[http://cljsrn.org/][ClojureScript + React Native]]
135 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject chat42 "0.1.0-SNAPSHOT"
2 |
3 | :description "Web chat using replikativ."
4 |
5 | :url "http://github.com/replikativ/chat42"
6 |
7 | :main chat42.core
8 |
9 | :license {:name "Eclipse Public License"
10 | :url "http://www.eclipse.org/legal/epl-v10.html"}
11 |
12 | :dependencies [[org.clojure/clojure "1.9.0-alpha14"]
13 | [org.clojure/clojurescript "1.9.229"]
14 |
15 | [com.cognitect/transit-cljs "0.8.239" :scope "provided"]
16 | [io.replikativ/replikativ "0.2.4"]
17 | [sablono "0.8.0"]
18 | [com.fzakaria/slf4j-timbre "0.3.5"]
19 | [org.omcljs/om "1.0.0-alpha46" :exclusions [cljsjs/react
20 | cljsjs/react-dom]]
21 | [cljs-react-material-ui "0.2.44"]]
22 |
23 | :plugins [[lein-figwheel "0.5.8"]
24 | [lein-cljsbuild "1.1.4" :exclusions [[org.clojure/clojure]]]]
25 |
26 | :source-paths ["src/cljs" "src/clj"]
27 |
28 | :clean-targets ^{:protect false} ["resources/public/js/compiled" "target"]
29 |
30 | :cljsbuild {:builds
31 | [{:id "dev"
32 | :source-paths ["src/cljs"]
33 |
34 | ;; the presence of a :figwheel configuration here
35 | ;; will cause figwheel to inject the figwheel client
36 | ;; into your build
37 | :figwheel {:on-jsload "chat42.core/on-js-reload"
38 | ;; :open-urls will pop open your application
39 | ;; in the default browser once Figwheel has
40 | ;; started and complied your application.
41 | ;; Comment this out once it no longer serves you.
42 | :open-urls ["http://localhost:3449/index.html"]}
43 |
44 | :compiler {:main chat42.core
45 | :asset-path "js/compiled/out"
46 | :output-to "resources/public/js/compiled/chat42.js"
47 | :output-dir "resources/public/js/compiled/out"
48 | :source-map-timestamp true
49 | ;; To console.log CLJS data-structures make sure you enable devtools in Chrome
50 | ;; https://github.com/binaryage/cljs-devtools
51 | :preloads [devtools.preload]}}
52 | ;; This next build is an compressed minified build for
53 | ;; production. You can build this with:
54 | ;; lein cljsbuild once min
55 | {:id "min"
56 | :source-paths ["src/cljs"]
57 | :compiler {:output-to "resources/public/js/compiled/chat42.js"
58 | :main chat42.core
59 | :optimizations :advanced
60 | :pretty-print false}}]}
61 |
62 | :figwheel {;; :http-server-root "public" ;; default and assumes "resources"
63 | ;; :server-port 3449 ;; default
64 | ;; :server-ip "127.0.0.1"
65 |
66 | :css-dirs ["resources/public/css"] ;; watch and update CSS
67 |
68 | ;; Start an nREPL server into the running figwheel process
69 | ;; :nrepl-port 7888
70 |
71 | ;; Server Ring Handler (optional)
72 | ;; if you want to embed a ring handler into the figwheel http-kit
73 | ;; server, this is for simple ring servers, if this
74 |
75 | ;; doesn't work for you just run your own server :) (see lein-ring)
76 |
77 | ;; :ring-handler hello_world.server/handler
78 |
79 | ;; To be able to open files in your editor from the heads up display
80 | ;; you will need to put a script on your path.
81 | ;; that script will have to take a file path and a line number
82 | ;; ie. in ~/bin/myfile-opener
83 | ;; #! /bin/sh
84 | ;; emacsclient -n +$2 $1
85 | ;;
86 | ;; :open-file-command "myfile-opener"
87 |
88 | ;; if you are using emacsclient you can just use
89 | ;; :open-file-command "emacsclient"
90 |
91 | ;; if you want to disable the REPL
92 | ;; :repl false
93 |
94 | ;; to configure a different figwheel logfile path
95 | ;; :server-logfile "tmp/logs/figwheel-logfile.log"
96 | }
97 | :profiles {:dev {:dependencies [[binaryage/devtools "0.8.2"]
98 | [figwheel-sidecar "0.5.8"]
99 | [com.cemerick/piggieback "0.2.1"]]
100 | ;; need to add dev source path here to get user.clj loaded
101 | :source-paths ["src" "dev"]
102 | ;; for CIDER
103 | ;; :plugins [[cider/cider-nrepl "0.12.0"]]
104 | :repl-options {; for nREPL dev you really need to limit output
105 | :init (set! *print-length* 50)
106 | :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}}}
107 | )
108 |
--------------------------------------------------------------------------------
/quick_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replikativ/chat42/b626e76e3e87ed0ed98d3747e6c16758c10596bf/quick_demo.gif
--------------------------------------------------------------------------------
/resources/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/clj/chat42/core.clj:
--------------------------------------------------------------------------------
1 | (ns chat42.core
2 | (:require [replikativ.peer :refer [server-peer]]
3 | [replikativ.stage :refer [create-stage! connect!]]
4 |
5 | [kabel.peer :refer [start stop]]
6 | [konserve.memory :refer [new-mem-store]]
7 | [konserve.filestore :refer [new-fs-store]]
8 |
9 | [superv.async :refer [? S]] ;; core.async error handling
10 | [clojure.core.async :refer [chan] :as async]))
11 |
12 |
13 | (def uri "ws://0.0.0.0:31744")
14 |
15 | (defn -main [& args]
16 | (let [store (? S #_(new-mem-store) (new-fs-store "/tmp/chat42-store"))
17 | peer (? S (server-peer S store uri))
18 | stage (? S (create-stage! "mail:your@email.com" peer))]
19 | (? S (start peer))
20 | ;; NOTE: you do not need to connect to the test net, but you can :)
21 | (connect! stage "ws://replikativ.io:8888")
22 | (println "Your chat42 replikativ server peer is up and running! :)" uri)
23 | ;; HACK blocking main termination
24 | (? S (chan))))
25 |
26 | (comment
27 | (require '[taoensso.timbre :as timbre])
28 | (timbre/set-level! :info)
29 |
30 | (stop peer)
31 |
32 | (-main))
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/cljs/chat42/core.cljs:
--------------------------------------------------------------------------------
1 | (ns chat42.core
2 | (:require [konserve.memory :refer [new-mem-store]]
3 | [replikativ.peer :refer [client-peer]]
4 | [replikativ.stage :refer [create-stage! connect!
5 | subscribe-crdts!]]
6 |
7 | [hasch.core :refer [uuid]]
8 | [replikativ.crdt.ormap.realize :refer [stream-into-identity!]]
9 | [replikativ.crdt.ormap.stage :as s]
10 | [cljs.core.async :refer [>! chan timeout]]
11 | [superv.async :refer [S] :as sasync]
12 | [cljsjs.material-ui] ;; TODO why?
13 | [om.next :as om :refer-macros [defui] :include-macros true]
14 | [om.dom :as dom :include-macros true]
15 | [cljs-react-material-ui.core :as ui]
16 | [cljs-react-material-ui.icons :as ic]
17 | [sablono.core :as html :refer-macros [html]]
18 | [cljs-react-material-ui.core :as ui]
19 | [cljs-react-material-ui.icons :as ic]
20 | [taoensso.timbre :as timbre])
21 | (:require-macros [superv.async :refer [go-try go-loop-try]]
22 | [cljs.core.async.macros :refer [go-loop]]))
23 |
24 |
25 | ;; 1. app constants
26 | (def user "mail:alice@replikativ.io")
27 | (def ormap-id #uuid "7d274663-9396-4247-910b-409ae35fe98d")
28 | (def uri
29 | ;; test net
30 | #_"ws://replikativ.io:8888"
31 | "wss://topiq.es/replikativ/ws"
32 | ;; alternatively use your own peer :)
33 | #_"ws://localhost:31744")
34 |
35 |
36 | (enable-console-print!)
37 |
38 | ;; Have a look at the replikativ "Get started" tutorial to understand how the
39 | ;; replikativ parts work: http://replikativ.io/tut/get-started.html
40 |
41 | ;; lets transform the OR-MAP operations to the val-atom
42 | (def stream-eval-fns
43 | {'assoc (fn [S a new]
44 | (swap! a update-in [:posts] conj new)
45 | a)
46 | 'dissoc (fn [S a new]
47 | (swap! a update-in [:posts] disj new)
48 | a)})
49 |
50 |
51 | ;; this is our main app state
52 | (defonce val-atom (atom {:posts #{}
53 | :snackbar {:open false
54 | :message ""}}))
55 |
56 |
57 | ;; standard setup
58 | (defn setup-replikativ []
59 | (go-try S
60 | (let [local-store ( S (new-mem-store))
61 | local-peer ( S (client-peer S local-store))
62 | stage ( S (create-stage! user local-peer))
63 | stream (stream-into-identity! stage
64 | [user ormap-id]
65 | stream-eval-fns
66 | val-atom)]
67 | ( S (s/create-ormap! stage
68 | :description "messages"
69 | :id ormap-id))
70 | ;; HACK to display OR-Map creation status message for a moment
71 | ( S (timeout 1000))
72 | (connect! stage uri)
73 | {:store local-store
74 | :stage stage
75 | :stream stream
76 | :peer local-peer})))
77 |
78 | (declare client-state)
79 | ;; this is the only state changing function
80 | (defn send-message! [app-state msg]
81 | (s/assoc! (:stage client-state)
82 | [user ormap-id]
83 | (uuid msg)
84 | [['assoc msg]]))
85 |
86 |
87 |
88 |
89 | ;; helper functions
90 | (defn format-time [d]
91 | (let [secs (-> (.getTime (js/Date.))
92 | (- d)
93 | (/ 1000)
94 | js/Math.floor)]
95 | (cond
96 | (>= secs 3600) (str (js/Math.floor (/ secs 3600)) " hours ago")
97 | (>= secs 60) (str (js/Math.floor (/ secs 60)) " minutes ago")
98 | (>= secs 0) (str " seconds ago"))))
99 |
100 |
101 | ;; Material UI with Om
102 | (defn create-msg [name text]
103 | {:text text
104 | :name (if (= name "")
105 | "Anonymous"
106 | name)
107 | :date (.getTime (js/Date.))})
108 |
109 |
110 | (defn target-val [e]
111 | (.. e -target -value))
112 |
113 | (timbre/merge-config!
114 | {:appenders {:chat42status
115 | {:enabled? true
116 | :fn
117 | (fn [data]
118 | (let [{:keys [msg_]} data
119 | {:keys [event] :as msg} (cljs.reader/read-string (force msg_))
120 | m (case event
121 | :connected (str "Connected to " (:url msg))
122 | :connecting-to (str "Connecting to " (:url msg))
123 | :creating-ormap (str "Creating OR-Map CRDT.")
124 | ; :streaming-ormap (str "Fetching data.")
125 | :fetching-commits (str "Fechting commits.")
126 | :websocket-error "Connection error. Reconnecting ..."
127 | nil)]
128 | (when m
129 | (swap! val-atom assoc
130 | :snackbar {:open true
131 | :message m}))))}}})
132 |
133 |
134 |
135 |
136 | (defn name-field [comp input-name]
137 | (dom/div #js {:className "center-xs"}
138 | (ui/text-field
139 | {:floating-label-text "Name"
140 | :class-name "w-80"
141 | :on-change #(om/update-state! comp assoc :input-name (target-val %))
142 | :value input-name})))
143 |
144 |
145 | (defn message-field [comp input-text input-name ]
146 | (let [app-state (om/props comp)]
147 | (dom/div #js {:className "center-xs" :key "message"}
148 | (ui/text-field {:floating-label-text "Message"
149 | :class-name "w-80"
150 | :on-change
151 | #(om/update-state!
152 | comp assoc :input-text (target-val %))
153 | :on-key-down
154 | (fn [e]
155 | (when
156 | (or (= (.-which e) 13)
157 | (= (.-keyCode e) 13))
158 | (send-message!
159 | app-state (create-msg input-name input-text))
160 | (om/update-state! comp assoc :input-text "")))
161 | :value input-text}))))
162 |
163 |
164 | (defn send-button [comp input-text input-name]
165 | (let [app-state (om/props comp)]
166 | (dom/div #js {:className "center-xs"}
167 | (ui/raised-button
168 | {:label "Send"
169 | :disabled (= input-text "")
170 | :on-touch-tap
171 | #(do
172 | (send-message! app-state (create-msg input-name input-text))
173 | (om/update-state! comp assoc :input-text ""))}))))
174 |
175 | (defn message-item [{:keys [text name date]}]
176 | (ui/list-item {:primary-text
177 | (dom/div nil name
178 | (dom/small nil (str " wrote " (format-time date))))
179 | :secondary-text text
180 | :secondary-text-lines 2
181 | :key (uuid (str date))}))
182 |
183 |
184 | ;; React App
185 | (defui App
186 | Object
187 | (componentWillMount [this]
188 | (om/set-state!
189 | this
190 | {:input-name ""
191 | :input-text ""}))
192 | (render [this]
193 | (let [{:keys [snackbar posts] :as state} (om/props this)
194 | {:keys [input-name input-text]} (om/get-state this)]
195 | (ui/mui-theme-provider
196 | {:mui-theme (ui/get-mui-theme)}
197 | (html
198 | [:div.col-xs-12.mar-top-10.row
199 | (ui/snackbar {:open (:open snackbar) :message (:message snackbar)
200 | :auto-hide-duration 10000})
201 | [:div.col-xs-3]
202 | [:div.col-xs-6
203 | (ui/paper {:className "mar-top-20"}
204 | (ui/list
205 | nil
206 | (name-field this input-name)
207 | (message-field this input-text input-name)
208 | (send-button this input-text input-name)
209 | (ui/subheader nil "Messages")
210 | (mapv message-item (sort-by :date > posts))
211 | (ui/divider nil)))]])))))
212 |
213 | (def reconciler
214 | (om/reconciler {:state val-atom}))
215 |
216 | (defn ^:export main [& args]
217 | (go-try S
218 | (def client-state ( S (setup-replikativ)))))
219 |
220 | ;; for figwheel not in main
221 | (om/add-root! reconciler App (.getElementById js/document "app"))
222 |
223 |
224 |
--------------------------------------------------------------------------------
/test/chat42/core_test.clj:
--------------------------------------------------------------------------------
1 | (ns chat42.core-test
2 | (:require [clojure.test :refer :all]
3 | [chat42.core :refer :all]))
4 |
5 | (deftest a-test
6 | (testing "FIXME, I fail."
7 | (is (= 0 1))))
8 |
--------------------------------------------------------------------------------