4 ms·
The main problem is discovery. When I hold some object I can easily discover the things I can do with that object—just by typing "." after the object reference
by still_grokking 4y ago
The main problem is discovery.
When I hold some object I can easily discover the things I can do with that object—just by typing "." after the object reference.
In something like Haskell I need to know upfront what I may do with some "object". The IDE can't help me discover the methods I need. All it can do is to show me all available functions in scope. Most of this functions aren't what I'm looking for. But the IDE can't know that as it's missing the "context" in which I want to call my functions / methods.
And regarding records in Haskell: First of all you will get beaten when you use them (that's part of the religious movement and has nothing to do with comprehensible reasons). The other part is: Records are no substitute for objects. Not even close. Those are just quite primitive structs. (To learn how unpractical is it to try to build objects from structs without proper language support just ask someone who was forced to use C after coming from a more complete OO language).
- TremendousJudge 4y agoto quote grugbrain.dev: >grug very like type systems make programming easier. for grug, type systems most value when grug hit dot on keyboard and list of things grug can do pop up magic. this 90% of value of type system or more to grug >big brain type system shaman often say type correctness main point type system, but grug note some big brain type system shaman not often ship code. grug suppose code never shipped is correct, in some sense, but not really what grug mean when say correct >grug say tool magic pop up of what can do and complete of code major most benefit of type system, correctness also good but not so nearly so much
- CharlieDigital 4y agoHaha, I love this. I wrote up something similar [0] but not quite as creative. Discoverability and Locus of Control are two of the best reasons to use OOP that rarely comes up in the academic discussion of OOP as a practice. Honestly, OOP is many cases is simply more practical. Even imperative code has its merits; case in point: it's often much easier to debug. [0] https://medium.com/@chrlschn/weve-been-teaching-object-oriented-programming-all-wrong-part-1-e171f57aa209 https://medium.com/@chrlschn/weve-been-teaching-object-orien...
- skhm 4y ago>but grug must to grug be true, and "no" is magic grug word. Hard say at first, especially if you nice grug and don't like disappoint people (many such grugs!) but easier over time even though shiney rock pile not as high as might otherwise be >is ok: how many shiney rock grug really need anyway? ... I actually needed to see this today
- still_grokking 4y ago> https://grugbrain.dev/ https://grugbrain.dev/ How could I miss that? Gold! Thanks!
- pencilguin 4y agoGreat, but anybody who says correctness is the point of types has completely failed to understand what types are for. Good types do work for you, so that you don't need to write the code to do that work. Correctness is a side effect, because being wrong would take more work.
- pera 4y agoYour first point is also true for procedural programming languages, but at least in Haskell you know that if something is, say, a functor then you know what sort of things you can do with that value. I think it should be possible for an IDE to suggest functions if you used the reverse application operator: [1, 2, 3] & ^-- suggestion here Unfortunately this wouldn't be very idiomatic in Haskell, maybe in Idris with |> this would make more sense?
- still_grokking 4y ago> if something is, say, a functor then you know what sort of things you can do with that value Sure. Because you memorized all the functor methods and remembered that there is a functor instance defined for the data type at hand… ;-) > Unfortunately this wouldn't be very idiomatic in Haskell, maybe in Idris with |> this would make more sense? Or you could just admit that the "OOP style syntax" is superior. (I'm not saying that OOP is superior, though)
- zopa 4y ago> In something like Haskell I need to know upfront what I may do with some "object". The IDE can't help me discover the methods I need. All it can do is to show me all available functions in scope. Sorry, but this just isn't true. Hoogle <https://hoogle.haskell.org/ https://hoogle.haskell.org/> searches function by type, fuzzily: ask for functions whose first parameter is the type of the object-like thing, and you'll get just what you're looking for. And it's perfectly possible to run hoogle locally and integrate it with your editor. Now, the tooling for a language like Java have had several centuries more of aggregate development work done on them compared to Haskell's tools, and if that polish is a difference-maker for you, that's fine! But it's not a fundamental limitation, and claiming it is is just fud.
- lolinder 4y agoThe main point is that the noun-first syntax of OO languages means that by just starting to write the code that you know you need, you've already given the IDE enough information to give you a list of options for which function to use. That kind of tight integration directly into the editor is hard with Haskell because you have to write out the function name first. I could imagine an editor having some sort of "holes" capability, where I can hit a key combo to insert a hole where a function should go, provide the function arguments, then double back and fill in the hole with search results from Hoogle. Done right, such a system would be marginally harder to use than Java-style IDE completions, but not enough to be a problem. The main difficulty is that the implementation and UX complexity of such a system is far greater than with a noun-first syntax.
- tome 4y ago> That kind of tight integration directly into the editor is hard with Haskell because you have to write out the function name first. Sort of. But you can just write _ x ^ and get the same sort of benefit as you get from x. ^
- zopa 4y agoThe approach you're describing sounds a lot like proof-search in idris and agda (coq too, I thing), and it's nicer than you think: you're very often working with definition-stubs created by automated case-splitting, which adds holes for you. Jumping around between holes becomes how you program, not an irritating interruption. But even aside from that, I don't think verb-first needs to be a showstopper: given that we've got type signatures, there's a lot of local context to work with. You're probably calling a function on one or more of the parameters, or else you're typing the leftmost-piece of a composition chain that gives you your result type. So throw Param1Type -> a, b -> ResultType etc at hoogle and populate a completion list. Completion doesn't have to be perfect to be useful. The hard part would be performance: if completion isn't fast, what's the point?
- skavi 4y agoWith Haskell, you learn to discover in a different way. Just think about the shape of the function you’d want, then type that into Hoogle. I learned Rust before Haskell, but prefer Hoogle to Rust’s searching via dot chaining.
- still_grokking 4y ago> Just think about the shape of the function you’d want, then type that into Hoogle. Do you really think this is a adequate replacement for proper IDE support?
- likeclockwork 4y agoAll you work with is method calls? No free functions? No library functions? I perform operations on stuff, if I don't know the name of the operation I intend to perform I clearly have some reading to do. Integrated look up for one specific class of operation which isn't even omnipresent in Class-oriented languages isn't a killer feature for me. Nor is structuring every module even if it's completely stateless as a class, just to benefit from IDE driven development a reasonable price to pay.
- still_grokking 4y ago> Nor is structuring every module even if it's completely stateless as a class, just to benefit from IDE driven development a reasonable price to pay. That's a question of language. object SomeModule: def someMethod(someRandomNumber: 42) = s"The answer is: $someRandomNumber." def someOtherMethod = someMethod(42) @main def entryPoint = val someTheory = SomeModule.someMethod(23) import SomeModule.someOtherMethod import StringExtension.* (someOtherMethod :: "No!" :: someTheory :: Nil) .fold("")(_ + _ + "\n") .printLine() object StringExtension: extension (s: String) def printLine() = println(s) // Because methods compose better in a fluent style… // You don't need to read your code backwards! https://scastie.scala-lang.org/Zt6xTucJRyK1IEQQgFNRLQ https://scastie.scala-lang.org/Zt6xTucJRyK1IEQQgFNRLQ That's Scala. Two cute stateless modules. Nicely wrapped in singletons. Not quite sure why I had to add the handy `printLine()` method to the String class myself, after the fact… But as extending arbitrary classes with new methods without touching them is trivial, who cares. (Frankly it does not compile. It says something like `Found: (23 : Int); Required: (42 : Int)`. No clue)
- lolinder 4y ago> The other part is: Records are no substitute for objects. Not even close. Those are just quite primitive structs. (To learn how unpractical is it to try to build objects from structs without proper language support just ask someone who was forced to use C after coming from a more complete OO language). I agree with you about C and Haskell's records, but I'm curious if you'd say the same about Rust's struct + impl system? Are there problems that can only be solved with real, Smalltalk-inspired objects, or can lightweight structs do the job if supported by enough syntactic sugar?
- still_grokking 4y agoThe answer lies in the answer to why Rust added `dyn traits`… Indeed Rust striked some kind of sweet spot because it got the defaults right. Syntactic it mimics mostly the OOP approach, but without the conceptional burdens behind it. Still Rust could not do without dynamic dispatch (which is the—wrong—default in OOP). In the (admittedly) seldom cases where you need dynamic dispatch & runtime polymorphism you just need them. You could build this stuff by hand (and people did in the past in languages without this features built-in) but having dedicated language level support for that makes it much more bearable. Structs + the impl system is static dispatch. This just can't replace dynamic dispatch. If it could nobody would have ever invented v-tables…