5 ms·
Some thoughts as I read through this (I'm far from an expert in PL, so... forgive me any naivety, please). I can't decide if I like the pipeline operator (1).
by DonaldPShimoda 9y ago
Some thoughts as I read through this (I'm far from an expert in PL, so... forgive me any naivety, please).
I can't decide if I like the pipeline operator (1). Is it really better? Perhaps I'm just used to chaining multiple function calls together.
Pattern-matching (2) is my favorite language feature, or at least it's pretty high up there. Super useful. I always wish I had it in Python (my go-to language most of the time).
Not sure I really "get" reactive programming (3).
With regard to implicit names (4), I think I actually prefer writing lambdas with my own variable names, and I particularly like Haskell's syntax for it.
Even Scala's usage of the underscore seems a little better than this "it" business, if only because the underscore doesn't see much use elsewhere so it's an indicator that "Hey, something's going on here!"
But using a regular-looking variable name seems a bit unintuitive for somebody unfamiliar. Though I suppose they may not be the people for whom this sort of code is written.
Isn't destructuring (5) just an effect of ADTs? Which are also what produce (2) pattern-matching, so perhaps really what the author enjoys is ADTs. Or maybe I don't know things.
No strong feelings on (6). Maybe I'd need more exposure.
Are if-expressions (7) actually on this list? In the comments they practically suggest that the ternary conditional is the "regular" way of handling such things, which is... silly. The rest of this list seemed interesting, so I'm confused why these make the cut. Perhaps the author was just being cheeky.
Try-expression (8) are nice and all, but the best is when you can eschew them completely. I like the way optionals are wrapped into so much in Swift; almost makes obsolete all the try-expressions (except for certain cases, such as whenever you call into an older Obj-C library or something).
Definitely a fan of auto-currying (9).
Is method extension (10) similar to overriding the "magic" double-underscore methods in Python, such as `__add__`? That definitely seems useful.
- tom_mellior 9y ago> I can't decide if I like the pipeline operator (1). Is it really better? I think it can be useful for processing that is clearly separated into stages. You can read it forward: foo |> do_this_first |> then_do_this |> this_must_absolutely_be_last instead of backward: this_must_absolutely_be_last (then_do_this (do_this_first foo)) On the other hand, we are used to understanding such "backward" pipelines, so your mileage may vary. > With regard to implicit names (4), I think I actually prefer writing lambdas with my own variable names I would agree, especially because "it" is not a very good name in some cases. "it.length == 5" is really jarring. In Smalltalk, the custom is to name the variable "each", so for the article's examples you would get "each length = 5" and "each toUpperCase", which look nicer IMHO. > Isn't destructuring (5) just an effect of ADTs? Do you mean algebraic data types? I think ADT typically refers to abstract data types, but I'm old. Anyway, yes, destructuring is just syntactic sugar for a pattern match where there is only one variant, so the match can never fail. > Definitely a fan of auto-currying (9). I'm a fan of writing and using curried functions by default. I'm not a fan of Reason's syntax that suggests the opposite. > Is method extension (10) similar to overriding the "magic" double-underscore methods in Python, such as `__add__`? No. It is adding a new method to an existing class from someone else's library.
- mhandley 9y agoI'm not sure about mixing prefix (for functions), infix (for operators) and postfix (for pipelines) notation in the same languaqe. Does having all three in the same language actually help readability? If you really want to read things forward, you could always adopt forth, where everything is postfix: foo do_this_first then_do_this this_must_absolutely_be_last No need for pipelines when you've got a stack.
- tom_mellior 9y ago> you could always adopt forth Yes, but that would be throwing the baby out with the bathwater. Just because of a single operator that I'm not sure is useful in a language I'm otherwise satisfied with. > where everything is postfix Except for parsing words, which are commonly used to change exactly this property of the syntax, no? The fact that postfix is nice in many cases does not imply that postfix is always a good idea.
- Sean1708 9y ago> postfix (for pipelines) Isn't the pipeline operator infix?
- DonaldPShimoda 9y agoHmm that's a good point about the pipeline operator. But I'm not sure how I feel about having both idioms readily available. In which cases would you choose forwards processing instead of backwards, you know? I'll have to think more about that. > In Smalltalk, the custom is to name the variable "each" Ooh I like that! I think I would like it even more if "each" was a reserved word, like "this" in e.g. Java. But maybe that's a bit much. > Do you mean algebraic data types? Yes, sorry. I talk to functional people a lot so we tend to use "ADT" for the algebraic ones (those FP people are all about brevity it seems). Glad to have that confirmed, though. Just wanted to be sure I wasn't missing something. > I'm a fan of writing and using curried functions by default. I'm not a fan of Reason's syntax that suggests the opposite. Could you clarify a bit? I'm not sure I follow exactly what you mean about the syntax being unclear. It seems similar to Haskell, except with parentheses, which doesn't seem terrible. > No. It is adding a new method to an existing class from someone else's library. Oh I see! I totally misread that example. Thanks for the clarification!
- always_good 9y agoOne issue with auto-currying is that, afaict, it prevents arity overloading. For example, I find this pretty silly: http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Json-Decode#map6 http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Jso... One feature that always impressed me is how Kotlin has bring-your-own-chaining methods defined on everything. request(url).let { req -> req.setQuery(mapOf("a" to 42)) }.let { it.body = "foo" }.apply { // `this` is implicit receiver inside apply() send() } Not the best example of how it's useful because I'm rusty, but it's nice to just chain your own transformations on anything. I miss it all the time.
- yorwba 9y agoYou can do arity overloading with curried functions if you allow overloading the function application operator itself. E.g. if f is of type A → B and a is of type Decoder A then map f a is of type Decoder B and in the special case where B is a function type C → D, that Decoder B = Decoder (C → D) can be applied to a value c of type Decoder C making map f a c a Decoder D. Caveat: I'm not aware of any language that actually does this, likely because redefining the core syntax of function application would be so easy to abuse. EDIT: Removed my Haskell example, because I think I got it wrong.
- PeterisP 9y agoIn my code, the main use of arity overloading is to implement default parameters in Java, i.e., the feature has no intrinsic value, it's a workaround for the missing feature of default values; if a language has reasonable support for default parameters then arity overloading can go, leaving only type-dependent overloading.
- bunderbunder 9y agoAt least in F#, I think that having too much need for arity overloading might be considered a code smell. In a nutshell, the idea is that, if you've got a bunch of functions with the same name but different arities, collectively they add up to a violation of the single responsibility principle. If you do need it, though, you can make a 1-arity function whose only argument is a tuple of a given size. This is how methods from C# and VB libraries are presented. I believe that it just compiles to a non-curried function behind the scenes.
- mattnewport 9y agoAs well as the readability advantages, a major reason for the pipeline operator in F# (the first place I encountered it) is to help with type inference. Because type inference happens left to right in F# the compiler usually knows the type you're dealing with already with code written using the pipeline operator but you may have to manually specify the type without it. It also makes for a better intellisense experience.
- DonaldPShimoda 9y agoThis makes a lot of sense. Thanks for the insight!
- imtringued 9y ago>With regard to implicit names (4), I think I actually prefer writing lambdas with my own variable names, and I particularly like Haskell's syntax for it. Introducing implicit variable names is my most hated "modern" feature. I still explicitly write down the default name. The only defensible way to implement this is by making "it" a keyword and allowing you to access the outer scopes with a syntax like "outer.it" the same way some languages support "outer.this" or "outer.super" when you want to explicitly access the parent class of a nested class.
- eindiran 9y agoRegarding pattern-matching, if Python is your go-to programming language and you are looking for convenient pattern matching, you should check out Coconut(http://coconut-lang.org http://coconut-lang.org): it's a superset of Python 3 that transpiles to any target version of Python, which adds a lot of useful features like pattern-matching.