
First look at Clojure.core.typed - fogus
http://logaan.github.io/clojure/core.typed/2013/08/29/first-look-at-core.typed.html?utm_source=dlvr.it&utm_medium=twitter
======
apaprocki
Since no one has said anything about it -- I really admire the willingness to
post a recording of oneself attempting to use someone else's code. A lot of
people would shy away from this because viewers might have arbitrary unrelated
criticism ("you type so slow!"). This type of thing could be really useful to
authors who can notice if a certain concept isn't grasped right away or if
documentation didn't immediately solve someone's need.

This is used regularly in UX labs with two-way mirrors and cameras watching
how users interact with design -- why should it not be useful to programming
as well?

~~~
logan-campbell
Ooh god now I'm going to be self conscious about my typing. But seriously I
was mainly just worried that Ambrose (the brains behind core.typed) might take
it the wrong way. Many of the problems I had could've been avoided if I'd just
taken my time and read the docs, and I don't mean for my failing to reflect
poorly on his library. He took it like a champ though and released a patch
fixing a few issues I hit the same night I posted.

------
ryanbrush
A perhaps naive question: could this sort of thing tap into Clojure's type
hints rather than using external annotations? So rather than writing the
example in the post:

(ann my-inc [Number -> Number])

(defn my-inc [n] (let [internal-number (Integer/parseInt "1")] (+ n internal-
number)))

one could write this:

(defn ^Number my-inc [^Number n] (let [internal-number (Integer/parseInt "1")]
(+ n internal-number)))

...and then run a type checker that reads and analyzes the hints?

Of course, the core.typed annotations could preserve whatever information they
need in the compiled output, whereas the type hints aren't preserved past the
compile phase (as far as I can tell.) Is that the limitation that requires
defining type information in separate annotations?

~~~
swannodette
Clojure's type hints are only about Java interop and performance, and
pretending otherwise is not a good idea. They aren't nearly rich enough to do
the things that Typed Clojure does - think about less trivial examples like
type checking heterogenous maps.

~~~
ryanbrush
Ah, I didn't realize that core.typed could deal with rich data structures like
that. That's actually pretty amazing.

------
levosmetalo
For truly typed lisp you should also check Shen. Its type system is built in
and very advanced and flexible (based on sequent calculus). You can write
programs in both type checked and unchecked mode. One of the supported
platforms is also Java.

Project is still young and in an early phase, but definitely promising.

~~~
agumonkey
The source code is quite surprising
[https://github.com/hraberg/shen.clj/blob/master/shen/src/pro...](https://github.com/hraberg/shen.clj/blob/master/shen/src/prolog.shen#L231)

~~~
vutekst
Indeed, I don't really understand how that could be code. Are those just cute
symbols to match on?

~~~
chrismonsanto
Yes -- see
[https://github.com/hraberg/shen.clj/blob/master/shen/src/pro...](https://github.com/hraberg/shen.clj/blob/master/shen/src/prolog.shen#L305)

Upper case symbols are variables, as in Prolog. The others are literal
matches.

------
klibertp
Damn it - another unique Racket feature stops being unique.

I hoped that in 2020 Racket will be around 5th position in "the most popular
language" rankings with Clojure in the first ten too. Now it looks like
Clojure is taking the lead - maybe Racket2 will change this again.

But hey, either way that's a popular, mainstream Lisp! That's unprecedented!

~~~
graue
The presence in Clojure of persistent vectors and hashmaps, which are very,
very fast, and are used pervasively throughout the core libraries and Clojure
code at large, makes it way more interesting to me than other Lisps I've seen.
Does modern Racket have anything comparable to Clojure's collection
primitives?

My introduction to Lisp, some years ago, was reading The Little Schemer, which
I loved. Afterwards, I was very disillusioned when I needed an array and
learned the canonical solution was to use mutable arrays and set! (as if
adding a bang after its name made it less of a sin). I understand why it's
like that: persistent data structures are tricky to implement efficiently, and
Scheme emphasizes simplicity of implementation. But it still bums me out.

~~~
6cxs2hd6
Standard datatypes and generics (including hash tables, dictionaries, vectors,
sets, sequences, streams, etc.):

[http://docs.racket-lang.org/reference/data.html](http://docs.racket-
lang.org/reference/data.html)

Additional datatypes (including imperative queues, growable vectors, orders
and ordered dictionaries, splay trees, skip lists, interval maps, binary
heaps, integer sets, bit vectors, etc.):

[http://docs.racket-lang.org/data/index.html](http://docs.racket-
lang.org/data/index.html)

~~~
chongli
Are all of those standard datatypes thread-safe? If not, they are not what the
parent was asking for. Clojure's maps, sets and vectors are all thread-safe
due to being immutable. They're all built-in to the standard library and have
extremely slick syntactic sugar.

Sure, you could implement all of this in Racket, I'm not saying you couldn't;
the advantage with Clojure is that these things are standard and that
everybody else's libraries make use of this stuff.

This is probably the biggest advantage with starting any new programming
language: you get a chance to redo the standard library (and tie it into some
syntactic sugar) without worrying about compatibility.

~~~
klibertp
> Are all of those standard datatypes thread-safe?

Some of them have both mutable and immutable versions (lists, hashes) where
immutability is the default, some are purely functional (Okasaki's data
structures implementation [https://pkg.racket-
lang.org/info/pfds](https://pkg.racket-lang.org/info/pfds)), and some are
mutable by default (the "data" package). Most of the stdlib is immutable by
default or plain immutable. There is no "slick" syntactic sugar for these by
default, but Racket being as good in "language creation" department as it is,
there are 3rd party implementations
([https://github.com/greghendershott/rackjure](https://github.com/greghendershott/rackjure)).

> the advantage with Clojure is that these things are standard and that
> everybody else's libraries make use of this stuff

For the most part it is true for Racket, too. Where it differs is syntactic
sugar and more complex data structures; I think some of this will change in
racket2 (which is being planned if I understand correctly).

> you get a chance to redo the standard library (and tie it into some
> syntactic sugar) without worrying about compatibility.

That's why every "racket" program starts with a #lang specifier. Racket, in
reality, is many different languages at once; because of this design and
matching implementation, there is absolutely no problem with redoing stdlib
and syntax _completely_ \- you can write Racket that is lazy, that is FRP,
that is Algol(try it, it's fun!) that is typed or is logic oriented
([http://en.wikipedia.org/wiki/Datalog](http://en.wikipedia.org/wiki/Datalog))
already. And you can mix modules written in them freely and conveniently
(provide in one and require form in the other module are all it takes in
practice).

This alone makes Racket so much more flexible than any other language. What
Racket lacks now to implement everything Clojure (and other languages) offers
as one of the Racket languages are only man-hours of development team. Other,
less complicated features from other languages are being ported all the time,
for example _generator function_ in a Python style (with yield) implementation
is in a stdlib since a long time, so I believe assimilating proven concepts
from other languages is both possible and a "Racket way".

~~~
chongli
Yeah, it really sounds like Racket is the most flexible programming language
out there due to its metalinguistic abstraction capability. The question is:
what is the secret to attracting developers to join the community? Clojure
seems to really benefit from the rather iconic personality of Rich Hickey;
this is in common with other communities and their leaders.

The other thing I wonder is if there is such a thing as _too flexible_? Rich
deliberately resisted adding user-definable reader macros because he wanted
Clojure to have a consistent, identifiable syntax. Whether that benefits the
language's adoption or not, I have no idea.

I have to say I am hopeful that more people are exposed to Lisp languages on
all fronts and that it cross-polinates all of the communities. We all have
much to learn from one another and a great deal of potential for innovation.
Despite how old Lisp is it still seems like we've barely scratched the surface
of its potential.

~~~
graue
Though I can't speak from personal experience, I do get the impression that
many past Lisps suffered from too much flexibility, in the sense that no one
could agree what object system to use, what libraries to use, etc. It seems
that Clojure is doing much better that regard, which could partially explain
its success.

~~~
klibertp
> what object system to use, what libraries to use, etc.

But this is easily solved by just having a standard library with preferred
versions of these things. Many Lisps in the past, and especially Scheme,
officially defined very little besides the core language. This is changing, I
hear that the next, 7th revision of Scheme standard is going to have two
parts, one for the core, and another for the batteries which should be
included.

But! Racket comes with it's own selection of libraries in stdlib, which is
richer than any other Scheme implementation. So it's not that different from
Clojure in this regard.

Racket has reader macros, but it is necessary for it to have them, considering
that part of it's focus is to be able to build new languages easily. I haven't
seen them used outside of defining these new languages, so I doubt it is a
common problem for new users.

There are few things where Clojure and Racket differ. One is the runtime
system used - Racket has it's own virtual machine implementation with the JIT
while Clojure uses those from Java. This means that a) Clojure devs don't have
to spend time on a VM and can focus on the language and b) interop with Java
is very strong point of Clojure. Racket on the other hand has a very nice FFI
story for C, which makes writing bindings to C libraries very easy.

Clojure is being developed with concurrency in mind and every aspect of a
language reflects this. Racket includes support for concurrency as one of many
features and so the defaults are not always suited for it and there are things
Racket doesn't have. The reason for this is that the Racket developers don't
have enough time to bring native threads to VM and rewrite some of the
language and library and maintain normal development at the same time. (But
there is racket2 planned, which may be a language with most of the Clojure
features in it)

And of course Clojure is being much better marketed. Clojure has a few simple
selling points and they are being advertised everywhere; Racket is really very
complex thing with many man-decades of research behind it and even where it is
being advertised, it looks like a typical "academic" thing.

So these are the reasons I think made Clojure more popular than Racket. But I
really believe that, in the long run, Clojure's popularity will help Racket,
too - it's much smaller jump for people to make from Clojure to Racket than
from almost any language to Racket directly. So I really believe that Racket
will become successful some time in the future.

Meanwhile, the only thing I can do, is to comment on every Lisp article with
reference to Racket and continue hacking in it happily :)

~~~
takikawa
> Racket is really very complex thing with many man-decades of research behind
> it and even where it is being advertised, it looks like a typical "academic"
> thing.

As a fellow Racketeer, I'm curious: what do you think can be done to shake
this "academic" image?

~~~
minikomi
More people openly sharing good experiences of building stuff.

------
moron4hire
Racket also has a typed version. It seems to be the preferred version for many
of the more serious projects.

~~~
takikawa
It does[1], but it's not necessarily preferred. The whole point of Typed
Racket is that you can write your program in (untyped) Racket originally
(maybe starting out as a small script), and then add types if you need your
program to be more robust. Since typed and untyped modules interoperate
smoothly, you choose whatever combination works for your project.

That said, some libraries start out typed too (e.g., the Racket math
library[2]) and do benefit from type-driven optimizations and type-checking.

[1]: [http://docs.racket-lang.org/ts-guide/](http://docs.racket-lang.org/ts-
guide/) [2]: [http://docs.racket-lang.org/math/](http://docs.racket-
lang.org/math/)

------
TylerE
Admittedly, I'm only a novice at clojure (working on it), but I find that type
error really unfriendly. It doesn't provide any clearly actionable
information, something like "\+ expects it's 2nd argument to be Integer, but
internal-number is a Number"

~~~
boothead
Some terminology might help..

The domain is the inputs of a function and the range is the output. I assume
that if there were multiple arguments to the function it would git you the
other ones too.

BTW compare it to the haskell version of the same:

    
    
      > 1 + "1"
      No instance for (GHC.Num.Num [GHC.Types.Char])
      arising from a use of `GHC.Num.+'
      Possible fix:
      add an instance declaration for (GHC.Num.Num [GHC.Types.Char])
    

The shirt looks significantly less hairy in clojure :-)

~~~
kenko
The Haskell error is made more complicated by the fact that Num is a typeclass
that other types can participate in. Other type errors are more tractable:

    
    
        Prelude> 'a' : 'a'
        
        <interactive>:3:7:
            Couldn't match expected type `[Char]' with actual type `Char'
            In the second argument of `(:)', namely 'a'
            In the expression: 'a' : 'a'
            In an equation for `it': it = 'a' : 'a'

------
boothead
This looks really nice.

I've been wanting to get into clojure (mainly for the web aspect of it) for a
while, but as someone who hasn't done much lisp and really loves Haskell's
type system I've been hanging back a bit.

I'm assuming that clojurescript will also work fine with this?

~~~
kenko
This doesn't get you anything like the expressive power of Haskell's type
system, though.

~~~
swannodette
If you're going to make such a claim can you explain the capabilities,
tradeoffs, and limitations of Haskell's support for typed records over what
Typed Clojure provides?

~~~
kenko
No, I can't.

I claimed that Typed Clojure doesn't get you the expressive power of Haskell's
type system. I understand that Typed Clojure has some sophisticated facilities
for refining types, and (e.g.) tracking what keys a map has, and lots of other
neat stuff. This does not seem to me to increase the expressivity of the
language, whereas Haskell's type system does (well, since I don't think one
can separate Haskell into the language on the one hand and its type system on
the other, it's misleading to talk about Haskell's type system _increasing_
its expressivity). (I just about plotzed when I saw the definitions for `eval`
and `view` in <[http://okmij.org/ftp/tagless-
final/course/Intro2.hs>.](http://okmij.org/ftp/tagless-
final/course/Intro2.hs>.))

Everyone who writes a monad library for Clojure, for instance, has to take
special measures to implement a properly polymorphic "return", or punts and
doesn't do it. If Typed Clojure let me annotate `(return 5)` [ETA: or annotate
something dynamically enclosing `(return 5)` with something that would force
the given interpretation of the return call] with the type list of integer, or
maybe integer, or whatever, and have it actually evaluate to [5] or #<Just 5>,
or whatever, that would be great. AFAICT, it doesn't do that.

~~~
brandonbloom
> This does not seem to me to increase the expressivity of the language,
> whereas Haskell's type system does

Some of us don't view that as a wanted feature.

I wrote more optional/modular/pluggable type systems before. See:
[https://news.ycombinator.com/item?id=6196466](https://news.ycombinator.com/item?id=6196466)

> polymorphic "return", or punts and doesn't do it

Haskell's type inferencer is doing a "search" for a type that fits and
slotting that in there as an implicit argument. An error occurs if two match
and you need to provide a type signature to differentiate. You could do
precisely the same thing using first-class type objects, which Clojure has.
Haskell has _finally_ added them too, but required a compiler change. (See:
Typeable).

That's how type classes work in general: A dictionary of methods has to be
threaded through, unless the compiler can prove that the dictionary is a
constant. Clojure does the same thing by embedding a pointer to that data in
the first-class function object.

Let's assume I have a typed module. I could trivially _query_ that module,
since the type descriptions are _data_. That's what the Haskell compiler is
doing, but with first-class types, I can do it myself. There is no reason that
a _user-level macro_ can't add enough inference to perform polymorphic
returns.

But even if I _could_ do this, I wouldn't want to. At most, I'd want an
"infer-monad" form where I can _explicitly_ say that I'm using my types to
convey intent and am willing to pay the cost of coupling my program to a
particular type system.

~~~
kenko
> Some of us don't view that as a wanted feature.

No doubt! But someone who was describing his or her fondness for Haskell's
type system---the person I was initially responding to---probably does.

I'm familiar (though not intimately familiar) with the idea of dictionary
passing and with the fact that type classes are implemented that way. I'm not
sure what the point of the remark in the present context is, though. I gather
that you aren't a fan of the compiler doing this for you. (I'm not precisely
sure what you mean by first-class type objects here, given the mention of
Data.Typeable; concretely, I'm aware of three monad libraries that offer
something like a polymorphic return and I know how two of them do it; one with
symbol macros and regular old maps, and one that (in the presently released
version) uses a run-monad function that threads through a regular old map. You
presumably are thinking of something else; if so, I'd be glad to know what it
is.)

> There is no reason that a user-level macro can't add enough inference to
> perform polymorphic returns.

Well, if you say so. I can't quite picture how this would work as a macro, but
I'm not going to deny it can be done on that ground.

FWIW, regarding your last sentence, I find the idea of a program _separate
from_ a particular type system kind of hard to grasp.

------
graue
Forgive me for being the nitpicker on this article, but, dear author: check
the CSS font-family on your <pre> tags. You have some proportional fonts in
there, and for me on Linux, your code snippets are showing up as proportional
fonts which makes them not line up.

Specifically, I'd recommend changing

    
    
        font-family: Monaco,Bitstream Vera Sans Mono,Lucida Console,Terminal;
    

to

    
    
        font-family: Monaco,Bitstream Vera Sans Mono,Lucida Console,Terminal,monospace;
    

The "monospace" at the end is generic and will always produce a fixed-width
font.

Great post anyway, thanks!

~~~
logan-campbell
Fixed and pushed. Should appear on the site shortly. Thanks for the feedback.
I'll send a pull request to the template author as well.

------
auggierose
So, is this also working with Clojurescript?

~~~
logan-campbell
I haven't tried it myself but based on the twitter chatter between
@swannodette and @ambrosebs I believe so.

------
juskrey
Is not Rich Hickey in opposition to typing systems?
[http://www.reddit.com/r/programming/comments/lirke/simple_ma...](http://www.reddit.com/r/programming/comments/lirke/simple_made_easy_by_rich_hickey_video/c2u1t1n)

~~~
ddellacosta
Rich Hickey is generally pretty open to all different kinds of ideas and ways
of thinking, which becomes apparent once you watch his talks, listen to his
interviews, or read what he's written (other than tail ends to Reddit
threads).

[https://groups.google.com/d/msg/clojure-
dev/ryYzI1Bj0p0/uWdE...](https://groups.google.com/d/msg/clojure-
dev/ryYzI1Bj0p0/uWdEwO9T6xAJ)

~~~
juskrey
Thanks!

------
leishulang
a lein plugin that allows cf-ns all namespaces without firing up repl would be
awesome.

