10 ms·
> An incoming HTTP request? it is a plain Clojure dictionary. I learned to code in Python. Loved it. Dynamically typed dicts up the wazoo! Then I learned why
by EdwardDiego 5y ago
> An incoming HTTP request? it is a plain Clojure dictionary.
I learned to code in Python. Loved it. Dynamically typed dicts up the wazoo!
Then I learned why I prefer actual types. Because then when I read code, I don't have to read the code that populates the dicts to understand what fields exist.
- lvh 5y agoThe two are not mutually exclusive. Clojure has namespaced keywords and specs[0] to cover that. (There is also the third-party malli, which takes a slightly different appproach.) The advantage is that maps are extensible. So, you can have middleware that e.g. checks authentication and authorization, adds keys to the map, that later code can check it directly. Namespacing guarantees nobody stomps on anyone else's feet. Spec/malli and friends tell you what to expect at those keys. You can sort of do the same thing in some other programming languages, but generally you're missing one of 1) typechecking 2) namespacing 3) convenience. [0]: spec-ulation keynote from a few years ago does a good job explaining the tradeoffs; https://www.youtube.com/watch?v=oyLBGkS5ICk https://www.youtube.com/watch?v=oyLBGkS5ICk
- kitd 5y agoYeah, he mentions that later on as a drawback
- taeric 5y agoTo be fair, an incoming request is, almost by definition, dynamic. It makes sense to have that as a map, since the main sensible thing to do on receipt is validation/inspection. Granted, you may have a framework do a fair bit of that. Depends how much you want between receipt of the request and code you directly control.
- Zababa 5y agoUsually the approach in a statically-typed language is to transform your dynamic request into something that you know through parsing instead of validation. Here's a great article about this: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/ https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va....
- taeric 5y agoThat is a valid approach in any language. Static or not. Doesn't change my point that heavily. And it is all too possible to pick a bad parsing/binding language such that protocol changes in the request are now foot guns.
- Zababa 5y agoThat's true, but static languages are not worse at handling dynamic data. From the same author: https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/ https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-typ....
- taeric 5y agoTo an extent, I agree. I'm just pointing out that this is a bit of a bad example. I want there to be dynamic inspection of input. That said, maps as the only tool is clearly messy. And is a straw man.
- kingdomcome50 5y agoThis is the second time I've seen the link above. And while I agree with the premise, the author clearly does not understand how to properly use the `Maybe` monad (a term that does not make an appearance!). There is little use in wrapping a call in `Maybe` to then immediately unwrap the result on the next line. Doing so isn't really using the construct... One would expect the lines following the creation of `Maybe` to bind calls through the monad. In the end I see almost no meaningful difference between their "Paying it forward" example and simply utilizing an `if` to check the result and throw. In essence the author is using a parse and validate approach!
- dharmaturtle 5y agoYou might try re-reading it with some charity - the example's purpose isn't to teach the `Maybe` monad, but to remove the redundant check. To go into what `bind` does would be a diversion from the main topic (parsing vs validating). FWIW SPJ has called this blog's author a "genius" so... I think they do know how `Maybe` works. https://gitlab.haskell.org/ghc/ghc/-/issues/18044#note_266170 https://gitlab.haskell.org/ghc/ghc/-/issues/18044#note_26617...
- deleted 5y ago[deleted]
- robertlagrant 5y agoI agree. This doesn't seem much different to saying they're all objects. You still need to know what to expect inside the dictionary.
- goatlover 5y agoThe difference being that objects have a class where you can look to see what fields it specifies.
- robertlagrant 5y agoSure, depending on the language. What I mean is having dictionaries doesn't mean you don't have to learn schemas.
- dan-robertson 5y agoJava doesn’t really have a nice interface for interacting with objects in general. Closure does have a nice interface for interacting with dictionaries. They have namespaces keyword symbols for keys which are much more ergonomic than typing strings, and they have lots of functions for modifying dictionaries. I think the big difference is in the philosophy of what the language thinks data is, and how the world ought to be modelled.
- dan-robertson 5y agoQuestion: 1. Can a GET request have a non-empty request body? 2. Assuming you don’t know the answer to that question, will the type system you use be able to tell you the answer to that question? This is a pretty simple constraint one might want (a constraint that only certain requests have a body) but already a lot of static type systems (e.g. the C type system) cannot express and check it. If you can express that constraint, is it still easy to have a single function to inspect headers on any request? What about changing that constraint in the type system when you reread the spec? Is it easy? The point isn’t that type systems are pointless but that they are different and one should focus on what the type system can do for you, and at what cost.
- lkitching 5y agoAny statically-typed language with generics can express that by parameterising the request type with the body type. A bodiless request is then just Request[Nothing] (or Request[Unit] if your type system doesn't have a bottom type). Accessing the headers just requires an interface which all static languages should be able to express.
- xapata 5y agoHow about values restricted to identifiers currently in the database table? There's always something the type system can't do.
- jolux 5y agoF# has a feature called type providers that make this sort of bookkeeping between the database and the code less tedious, but even if you mess it up, static typing still gives you more safety than dynamic. If your code blew up because it should have accepted an identifier it didn’t, you know that the code has not been written to handle that case and can fix it. Alternatively, you can just choose to ignore this, and do what a dynamic language does. There is nothing stopping you from being dynamic in a static language, passing everything around as a map, etc.
- tragomaskhalos 5y agoNamedtuples FTW! A de-facto immutable dict with the keys listed right there in the definition to obviate all the usage head-scratching. Then, if you need more functionality (eg factory functions to fill in sensible defaults), you can just subclass it. TBH I've never understood the attraction of the untyped dict beyond simple one-off hackups (and even there namedtuples are preferable), because like you say you typically have no idea what's supposed to be in there.
- fmakunbound 5y agoThis is one of those self-inflicted Clojure problems. In Common Lisp you might use an alist or a plist for small things, but you'd definitely reach for CLOS classes for things that had relationships to other things and things that had greater complexity. IIRC, the preference for complecting things via maps, and then beating back the hordes of problems with that via clojure.spec.alpha (alpha2?) is a Hickey preference. I don't recall exactly why.
- blacktriangle 5y agoNo source to back this up, but my guess is that Clojure was driven by the need to interopt with Java so is to not get kicked out of production. This meant absorbing the Java object model. Shipping a language with both Java objects and CLOS and making them both play nice together sounds like a nightmare.
- Jach 5y agoThere's a Common Lisp implementation on the JVM, called ABCL: https://www.abcl.org/ https://www.abcl.org/ The interop is... not the best, but it's something. I've only used it for proof-of-concept stuff (e.g. how-to make a Lisp module, export it as a jar that java code can include in their pom and use without knowing it's Lisp) and for minor development experience enhancements in a giant Java codebase (e.g. change method in Java, it gets hot-swapped in, I invoke it or an upstream method from Lisp with real data so I don't have to make an even higher upstream network request via some deep UI section).
- joncampbelldev 5y agoThis comment helpfully explains many of the reasons Rich had for choosing immutable, persistent, generic data structures as the core information model in clojure (instead of concrete objects / classes): https://news.ycombinator.com/item?id=28041219 https://news.ycombinator.com/item?id=28041219 Not wanting to misquote the above / Rich himself I would TLDR it to: - flexibility of data manipulation - resilience in the face of a changing outside world - ease of handling partial data or a changing subset of data as it flows through your program Please note that no one (I hope) is saying that the above things are impossible or even necessarily difficult with static typing / OOP. However myself and other clojurists at least find the tradeoff of dynamic typing + generic maps in clojure to be a net positive especially when doing information heavy programming (e.g. most business applications)
- iLemming 5y ago> Then I learned why I prefer actual types I do like static typing. But honestly, no other PL¹ (statically typed or otherwise) even comes close in terms of the ergonomics and joy of writing software. Nothing is quite enjoyable for me like Clojure. Haskell is great but hard, and I'm years away from claiming I achieved production-ready proficiency with it. I don't want to berate other languages, but I looked into OCaml, F#, Kotlin, Scala, Rust, and a few others. And none of them feel to me as enjoyable as Clojure. After so many years of programming, I finally, truly feel like I love my job. Also, I never liked Python. Maybe just a little, in the beginning. Once I get to know it, I disliked it forever. ------- ¹ I mean languages used in the industry, not counting even more "esoteric" PLs