7 ms·
Clojure is a decent language. I've worked with it a good deal in work. But I would take any functional language with types over it. Some thoughts: - its dynami
by keithasaurus 7y ago
Clojure is a decent language. I've worked with it a good deal in work. But I would take any functional language with types over it. Some thoughts:
- its dynamic-ness makes it not suited to large projects IMO.
- The currently-accepted attempt at a non-type-system-type-system 'spec' is not well thought out.
- Biggest wins I think are immutability by default, homoiconicity, and "simplicity"
- The repl is fine but can be a crutch -- see startup time
- Clojure still hasn't really found a niche, and I'm not sure I would ever reach for it first unless I was integrating with other clojure projects.
- tekacs 7y ago> The currently-accepted attempt at a non-type-system-type-system 'spec' is not well thought out. This is quite possibly true (I certainly have my complaints with it), but I'd love to hear more about what you're getting at when you say this.
- keithasaurus 7y agoWhen the canonical example of an fdef takes significantly more code than the function itself, I think you have to re-evaluate your approach: https://clojure.org/guides/spec#_specing_functions https://clojure.org/guides/spec#_specing_functions
- tekacs 7y agoHm the /amount/ of syntax isn't something I think about I guess, this being a Lisp. I think they've taken the approach of making the system thorough and capable and this being a Lisp, we users can skin it with any level of shorthand that we please. Datomic (the database from Cognitect) has a similar approach with its schema, which is quite verbose, but many folks that I know tend to simply write a function which spits out the schema, since it's 'just data'. I tend to use Ghostwheel [1], but there's also spec-tools [2] and others. [1]: https://github.com/gnl/ghostwheel https://github.com/gnl/ghostwheel [2]: https://github.com/metosin/spec-tools https://github.com/metosin/spec-tools
- keithasaurus 7y agoSchema is a pretty nice alternative: https://github.com/plumatic/schema https://github.com/plumatic/schema
- peferron 7y agospec.alpha is implemented as a bunch of macros that make it difficult to work with spec forms as if they were "just data". This is one of the issues that spec-alpha2 tries to address [1]. Sure, you can import libraries to try to work around it but that's hardly ideal. [1] https://github.com/clojure/spec-alpha2/wiki/Differences-from-spec.alpha https://github.com/clojure/spec-alpha2/wiki/Differences-from...
- andreareina 7y agoThat seems an unfair criticism, most of that fdef is about the relationship between the args/return as opposed to just the types.
- keithasaurus 7y agoPerhaps. I was trying to find something pithy to give as an example. It's difficult to express all the things about spec that feel off to me. I'll try to enumerate some: - verbosity (I think schema does this better https://github.com/plumatic/schema https://github.com/plumatic/schema) - because it tries to not be about validating types of inputs and outputs, and rather treats everything as predicates, it tends to live in a grey area between type validation and unit-testing, and I think hinders both - it only helps at run-time - over-abundance of macro-usage
- thom 7y agoI suspect what you consider a 'gray area', spec considers the whole point. What is the specification for this function? It has a signature and a return value and some behaviour in between. Spec verifies all of that together because it doesn't make sense to do it separately. Also I'm not sure what you mean about run-time, you can and should use spec as part of your development workflow. Other two points are totally fair though, and the macros are part of the reason it never got out of alpha before a rewrite.
- andreareina 7y agoFair. I find that I naturally tend to think about the general properties of argument/return values and not just the types (or perhaps equivalently expand the meaning of "type" to encompass such usage) so I like the design, but I haven't yet used spec in anger and so might yet change my mind.
- cageface 7y agoA few years ago I started a personal project of implementing a lot of common ML algorithms in both Scala and Clojure simultaneously. I expected Clojure to be a much better fit for this sort of thing but I was surprised to find that as my codebase grew in both languages the Scala code was much, much easier to understand and maintain. There were a lot of time consuming bugs in my Clojure code that were prevented by Scala’s strong typing. I was able to refactor with a lot more confidence in Scala. So as much as I like some things in Clojure I’d pick a statically typed language for most jobs now.
- mercer 7y agoI'm honestly curious how Clojure + spec would fare in that comparison. Is that something you looked into? The reason I'm asking is that while my background the typical webdev stuff, I loved TypeScript. And now while much of my work is backend, it's similarly dynamically typed by default because I'm using Elixir. I've dabbled in Elixir's 'spec' and quite enjoyed some of the benefits Dialyzer/Dialixir offered me, but not enough to be able to tell how beneficial it is in my projects. More than once I've run into issues that I wouldn't have had if I had used specs, but the fact that it's not a default in the community gives me pause. It makes me wonder how much advantages 'gradual typing' actually give me over just being a more thoughtful developer.
- cageface 7y agoIt’s an interesting question. I’m not sure if the spec stuff existed at that time. Like you say I think it makes a big difference if it’s built into the language. Typescript is a lot more useful now that it’s become so mainstream, for example.
- _bxg1 7y agoThis is the best argument I've seen for dynamic types beyond the prototyping phase: https://lispcast.com/clojure-and-types/ https://lispcast.com/clojure-and-types/ Not sure if I'm 100% won over, but it makes some good points, particularly the section about JSON. Using TypeScript at work, one of the most painful (yet quite common) things is dealing with foreign JSON from an API, which is sometimes just... wrong. And then all our carefully-typed code falls to pieces. The argument is that in a language like Clojure you can skip pretending to make guarantees about messy external data, and trade that for simple and elegant traversal code. Probably this is why dynamic languages tend to gravitate towards "glue code" use cases while static types gravitate towards insulated systems with minimal surface area to the outside world (compilers, anyone?).
- hombre_fatal 7y agoI don't understand your JSON example. If your Clojure code expects an API response's json.{id,username} to exist, and to not exist is a hard violation of your assumptions and thus your code, then how is that any different from formalizing it into: struct UserResponse { id: Int username: String } ? (Damn, rest of the comment is pretty long but let me try to both sympathize and present a solution that I do like). What I will say is that most statically-typed languages I've used do get annoying when there's no 1:1 mapping between your struct and the JSON body. I've dealt with some hairy JSON, like APIs that accidentally re-encode nested JSON strings so they become like triple escaped. Or values that can have like 5 different shapes so you really just want to try 5 different decoders until one is successful. (These cases are trivial in my upcoming code example) For example, Rust's serde and Swift's Codeable are both pretty annoying if you want to apply arbitrary transformations between JSON response -> your canonical representation. Serde, for example, has you implementing the whole trait + visitor if you need to do anything the cute decorator DSL doesn't support. Or you implement a bunch of envelope structs even though you just want to pull out the value at "obj.a.b.c". Or, what happens when you can't simply use a codec from https://app.quicktype.io/ https://app.quicktype.io/? JSON decoder combinators (e.g. in Elm) make JSON pleasant again while retaining static typing. https://package.elm-lang.org/packages/elm/json/latest/Json-Decode https://package.elm-lang.org/packages/elm/json/latest/Json-D... For example, data Role = Member | Admin data User { id: Int username: String role: Role } -- API example body: { id: "12", role: "a", info: { uname: "foo" } } decodeUser : D.Decoder<User> decodeUser = User -- Crappy API gives us either an int or a string (D.field "id" (D.oneOf [ D.int, D.string |> D.map parseInt ])) -- Pull out value from nested object (D.fieldAt [ "info", "uname" ] D.string) -- Arbitrarily transform value and create our own error (D.field "role" D.string (D.andThen (\value -> case value of "m" -> Ok Role.Member "a" -> Ok Role.Admin unknown -> Err ("Unexpected role " ++ unknown)))) That almost compiles despite shooting from the hip directly into this textarea, but the point is that it's a pleasant way to decode JSON while allowing arbitrary transformation, and I think it's the ultimate middleground between static-typing and the sort of on-the-fly decision making you get with dynamic typing.
- didibus 7y agoDifferent strokes for different folks I guess. I would take it over Haskell and Scala any day. Haven't tried F# or OCaml so can't comment. > its dynamic-ness makes it not suited to large projects IMO I'm really curious about this. I work on large Clojure projects, and it works quite well. We have low defects, and quick pace of change. Maybe we're just lucky, touch wood. > The currently-accepted attempt at a non-type-system-type-system 'spec' is not well thought out If you thought of it as a type system, than it's normal you think it's not well thought out. It isn't trying to be a type system at all. It's a contract system. The goal is validation, generative testing, formal spec, parsing, etc. Not type checking. Even given a strong static type system I would still use it. > The repl is fine but can be a crutch -- see startup time Heard about this from some people, but dunno, it never affected me. I start my REPL at most once a day, often less then that. So waiting a few seconds doesn't bother me. > Clojure still hasn't really found a niche That's true. That said, it is great at everything. While it didn't find itself a popular sub-domain to dominate, I now choose it for everything. There's barely anything it can't handle well. I reach for it for everything nowadays. That's actually something I really like about it, it has huge reach.
- mercer 7y agoI went pretty much all-in for Elixir/Erlang, but I do have to admit Clojure has always been a close second, and I envy the amount of interop it has. There's various initiatives in the Elixir ecosystem, and tools such as LiveView make my life a ton easier, but man do I wish I could use Elixir as flexibly as Clojure!
- iLemming 7y ago> But I would take any functional language with types over it Which is what? Scala? Haskell? OCaml? F#? Typed Racket? Idris? I think Clojure really hits the sweet-spot between stupidly boring, "pragmatic" PLs and idealistic, novelty, academic ones. Haskell is awesome, but realistically it is really difficult to quickly train someone to the point of them being able to write production-ready Haskell code. Scala has its own warts (besides, Kotlin seems to be slowly eating its pie); OCaml still struggling to get any recognition in the industry, despite all attempts from a few good players. I disagree with the notion that Clojure is not suited for large projects, but I agree that Spec needs to be improved (and it is being actively worked on). And I don't think it is not well thought out. Rich Hickey is famous for slowly, carefully, patiently designing things. Stability and predictability of Clojure and its standard library is phenomenal. For every single language (even those I have never worked with) I can probably name a thing or two off my head that involved breaking changes. Not for Clojure.
- kamaal 7y ago>>Which is what? Scala? Haskell? OCaml? F#? Typed Racket? Idris? These days Java works just fine. Why use anything else, that neither has dev mind share nor the library ecosystem remotely the same as Java.
- kaoD 7y agoThe library ecosystem is accessible from the many compile-to-JVM languages. Half tongue-in-cheek, half serious: not having Java's dev mindshare is actually a benefit.
- ilikehurdles 7y agoWhen I was last at a Java shop, we we're pushing multiple-thousand line PRs every week, a good 40% of those lines were simply boilerplate generated by IntelliJ, and it would have been worse had we not fully instrumented our codebase with Lombok, and all this was for a pretty mature product that wasn't seeing any radical changes from day to day. At the clojure shops I've worked at since, we're building more and delivering product faster, and we're doing it on the JVM with less than a third of the lines of code. A thousand lines of clojure is a significant refactor or a big feature. A thousand lines of java is wednesday. For me, clojure provides a simple-to-use API, high-level functional abstractions, an instantaneous feedback loop, access to a battle-tested JVM ecosystem, and more than fast enough performance. This is its killer combination, in my opinion. The immutable-by-default data structures are also an underappreciated boon for writing multi-threaded code without breaking a sweat.
- Random_ernest 7y ago> Clojure still hasn't really found a niche To me Clojures sweet spot was dynamically typed, functional programming with an extremely large ecosystem of tools (due to Java interop).
- emmanueloga_ 7y agoIf you are the type of person that fiddles with Clojure and other functional languages (OCaml? Haskell? Scala?) I'm pretty sure you will change your mind on and off, in different seasons of your career :-) I used to think only languages with good algebraic data types (mostly ML based) where worth my time... but types can really become an straight jacket in large programs and become tedious to use. Clojure code, when reasonably well structured, is not a bad solution for a lot of varied problems, and is beyond great for personal projects! Low ceremony, fast feedback loop, excellent performance, and a huge library of ready made quality libraries.
- cutler 7y agoQuality libraries built with a statically typed object -oriented language designed with mutability in mind. See the puroblem?
- emmanueloga_ 7y agoHonestly I don't think it is a problem. There's no perfect software and neither type systems nor immutability guarantee correctness. Clojure offers best of both worlds by minimizing and managing mutability.
- logistark 7y agoHaving to maintain more than 42k LOC of Clojure code, couldn't agree more. I would also add that i haven't see a community with such fanatism towards a language and his creator. So i always avoid to interact with the community.
- iLemming 7y agoI don't know what you're talking about. I've personally worked in several language ecosystems and interacted with members of their communities. Clojure people are one of the most humble, reasonable, friendliest people in the entire CS universe. Many of them have years of experience working in different languages, they are not fanatics, most of the time their answer to the question "is that the right way?" would be "it depends." While devs in other programming language ecosystems either debate endlessly or rush to add more "features" to their language, Clojurists quietly make things that simply work. If you follow the trends and dig a bit more, you realize that Lispers carefully filter out good ideas and put them into use, they know how to tell the difference between a brilliant idea and a fad. And there's no fanaticism and worshiping of language creator. Only respect and gratitude.
- dgb23 7y ago> - its dynamic-ness makes it not suited to large projects IMO. Clojure can be typed https://github.com/clojure/core.typed/ https://github.com/clojure/core.typed/ > - The currently-accepted attempt at a non-type-system-type-system 'spec' is not well thought out. Spec is not a substitute for a type system. It is much more loose, since you are just dealing with implicit predicates but at the same time it is more powerful than what mainstream type systems give you in terms of validity and expressiveness.
- slifin 7y agoLooking at spec through the lens of a type system is weird because types solve two problems where spec solves one problem So I'm getting the most leverage out of types when they solve: - Static analysis - Specification Clojure's static analysis tools do use type analysis since the underlying primitives are typed, but a good static analysis does type analysis and other checks so types aren't the be all end all there Personally I love types that track to primitives, the problem with types is when you let developers use them to express their Person class or whatever, your Person class is not as fundamental as that language's int class, for example, the semantics are completely different between your primitive types but not your Person class vs your Address class Anyway what I wanted to say is I have yet to meet a type system where I'm happy with its specification capabilities without moving into slot based programming or so much syntax for the types it becomes overhead to read them, i.e. you can write complete programs in TypeScript's Type system Spec is the competitor for type specification here, it is much nicer you can translate it into API, GraphQL, Database schema etc but it's still getting work for Spec 2, but you can generate data examples out of it, use it for generative testing, use it for parsing, validation etc It's good if you have that specification problem The static analysis problem is getting more and more attention through clj-kondo and inbuilt analysis via Cursive and other editors, they'll stop you from doing things like (inc "hello world") because you can't increment a string, I think this feature is actually why people love types so much the fast feedback for silly mistakes is soo good and we have that too What we didn't do was complect those two things together, it also doesn't mar our thinking, I don't think there's an end game where a sufficiently smart type system where it eliminates all programming problems, the next breakthroughs will be in data, not types see the whole area of deep learning, but we're so in love with types we create languages where their USP in the type system
- slifin 7y agoWhen you get heavy into types you start seeing the world through the lens of types and that's super dangerous for the same reasons that trying to view the entire world as SQL tables is not a maintainable solution, the problem isn't reality it's your lense that you're viewing the world with Also please don't read this as types are bad that's not what I'm saying, or tables are bad, letting them consume your thinking means you start thinking about problems in terms of typed or not typed boolean thinking instead of nuanced, since nearly all languages are "typed" at runtime and static analysis time anyway
- yogthos 7y agoI would pick Clojure over any statically typed FP language simply because it's far easier to train news devs for it. My team has been using it for close to a decade now, and we've only hired a single person who already knew Clojure during that time. All the other devs came from mainstream languages, and we were able to get them productive within a couple of weeks on average. There's simply no way I could teach a language like Haskell in a couple of weeks and get somebody reasonably productive with it. Monolithic design is not a good way to structure projects IMO. Any large project can, and should, be broken down into small isolated components that can be reasoned about independently. Not only does this reduce overall mental overhead of understanding the code, but it also makes it much easier to reuse. Static typing appears to encourage coupling via the types due to them having to be expressed globally. So, it naturally steers the design towards building a monolith. I also completely disagree that the REPL is some kind of a crutch. It's a strictly superior workflow because it allows you to put the application in a particular state, and then develop functionality against the state you've already built up. Having to restart the application, and rebuild that state every time you make a change is simply not a productive way to spend your time. Finally, the claim that Clojure hasn't found a niche is pretty weird seeing how it's used all over the place by companies big and small. It's certainly found a very nice niche in space of web development and companies like Apple, Atlassian, and Walmart are getting pretty good mileage from it.
- lenkite 7y agoClojure Spec is what C++ 20 concepts is evolving to! The idea that there are no truly fixed types but concepts with constraints and requirements. This is around 5/10 years ahead of its time in the general PL community.
- amw-zero 7y agoInteresting perspective on spec since I find it very well designed, very consistent, and very useful. What isn’t well thought out about it?