6 ms·
Counterpoint: I feel like static typing is a bandaid for the fundamental problem that the language isn't powerful enough to allow one's code to truly be OAOO.
by ken 7y ago
Counterpoint: I feel like static typing is a bandaid for the fundamental problem that the language isn't powerful enough to allow one's code to truly be OAOO.
Back when I wrote C#, static typing was indeed helpful for making large-scale changes and providing an automated check that this wasn't destroying everything. But then, it's only in languages like C# that I have to make these kinds of large-scale changes. There's no macros or syntactic abstraction. When you want to construct the same category of operation in different contexts, and functions and classes don't operate on the right axis, you're SOL. You just end up repeating yourself.
When I write Lisp, I never have to make changes that cover more than their one area of responsibility. At worst, I'll rename a function, and a simple textual find-and-replace is more than sufficient -- better than most refactoring browsers, even, since it will hit my comments and documentation. Do fancy refactoring tools update your README and docs/ folder yet?
(In one case, I knew an experienced C# programmer to build and compile an expression at run-time using LambdaExpression [1]. It takes about 10 times as many lines to achieve the same thing, and you have to write in a style that looks nothing at all like a normal function, so in practice nobody ever does this. In contrast, the way to accomplish this in Lisp takes one character, and the code looks identical to a normal function, so it's not unheard of.)
I feel that Python, in many ways, combines the worst aspects of both worlds. It's not strict enough to be a good static language, and not powerful enough to be a good dynamic language. Sadly, the most popular dynamic languages today are Python and JS and PHP, so a lot of failures and limitations of these languages get blamed on dynamic languages in general.
[1]: https://docs.microsoft.com/en-us/dotnet/api/system.linq.expressions.lambdaexpression?view=netframework-4.8 https://docs.microsoft.com/en-us/dotnet/api/system.linq.expr...
- whb07 7y agoThere is a better approach by using statically typed functional languages like F#, Ocaml, or Haskell. They don’t need to have the type declaration as most things can be inferred. So you get the feelings of writing Python with the full suite of benefits of a strongly typed functional language when using say F#
- mixmastamyk 7y agoI’d be very interested if any of these decided on non-atrocious syntax. Any progress on that front?
- whb07 7y agoCan you expand on this? Haskell or F# have less noise than Ocaml. Plus with F# you can just pipe things neatly. I started and still love Python, these languages have ALOT of stuff going for them that blows the standard language used nowadays out of the water. Download .netcore and setup VSCode + Ionide plugin and you’re ready to rock and roll (fastest IDE setup). As you write your code, you’ll see the function signature and what the compiler can infer from your code. Also checkout the basic intro, syntax is very clean and straight to the point. https://docs.microsoft.com/en-us/dotnet/fsharp/introduction-to-functional-programming/ https://docs.microsoft.com/en-us/dotnet/fsharp/introduction-...
- mixmastamyk 7y agoYeah, f# is not bad. How is it on linux, are there enough third party libs? Not inclined to use MS tools unless both reqs are met.
- whb07 7y agoI’ve used it on osx and it runs great actually, and the Linux story is the same. The standard .NET libraries are obviously sharpened steel and the third party libs are fewer than say NPM but higher quality and address some of the missing pieces. Though again, the F# batteries are great and the .NET libs cover most if not all of your needs. Check it out. You get sweet syntax, great compiler and tooling, and you won’t suffer things like nulls etc. Also parallel and or concurrent work is a breeze since everything is immutable by default, and it makes it ugly to try to stick it to a mutable procedural or oop style. I highly, highly, suggest you check it out. It’s the step child that Microsoft refuses to acknowledge in full force. But I think that story will change soon as they realize how GIANT the Python sector is and so F# is a great gateway drug into the .NET world. edit: By the way I used to hate on MSFT for the longest and I haven’t used Windows in over a decade. But F# is fire.
- randomsearch 7y agoYes, refactoring tools can do text in comments and docs, other files across your project, and will provide plenty of options to specify what to change, and also provide previews before “enacting” the changes. One of the many reasons I find IDEs superior to plain editors.
- seanmcdirmid 7y agoLambdaExpression is a part of the dynamic language runtime (DLR) and is absolutely useful if you are into run-time code generation. The analogous contract for this in the lisp world would be quoting, but wouldn’t be as fine grained as the DLR since it limits what abstraction can be applied (eg if you want to compute what parameters or what operations are needed at run-time). Given the performance it allows for run-time generated code, nothing in production really beats the DLR. It is a shame Microsoft nerfed it in UWP because they ripped out the jit in favor of ahead of time compilation.
- TeMPOraL 7y agoI'm not familiar with how .NET runtime works, but why does AOT compilation nerf runtime code generation? Take SBCL - an open source Common Lisp implementation that compiles everything by default. That includes any run-time generated code. The code is simply AOT-compiled at runtime, at the point of generation.
- seanmcdirmid 7y agoThey don’t include a JIT in UWP, so run time generated code is interpreted rather than dynamically compiled. Run-time generated code in the DLR is very programmatic using the full C# language (eg expressions are values that you can store in a field to be retrieved later and completed), they aren’t just simply quoted templates. So an AOT compilation of run-time generates code is very impossible (consider if you used the DLR to execute code for your own live programming language layered on top...).
- lispm 7y ago> they aren’t just simply quoted templates how is that related to Lisp? Is code generation in Lisp limited to 'quoted templates'?
- seanmcdirmid 7y agoIt is if you want ahead of time compilation to work on your generated code. People often confuse macros with full programmatic code generation, but they aren’t equivalent at all, of course.
- TeMPOraL 7y agoSeconding. I write a Lisp a lot (including professionally), and I don't think there was even one time I was wishing for the kind of automated refactoring I had available when I worked in Java. 99% of the time, manual fixing, Emacs search and replace (or editable Occur), or grep are enough. The remaining 1% of the times are the cases like renaming a slot name in a class, where I need to track down and fix all the relevant slot-value and with-slots calls, but somehow this never ends up too big of a problem. I didn't figure out why things are like this. I'm guessing something about Lisp makes me write software in a way that defeats the need for automated refactoring. It's not only that they're not possible to implement reliably[0], I don't find myself in any need of them. Also, spot on with OAOO. I have cases where equivalent Java code could occasionally benefit from automatic refactoring of couple dozen functions and classes, because they're all hand-written. My Lisp code is usually one macro invocation that generates all these functions/classes, so "automated refactoring" involves changing the macro definition (and rerunning the top-level form using it, in the live image I'm working in). -- [0] - Good luck automatically refactoring calls to slot-value that use slot names generated at runtime, or any kind of non-trivial macro. That said, I sometimes wish this wasn't the case, because I could use better autocomplete, or more reliable "who references this" queries.
- daxfohl 7y agoInteresting. I did two years of Clojure after a decade plus of Java and C#. It never particularly clicked for me. This was developing misc LOB apps with a bunch of misc business logic. Maybe the wrong domain for a lisp to shine. Boilerplate wasn't much of an issue in that domain, and without type safety I constantly felt like I was driving blindfolded. Given that feeling, I felt the density and power of Clojure was actually a disadvantage relative to, say, Python. The clencher was after rewriting a large app in F# and having two working versions for a while, I decided to move to async calls. In F# I was able to change a few core functions and then follow the red squiggly lines, wrapping logic into async and asyncSeq monads until everything compiled again. And after a day of doing so, everything worked the first time. With Clojure I couldn't even find the courage to begin something like that. It would have to be a complete rewrite. I never touched Clojure again. Maybe lisp is different.
- mixmastamyk 7y agoFinally a decent critique of Python. Have read so many type and whitespace complaints that aren’t particularly useful. “Middle-brow dismissal” I think it’s been called here. But this one, wow made me think.
- emmelaich 7y agoI had to look up OAOO. For others - http://c2.com/xp/OnceAndOnlyOnce.html http://c2.com/xp/OnceAndOnlyOnce.html
- progval 7y agoSounds like you might like Hy: http://docs.hylang.org/en/stable/ http://docs.hylang.org/en/stable/
- throwaway287391 7y ago> [Python is] ... not powerful enough to be a good dynamic language. Could you elaborate? I guess you're comparing it with Lisp -- what makes it significantly more powerful than Python? (I read your paragraph about Lisp and I don't see why it couldn't just as easily apply to Python, apart from `lambda` being 6 characters instead of 1.)
- seunosewa 7y ago> Python ... is not strict enough to be a good static language Yes, but Python was never meant to be a static language. The static type annotation feature was borrowed from static languages to reduce bugs, not to make Python a static language. > Python ... not powerful enough to be a good dynamic language. I strongly disagree with this. Python is a very powerful dynamic language. It lets you code with any paradigm that suits the problem you’re trying to solve. Functional, OOP, procedural, or whatever. CPython has hidden features that allow for low level customization of how it works (when it is appropriate). > Sadly, the most popular dynamic languages today are Python and JS and PHP There is a big difference between Python and those other languages. JavaScript and PHP became popular because they had to be used to write frontend code and backend code respectively for websites on affordable web hosts. Python became popular because programmers chose to use it; because it’s really good. Perhaps you think Lisp is better; that’s fine, but notion that Python is not good or not powerful is just wrong.
- verttii 7y agoAs someone mostly writing functional languages I'm always having a hard time writing decent functional code in Python. There's almost no facilities to support modern functional programming so it just doesn't feel idiomatic in Python.
- y4mi 7y agoAnd unanimous functions are basically non-existent. Yes, lambda works but only allows a single line, which forces you to name the function anyway.
- zo1 7y agoI find that a good thing rather than as a shortcoming. JS/TS looks horrendous with the amount of function nesting. Luckily, it looks like futures and await are resolving some of that mess. In my mind, the following is better than the JS equivalent using closures: response = await AsyncHTTPClient().fetch("http://www.google.com") self.result = json.loads(response.payload) You see exactly what is going on, no additional nesting, and no need for semi-inner state that potentially complicates state reasoning. I.e. people get tempted to put code afterwards thinking it'll run after the previous line. I may get some flack for this or my language, but I have absolutely no idea why anyone bothers so much with the closure garbage outside of select places where they make sense. It's non-intuitive and looks bad. Heck, I have a bad enough time explaining to seasoned devs the difference between threads and async concepts. Then you throw that sort of stuff at a junior JS dev and chaos will most certainly ensure with a buggy FE where no one can reason about state and there are null checks all over the place, just because. I've seen it in static FE languages like C#, too. State becomes too difficult to reason about, so null-checks are required everywhere. Edit. Formatting.
- stult 7y ago>Do fancy refactoring tools update your README and docs/ folder yet? Yes actually. Resharper plus Swagger handle 95% of that for me automatically. And I absolutely do not agree with your point about syntactic abstraction. Interfaces, dependency injection, generics, very light use of inheritance/abstract base classes, and extension methods generally provide all the abstraction I need to avoid repeating myself.
- pas 7y agoOAOO, and the power (expressiveness) required to actually achieve it, reminds me of the Alan Kay paper about DSLs (STEPS paper from VPRI https://news.ycombinator.com/item?id=11686325 https://news.ycombinator.com/item?id=11686325 - the breakthrough is that it should be easy to make DSLs) And of course macros are useful for making DSLs. Rust's macro system(s) are pretty powerful (ergonomic, hygenic - typesafe, simple, as expressive and powerful as you wish via procedural macros that can do almost everything with the AST). Safely and cleanly abstracting any kind of program even with DSLs is rarely easy because of the need to pass the right context, to find the right interfaces, to manage data/state/context/systems dependencies, and something like Scala3's implicits might help with it. (Though maybe simply more vigorous refactoring might also work to keep data-passing to a minimum. But then I fear that turns into an over-abstraction fest, and we get back Java's FactoryFactory-like monsters.)
- LadyCailin 7y agoI highly disagree with using macros in place of actual refactoring. Macros can have their place I guess, but when you use them instead of refactoring, you’re simply making code less straightforward to read for other people, creating layers of redirection that have to be fully understood before being able to grok the code. Basically every IDE for modern languages quickly and correctly refractors, without any risk that you accidentally change a string value, for instance, or have to worry about scope rules, and then you have code that is understandable without redirections or having to resort to clunky text replacement.