31 ms·
First look at Clojure.core.typed
- boothead 13y agoThis 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?
- dsabanin 13y agoYes.
- kenko 13y agoThis doesn't get you anything like the expressive power of Haskell's type system, though.
- swannodette 13y agoIf 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 13y agoNo, 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 13y ago> 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 13y ago> 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.
- deleted 13y ago[deleted]
- levosmetalo 13y agoFor 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 13y agoThe source code is quite surprising https://github.com/hraberg/shen.clj/blob/master/shen/src/prolog.shen#L231 https://github.com/hraberg/shen.clj/blob/master/shen/src/pro...
- vutekst 13y agoIndeed, I don't really understand how that could be code. Are those just cute symbols to match on?
- chrismonsanto 13y agoYes -- see https://github.com/hraberg/shen.clj/blob/master/shen/src/prolog.shen#L305 https://github.com/hraberg/shen.clj/blob/master/shen/src/pro... Upper case symbols are variables, as in Prolog. The others are literal matches.
- kenko 13y agoElsewhere in the file he matches on things like "rename the variables": https://github.com/hraberg/shen.clj/blob/master/shen/src/prolog.shen#L305-L327 https://github.com/hraberg/shen.clj/blob/master/shen/src/pro...
- saosebastiao 13y agoIt looks really cool! Shame about that license though...I can appreciate the fact that he wants a focused community, but he risks not having a community at all.
- 13y ago
- TylerE 13y agoAdmittedly, 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 13y agoSome 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 13y agoThe 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'
- FreeFull 13y agoThe reason behind that error message is how generic numbers are in Haskell by default. 1 + "1" will actually be working code if you make an instance for Num [Char].
- apaprocki 13y agoSince 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 13y agoOoh 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.
- moron4hire 13y agoRacket also has a typed version. It seems to be the preferred version for many of the more serious projects.
- swannodette 13y agoTyped Clojure is in fact based on the research behind Typed Racket.
- takikawa 13y agoIt 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/
- klibertp 13y agoDamn 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!
- coldtea 13y ago>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. I don't think will ever see a Lisp in the top 5 most popular rankings (assuming we can measure actual use, which, somewhat roughly, we can). In 2020 we'll probably see imperative and functional imperative hybrids topping the charts.
- adambard 13y agoThe top languages today all existed by 2000 -- many well before that. C: 1972 C++: 1983 Objective-C: 1983 Python: 1992 PHP: 1995 JavaScript: 1995 Ruby: 1995 Java: 1995 C#: 2000 Of course, these rankings are skewed by existing codebases, but they're still topping the charts. In 2020, the languages at the top of the charts will surely have existed in 2010 -- what imperative-functional hybrid that is presently in use did you have in mind?
- klibertp 13y agoI don't know what specific language GP had in mind, but I would guess it's probably one of OCaml, F# or Scala. Or Rust - did Rust development start before 2010? Or one of the Lisps, where Clojure has a bit "worse" (or better, depending on who you ask:)) imperative story than Racket (the latter has mutable lists and hashes, although they are discouraged and not the default), but both are on the way to popularity (I still believe in this) and have mixed, imperative, generic, OO (Racket) and functional features. Of course Common Lisp, which is similarly a mix of paradigms, has a chance to make a great comeback too. There's also JavaScript, especially with improvements it's going to get in the future, but this damned thing already is 9th according to TIOBE[1]. Honestly, I wouldn't be very disappointed if it proved to be like this. All these languages are interesting and in 7 years time could become true "languages of the future". However, despite hoping so, I don't believe it will come true. Most probably some languages will just move slightly higher with others moving a bit lower. Java will remain in the first 3, at the very least. Maybe Objective-C will swap places with C++ and Python or Ruby (or both) will come close to PHP. That's all I think will change, although it would be nice to see Rust instead of C++ for example or Clojure in place of (Visual) Basic. Also, all the currently most popular languages are this young because there was a major paradigm shift from imperative to OO in the '80. Without similar shift happening sometime soon we will be stuck with Java not just to 2020, but to 2040 at least :) [1] http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html http://www.tiobe.com/index.php/content/paperinfo/tpci/index....
- auggierose 13y agoSo, is this also working with Clojurescript?
- logan-campbell 13y agoI haven't tried it myself but based on the twitter chatter between @swannodette and @ambrosebs I believe so.
- ryanbrush 13y agoA 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?
- JackMorgan 13y agoI wanted something like this, so I made this library that checks annotations at run time rather than compile time. https://github.com/steveshogren/deft https://github.com/steveshogren/deft Right now it only checks maps for a set of keys, the idea longer term is to add in other types of "type" checking. I haven't yet tied into the built-in annotations, just because those didn't appear to check anything but valid Java types. If it is possible, I would be really interested in seeing how.
- swannodette 13y agoClojure'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 13y agoAh, I didn't realize that core.typed could deal with rich data structures like that. That's actually pretty amazing.
- graue 13y agoForgive 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 13y agoFixed and pushed. Should appear on the site shortly. Thanks for the feedback. I'll send a pull request to the template author as well.
- juskrey 13y agoIs not Rich Hickey in opposition to typing systems? http://www.reddit.com/r/programming/comments/lirke/simple_made_easy_by_rich_hickey_video/c2u1t1n http://www.reddit.com/r/programming/comments/lirke/simple_ma...
- ddellacosta 13y agoRich 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/uWdEwO9T6xAJ https://groups.google.com/d/msg/clojure-dev/ryYzI1Bj0p0/uWdE...
- juskrey 13y agoThanks!
- leishulang 13y agoa lein plugin that allows cf-ns all namespaces without firing up repl would be awesome.