1679732340
A picture is worth a thousand words. Or is it? Tables, charts, pictures are all useful in understanding our data but often we need a description – a story to tell us what are we looking at. Accelerated Text is a natural language generation tool which allows you to define data descriptions and then generates multiple versions of those descriptions varying in wording and structure.
Accelerated Text can work with all sorts of data:
With Accelerated Text you can use such data to generate text for your business reports, your e-commerce platform or your customer support system.
Accelerated Text provides a web based Document Plan builder, where:
Document Plans and the connected data are used by Accelerated Text's Natural Language Generation engine to produce multiple variations of text exactly expressing what was intended to be communicated to the readers.
Natural language generation is a broad domain with applications in chat-bots, story generation, and data descriptions to name a few. Accelerated Text focuses on applying NLG technology to solve your data to text needs.
Data descriptions require precision. For example, generated text describing weather conditions should not contain things beyond those provided in the initial data – temperature: -1C, humidity: 40%, wind: 10km/h. Despite this, the expression of an individual fact – temperature – could vary. It could result in "it is cold", or "it is just below freezing", or "-1C", but this fact will be stated because it is present in the data. A data to text system is also not the one to elaborate on a story adding something about the serenity of some freezing lake – again, it was not in the supplied data.
Accelerated Text follows the principle of this strict adherence to the data-bound text generation. Via its user interface it provides instruments to define how the data should be translated into a descriptive text. This description – a document plan – is executed by natural language generation engine to produce texts that vary in structure and wording but are always and only about the data provided.
The easiest way to get started is to use Accelerated Text Project Template. It will provide you with the necessary project configuration structure.
If you want to start tinkering and run it based on the latest code in the repository, first make sure that you have make and docker-compose installed, then clone the project and run
make run-app
After running this command the document plan editor will be availabe at http://localhost:8080, while AMR and DLG editors will be reachable via http://localhost:8080/amr/ and http://localhost:8080/dlg/ respectively.
For more detailed description of text generation workflow visit the Documentation.
For a demonstration of how Accelerated Text can be used to provide descriptions for various items in an e-commerce platform (https://www.reactioncommerce.com/) please check the following repository: https://github.com/tokenmill/reaction-acc-text-demo.
To get started with a development environment for Accelerated Text please follow the instructions in our developer's guides for the front-end, api and the text generation engine.
If you have any questions, do not hesitate asking us at info@acceleratedtext.com
If you'll submit an Issue this will help everyone and you will be able to track the progress of us fixing it. In order to facilitate it please provide description of needed information for bug requests (like project version number, Docker version, etc.)
Author: Accelerated-text
Source Code: https://github.com/accelerated-text/accelerated-text
License: View license
1676513962
Electric (formerly known as Photon) is a reactive and network-aware Clojure/Script DSL that fully abstracts over client/server state sync at the programming language layer, in order to achieve strong composition across the frontend/backend boundary in dynamic web apps. With Electric, backend expressions (i.e. queries) and frontend expressions (i.e. views) compose directly. There is no incidental network divide. The Electric macros will, at compile time, perform deep graph analysis of your hollistic program's data flow in order to transparently partition and distribute it across the client/server distributed system. There is no client/server dichotomy from the programmer's perspective. All IO and effects are managed.
Figure: This is not RPC or client-side ORM. The Electric compiler performs deep graph analysis of your unified frontend/backend program to automatically determine the optimal network cut, and then compile it into separate client and server target programs that cooperate and anticipate each other's needs.
Fully reactive: unlike javascript frameworks, in Electric, reactivity is built directly into the programming language itself. Reactive-if, reactive-for, reactive try/catch. When everything is reactive, it feels like nothing is reactive. No observables! No async types! No function coloring problem! De-load your mind and relax.
Multi-tier: frontend and backend are defined in the same expression, same function, same file. It's not code sharing, it's code splitting. Let the compiler infer the boundary from your code, instead of contorting your code — nay, your entire architecture — to fit the boundary.
Network-transparent: Electric closures close over server and client scope bindings, all in the same expression. The Electric compiler uses compile-time static knowledge of your source code to slice your expressions into client and server portions. Right through closures, loops and deeply nested function calls.
Strong composition: Network-transparent Electric functions are true functions. They follow function laws and work at the Clojure/Script REPL. You have lambda, recursion, HOFs, closures, dynamic scope, macros, etc: the full undamaged composition power of Lisp. Goodbye "functional core imperative shell"; with Electric the entire system is a function.
Multiplayer-native: everything is automatically multiplayer, 0 LOC cost.
Our mission is to raise the abstraction ceiling in web development in the same way that garbage collection did for functional programming, paving the way for something new.
; stable {:deps {com.hyperfiddle/electric {:mvn/version "v2-alpha-0-g40c3384e"}}}
Current development priorities:
To date we have focused on correct semantics over syntax and performance. Now that we are useful in production, we are using production learnings to drive our priorities.
Standalone starter repo to fork:
Demos, examples, tutorials are in this repo, see src-docs/user/.
clj -A:dev -X user/main
serves demos at http://localhost:8080
dev
alias; (user/main)
compiles assets and serves app. see src-dev/user.clj & user.cljsWe target full Clojure/Script compatibility (say 99%). That means you can take a pre-existing Clojure snippet and copy/paste it into an Electric function body and it will "work" and produce the correct result. Including host interop syntax, use of pre-existing macros, etc.
Gaps:
Unbound var.
Usually means wrong peer, i.e. accessed server-only var on clientAuthor: Hyperfiddle
Source Code: https://github.com/hyperfiddle/electric
License: EPL-2.0 license
1667780040
CHANGELOG | API | current Break Version:
[com.taoensso/sente "1.17.0"] ; See CHANGELOG for details
See here if you're interested in helping support my open-source work, thanks! - Peter Taoussanis
Sente: realtime web comms for Clojure/Script
Or: We don't need no Socket.IO
Or: core.async + Ajax + WebSockets = The Shiznizzle
Sente is a small client+server library that makes it easy to build reliable, high-performance realtime web applications with Clojure + ClojureScript.
Sen-te (先手) is a Japanese Go term used to describe a play with such an overwhelming follow-up that it demands an immediate response, leaving its player with the initiative.
(I'd also recommend checking out James Henderson's Chord and Kevin Lynagh's jetty7-websockets-async as possible alternatives!)
make-channel-socket!
and you're good to goProtocol | client>server | client>server + ack/reply | server>user push |
---|---|---|---|
WebSockets | ✓ (native) | ✓ (emulated) | ✓ (native) |
Ajax | ✓ (emulated) | ✓ (native) | ✓ (emulated) |
So you can ignore the underlying protocol and deal directly with Sente's unified API. It's simple, and exposes the best of both WebSockets (bidirectionality + performance) and Ajax (optional evented ack/reply model).
Note that there's also a variety of full example projects available
Add the necessary dependency to your project:
Leiningen: [com.taoensso/sente "1.17.0"] ; or
deps.edn: com.taoensso/sente {:mvn/version "1.17.0"}
First make sure that you're using one of the supported web servers (PRs for additional server adapters welcome!).
Somewhere in your web app's code you'll already have a routing mechanism in place for handling Ring requests by request URL. If you're using Compojure for example, you'll have something that looks like this:
(defroutes my-app
(GET "/" req (my-landing-pg-handler req))
(POST "/submit-form" req (my-form-submit-handler req)))
For Sente, we're going to add 2 new URLs and setup their handlers:
(ns my-server-side-routing-ns ; .clj
(:require
;; <other stuff>
[taoensso.sente :as sente] ; <--- Add this
[ring.middleware.anti-forgery :refer [wrap-anti-forgery]] ; <--- Recommended
;; Uncomment a web-server adapter --->
;; [taoensso.sente.server-adapters.http-kit :refer (get-sch-adapter)]
;; [taoensso.sente.server-adapters.immutant :refer (get-sch-adapter)]
;; [taoensso.sente.server-adapters.nginx-clojure :refer (get-sch-adapter)]
;; [taoensso.sente.server-adapters.aleph :refer (get-sch-adapter)]
))
;;; Add this: --->
(let [{:keys [ch-recv send-fn connected-uids
ajax-post-fn ajax-get-or-ws-handshake-fn]}
(sente/make-channel-socket! (get-sch-adapter) {})]
(def ring-ajax-post ajax-post-fn)
(def ring-ajax-get-or-ws-handshake ajax-get-or-ws-handshake-fn)
(def ch-chsk ch-recv) ; ChannelSocket's receive channel
(def chsk-send! send-fn) ; ChannelSocket's send API fn
(def connected-uids connected-uids) ; Watchable, read-only atom
)
(defroutes my-app-routes
;; <other stuff>
;;; Add these 2 entries: --->
(GET "/chsk" req (ring-ajax-get-or-ws-handshake req))
(POST "/chsk" req (ring-ajax-post req))
)
(def my-app
(-> my-app-routes
;; Add necessary Ring middleware:
ring.middleware.keyword-params/wrap-keyword-params
ring.middleware.params/wrap-params
ring.middleware.anti-forgery/wrap-anti-forgery
ring.middleware.session/wrap-session))
The
ring-ajax-post
andring-ajax-get-or-ws-handshake
fns will automatically handle Ring GET and POST requests to our channel socket URL ("/chsk"
). Together these take care of the messy details of establishing + maintaining WebSocket or long-polling requests.
Add a CSRF token somewhere in your HTML:
(let [csrf-token (force ring.middleware.anti-forgery/*anti-forgery-token*)]
[:div#sente-csrf-token {:data-csrf-token csrf-token}])
You'll setup something similar on the client side:
(ns my-client-side-ns ; .cljs
(:require-macros
[cljs.core.async.macros :as asyncm :refer (go go-loop)])
(:require
;; <other stuff>
[cljs.core.async :as async :refer (<! >! put! chan)]
[taoensso.sente :as sente :refer (cb-success?)] ; <--- Add this
))
;;; Add this: --->
(def ?csrf-token
(when-let [el (.getElementById js/document "sente-csrf-token")]
(.getAttribute el "data-csrf-token")))
(let [{:keys [chsk ch-recv send-fn state]}
(sente/make-channel-socket-client!
"/chsk" ; Note the same path as before
?csrf-token
{:type :auto ; e/o #{:auto :ajax :ws}
})]
(def chsk chsk)
(def ch-chsk ch-recv) ; ChannelSocket's receive channel
(def chsk-send! send-fn) ; ChannelSocket's send API fn
(def chsk-state state) ; Watchable, read-only atom
)
The client will automatically initiate a WebSocket or repeating long-polling connection to your server. Client<->server events are now ready to transmit over the ch-chsk
channel.
Last step: you'll want to hook your own event handlers up to this channel. Please see one of the example projects for details.
ch-recv
is a core.async channel that'll receive event-msg
schsk-send!
is a (fn [event & [?timeout-ms ?cb-fn]])
for standard client>server req>resp callsch-recv
is a core.async channel that'll receive event-msg
schsk-send!
is a (fn [user-id event])
for async server>user PUSH calls===============
Term | Form |
---|---|
event | [<ev-id> <?ev-data>] , e.g. [:my-app/some-req {:data "data"}] |
server event-msg | {:keys [event id ?data send-fn ?reply-fn uid ring-req client-id]} |
client event-msg | {:keys [event id ?data send-fn]} |
<ev-id> | A namespaced keyword like :my-app/some-req |
<?ev-data> | An optional arbitrary edn value like {:data "data"} |
:ring-req | Ring map for Ajax request or WebSocket's initial handshake request |
:?reply-fn | Present only when client requested a reply |
chsk-send!
to send event
s to the server and optionally request a reply with timeoutchsk-send!
to send event
s to all the clients (browser tabs, devices, etc.) of a particular connected user by his/her user-id
event-msg
's ?reply-fn
to reply to a particular client event
using an arbitrary edn valueIt's worth noting that the server>user push
(chsk-send! <user-id> <event>)
takes a mandatory user-id argument. See the FAQ later for more info.
(jayq/ajax ; Using the jayq wrapper around jQuery
{:type :post :url "/some-url-on-server/"
:data {:name "Rich Hickey"
:type "Awesome"}
:timeout 8000
:success (fn [content text-status xhr]
(do-something! content))
:error (fn [xhr text-status] (error-handler!))})
(chsk-send! ; Using Sente
[:some/request-id {:name "Rich Hickey" :type "Awesome"}] ; Event
8000 ; Timeout
;; Optional callback:
(fn [reply] ; Reply is arbitrary Clojure data
(if (sente/cb-success? reply) ; Checks for :chsk/closed, :chsk/timeout, :chsk/error
(do-something! reply)
(error-handler!))))
Some important differences to note:
(chsk-send! "destination-user-id" [:some/alert-id <arb-clj-data-payload>])
Each time the channel socket client's state changes, a client-side :chsk/state
event will fire that you can watch for and handle like any other event.
The event form is [:chsk/state [<old-state-map> <new-state-map>]]
with the following possible state map keys:
Key | Value |
---|---|
:type | e/o #{:auto :ws :ajax} |
:open? | Truthy iff chsk appears to be open (connected) now |
:ever-opened? | Truthy iff chsk handshake has ever completed successfully |
:first-open? | Truthy iff chsk just completed first successful handshake |
:uid | User id provided by server on handshake, or nil |
:csrf-token | CSRF token provided by server on handshake, or nil |
:handshake-data | Arb user data provided by server on handshake |
:last-ws-error | ?{:udt _ :ev <WebSocket-on-error-event>} |
:last-ws-close | ?{:udt _ :ev <WebSocket-on-close-event> :clean? _ :code _ :reason _} |
:last-close | ?{:udt _ :reason _} , with reason e/o #{nil :requested-disconnect :requested-reconnect :downgrading-ws-to-ajax :unexpected} |
Link | Description |
---|---|
[Official example] | Official Sente reference example, always up-to-date |
[@fiv0/spa-ws-template] | Example Single Page App with ReFrame, http-kit, shadow-cljs |
[@dharrigan/websockets] | Example using [Reitit], Jetty 9/10 and [@dharrigan/websockets-js] (JS not Cljs!) |
[@laforge49/sente-boot] | Example using Sente v1.11.0, Boot (also works with Windows) |
[@laforge49/sente-boot-reagent] | Example using Sente v1.11.0, Boot, and Reagent |
[@tiensonqin/lymchat] | Example chat app using React Native |
[@danielsz/system-websockets] | Client-side UI, login and wiring of components |
[@timothypratley/snakelake] | Multiplayer snake game with screencast walkthrough |
[@theasp/sente-nodejs-example] | Ref. example adapted for Node.js servers ([Express], [Dog Fort]), as well as a node.js client |
[@ebellani/carpet] | Web+mobile interface for a remmitance application |
[@danielsz/sente-system] | Ref example adapted for [@danielsz/system] |
[@danielsz/sente-boot] | Ref example adapted for [boot] |
[@seancorfield/om-sente] | ?? |
[@tfoldi/data15-blackjack] | Multiplayer blackjack game with documented source code |
[@davidvujic/sente-with-reagent-and-re-frame] | Example code that combines Sente with Reagent and re-frame in a single page application |
Your link here? | PR's welcome! |
user-id
provided to the server>user push fn?There's now also a full
user-id
,client-id
summary up here
For the server to push events, we need a destination. Traditionally we might push to a client (e.g. browser tab). But with modern rich web applications and the increasing use of multiple simultaneous devices (tablets, mobiles, etc.) - the value of a client push is diminishing. You'll often see applications (even by Google) struggling to deal with these cases.
Sente offers an out-the-box solution by pulling the concept of identity one level higher and dealing with unique users rather than clients. What constitutes a user is entirely at the discretion of each application:
To give a user an identity, either set the user's :uid
Ring session key OR supply a :user-id-fn
(takes request, returns an identity string) to the make-channel-socket!
constructor.
If you want a simple per-session identity, generate a random uuid. If you want an identity that persists across sessions, try use something with semantic meaning that you may already have like a database-generated user-id, a login email address, a secure URL fragment, etc.
Note that user-ids are used only for server>user push. client>server requests don't take a user-id.
As of Sente v0.13.0+ it's also possible to send events to :sente/all-users-without-uid
.
This is trivially easy as of Sente v0.13.0+. Please see one of the example projects for details.
Sure! Sente's just a client<->server comms mechanism so it'll work with any view/rendering approach you'd like.
I have a strong preference for Reagent myself, so would recommend checking that out first if you're still evaluating options.
As of v1, Sente uses an extensible client<->server serialization mechanism. It uses edn by default since this usu. gives good performance and doesn't require any external dependencies. The reference example project shows how you can plug in an alternative de/serializer. In particular, note that Sente ships with a Transit de/serializer that allows manual or smart (automatic) per-payload format selection.
To add custom handlers to the TransitPacker, pass them in as writer-opts
and reader-opts
when creating a TransitPacker
. These arguments are the same as the opts
map you would pass directly to transit/writer
. The code sample below shows how you would do this to add a write handler to convert Joda-Time DateTime
objects to Transit time
objects.
(ns my-ns.app
(:require [cognitect.transit :as transit]
[taoensso.sente.packers.transit :as sente-transit])
(:import [org.joda.time DateTime ReadableInstant]))
;; From https://increasinglyfunctional.com/2014/09/02/custom-transit-writers-clojure-joda-time.html
(def joda-time-writer
(transit/write-handler
(constantly "m")
(fn [v] (-> ^ReadableInstant v .getMillis))
(fn [v] (-> ^ReadableInstant v .getMillis .toString))))
(def packer (sente-transit/->TransitPacker :json {:handlers {DateTime joda-time-writer}} {}))
However you like! If you don't have many events, a simple cond
will probably do. Otherwise a multimethod dispatching against event ids works well (this is the approach taken in the reference example project).
Yup, it's automatic for both Ajax and WebSockets. If the page serving your JavaScript (ClojureScript) is running HTTPS, your Sente channel sockets will run over HTTPS and/or the WebSocket equivalent (WSS).
This is important. Sente has support, and use is strongly recommended. You'll need to use middleware like ring-anti-forgery or ring-defaults to generate and check CSRF codes. The ring-ajax-post
handler should be covered (i.e. protected).
Please see one of the example projects for a fully-baked example.
You'll want to listen on the receive channel for a [:chsk/state [_ {:first-open? true}]]
event. That's the signal that the socket's been established.
Update: @danielsz has kindly provided a detailed example here.
Recall that server-side event-msg
s are of the form {:ring-req _ :event _ :?reply-fn _}
, so each server-side event is accompanied by the relevant[*] Ring request.
For WebSocket events this is the initial Ring HTTP handshake request, for Ajax events it's just the Ring HTTP Ajax request.
The Ring request's :session
key is an immutable value, so how do you modify a session in response to an event? You won't be doing this often, but it can be handy (e.g. for login/logout forms).
You've got two choices:
Write any changes directly to your Ring SessionStore (i.e. the mutable state that's actually backing your sessions). You'll need the relevant user's session key, which you can find under your Ring request's :cookies
key. This is flexible, but requires that you know how+where your session data is being stored.
Just use regular HTTP Ajax requests for stuff that needs to modify sessions (like login/logout), since these will automatically go through the usual Ring session middleware and let you modify a session with a simple {:status 200 :session <new-session>}
response. This is the strategy the reference example takes.
Using something like @stuartsierra/component or @palletops/leaven?
Most of Sente's state is held internally to each channel socket (the map returned from client/server calls to make-channel-socket!
). The absence of global state makes things like testing, and running multiple concurrent connections easy. It also makes integration with your component management easy.
The only thing you may[1] want to do on component shutdown is stop any router loops that you've created to dispatch events to handlers. The client/server side start-chsk-router!
fns both return a (fn stop [])
that you can call to do this.
[1] The cost of not doing this is actually negligible (a single parked go thread).
There's also a couple lifecycle libraries that include Sente components:
@arichiardi has kindly provided notes on some of Sente's current implementation details here.
If I've missed something here, feel free to open a GitHub issue or pop me an email!
Please use the project's GitHub issues page for all questions, ideas, etc. Pull requests welcome. See the project's GitHub contributors page for a list of contributors.
Otherwise, you can reach me at Taoensso.com. Happy hacking!
Author: ptaoussanis
Source Code: https://github.com/ptaoussanis/sente
License: EPL-1.0 license
1667603640
This package provides a lisp-to-julia syntax translator with convenience macros that let you do this:
lisp"(defn fib [a] (if (< a 2) a (+ (fib (- a 1)) (fib (- a 2)))))"
@test lisp"(fib 30)" == 832040
@test fib(30) == 832040
LispSyntax.jl is implemented as an expression translator between lisp/clojure-like syntax and julia's AST. Julia's compiler, JIT and multiple-dispatch infrastructure is used for code generation and execution. Because of this, LispSyntax.jl is not really clojure or lisp in most meaningful ways. The semantics are entirely julia-based (which are very similar to scheme/lisp in many ways). The net result is that LispSyntax.jl is really an alternative S-expression-like syntax for julia, not an implemention of clojure or lisp.
(def symbol init)
(quote form)
(defn symbol [param*] expr*)
(defmacro symbol [param*] expr*)
(lambda [param*] expr*)
(fn [param*] expr*)
(let [binding*] expr*)
(global symbol*)
(while test expr*)
(for [binding*] expr*)
(import package*)
LispSyntax.jl
.(global symbol*)
.let
, fn
parameter lists, etc.). This is not currently implemented.defn
are translated to normal julia function
expressions. This means the act as named lambdas in local scope.LispSyntax.jl
look like standard Lisp macros but because expressions are special objects in julia, S-expressions returned from macros require a special translation step to generate julia expression trees. The result is that LispSyntax.jl
macros are directly translated into Julia macros and must be called via special syntax (e.g. (@macro expr)
). Macro hygiene follows the Julia approach of hygenic-by-default with explicit escaping using esc
. This is the opposite of Clojure's macros which use explicit hygiene with specially named variables.@r_str
which in Julia can be called via r""
, it is currently necessary to call these via standard macro syntax: (@r_str "string")
LispSyntax.jl provides a convenience REPL, alleviating one from having to type lisp"( ... )"
for each top level expression. In order to use REPL mode, simply initialize it:
julia> using LispSyntax
julia> LispSyntax.init_repl()
REPL mode Lisp Mode initialized. Press ) to enter and backspace to exit.
At this point, type )
, and you're ready to Lisp:
jλ> (* 2 (reduce + (: 1 6)))
42
jλ> (defn fib [a]
(if (< a 2)
a
(+ (fib (- a 1)) (fib (- a 2)))))
fib (generic function with 1 method)
jλ> (fib 10)
55
To return to the Julia prompt, simply type the backspace type or Ctrl-C
. Once there, you'll still have access to the fuctions you defined:
julia> fib
fib (generic function with 1 method)
julia> fib(10)
55
You may also create a customized REPL.
using
is currently implemented and confusingly, it matches Clojure's import form.Author: swadey
Source Code: https://github.com/swadey/LispSyntax.jl
License: View license
1664209080
> Pkg.clone("https://github.com/vshesh/Sexpr.jl.git")
$ julia -e 'import Sexpr; Sexpr.main()' --
usage: Sexpr.jl [-i] [-c] [-l LINES] [-o OUTPUT] [-e EXTENSION] [-h]
[files...]
A program to port clojure-like s-expression syntax to and from
julia. By default, this program takes clojure syntax and outputs
the julia version. Use -i to flip direction.
positional arguments:
files If given one file and no output directory,
will dump to stdout. If given a directory or
multiple files, eg "sjulia file1 dir file2",
an output directory must be specified with
-o/--output where the files will go.
optional arguments:
-i, --invert take julia code and print out s-expression
code instead
-c, --cat cat all the input from STDIN rather than read
from file. Ignores all positional args to the
program.
-l, --lines LINES how many blank lines should exist between top
level forms, default 1 (type: Int64, default:
1)
-o, --output OUTPUT where to write out files if there are multiple
positional arguments to the file. If this is
empty, and there are >1 argument, the program
will throw an error.
-e, --extension EXTENSION
add an extension that qualifies as a lisp file
(can use multiple times). Defaults: clj, cljs,
cl, lisp, wisp, hy.
-h, --help show this help message and exit
$ julia -e 'import Sexpr; Sexpr.main()' -- -o test/output/ test/programs/
# will transpile all .clj files in test/programs and dump them into test/output.
This project aims to make s-expression syntax interoperable with julia's own Expr objects.
If you've seen LispSyntax.jl, it's a similar idea, but IMHO this project does a bit more, such as allow you to transpile file->file rather than just read in a program, and also transpile back, so you can convert your julia files (minus a few special forms that aren't supported yet) into clojure syntax. This makes it possible to go from julia to python (again, not that anyone needed another route b/c pycall) via Hylang, or to JS via WispJS. The benefit here is that the awkward macro syntax in both of those languages is avoided (Hy necessitates wrapping everything in HyModel objects yourself, which is ridiculous, and WispJS's module system is broken, because it is Javascript, so resolving variable names is not working properly).
The final goal is to use interoperability to do a macroexpand
operation on the input clj syntax. So you would be able to give a folder of clj files, and a temp folder with jl files would be created, then each file would be read in and macroexpanded, converted back to clj syntax, and written out to a third folder. Unfortunately, it's necessary to write the jl files out as an intermediary step, because they need to be able to find each other to resolve imports. Alternatively, you could write the clj files as jl files with the macro @clj_str
, but that makes your whole file a string, which breaks most syntax highlighters, which can be annoying.
I know that you're probably thinking "why?" and it was mostly a project for me to learn Julia and muck around with its internals. I learned quite a bit, so mission accomplished! CLJS has self-hosting now, which means that they will hopefully have a js-only package soon. However, dealing with google closure compiler and leiningen's java/jvm dependencies are a larger problem to be solved, and until then, I still consider it unwieldy, so there's still some practical use to be had here.
Effectively, this is just the reader portion of implementing a lisp - Julia does everything else using its inbuilt mechanisms.
nil
translates to julia's nothing
. They work exactly the same.true
-> true
and false
-> false
. No surprises at all there.number
constants compile to either Int64
or Float64
types.3/5
-> 3//5
in Julia.character
any atom starting with a \
is a character.\newline
, \space
, \tab
, \formfeed
, \backspace
, \return
for escapes\xyz
-> \x
string
is any sequence of characters inside double quotes.keyword
basically a symbol that starts with a :
. In julia, these are confusingly called symbols, and symbols are called variables.symbol
which is any identifier for a variable./
or .
character is converted to a .
in julia. Eg, module/function
becomes module.function
as an identifier. This should be relatively consistent with clojure semantics.*+?!-_':><
are all allowed inside a symbol.::
in a symbol identifier compiles to a type. eg, x::Int
compiles to (:: x Int)
::T1::T2
compiles to a union like x::T1::T2
-> (:: x T1 T2)
'(a b c)
a list - if not quoted, it's evaluated and transpiled.[a b c]
a vector - transpiles to a julia array.{a b c d}
a map - transpiles to a julia Dict(a => b, c=> d)
form.#{a b c}
a set, which can map to Set()
in julia.and
/&&
(what you expect this to be) - needs to be a special form because of short circuiting. Julia defines the and
and or
forms this way on purpose.or
/||
(again, what you expect), see above.x[i]
family (getting/setting/slicing arrays)(aget x 1)
-> Expr(:ref, :x, 1)
-> x[1]
.(aget x 1 2 4 5)
-> Expr(:ref, :x, 1, 2, 4, 5)
-> x[1, 2, 4, 5]
(aget x (: 1 3))
-> x[1:3]
(aget x (: 6))
-> x[6:end]
(preferred)(aget x (: 6 :end))
-> x[6:end]
(not preferred)(:: x Int)
-> x::Int
The ::
form defines types.(:: x Int In64)
-> x::Union{Int, Int64}
there's auto-union if many types are defined.(curly Array Int64)
-> Array{Int64}
will allow parameterized types.(.b a x y)
-> a.b(x,y)
is the dot call form.(. a b c d)
-> a.b.c.d
is the dot access form.((. a b) x y)
is equivalent to (.b a x y)
.(module M ... end)
creates a module. This is visually annoying since you indent your whole file by two spaces just for this call to module, however I haven't figured out any better way to do this - the other option is to make #module M
a special hash dispatch that wraps the whole file but... meh, I don't consider this a high enough priority.(import|using X y z a b)
contrary to my expectations, this will give you import X.y.a.b
. There will be a separate import statement for each function/file you want to use.(import X [y z a])
will expand to import X.y; import X.z; import X.a
instead. This should shorten the writing. Ideally should make this a system macro (in a system.clj file that I define) and call it import*
or something.(export a b c)
-> export a, b, c
. It makes sense from julia's point of view, since modules are flat things, and you only ever have one level of definitions to export.()
/'()
or empty list.
(do exprs...)
does each expression and returns the results of the last one.
(if test true-case false-case?)
standard if, evaluates form #2 and branches.
(let [var1 value1 var2 value2...] exprs...)
binds pairs of variables to their values, then evaluates exprs in an implicit do
.
(fn name? [params...] exprs...)
defines a function.
->
form. Eg: (fn [x] x)
-> (x) -> x
.(defn name docstring? [params...] exprs...)
named defined function.
(def var expr)
defines a variable.
throw
is a function already in julia, so there's no special form dedicated to it.
include
is a function already in julia, so there's no dedicated special form for it.
TODOS
(fn [& rest])
defmulti
and related (does this even mean anything given julia's multiple-dispatch?)deftype
-> type
in Julia.(@m x y z)
how to call a macro - prepend it's name with @
. There is unfortunately no way around this, since julia requires this distinction and for me to resolve what things are macros without it would involve writing an entire compiler. To keep it simple, I'm leaving this requirement in place.@x
means deref in clojure, I might choose to use a different symbol to denote macrocall in the future. maybe μ
or something. Another idea is abusing # dispatch so `#macro (html [:div "helloworld"])`` calls the next form as a macro rather than a regular function. The hash dispatch one seems worse, though.defmacro
defines a macro, as expected.Sexpr.rehydrate()
which will translate the expression back to julia's native AST.quote
or '
gives a literal list of the following expression.'x
is equal to :x
in Julia, but in order to stop the gensym pass from running you actually have to do esc(:x)
to get the equivalent. I'm unclear as of yet how the translation should work to get the desired results, so right now quote
and syntax-quote
do the same thing, which needs to be changed.syntax-quote
or backtick character. the :()
quoting form in julia is actually a syntax quote. It also has an auto-gensym (which can be a pain to get around if you want to return the original name without obfuscation).unquote
or ~
is $
in julia inside expressions. It should evaluate the variable that's given to the macro and use the evaluated value.unquote-splice
or ~@
unquotes, and also expands the form by one layer into the form that's being returned. Ie, (f ~@x)
is the same as :(f($x...))
in julia.Author: VShesh
Source Code: https://github.com/vshesh/Sexpr.jl
1657425840
Light Table is a next generation code editor that connects you to your creation with instant feedback. Light Table is very customizable and can display anything a Chromium browser can.
Light Table is putting out a call for additional maintainers! There is plenty to do for nearly every aspect of the project. Ranging from digging deep down into the internals, writing documentation, fixing bugs, writing plugins, or triaging new issues, Light Table could use a hand. If you are interested, please reach out.
Prebuilt binaries are available through lighttable.com. To build and use a developer version of Light Table see these instructions.
For OSX users, the install process involves the following steps until we officially sign our OSX App:
Light Table has a powerful plugin system that allows almost any aspect of the editor to be extended and customized. With over 100+ plugins, the community is able to offer eval support for new languages, create domain-specific IDEs and much more. If you're interested in writing your own plugin, see the Write a Plugin and Submit a Plugin docs. For an example ClojureScript plugin, see LightTable-Declassifier.
Want to ask a question or just say hi? Please do :). Our mailing list is the Light Table Google group. We also hang out in #lighttable
on Freenode IRC.
LightTable is primarily written in ClojureScript. If you aren't familiar with it, check out David Nolen's tutorial.
In order to develop for Light Table, you will need to install a developer version of Light Table. For more information, read CONTRIBUTING.md and For Developers.
Big thanks to all our contributors! Thanks of course to Kodowa for all they have done for Light Table and also to Cognitect for providing friday contributions for one of the core team members.
Author: LightTable
Source Code: https://github.com/LightTable/LightTable
License: MIT license
1657384560
a minimalistic github flavored markdown editor
On mac, install it through cask:
brew install --cask markright
Alternatively, check out the latest release to quickly find the latest version. Here are the current binaries:
On mac, you can launch markright from the command line with a little alias:
alias markright="open -a /Applications/MarkRight.app"
markright README.md
MarkRight is written in clojurescript. To build, make sure you have clojure and leiningen installed on your system.
npm
leiningen
bower
npm install
bower install
All commands you need are available inside package.json
. To compile the code, run npm run compile:<prod/dev>
. node/
is the folder that goes into electron.
To develop, run the self-reloading build:
lein run -m build/ui-dev
lein run -m build/main-dev
shadow-build will live-reload the frontend so you don't need to refresh. If you change main.cljs
however, you'll have to restart electron to reload your changes.
This project is currently frozen. If you want to help developing it, please feel free to ping me
Author: dvcrn
Source Code: https://github.com/dvcrn/markright
License: GPL-3.0 license
1657366620
What if creating a database would be as cheap as creating a Hashmap?
An immutable in-memory database and Datalog query engine in Clojure and ClojureScript.
DataScript is meant to run inside the browser. It is cheap to create, quick to query and ephemeral. You create a database on page load, put some data in it, track changes, do queries and forget about it when the user closes the page.
DataScript databases are immutable and based on persistent data structures. In fact, they’re more like data structures than databases (think Hashmap). Unlike querying a real SQL DB, when you query DataScript, it all comes down to a Hashmap lookup. Or series of lookups. Or array iteration. There’s no particular overhead to it. You put a little data in it, it’s fast. You put in a lot of data, well, at least it has indexes. That should do better than you filtering an array by hand anyway. The thing is really lightweight.
The intention with DataScript is to be a basic building block in client-side applications that needs to track a lot of state during their lifetime. There’s a lot of benefits:
[datascript "1.3.13"]
Important! If you are using shadow-cljs, add
:compiler-options {:externs ["datascript/externs.js"]}
to your build (see #432 #298 #216)
Support:
Books:
Docs:
Posts:
Talks:
Projects using DataScript:
Related projects:
Demo applications:
For more examples, see our acceptance test suite.
(require '[datascript.core :as d])
;; Implicit join, multi-valued attribute
(let [schema {:aka {:db/cardinality :db.cardinality/many}}
conn (d/create-conn schema)]
(d/transact! conn [ { :db/id -1
:name "Maksim"
:age 45
:aka ["Max Otto von Stierlitz", "Jack Ryan"] } ])
(d/q '[ :find ?n ?a
:where [?e :aka "Max Otto von Stierlitz"]
[?e :name ?n]
[?e :age ?a] ]
@conn))
;; => #{ ["Maksim" 45] }
;; Destructuring, function call, predicate call, query over collection
(d/q '[ :find ?k ?x
:in [[?k [?min ?max]] ...] ?range
:where [(?range ?min ?max) [?x ...]]
[(even? ?x)] ]
{ :a [1 7], :b [2 4] }
range)
;; => #{ [:a 2] [:a 4] [:a 6] [:b 2] }
;; Recursive rule
(d/q '[ :find ?u1 ?u2
:in $ %
:where (follows ?u1 ?u2) ]
[ [1 :follows 2]
[2 :follows 3]
[3 :follows 4] ]
'[ [(follows ?e1 ?e2)
[?e1 :follows ?e2]]
[(follows ?e1 ?e2)
[?e1 :follows ?t]
(follows ?t ?e2)] ])
;; => #{ [1 2] [1 3] [1 4]
;; [2 3] [2 4]
;; [3 4] }
;; Aggregates
(d/q '[ :find ?color (max ?amount ?x) (min ?amount ?x)
:in [[?color ?x]] ?amount ]
[[:red 10] [:red 20] [:red 30] [:red 40] [:red 50]
[:blue 7] [:blue 8]]
3)
;; => [[:red [30 40 50] [10 20 30]]
;; [:blue [7 8] [7 8]]]
DataScript can be used from any JS engine without additional dependencies:
<script src="https://github.com/tonsky/datascript/releases/download/1.3.13/datascript-1.3.13.min.js"></script>
or as a CommonJS module (npm page):
npm install datascript
var ds = require('datascript');
or as a RequireJS module:
require(['datascript'], function(ds) { ... });
Queries:
q
are returned as regular JS arraysEntities:
entity
call are lazy as in Clojuree.get("prop")
, e.get(":db/id")
, e.db
to access entity propertiesTransactions:
":db/id"
, ":db/add"
, etc. instead of db-namespaced keywordstransact
and db_with
Transaction reports:
report.tempids
has string keys ("-1"
for entity tempid -1
), use resolve_tempid
to set up a correspondenceCheck out test/js/tests.js for usage examples.
Stable. Most of the features done, expecting non-breaking API additions and performance optimizations. No docs at the moment, use examples & Datomic documentation.
The following features are supported:
:db/cardinality :db.cardinality/many
:db/valueType :db.type/ref
auto-expansiontransact!
listen!
datoms
and seek-datoms
filter
Query engine features:
:in
clause:in
clauseInterface differences:
@conn
instead of (d/db conn)
#db/id[:db.part/user -100]
just use -100
in place of :db/id
or entity id[:db.fn/call f args]
where f
is a function reference and will take db as first argument (thx @thegeez)resolve
in CLJS):find (aggregate ?myfn ?e) :in $ ?myfn
:db.fn/retractAttribute
shortcut:db/txInstant
Expected soon:
:db/ident
attributes, keywords are literally attribute values, no integer id behind themAimed at interactive, long-living browser applications, DataScript DBs operate in constant space. If you do not add new entities, just update existing ones, or clean up database from time to time, memory consumption will be limited. This is unlike Datomic which keeps history of all changes, thus grows monotonically. DataScript does not track history by default, but you can do it via your own code if needed.
Some of the features are omitted intentionally. Different apps have different needs in storing/transfering/keeping track of DB state. DataScript is a foundation to build exactly the right storage solution for your needs without selling too much “vision”.
Setup
npm install ws
Running the tests
clj -M:test -m kaocha.runner
Watching tests:
./script/watch.sh
datomic-free
is a dependency not available on Clojars or Maven Central.
datomic-free
from https://my.datomic.com/downloads/free./bin/maven-install
Run compatibility checks:
clj -M:datomic
Benchmark:
cd bench
./bench.clj
Author: Tonsky
Source Code: https://github.com/tonsky/datascript
License: EPL-1.0 license
1653966000
This is an introductory video on creating a simple web server using deps.edn and some clojure libraries
------
Links
My Code - https://github.com/kelvin-mai/learn-deps/tree/http-kit-intro
You can see more at: A Clojure/Clojurescript Notebook Application
1653958800
In this video, we learn about clojure cli and deps.edn as a build tool.. An introduction to the clojure cli and deps.edn as a build tool.
My Code - https://github.com/kelvin-mai/learn-deps
You can see more at: A gentle intro to Clojure
#clojure
1653004020
serverless-cljs-plugin
A Serverless plugin which uses lein/cljs-lambda (or, optionally Lumo) to package services written in Clojurescript.
$ lein new serverless-cljs example
example$ lein deps
Will generate an example
directory containing a minimal serverless.yml
and project.clj
demonstrating this plugin's functionality.
functions:
echo:
cljs: example.core/echo
plugins:
- serverless-cljs-plugin
With the above serverless.yml
, serverless deploy
will create a zip file containing your functions. Doing this is similar to setting the Serverless packaging.artifact
option - cljs-lambda
is responsible for the zip contents, and Serverless includes/excludes will be skipped (cljs-lambda
offers equivalent functionality).
In the example above, there needn't be a corresponding entry for echo
in project.clj
.
Alternatively you can use the Lumo compiler.
In order to enable it, pass the --lumo
switch to either deploy
or package
:
$ serverless deploy --lumo
Or add the following to your serverless.yml
:
custom:
cljsCompiler: lumo
Compiler options
The source paths and compiler options will be read from the optional file serverless-lumo.edn
. Below are the defaults:
{:source-paths ["src"]
:compiler {:output-to "out/lambda.js"
:output-dir "out"
:source-map false ;; because of a bug in lumo <= 1.8.0
:target :nodejs
:optimizations :none}}
Lumo Configuration
As an alternative to cljsCompiler: lumo
, cljsCompiler.lumo
may be specified as a map of options. These options are passed directly to the lumo
process. Currently supported:
custom:
cljsCompiler:
lumo:
dependencies:
- andare:0.7.0
classpath:
- /tmp/
localRepo: /xyz
cache: /cache | none
index: true | false
exitOnWarning: true | false
Note: caching is always on unless you specify "none" in the config.
The index.js file
The index
option will materialize a custom index.js
in :output-dir
's parent folder. This file should be thought as managed by serverless-cljs-plugin
and it is necessary for some plugin (e.g.: serverless-offline
) to work properly.
Note: with the default compiler options, index.js
will be saved in the project root, overwriting without warning.
Exit on compilation warnings
Lumo generates warnings such as WARNING: Use of undeclared Var
to signal failures. You can tune the ones you want to see by using the :warnings
compiler option in serverless-lumo.edn
, but by default the lumo
process emits the warnings, does not throw and returns 0
. This means that serverless
will keep going in presence of warnings.
Author: Nervous-systems
Source Code: https://github.com/nervous-systems/serverless-cljs-plugin
License: Unlicense license
1650723420
Clojurescript re-mount module, that provides mobile integration.
Add [district0x/district-ui-mobile "1.0.0"]
into your project.clj
district.ui.mobile
first needs to be initialized, as defined in the re-mount pattern.
Include district.ui.mobile
within the main file where you call mount/start
;; core.cljs, or main.cljs, etc...
(require '[mount.core :as mount])
(require '[district.ui.mobile])
;; optional initialization options
(def district-ui-options
{:mobile {:force-mobile-device false}})
(mount/start (with-args district-ui-options))
Subscriptions can then be made to determine if the web application is being viewed from mobile devices, specifically Android or iOS devices.
;; re-frame view file
(require '[re-frame :refer [subscribe]])
(require '[district.ui.mobile.subs :as mobile-subs])
(defn show-device []
(let [android? (subscribe [::mobile-subs/android?])
ios? (subscribe [::mobile-subs/ios?])]
(fn []
[:div
(cond
@android? [:span "You are on an Android device."]
@ios? [:span "You are on an iOS (iPhone, iPad) device."]
:else [:span "We don't know if you're on a mobile device."])])))
Another use-case is determining if the device is coinbase-compatible.
;; re-frame view file
(require '[re-frame :refer [subscribe]])
(require '[district.ui.mobile.subs :as mobile-subs])
(defn coinbase-dialog []
(let [coinbase-compatible? (subscribe [::mobile-subs/coinbase-compatible?])]
(fn []
[:div
(if @coinbase-compatible?
[:span "Mobile device is coinbase compatible."]
[:span "Device is not coinbase compatible."])])))
This namespace contains the mobile
mount module.
You can pass the following args while initiating this module:
:force-mobile-device
Used to imitate a mobile browser. Accepts one of the values: true
(android), :android
, :ios
, or false
. By default, this value is set to false
.For example, if I wanted to imitate an iOS device:
(ns my-district.core
(:require [mount.core :as mount]
[district.ui.mobile]
;;...
))
(-> (mount/with-args
{:mobile {:force-mobile-device :ios}
;; Additional mount options...
})
(mount/start))
re-frame subscriptions provided by this module:
::android? []
Returns true
if an Android mobile device is viewing the web application, otherwise false
::ios? []
Returns true
if an iOS mobile device (iPhone, iPad) is viewing the web application, otherwise false
.
::coinbase-compatible? []
Returns true
if an coinbase-compatible mobile device is viewing the web application, otherwise false
.
$ lein doo
Download Details:
Author: district0x
Source Code: https://github.com/district0x/district-ui-mobile
License: EPL-1.0 License
1650716040
Clojurescript re-mount module, that synchronises UI time with the blockchain time.
Add [district0x/district-ui-web3-sync-now "1.0.3-2"]
into your project.clj. Include [district.ui.web3-sync-now]
in your CLJS file, where you use mount/start
.
Warning: district0x modules are still in early stages, therefore API can change in a future.
This namespace contains now mount module. This module has no configuration parameters.
(ns my-district.core
(:require [mount.core :as mount]
[district.ui.now]
[district.ui.web3]
[district.ui.web3-sync-now]
[district.ui.logging]))
(-> (mount/with-args {:logging {:level :info}
:web3 {:url "http://127.0.0.1:8549"}})
(mount/start))
After the :start
lifecycle method gets called this module waits for the :district.ui.web3.events/web3-created
event and sets :district.ui.now.subs/now
time to the last block time on the blockchain. Set the logging level to get notified of errors and/or successfull events.
re-frame events provided by this module:
::increment-now
Event to increment now time in a re-frame db and the (testrpc) blockchain time by a number of seconds.
(ns my-district.core
(:require [district.ui.web3-sync-now.events :as sync-now-events]
[re-frame.core :as re-frame]))
(re-frame/dispatch [::sync-now-events/increment-now 300])
Errors and successfully handled events will be logged to the JS console.
::block-number
This is an utility event called by the :start
lifecycle method of the module. It wraps the re-frame-web3-fx web3/call
effect and chains the returned last block number to the ::get-block
event. In a typical application you will never need to call this event yourself.
::get-block
This is an utility event which wraps the re-frame-web3-fx web3/call
effect and chains the returned last block object to the ::set-now
. In a typical application you will never need to call this event yourself.
::set-now
This is an utility event which sets the :district.ui.now.subs/now
time from the last block time. Upon success it will log to the JS console. In a typical application you will never need to call this event yourself.
Run test suite:
lein deps
# To run tests and rerun on changes
lein doo chrome tests
Install into local repo:
lein install
Download Details:
Author: district0x
Source Code: https://github.com/district0x/district-ui-web3-sync-now
License: EPL-1.0 License
1650708720
Clojurescript mount + re-frame component for a district UI, that provides reagent UI component for adding calendar reminders. Works with Google Calendar and Apple Calendar.
Add [district0x/district-ui-add-to-calendar "1.0.0"]
into your project.clj.
Include [district.ui.component.add-to-calendar]
in your CLJS file.
district.ui.component.add-to-calendar
This namespace contains reagent UI component.
Basic usage example:
(ns my-district
(:require [cljs-time.core :as t]
[reagent.core :as r]
[district.ui.component.add-to-calendar :as add-to-calendar]))
(defn main-panel []
[:div#page1 [add-to-calendar/add-to-calendar {:title "Test"
:url "https://district0x.io"
:description "description"
:start-time (t/now)
:end-time (t/now)}]])
(defn ^:export init []
(r/render [main-panel] (.getElementById js/document "app")))
Download Details:
Author: district0x
Source Code: https://github.com/district0x/district-ui-component-add-to-calendar
License:
1650701400
First time run
lein deps
Compile contracts (assumes you have solc
installed):
lein solc
Auto compile contracts on changes:
lein solc auto
Start server:
ganache-cli -d -p 8545 -m district0x
lein repl
(start-server!)
node dev-server/district0x-tasks.js
Start UI:
lein garden once
lein repl
(start-ui!)
# go to http://localhost:4598/
How to do tests:
ganache-cli -d -p 8545 -m district0x
lein test-doo
If change contracts code:
; auto compile Solidity code in project when files changed
lein solc auto
Remember this doesn't trigger cljs tests. So you have to change cljs tests files to trigger. For example add new line.
QA env and Dockerfiles
See docker-compose file in https://github.com/district0x/pipelines
Download Details:
Author: district0x
Source Code: https://github.com/district0x/district0x-tasks
License: EPL-1.0 License