[HN Gopher] Coalton: How to Have Our (Typed) Cake and (Safely) E... ___________________________________________________________________ Coalton: How to Have Our (Typed) Cake and (Safely) Eat It Too, in Common Lisp Author : reikonomusha Score : 85 points Date : 2021-09-10 17:42 UTC (5 hours ago) (HTM) web link (coalton-lang.github.io) (TXT) w3m dump (coalton-lang.github.io) | reikonomusha wrote: | In a lot of programming discussions, especially those about | Haskell or those about Lisp, types invariably come up as a | feature (or a bug). Some even suggest static or dynamic types are | most useful at different phases of a programming project. I've | heard opinions go every which way with types. By and large, the | concept of static types (to Haskell levels) and dynamic types (to | Smalltalk levels) are typically spoken about as a sort of | dichotomy. | | Many technologies have tested this dichotomy; even very | mainstream languages, like Python with type annotations and mypy. | However, what I've found consistently lacking is the | "completeness" of either side of the experience: either half- | baked dynamicism buried in a static language, or type annotations | that actually don't have predictable correctness guarantees-- | typically, type checkers in this arena are "best effort". | | Coalton doesn't try to paper over Lisp as a dynamic language, or | try to change the philosophy of Common Lisp code by restricting | it, but instead invites static features as a separate, well- | defined language within Lisp. Critically, though, inter-operation | between Lisp and this separate language must be essentially free. | | Coalton makes use of what many cite as Lisp's greatest power: the | ability to do seamless metaprogramming and syntactic abstraction. | Often, non-Lisp programmers, especially in forums like these, see | these features get used for programming party tricks. (I myself | have even engaged in such shenanigans, like saying macros are | good for making DSLs like (rpn 1 2 + 3 *) | ; RPN notation embedded in Lisp | | even though the next natural question would be, "Why would I ever | actually use that?") In that regard, Coalton is implemented | _actually_ as quite sophisticated Lisp macros, without any | separate (pre-)processors. | | Though it has some sharp edges as an alpha release, we think | Coalton satisfies the objective of bridging static and dynamic, | and opens a very interesting and relatively under-explored | paradigm of functional programming. | | Coalton isn't the first to explore this space in earnest. In | fact, Racket, Typed Racket, and Hackett have done so quite | successfully. There are several others, as well. | | Lastly--though I think it goes without saying--there's value in | having an _entire language_ buy in to some core ideas, because it | means library authors and application developers can have solid | expectations of one another. As a Lisp developer, though, I 've | witnessed that a clean, modular coding style allows many | programming styles to participate with one another without the | headache one might imagine. | didibus wrote: | How do you handle the boundary between typed and untyped? My | experience is that this is where things fail, because most code | you'll want to leverage will be untyped. | reikonomusha wrote: | Everything in Coalton is statically typed. The boundary | happens at the "COALTON:LISP" operator, which lets the | programmer drop into dynamically typed Common Lisp to perform | a computation. (This is how a lot of the standard library is | implemented.) If you decide to drop into Lisp with the | COALTON:LISP operator, then it's on you to supply a correct | type annotation for that Lisp code. (This type will be | checked at run-time.) So there's certainly room for human | mistakes with types. | | However, if you're really scared, you can do | (declaim (optimize (speed 0) (safety 3))) | | and really precise run-time type assertions will be inserted | into the code. | tempodox wrote: | A strong static type system that looks much like Haskell's but | coupled with eager evaluation, the ability to use macros, and | SBCL's excellent x86 compiler? That's too enticing to be passed | over. I'm using mostly OCaml now but I'll have to try out | Coalton, see how it feels and how it works out in practice. | I've been looking for an excuse to do more Lisp again anyway :) | reikonomusha wrote: | I hate to admit this but there are definitely still a few | sharp edges. (What happens when you re-define types across | compilation units? What happens when you want to back out of | a type definition?) With that said, our criterion for | shipping was "did it become useful for our purposes?" and the | answer is "yes." So, there's still work to be done, and we'd | love any feedback at all that would improve your experience. | tempodox wrote: | Thanks for the warning, I'll buckle up :) | | If I should happen to find a fix for any problem I might | run into, or come up with ideas for improvements, I'll be | happy to contribute. | 12thwonder wrote: | what is your view on Clojure.spec? | srcreigh wrote: | Last weekend, I put together a working sample for how to show | multiple syntax errors from Racket macros. It works in DrRacket | and racket-xp-mode. It's based on code from Typed Racket. | | https://gist.github.com/srcreigh/f341b2adaa0fe37c241fdf15f37... | | The well-documented Racket `raise-syntax-error` will let you | display 1 syntax error at a time. It works by throwing an | exception during macro expansion hence you only get 1 error in | your IDE. That code lets you highlight 2+ errors. | | Please build a type system in Racket! I would love to try it out. | | Helpfully, the above sample code is an example of how to store | compiler-level state. It works by storing errors in a list until | its parsed the whole module, then sends all the errors to an | error handler (which is not documented so well). Next, just wrap | all your base forms, rewrite module code for your type-checking | lifecycle, add some type declaration syntax, type inference, etc. | | I would love to have a language that can be used to define a | customized type system in Racket. | | Typed Racket is an incredible feat. Can you imagine adding an | entire type system without writing a separate compiler? Using all | the batteries included with your language? Even IDE tooling has | its own API to let you display any error messages you want? Not | to mention Go-To-Definition is built into the language. | | I don't like that Typed Racket enforces soundness with Runtime | checks, I much prefer TypeScript style checking. I also have run | into some pretty confusing messages ie [1]. Diversity is healthy, | it'd be nice to have a selection of static typechecking systems | to choose from for Racket. | | [1] https://github.com/racket/typed-racket/issues/1021 | Y_Y wrote: | I like this comment. | | What doesn't add up for me, is how you can do compile-time | type-checking in a language that has eval, or an equivalent | mechanism. As far as I can see (which is not very far at all), | you have to either give up the dynamic Lisp magic of producing | and running new code whenever you like, or you give up the | static Haskell magic of proving (some) correctness at compile | time by having a rigid exoskeleton which limits dynamism, but | peels of cleanly before you run the program. | srcreigh wrote: | That must be how DrRacket works, right? | | I've also dreamed of accessing Racket macro syntax errors at | runtime and building my own interface for viewing the errors. | For example, send sandboxed DSL code over an HTTP request, | and send any errors in a response to view them on a web | application. | | I also cannot see very far, but I suspect you could whip | something like this up using the error-display-handler [1] | parameter and the macro stepper [2]. | | Of course, there is a paper written about DrRacket (aka | DrScheme) which you can study [3] as well as DrRacket source | code [4]. | | [1]: https://docs.racket- | lang.org/reference/exns.html#%28def._%28... | | [2]: https://docs.racket-lang.org/macro-debugger/index.html | | [3]: https://www2.ccs.neu.edu/racket/pubs/jfp01-fcffksf.pdf | | [4]: https://github.com/racket/drracket | phoe-krk wrote: | In case of Coalton, you can simply hide the dynamic nature of | `eval` behind a black box, as you would with any other non- | coaltonesque Lisp-Object. | pritambaral wrote: | > ... how you can do compile-time type-checking in a language | that has eval, or an equivalent mechanism | | 1. `(compile ...)` and `(eval ...)` don't always have to | succeed. Both those routines can be hooked into in Lisp -- by | and within the running process -- and made to fail upon Type- | incorrect code. | | 2. In some Lisps, 'compile-time' and 'eval-time' are the same | thing. | | > ... you have to either give up the dynamic Lisp magic of | producing and running new code whenever you like, or you give | up the static Haskell magic of ... | | Note that even the "static" Haskell has a way to do "the | dynamic Lisp magic of producing and running new code | whenever". The simplest example would be a Haskell REPL. | | ---- | | The "dynamic Lisp magic" you refer to is actually very | simple: allow code to be replaced within a running process. | Lots of non-Lisps have it. E.g., Python, Ruby, JS, even Bash. | The part where Lisp truly shines, and what sets it apart from | those other languages, is that those languages gained this | feature merely as a side-effect of dynamism, but not from the | purpose of enabling a truly dynamic software construction | developer experience. From simple things like | `defvar`/`defonce`, `undefine-function`, etc. to fabulous and | complicated ones like `defclass`, conditions and restarts, | the interactive debugging support, etc. | | A good DX is a first-class feature in some Lisps. | Y_Y wrote: | Ok. I'm happy with compile-time eval failing in normal | Lisp, I might have given it an undefined symbol or | something. And in Haskell I can go "off the grid" and write | something dynamically typed, or even make a Lisp | interpreter. | | Can I have it both ways though? Can I get compile-time | guarantees, about non-trivial run-time generated code? It | feels like there's going to be a Halting Problem that stops | me having my cake and eating it too. | pritambaral wrote: | > Can I get compile-time guarantees, about non-trivial | run-time generated code? | | Sure. If you control all sites where run-time code gen | happens, you can gate them all on a compiler-check. In | fact, the former is just `cl:eval`; and in some Lisps, a | compiler-check already guards it. | dannyobrien wrote: | I've been watching Coalton since it was first mentioned by | @stylewarning -- possibly here. | | I'm really happy they had the time and dedication to bring it to | this point. I hope it gets the community it deserves! ___________________________________________________________________ (page generated 2021-09-10 23:00 UTC)