13 ms·
> "With function specs, I could assert the inputs and outputs to specific functions at runtime; not quite as reassuring as compile-time but better than nothing!
by sdegutis 8y ago
> "With function specs, I could assert the inputs and outputs to specific functions at runtime; not quite as reassuring as compile-time but better than nothing!"
For 10 years I've seen many developers who have only really used statically typed languages like C++ and Java, now finding the "joy" of dynamically typed languages and all the "freedom" they give you, only to realize within a year or two that, wait a second, verifying those types at compile-time was actually pretty useful. I've also seen mass migrations away from languages like Ruby, Python and Clojure towards OCaml, Haskell, and F#, because people really enjoyed the functional programming aspects but wanted their static typing back. After writing the 10,000th unit test that only exists to ensure that you spelled some function/method/hash-key correctly, it gets a little tiresome. Personally I've settled on TypeScript, which has nice IDE integration in VS Code so that it validates your types live while you code, while still giving you the flexibility of a dynamically typed language (it's all still JS at the end of the day).
- juskrey 8y agoSpecs in Clojure are not static typing. Satisfying spec does not mean a value contains only items described in spec. It is an implementation of much larger messaging idea - message receiver only takes care about data it needs and nothing else.
- rafd 8y agoYou can use spec to verify that "a value only contains items described in the spec". The core library doesn't make it easy, but spell-spec [1] contains some useful helpers. [1] https://github.com/bhauman/spell-spec https://github.com/bhauman/spell-spec
- specialp 8y agoRich Hickey said himself "If a delivery truck comes to deliver a TV to you, do you care what else is on the truck? No, you are only interested in your TV" so the messaging idea is exactly right, and they weren't looking to implement strict static typing. In Clojure, Rich Hickey and others like Stuart Holloway speak of not "complecting" your code by passing around data structures other than uniquely namespaced maps where functions can take what they need and operate on it without having to mess with anything but data. so instead of data Person = Person String String Int Float String String you have :person/name .... By using simple data structures you can work on it much easier and even interoperate with other services and languages, while still checking to see if the data you are fed conforms to what you need.
- wild_preference 8y agoYou can get that with ad hoc structural typing or by transforming into canonical nominal types that contain the subset of data you care about. I’m not sure what these comments are arguing against, but it’s not compile-time analysis.
- lvh 8y agoThank $DEITY that impracticality is always because you’re not using the One True Static Typing School! Seriously: you’re describing a feature that only exists in a small subset of languages and only a small subset of contexts at that. OCaml and Go do this but not for data, just for methods.
- wild_preference 8y agoAbsolutely. I wish I could structurally type a db query function in every statically typed language that doesn’t have it. Instead I end up with the challenge of coming up with the smallest number of (what is essentially) User1, User2, etc. and hope my queries and subqueries can always fill one of the existing shapes which is a significant pain point. The language used in this thread so far has made it seem like runtime specs are the solution to this “only care about part of the bag” problem rather than what I consider a general feature of dynamic typing that certainly can be expressed statically as well.
- lvh 8y agoI can’t parse that second paragraph. Can you elaborate? Runtime specs absolutely also validate. And you can run them at test time. And you can put arbitrary code in them if you want. And sometimes you can statically validate them. What I’m saying is you get ad hoc structural typing on everything, including data, which you’ve already said is a good thing, so I’m not sure what we’re arguing about or if we’re even arguing :) EDIT: ah! You edited the paragraph. Simple question: where does this ideal static typing thing ship? Like, how do I do this on my machine right now? What package do I install?
- cube2222 8y agoDoes anybody know, or (better) have any source on, how common typescript is on the backend?
- boubiyeah 8y agoReally like typescript. You could code an algorithm with very dynamic types then expose its API with very rigorous typing (TS's type system can go pretty advanced)
- mhluongo 8y agoWould love to see some evidence of a mass migration to Haskell... anywhere.
- CodeArtisan 8y agoWould not call that a massive migration but https://code.facebook.com/security/fighting-spam-with-haskell/ https://code.facebook.com/security/fighting-spam-with-haskel...
- specialp 8y agoThis is a problem that is very well suited to Haskell, but it is a subset of general software development. Here it is worth the substantial overhead of making robust types as independent teams are making rules for this spam filter and typing can enforce order on it as they are probably easily categorized by type of filter. There are also quant developers that enjoy writing in OCaml, and AI devs too. But besides major companies using statically typed languages for some special teams, we don't see companies switching wholesale. Like I am still waiting for the day where $bigCompany says "we have hit so many type problems in our code that we have decided switching to staticaly typed languages will save us tons of money and are now requiring it".
- moocowtruck 8y agothere is no such thing so don't get your expectations too high
- deleted 8y ago[deleted]
- platz 8y agoIt is really not in a companies interest to ever do a 'mass migration' excepting dire or takeover situatuons
- mhluongo 8y agoI meant in the industry (as I believe the original comment intended), not one large enterprise migrating.
- azureus 8y agoOnce a month, I find myself wishing Clojure had static typing, before dramatically rediscovering why not having it can be such a relief. The reasons for this are expressed much more articulately than I can here: https://lispcast.com/clojure-and-types/ https://lispcast.com/clojure-and-types/ ... Personally, for the kind of work that I do, I've found clojure's compromises make my life on net easier, and my code, on net, more reasonable.
- bunderbunder 8y agoI'm finding that it really depends heavily on the domain I'm working in. Roughly speaking, if I'm dealing with "business problems" type stuff - grab data, report on it, keep a paper trail, etc. - I want a very loose and flexible programming environment with a REPL and dynamic typing and all that. Python, for example, treats me very well when I'm wearing my "data scientist" hat. OTOH, when I'm doing more "building infrastructure" type work - implementing a data store, writing a compiler or interpreter, stuff like that - I start getting more interested in rigidity and formality. Static languages treat me well in these situations. The more rigid, the better - I'll prefer Scala to Java, for example, specifically because it gives me more tools in the type safety department.
- mclehman 8y agoI pretty much agree with this. When I can get away with it performance-wise (and I almost always can for personal projects), this is a big part of why I love using Perl 6. I recently rolled my own (verg tiny and specific) build/deployment system for a project. Initial prototyping was a breeze, but once I had things more or less nailed down, I started adding types and, for example, was able to leverage the type system to make sure it can only be deployed listening on IP addresses in the private range. The next step is to rig up some sort of Zerotier integration to ensure I only make it available on my private networks.
- azureus 8y agoYes, I wish I had clarified that I meant that Clojure beats everything else for me for what you describe as "business problems" and also, I've found it quite enjoyable for writing simple games. Im afraid im not well qualified to comment on what "infrastucture" type work would be like, and what is best for it.
- ff_ 8y agoThis, so much. After some 3y spend doing Clojure, having a Haskell codebase is a totally new level of stress reduction. All of a sudden you can stop worrying about "what am I getting in this function" and actually concentrate on the business logic. (No, tests can't cover everything. Yes, we still have to write tests, but now they are all useful. Yes, the thing that types hinder you from doing changes is a lie, it's quite the opposite, much easier to change things since the compiler covers your ass)
- yawn 8y ago> All of a sudden you can stop worrying about "what am I getting in this function" I think what you're saying is something like "this function guarantees that the thing I get here conforms to this shape" and I do like that. Something I don't see a lot of people express is just the simple "I know what the shape of this thing is by looking at the signature". Whenever I find myself reading other people's Clojure code, it feels like I end up spending more time figuring out what is being passed into a function. I have to look around to find the origin of a value. I wanted to tear my hair out the first time I tried reading Ring's source code to figure out how it worked. With F#'s Suave, I can tell what each function gets and what can be done to it--it's self documenting. In Clojure, as the original author you can play around in the repl full steam ahead because the types are in your head at the moment. Those who come after you have to assemble the puzzle with less information.
- gered 8y agoI think you've really summed it up well here. I have exactly the same concerns when looking at large Clojure codebases. It gets incredibly tiresome having to do this and even at the end of it, I'm still left with that nagging doubt "did I miss some detail somewhere?". Hopefully there's a unit test or three to help put my mind at ease but, at least at my work, that's really quite the pipe dream.
- StreamBright 8y agoI usually put these into the description so the next engineer has a better time tracking what is what.
- truculent 8y agoI had a similar experience when I spent some time playing with Elm. Being able to (re)model the structure of your program and have the compiler verify it is absolutely freeing.
- ken 8y ago> After writing the 10,000th unit test that only exists to ensure that you spelled some function/method/hash-key correctly, it gets a little tiresome. One of the things I liked about Lisp when I first saw it was that, since it uses kebab-case and (usually) full words, I could just turn on my standard English spell-checker. After all, if "Programs must be written for people to read, and only incidentally for machines to execute", then it makes sense that I should be able to run a normal spell-checker on them. The only reason that was infeasible in languages like C is because they used (for both technical and cultural reasons) names like "memcpy". Whatever field I'm working in already has its own vocabulary. I don't need to create a new <domain>-in-<proglang> vocabulary for a program about it. Or if I do invent an abbreviation that's so useful that I want to use it in my source code, I'll probably also want to use it in my documentation, webpage, emails, etc. Most large programs that I've worked on, in other languages, have at least a couple misspellings in func/var names, which even Haskell's type system won't catch. "Referer" was clearly not written by someone with their spell-checker turned on for source code!
- rasteau 8y ago> finding the "joy" of dynamically typed languages and all the "freedom" they give you ... verifying those types at compile-time was actually pretty useful Indeed one of the "joys" is that one can apply that verification a la carte, and possibly with different approaches [0][1]. Similarly, one may desire to dispatch on type [2], but might sometimes want arbitrary dispatch [3]. > After writing the 10,000th unit test that only exists to ensure that you spelled some function/method/hash-key correctly This too is solvable with a la carte tooling [4]. Everything has trade-offs. Static type systems give a fixed set of benefits with a fixed set of costs. Some folks prefer to use their experience and judgement to choose when, where, and how to pay the associated costs (and that freedom of choice itself has a cost). It's okay to have different preferences. [0] https://github.com/plumatic/schema https://github.com/plumatic/schema [1] https://clojure.org/guides/spec#_entity_maps https://clojure.org/guides/spec#_entity_maps [2] https://clojure.org/reference/protocols https://clojure.org/reference/protocols [3] https://clojure.org/reference/multimethods https://clojure.org/reference/multimethods [4] https://clojure.org/guides/spec#_testing https://clojure.org/guides/spec#_testing
- specialp 8y agoI think the happy medium is a dynamically typed language that gives you the option to type or at least have a way to check typing. Java isn't really statically typed at all since it is necessary to cast in daily usage. Truly statically typed languages seem really cool but you don't see a lot of major things being done with them due to the overhead. I have found too that when you are programming functionally as in Clojure you don't get bit by shared state things not being what you expected as everything you are writing should be data in, data out and easily reasoned upon. Nil handling in Ruby is a huge problem as people are chaining methods on mutable objects which becomes complicated, and this is the source of most bugs in Ruby. This is somewhat mitigated lately with safe navigation operator. That is why TypeScript is appealing as you can use typing when you'd like to but still take in "unsafe" JavaScript or write in JS. Unlike if you were using Elm and had to write a wrapper to conform all JS code coming in. Spec in Clojure is very nice as you can enforce and conform types where you need it, and also make generative testing for free. If there is some mass migration to OCaml, F# and Haskell I have yet to see it because very few businesses use them and the communities are much smaller.
- gered 8y agoI can't say I've personally seen any evidence of "mass migrations" of this sort. However, I can personally say that after 5 years of working with Clojure and with several large Clojure projects under my belt now, I'm starting to realize that I'm losing confidence in using Clojure for large projects and the feeling of how _maintainable_ that project will be in 2-3 years or longer. I think Clojure hits a sweet spot (at least for _me_ personally) for small-to-medium sized projects though, and I don't hesitate to use it in those cases. The thing I've _consistently_ seen with every large Clojure project I've been a part of is that as the project grows, it becomes harder to make sense of the types used across the project. On the last couple of these projects I've been on, Schema was used to annotate many types, but I've never found this to be quite enough and I still remain firmly convinced that gradual typing just doesn't work well enough generally. I think that with a _very_ highly disciplined team of experienced developers it could probably work well. But we don't all get the opportunity to work on such teams. What I've personally seen is developers are all too willing to either add a partial type spec/annotation (with probably some s/Any's lazily thrown in where there should most definitely _not_ be), or just flat out not adding any at all ("Why would I need it here? This is very simple code and it's obvious what is happening here!" ... sure, maybe it is to you now... what about in 6 months? Or what about to one of the other developers on the team?). When discussions about static vs dynamic typing come up (which is almost always a fruitless argument in my experience), I've noticed that the dynamic typing proponents tend to get overly focused on the idea of "correctness". This has always disappointed me. Correctness is certainly an important benefit (though I think it tends to get exaggerated a bit much), however, to me the idea of documentation in the form of a type definition is _much_ more valuable over the longer term, especially so for large projects. Developers are usually quite lazy, especially when it comes to documentation, and often when looking at a piece of code in a large code base, the only documentation about the values being passed to a function is going to be the type definitions (aside from reading the code itself of course).
- StreamBright 8y agoEvery programming environment has down sides and Clojure is no exception but I still think that using Java instead of Clojure is not better because simply you have 3-10x source code to maintain and the number of bugs are correlating with number of source code lines. On the top, I quite often see null pointer exceptions in Java when in theory that should never happen because we are statically typed.
- moocowtruck 8y agodoes typescript allow you to write less tests? does it have dependent typing abilities? or something like clojure spec? to me it looks more like a slightly better java but you still need to write lots of tests
- typon 8y agoAlmost every week a new article comes out from some language index saying Python is at the top or overtaking Java or C or C++ to reach the top very soon. I think the evidence is simply not there to support that static typing is somehow preferred by the average programmer.
- bunderbunder 8y agoI think there's a very good chance that this is being driven by the flap around data science, and the big push toward teaching programming in schools. Assuming, for the sake of argument, that that's true, it means that we've got to think about how you define "programmer". Does it mean anyone who uses a programming language, or does it mean something more like "software developer"? TIOBE, for example, effectively measures popularity among the former group, not the latter. Which means that, in the case of Python popularity, it's going to be picking up on all sorts of things: analysts and BI people ditching Excel in favor of Pandas, ops people ditching piles of shell scripts in favor of Ansible, graduate students choosing Scipy over R or Matlab, 7th graders with Pi-tops, ...
- hota_mazi 8y agoThere is plenty of evidence showing that the trend is toward static typing, not dynamic typing. Starting with the fact that most dynamically typed languages today are progressively adding type checking (spec in Clojure, gradual typing in Groovy, etc...) while no statically typed languages are going the opposite way.
- vorg 8y agoWhen statically typed language C# added a dynamic keyword, that's a language going the other way. Of course, no-one actually thinks of C# as being a dynamically typed language despite its dynamic keyword, nor does anyone think of Apache Groovy as being a statically typed language despite its @CompileStatic annotation. In the everyday world, C# is used for building performant systems and Groovy is used for glue code and build scripts. Languages tend to go both ways nowadays. Go first shipped with both static typing (though without generics except for builtin datatypes) and dynamic typing (though using the wordy interface{} marker).
- lvh 8y agoFunction specs are not about compile time verification: they are about checking that the object you got is the thing you thought you had. Compile time verification is nice if you know all your inputs at compile time. At some point, data from elsewhere comes in. Specs are an answer that preserve the composability goals of Clojure (who cares if I have a bit more information than I strictly need?) and allow you to validate them at runtime with useful error messages. spec has incidentally made Clojure's compile term error messages better! But that's just because one program's compile time is the compiler's runtime, and the compiler is running spec verifications on your code (which is after all, just data!) during its runtime :-)
- dustingetz 8y agoClojure goes to great lengths in the design-space to craft the puzzle pieces – immutability, lambdas, simplicity, blessed primitives for state and identity – in such a way that Clojure's idioms are centered on clarity of thought instead of guard rails. This is why Clojure works (for people willing to go deep) in many places where Js/Ruby/Python don't.
- yogthos 8y agoThat is pure and utter FUD I'm afraid. My team has been working with Clojure for over 8 years now. We have tons of code in production, and we don't have any problems maintaining these projects. Furthermore, there's absolutely no empirical evidence to support the notion that static typing results in statistically significant reduction in defects, speed of development, or ease of maintenance. Considering how long both disciplines have been around, and the sheer amount of software written in each, that's quite the elephant in the room. I also have no idea who the people migrating from Clojure to OCaml, Haskell, or F# are. I don't think I've seen a single story about a company doing that. My experience is that dynamic typing is problematic in imperative/OO languages. One problem is that the data is mutable, and you pass things around by reference. Even if you knew the shape of the data originally, there's no way to tell whether it's been changed elsewhere via side effects. The other problem is that OO encourages proliferation of types in your code. Keeping track of that quickly gets out of hand. What I find to be of highest importance is the ability to reason about parts of the application in isolation, and types don't provide much help in that regard. When you have shared mutable state, it becomes impossible to track it in your head as application size grows. Knowing the types of the data does not reduce the complexity of understanding how different parts of the application affect its overall state. Immutability plays a far bigger role than types in addressing this problem. Immutability as the default makes it natural to structure applications using independent components. This indirectly helps with the problem of tracking types in large applications as well. You don't need to track types across your entire application, and you're able to do local reasoning within the scope of each component. Meanwhile, you make bigger components by composing smaller ones together, and you only need to know the types at the level of composition which is the public API for the components. [REPL driven development](http://blog.jayfields.com/2014/01/repl-driven-development.html http://blog.jayfields.com/2014/01/repl-driven-development.ht...) also plays a big role in the workflow. Any code I write, I evaluate in the REPL straight from the editor. The REPL has the full application state, so I have access to things like database connections, queues, etc. I can even connect to the REPL in production. So, say I'm writing a function to get some data from the database, I'll write the code, and run it to see exactly the shape of the data that I have. Then I might write a function to transform it, and so on. At each step I know exactly what my data is and what my code is doing. Where I typically care about having a formalism is at component boundaries. Spec provides a much better way to do that than types. The main reason being that it focuses on ensuring semantic correctness. For example, consider a sort function. The types can tell me that I passed in a collection of a particular type and I got a collection of the same type back. However, what I really want to know is that the collection contains the same elements, and that they're in order. This is difficult to express using most type systems out there, while trivial to do using Spec.