In ClojureScript, we have the re-frame pattern/framework, which is built on Reagent, which is a ClojureScript wrapper of React.
re-frame is all about subscriptions, using a "big atom" to hold application state client-side. Seeing Elm implement the same subscription pattern makes it look pretty tempting.
My understanding is that ClojureScript and Elm have some similarities - functional, pleasant to work with - with one significant difference being that Elm is typed.
Elm is typed, Clojurescript is homoiconic. 2 great features.
Wondering if you could make a lisp where `lambda` or `fn` required type annotations, such as
(defn add
[int -> int -> int] ;; type annotation
[x y] ;; arguments list
(+ x y))
Then it would be homoiconic - something that has saved me hundreds of lines of code (and the less code, the less bugs as a rule of thumb).
Then every function down to the very basic lisp functions would have types. Dunno how doable this is - but it doesnt matter. Someone implemented it in Common Lisp in the 80s I'm sure.
There's Typed Racket [1] and for Clojure there's core.typed [2]which do pretty much that. For Clojure there's also Schema [3], which is a bit lighter weight (it's not a full type system), but still gets you some of the benefits like validation and documentation.
Neither fit the bill though - because not every function ever written in the language is annotated. To be nice to use, it should be an all-or-nothing affair.
I say this as a huge clojure fan - my clojure programs with dynamic types work great. I rely on predicates (functions ending in -?) in I/O, otherwise I'm sure things work.
Why is annotation important? I agree it needs to be possible, but ultimately I want to push the majority off into inference, and only explicitly annotate when necessary or formalizing interfaces OCaml-style.
Though I sure do wish I could have lisp (or clojure, specifically) syntax in OCaml-land.
This is why (ignoring the batshit syntax) I'm quite fascinated by urbit's language hoon, since it handles type signatures by evaluating the function with type objects rather than straight arguments, so the above without the annotation would already be strongly typed and valid across anything that supported +
There's Typed Racket and Clojure's Core.Typed. In Common Lisp you can `declaim`/`declare` attributes to functions and values, and many implementations allow `declaim`-ing types. It's also common for them to propagate and check type assertions at compile-time e.g.
(defun foo (bar)
(if (numberp bar)
(car bar)
x))
should error out in SBCL because BAR is a number but CAR takes values.
> Wondering if you could make a lisp where `lambda` or `fn` required type annotations
…
> Someone implemented it in Common Lisp in the 80s I'm sure.
Even better, it's a standard part of Common Lisp: functions default to being 'typed' to take the universal type, but annotations can be used to declare any types you want (to include types like (integer 3 27), which specifies an integer between 3 and 27), which can be compiler-enforced.
I really don't understand why Common Lisp doesn't see more use. It's modelling clay for computation.
There was a small thread on the Shen Google Group about adding docstrings [1].
There is an BSD version of Shen that can be forked and docstrings added by whomever wants to pickup the work. The language's footprint is small enough now at the moment, that now's the time to get the first chunk done.
The BSD version is pretty impressive, and could be turned into a great project if people picked it up. A lot of interest has been expressed, but it is the commercial version that is moving ahead with lots of improvements. Shen's creator, Mark Tarver has added Griffin, an optimizing compiler, concurrency, and HTML generator in the form of SML (Shen Markup Language).
It is a very fun project. The small set of instructions it is based upon Klambda, has since been ported to many languages - Ruby, Python, Lisp, Haskell and more. The SBCL port is the main one. Aditya Siram (Deech) has just created an Elisp port, shen-elisp! [2]
I personally use ClojureScript with type annotations from Plumatic (former Prismatic) Schema and it works really well. Obviously it's not the same as an actual typed language, but it gives me a good-enough starting point to leverage some typing information and perform some rudimentary (runtime) type checking. I also feel it's really nice as some kind of "inline documentation" to be able to see the type of your parameters (obviously this is a given benefit for statically/explicitly typed languages).
Just dive into it. After wanting to be a bit more descriptive and assertive about the input and output of certain functions in some code I was writing, I must have spent all of half an hour reading documentation and successfully adding Schema to my code. Might be a bit of a rose-tinted memory here in respect to the pastimate :) But it certainly is both easy to understand and use.
Might be off topic but I was interested in the "a big atom". Reagent is using a big atom and some small atoms to simulate component state -- which breaks hot code swapping. And I tried a way to abstract out component states into a single atom to fix it by building my own React like library. Now it's "two big atoms" on my side.
Yeah, after the app grew a bit, I had state scattered here and there. re-frame solves this with a big atom, and avoids expensive rendering every time the big atom updates with subscriptions. Subscriptions hold reactions, which only update if the underlying value changes.
Not to get off-topic though! I am used to working with dynamic languages, but I really should work with a typed language. Elm looks like it offers many of the benefits of ClojureScript, but with typing as well.
Also, ClojureScript lets you write HTML and CSS in your code, but a quick web search indicates Elm may have gone further with incorporating CSS (?).
Can you talk a little more about your thoughts on one big atom? I've been looking at Redux and they also seem to follow this approach. The re-frame readme quotes the Elm Architecture as justification, but that same document goes on to describe nesting https://gist.github.com/evancz/2b2ba366cae1887fe621#nesting.
I come from a Haskell perspective and having one big blob of state sounds to me like it makes your type signatures less informative and increases the scope for error.
First, in Reagent/FRP, I was updating a view based on the value of an atom. This meant I had different default values in different atoms. The big atom let me unify where I stored values, and reuse values, without a performance hit. Second, the big atom made storing application state trivial: you can just throw the map/record in Redis or something.
However, the big atom is just a big map, and keys can be paired with any value, so it is not enforcing types. From a Haskell perspective (where I understand the program almost falls out of the types you define!), this probably does increase the scope for error.
This can be mitigated with Schema (which I never used effectively), logging changes to the console, and being able to see your default map easily (like, there's a map in one file which I load upfront, so I can see what values are there).
I'd welcome hearing from anyone with a more advanced or precise use of re-frame state.
In ClojureScript, we have the re-frame pattern/framework, which is built on Reagent, which is a ClojureScript wrapper of React.
re-frame is all about subscriptions, using a "big atom" to hold application state client-side. Seeing Elm implement the same subscription pattern makes it look pretty tempting.
My understanding is that ClojureScript and Elm have some similarities - functional, pleasant to work with - with one significant difference being that Elm is typed.