I think it's great that Keli is designed with IDE support in mind. However I believe that this is only half of the reason why FP still doesn't really break through in the corporate world.
The other reason is that many FP users are too enthusiastic about creating abstractions.
This is of course something that FP is exceptionally well suited for. An api that was written to simply process a list of Orders into a Report might be abstracted into a fold on some monad, which at first seems a great idea.
But if you're not careful, readability suffers a lot. It's much easier to get to know an application when its code deals with business objects that you already understand well, than to read hundreds of lines of code that deal only with abstractions.
Maybe there should be a new edition of Clean Code written specifically for FP, to address this anti pattern.
By the way this problem is not unique for FP, OOP suffered the same problem in the past. But the OOP community learned from its mistakes and now most developers know to 'prefer composition over inheritance', for instance.
> An api that was written to simply process a list of Orders into a Report might be abstracted into a fold on some monad, which at first seems a great idea. But if you're not careful, readability suffers a lot. It's much easier to get to know an application when its code deals with business objects that you already understand well, than to read hundreds of lines of code that deal only with abstractions.
Do you have a code example? It would be interesting to see what this kind of code looks like.
Personally, I find complaining about a ”fold over a monad” to be equivalent to complaining about an ”integer indexed loop over an array”. It’s a pretty straightforward implementation detail that have very little to do with your business logic.
And business logic is far, far easier to model using ADTs (it’s literally just AND and OR, applied to data structures) than confounding object inheritance hierarchies.
In fact, I’ve seen a lot more unnecessarily abstracted garbage (usually “design pattern” workarounds to limitations in the object model) in just about every oop based web framework I’ve worked with.
> By the way this problem is not unique for FP, OOP suffered the same problem in the past. But the OOP community learned from its mistakes and now most developers know to 'prefer composition over inheritance', for instance.
This is a great point. I think there are a lot of open questions about basic questions such as how ergonomic effect systems can become and which functional programming abstractions become intuitive through enough practice versus always feeling confusing. I think that stuff will shake out in a decade or two, and we will end up with much better styles to choose from. Who wants to program in 1990s/early 2000s Java style anymore? (Well, okay, a lot of people, but ignore them.) Nobody would judge OOP negatively based on that mess, because we know it can be done a lot better, and languages have evolved to cater to what we've learned. I think functional programming has a lot of evolution to come that will make it more ergonomic and more practical. It will be especially interesting to see what the cohort of programmers introduced to functional programming via modern front-end Javascript will do as some of them transition to the back end and look for statically typed languages that are better than Typescript.
>The other reason is that many FP users are too enthusiastic about creating abstractions.
Elm lacks typeclasses, yet I've found this lack is what keeps a lot of libraries at bay that would have otherwise turned out overly abstract (see any mainstream Haskell library, really). It's the same reasoning behind Go: With great power comes great responsibility. At large, programmers shoot their own feet with powerful languages. Therefore, take away their guns, ie make languages less powerful. I still wish there was something like Elm, but tailored for backend/network programming: https://news.ycombinator.com/item?id=21909087
> It's much easier to get to know an application when its code deals with business objects that you already understand well, than to read hundreds of lines of code that deal only with abstractions.
This feels backwards to me. If I‘m new and join your team I’m much more likely to understand what a monad and a fold are than to understand your domain specific business objects.
So if we are working for an insurance company and see the ‘Insurance’ type is an instance of Monad I now know a great deal about combing different insurance products into new derived ones. By reading a single word of code!
The funny thing (that I'm sure you're aware of) is that 'prefer composition over inheritance' is an instance of 'prefer referential transparency', an idea from FP that you should always know what a variable is referencing. In OOP, this means that inheritance is bad because it's not always clear what functions are inherited and/or overloaded.
> Maybe there should be a new edition of Clean Code written specifically for FP
I’ve posted links to this work-in-progress book a few times in the past, but I’m going to do it again because I think it fits the description perfectly.
Grokking Simplicity by Eric Normand [0] is a great book for introducing the premise behind FP in a pragmatic fashion. It doesn’t get bogged down in monads and type theory, just the benefits of pure functions and modeling applications as flows of data.
All the code examples are in JavaScript, so it requires no prior knowledge of functional programming, and it does a great job of building things up from first principals while highlighting the benefits vs a more object oriented approach.
It’s only half complete, with a nebulous completion date, but I’ve already purchased a copy, and it would the first book I’d recommend for any newcomer to FP (it’s already worth it just from the first half as far as I’m concerned).
"... too enthusiastic about creating abstractions."
Like others, I'm eager to hear more of your thoughts.
I also have a theory. A bit of a riff on Rob Pike's observation (IIRC): "Show me your data and I'll understand your code".
Enterprise-y (corp IT) projects are data centric. Functional programming emphasizes flow-of-control over the data flow. Even more so than functional decompensation (imperative programming) and object-oriented.
Donald Norman's notion of "affordances" applies. Any one can certainly "do COBOL" in Scheme, but pushing that rope requires uncommon insight and intention.
The reason why FP still doesn't really break through in the corporate world is a marketing failure, in the strictest sense of the word marketing, which means prioritizing the right features to target the right market.
A large proportion of this talk is based on the book “Crossing the Chasm". The book was originally written for startups, and this talk adopts the book to an open source audience, especially Haskell developers.
I think the word abstraction here is overloaded. OOP is also fundamentally driven by abstractions. The difference is that FP abstractions are mathematical abstractions, while OOP starts with human-centric conceptual abstractions - the kind every two year old is taught. These are not the same in terms of cognitive understanding.
I also use FP approaches all the time in OO languages (like C#), which I believe is more aligned with where the general purpose language world is moving.
I don't see a reason why some FP languages (e.g. lisp, or elisp) aren't IDE friendly. It might well be because where there is unpopularity, there is less effort to integrate, promote and maintain. If everything is a function and functions have positional parameters and everything named can be in a symbol table, why can't IDEs support (at least the basics of) FP languages?
I think the problem is FP has built its own set of abstractions that imperative programmers aren't used to.
Imperative languages have slowly been adopting some of these. 20 years ago you had people talking about how things like map and filter are confusing but nowadays most imperative languages have a version of it so obviously it isn't that confusing.
When giving an example on how infix notation reads better:
// This is obviously not too right
",".splitBy("1,2,3,4,5")
// This should be right, because it reads out more naturally
"1,2,3,4,5".splitBy(",")
It's funny that in Python split works this way but join doesn't. This is because in the case of split both arguments are strings, but for join one of the arguments is a Sequence, which is a general protocol rather than a class.
The proposed solution by Keli is to be inspired by the Smalltalk-style message syntax:
'Hello world' replace: 'Hello' with: 'Bye'
I think, in general, requiring named arguments is a good thing. Swift does it, and in codebases with a decent amount of TLC, it looks great.
Function calls are one of the weird places were the syntax of the language rarely helps you figure out what is going on, and for the most part is just a sequence of human-written names one after another, and in languages with custom operators it could be even terser.
In comparison, if-statements, loops, pattern matching, etc. were (hopefully) designed to be expressive. I think by requiring named arguments, function calls will also be much more readable, relying less on an active human effort to do so.
In Clojure, for functions that will probably have more than a couple args, I like to pass a map, then destructure it by its keys in the function definition. Two benefits are: you don't need to remember the order of args, and it makes refactoring easier in cases where you are simply adding a new optional arg (you don't need to update all the old calls of that function if they aren't using the new option).
I get this might just boil down to preference but I absolutely hate named parameters. They’re biased towards new users of a language and quickly become painful to write once you’re familiar with the function call.
Plus they don’t always improve writability outside of IDEs because you then have to memorise the parameter names and in some functions there’s several terms that could equally apply (if you’re using an IDE with hinting then the advantages become moot as the same IDE would hint the order of parameters).
The question is should the writer go through the effort of placing the parameter name, or should the reader go through the effort of looking up the function prototype? Since according to Clean Code (https://www.goodreads.com/quotes/835238-indeed-the-ratio-of-...) code is more often read than written, it should makes sense for the writer to do it.
> The proposed solution by Keli is to be inspired by the Smalltalk-style message syntax
But that doesn't actually solve the problem.
"Hello world".replace("Hello","Bye") // works fine
but
somesequence join: "," // still doesn't work
// unless every $Sequence implements join explictly
This particular set of examples is especially ambiguous because "splitBy" can be read two different ways: as a description of the result (noun having been split by a separator) or as an action (subject split direct object by a separator). Which one you choose will subtly affect the way you group the arguments. Conventions in functional programming tend to prefer the former, whereas imperative (including OOP) languages prefer the latter. Either way, however, the string which directly follows `splitBy` should be the separator. In the functional example:
-- Is this correct?
splitBy "," "1,2,3,4,5"
-- or this?
splitBy "1,2,3,4,5" ","
there really is no question that the first version is correct. Even oversimplifying as the article does and treating "basic English" as the only applicable domain knowledge, I'm not likely to read "split by ',' …" as an instruction to split the string "," by some other separator. To understand how the second string fits in you need more than basic English, because English doesn't have that sort of syntax. However, once you learn that it parses as `(splitBy ",") "1,2,3,4,5"` the result is pretty obvious.
In the OOP language example the second version with the string knowing how to split itself, as it were, does make more sense than the first version. The target of the method is always the subject, and the method name is usually read as a verb, with parameters as direct or indirect objects. (Read as: String "1,2,3,4,5" (S), split yourself (V) by the string "," (I.O.).) However, in functional programming the bias is exactly the opposite, because functional programming is about programming with first-class functions on data, not performing actions. You could easily arrange to write:
"1,2,3,4,5" `splitBy` ","
in Haskell using infix notation, and it even reads fairly well reinterpreted as English prose. However, defining `splitBy` this way would imply the curried prefix version:
splitBy ","
would not be a function that splits its input by ",", as one would expect, but rather one that splits "," by its input. Which just goes to show that not every function is well suited for both infix and prefix notation. Whichever version you choose needs to be used consistently. In general the convention has been to define multi-parameter functions such that they can easily be used as combinators, via currying, which seems fitting to me given the nature of functional programming. Functions which are designed to read well as infix operators usually require operator sections or `flip` to adapt them for use as combinators.
> I think, in general, requiring named arguments is a good thing.
I have no objection to optional named parameters, but making them required would necessarily eliminate currying and simple function composition, and that I have a problem with. Record parameters with named fields are generally sufficient for the situations where named parameters are useful, especially since they're ordinary data which you can manipulate at will and not some special syntax baked into the language just for function calls.
Elm single handedly got me into functional programming. Everything from the syntax to the standard library. I haven’t used a language that was that elegant and delightful to use perhaps since I discovered Ruby.
It makes me really sad that Elm turned to this niche language/framework that it is today. I had hoped for it to grow someday to have a mobile target in addition to web, to be able to use it on the server, hell even to be able to use it to build systems. So much wasted potential.
I also love Elm, but the fact that it is a niche language now doesn't mean it's all it ever will be. There's nothing really stopping anyone from adapting Elm to the server for example, but the reason it hasn't been done yet is because it's best to focus on solving one problem at a time. If Evan just translated the Node standard api 1:1 for example, it wouldn't be Elm or Node and there would be no reason to use it.
Looks beautiful. Love functional languages (Elixir, Scala). Python is my day-to-day language. Gets the job done. But it‘s not my biggest passion to program in it. Too many inconsistencies for my taste.
I personally don’t think keli is fixing the right problems:
I’m personally an ocaml user, and I don’t see what keli adds to ocaml.
They cite positional parameters (that they call "prefix notation"… i wonder why ?) but ocaml already has support for named parameters (you can write things like
split ~on:"," "a, b, c"
for example)
Moreover, guessing the order of parameters is generally not that hard :
— If you have good intellisense your IDE will tell you which parameter is which (how they are called in doc)
— If you have good intellisense, your ide will tell you the type of function, which, with strict typing is sufficient in a lot of cases (think "send : Unix.socket -> string -> unit", the type tells you the order of arguments)
— with named parameters, you don’t have to look up the order of parameters… but you have to look up their names, same problem
— This last one is pretty subjective, but in a lot of cases you can guess the order of parameters by thinking "on which argument does it make sense to do partial application ?" `splitBy ","` makes sense, `splitBy "a, b, c"` doesn’t really… so "," is the first argument (but that is subjective)
For IDE support, with the right tools, in my experience it is pretty good (ocaml-lsp or merlin do the job pretty well, and strict typing often allow for better intellisense than on non-functional languages)
Though there we arrive at the real problem I’ve experienced : the tooling.
It’s not actually that bad. more like it’s badly explained.
how do you know that you are supposed to install
— opam for package management (and reinstall ocaml via opam)
— dune for building
— utop as a REPL, because the basic REPL sucks
— batteries or janestreet-core for "standard library" because the standard library sucks (and which one anyway ?)
Plus these tools are hard to use at beginning / have a learning curve. (why can’t creating a project be as simple as
- type "cargo new project"
-> now you automatically have a project dir with a local opam switch, an opam file, a dune file, an example program, and basic utilities installed in that switch ? )
Personally, I think that’s what fp lacks the most right now (though I’m probably influenced a lot by ocaml) : easy to use and flat-learning curve tooling
On the topic of language IDE support. One of the things I've noticed from working in a few languages professionally (Python, Ruby, Java, Elixir) is that the level of power required in an IDE seems to be a function of the language.
My observation was that to feel comfortable in Java I tended to require a very powerful IDE (Intellej) to deal with refactoring and appeasing the type system.
When I write Elixir, I feel comfortable using a much less powerful (from a language integration standpoint) Vim, mostly due to the constraints Elixir has in the language. In Elixir, there is no mutable state, I can feel confident that the only things affecting a function are the things in front of my face when reading it. Elixir's alias/import syntax make it pretty easy to jump to the file that has a function definition in.
I think FP languages tend to have an edge in how much power an IDE is required to have, because those languages tend to have features like immutability and composable higher order functions)
The author lists Ocaml as "non-IDE-friendly." This is not my experience. Ocaml is one of the most IDE friendly languages I have in my toolkit right now -- not just functional languages, but all of them. Ocaml + Emacs + Merlin is stable, accurate, very fast (responsive), and easy to set up and configure.
Plus, named and named-optional parameters are supported in the language, and can be used to good effect for disambiguating function parameters of similar type (the split, join problem).
Great to see the motivation spelled out clearly, but something seems strange. If you read the Quora answer, it just rants about how functional programming is obsessed with big words and esoteric concepts that are hard to grok, and that this constitutes poor UX. But Keli’s interpretation seems to be that the moment-to-moment experience in the IDE is poor, and that that’s what the language wants to address with more explicit syntax and intellisense support. These seem like two very different problems under the umbrella term “UX”.
I'm very sympathetic to the problem. I'm working on Dark (https://darklang.com) which is a functional language, explicitly because we believe that functional languages are easier to learn, understand and provide great tooling for.
It seems to me there's a lot of focus on syntax, and I don't think they've gotten to a great place. For example:
(this Int).square | Int = this.*(this)
In Rust, they do
impl Int {
fn square (self) -> Int {
self * self
}
}
I can read the Keli version, but I'm note sure that `(this Int)`, `|`, and `.*` are great inventions. You need a good reason to deviate from commonly recognized syntax, and Keli doesn't feel like it's made things simpler with this.
In Dark, we use piping heavily to get the benefits of the dot-syntax. In particular, it can be type directed:
8 |> Int::square |> Int::log 2
The autocomplete will only show you integer functions in this case.
For the problem of "which parameter" - our IDE shows parameter names when your cursor is in a position to type the next parameter, so you know what you're adding it is. We also show the parameter names when your cursor is in the completed argument so you know what parameter it is. (I couldn't see if Keli did this, it might not be possible in modern editors).
I find the syntax as the author intended it to be: clean. I agree with the ambiguity of positional parameters at first glance. Although named parameters (e.g. in Scala) somewhat solve this, they are optional. Having the parameters with their explicit name makes it quite readable.
Edit: The same feature makes it look a little weird when put on a single line, because we mentally parse the whitespace as separator but in this case it is not:
$.name("Keli") age(50).isOld
It looks like a refreshing addition to function programming languages and I'd like to see where it goes from here, in terms of adoption and features.
The two downsides mentioned regarding FP are not an issue if you are using a REPL and a Lisp:
- excellent IDE support, since you are working inside your program (which can be inspected)
- paradigm a la carte:
you want named arguments? Go for it (split :string "foo,bar" :by ",")
you want different invocation syntax? (3 + 4) or (3 4 +) is just a macro away
and a bit easier to read than: (this Int).square | Int = this.*(this) YMMV
you want to define your data shape? use your favorite schema language
you want monads? just use a library
you want go-routines? just use a library
you want compile time types? just use a library
split was the clear winner here. Given splitBy, I basically recreated split as a lambda.
I tried to create an analog to that where splitBy would come out looking better. I figured that if we didn't know the dimensionality of our data, then delimiters becomes an array of arbitrary length and we could pass data.!splitBy into something like delimiters.reduce. When actually writing that, however, I wound up recreating split again:
At first sight you might think, oh great don't even need to read the documentation (which is the author argument in favour of this). But, is "toIndex" inclusive or exclusive? Don't know, so now I need to read the documentation anyways and just learn the behavior of the function. And once I did, all those names just become visual noise that my eyes need to skip over when reading and verbosity when writing.
Also, if you're going to go with this style, I'd much rather have function first (as FP should):
Which brings me to my second point, I prefer languages that just allow both. Some functions are better positional and other named and some a mix of both. Let each function choose the most appropriate one. For example, are we really going to do this?
100 divide with: 5
or
divide theNumber: 100 with: 5
Ya, it reads like an explanation for a 5 year old, but as a professional programmer I'd much rather:
divide 100 5
My point being, certain functions are intuitive even with positional args and others arn't, only the latter should have named args.
Now for the intellisense section I agree, but I think you can solve it with IDE UX. Like just provide a command that lists all locals for you to pick and then lists all functions whose first argument is of that type. Or heck, have it that after you type a var name, it lists functions over it but the list shows to the left and when you pick it autocompletes the function text to the left of it.
I really appreciate the effort of designing the syntax to match common IDE expectations. However, I think it shouldn't be too difficult to add intellisense for Haskell-like syntax to IDEs as well. Especially with all the types available!
The only unconventional thing is that the function might be put in-front of the value and some parentheses need to be added, instead of just putting it after the cursor like with method syntax. I assume you get used to that rather quickly.
> I think it shouldn't be too difficult to add intellisense for Haskell-like syntax to IDEs as well. Especially with all the types available!
On the contrary, this should give Haskell an advantage because all the types are known and the IDE should be able to autocomplete only the correct parameters. But that requires compiler-as-a-service, and GHC is not. And writing your own analyser for a language is quite a task.
I feel like the named reasons, aside from being invalid (it should be really really easy to make a popup that tells you the function parameter names and/or types, like SLIME has been doing for...decades? with Common Lisp), are really bad justifications for creating an entirely new programming language. Surely the effort would be better spent on improving the tooling, because all of their concerns are tooling-related.
That also makes the language not about a "good user experience" as given in the docs, but "being compatible with modern IDEs". No thanks, I would rather improve tooling for an existing language that already has a community and ecosystem.
Code completion (autocomplete) is a subset of intellisense. Intellisense also provides e.g. info tooltips on hover and more.
But yes, I agree since the author specifically talks about the way OO languages provide a more easily autocompleted syntax and parameter help doesn't really differ, perhaps the better term to use here would be autocomplete or code completion.
Seems like they're complaining (for lack of a better word) about only having word autocomplete available for most functional languages versus a more refined suggestion tool like Intellisense. But yeah, they don't get that specific about that fact. I guess Intellisense has entered the realm of Kleenex in how it's used.
I'm not sure those are the things holding FP back. The statement "The user experiences of functional programming languages sucks." sounds really weird and is the exact opposite of my experience. I write Elixir professionally. The developer experience is unparalleled and I definitely wouldn't trade it for a job writing Java 9 to 5.
Maybe it's trying to refer to stuffs such as existing libraries and package management etc. when you want to quickly boot up a real-world project. I once tried to write some web app in Haskell and that experience definitely was anything but smooth. Fortunately Elixir has a really vibrant community and libraries for the majority of common tasks (plus 20+ years of Erlang ecosystem). It's also really easy to roll your own solutions.
I don't think the issues of IDE support and parameter ordering mentioned in this article are problems to FP programmers at all. Powerful language servers exist nowadays for all major functional languages and they're not that different from language servers for OO languages. So yeah I tend to agree with a lot of commentators that this article doesn't seem to make much sense unfortunately.
It also mitigates the parameter naming problem. With a type hole, the language server will infer the type. But in the case of two parameters having the same type, you have to consult the definition or doc.
I disagree with all points in the article - the user experience with existing FP languages is superior, and the IDE integration for strongly typed languages is generally very impressive.
Syntactic window dressing isn’t what makes or breaks a language.
While I could get onboard with functional languages being more user friendly I didn't find the motivation to be compelling.
> Ambiguous functions argument positions
It just seems to be advocating for named arguments, something which the majority of languages (OO or FP) support these days. You could say that requiring named parameters is a feature but it's hard to get excited about.
> Not Intellisense friendly
I use Elm and Elixir, both of which have decent autocomplete in vscode (and I believe many other editors).
Critique aside I definitely applaud more towards increasing FP adoption. I think Elm has brought many good ideas to the table already (though admittedly it's bias is more towards ideal than practical). On the flip side Elixir is very pragmatic and easy to get going with for developers coming from OO languages.
[+] [-] misja111|5 years ago|reply
The other reason is that many FP users are too enthusiastic about creating abstractions. This is of course something that FP is exceptionally well suited for. An api that was written to simply process a list of Orders into a Report might be abstracted into a fold on some monad, which at first seems a great idea. But if you're not careful, readability suffers a lot. It's much easier to get to know an application when its code deals with business objects that you already understand well, than to read hundreds of lines of code that deal only with abstractions.
Maybe there should be a new edition of Clean Code written specifically for FP, to address this anti pattern.
By the way this problem is not unique for FP, OOP suffered the same problem in the past. But the OOP community learned from its mistakes and now most developers know to 'prefer composition over inheritance', for instance.
[+] [-] cannabis_sam|5 years ago|reply
Do you have a code example? It would be interesting to see what this kind of code looks like.
Personally, I find complaining about a ”fold over a monad” to be equivalent to complaining about an ”integer indexed loop over an array”. It’s a pretty straightforward implementation detail that have very little to do with your business logic.
And business logic is far, far easier to model using ADTs (it’s literally just AND and OR, applied to data structures) than confounding object inheritance hierarchies.
In fact, I’ve seen a lot more unnecessarily abstracted garbage (usually “design pattern” workarounds to limitations in the object model) in just about every oop based web framework I’ve worked with.
[+] [-] dkarl|5 years ago|reply
This is a great point. I think there are a lot of open questions about basic questions such as how ergonomic effect systems can become and which functional programming abstractions become intuitive through enough practice versus always feeling confusing. I think that stuff will shake out in a decade or two, and we will end up with much better styles to choose from. Who wants to program in 1990s/early 2000s Java style anymore? (Well, okay, a lot of people, but ignore them.) Nobody would judge OOP negatively based on that mess, because we know it can be done a lot better, and languages have evolved to cater to what we've learned. I think functional programming has a lot of evolution to come that will make it more ergonomic and more practical. It will be especially interesting to see what the cohort of programmers introduced to functional programming via modern front-end Javascript will do as some of them transition to the back end and look for statically typed languages that are better than Typescript.
[+] [-] bauerd|5 years ago|reply
Elm lacks typeclasses, yet I've found this lack is what keeps a lot of libraries at bay that would have otherwise turned out overly abstract (see any mainstream Haskell library, really). It's the same reasoning behind Go: With great power comes great responsibility. At large, programmers shoot their own feet with powerful languages. Therefore, take away their guns, ie make languages less powerful. I still wish there was something like Elm, but tailored for backend/network programming: https://news.ycombinator.com/item?id=21909087
[+] [-] sfvisser|5 years ago|reply
This feels backwards to me. If I‘m new and join your team I’m much more likely to understand what a monad and a fold are than to understand your domain specific business objects.
So if we are working for an insurance company and see the ‘Insurance’ type is an instance of Monad I now know a great deal about combing different insurance products into new derived ones. By reading a single word of code!
[+] [-] pjmlp|5 years ago|reply
Meanwhile multiple paradigm languages keep getting most of the features that actually matter to Joe/Jane developer.
[+] [-] pgustafs|5 years ago|reply
[+] [-] adamkl|5 years ago|reply
I’ve posted links to this work-in-progress book a few times in the past, but I’m going to do it again because I think it fits the description perfectly.
Grokking Simplicity by Eric Normand [0] is a great book for introducing the premise behind FP in a pragmatic fashion. It doesn’t get bogged down in monads and type theory, just the benefits of pure functions and modeling applications as flows of data.
All the code examples are in JavaScript, so it requires no prior knowledge of functional programming, and it does a great job of building things up from first principals while highlighting the benefits vs a more object oriented approach.
It’s only half complete, with a nebulous completion date, but I’ve already purchased a copy, and it would the first book I’d recommend for any newcomer to FP (it’s already worth it just from the first half as far as I’m concerned).
[0] https://www.manning.com/books/grokking-simplicity
[+] [-] agumonkey|5 years ago|reply
[+] [-] specialist|5 years ago|reply
Like others, I'm eager to hear more of your thoughts.
I also have a theory. A bit of a riff on Rob Pike's observation (IIRC): "Show me your data and I'll understand your code".
Enterprise-y (corp IT) projects are data centric. Functional programming emphasizes flow-of-control over the data flow. Even more so than functional decompensation (imperative programming) and object-oriented.
Donald Norman's notion of "affordances" applies. Any one can certainly "do COBOL" in Scheme, but pushing that rope requires uncommon insight and intention.
[+] [-] platz|5 years ago|reply
A large proportion of this talk is based on the book “Crossing the Chasm". The book was originally written for startups, and this talk adopts the book to an open source audience, especially Haskell developers.
Gabriel Gonzalez – How to market Haskell to a mainstream programmer: https://www.youtube.com/watch?v=fNpsgTIpODA
[+] [-] achr2|5 years ago|reply
[+] [-] smithza|5 years ago|reply
[+] [-] solomonb|5 years ago|reply
``` ourReportAccumulatorFunc :: Monad m => m a -> m a -> m a ourReportAccumulatorFunc = blah
ourFunc :: (Foldable t, Monad m) => t (m a) -> m a ourFunc = foldr ourReportAccumulatorFunc someUnitValue ```
Why do you think a fold would be hundreds of lines outside of the business logic?
[+] [-] bcrosby95|5 years ago|reply
Imperative languages have slowly been adopting some of these. 20 years ago you had people talking about how things like map and filter are confusing but nowadays most imperative languages have a version of it so obviously it isn't that confusing.
[+] [-] enricozb|5 years ago|reply
The proposed solution by Keli is to be inspired by the Smalltalk-style message syntax:
I think, in general, requiring named arguments is a good thing. Swift does it, and in codebases with a decent amount of TLC, it looks great.Function calls are one of the weird places were the syntax of the language rarely helps you figure out what is going on, and for the most part is just a sequence of human-written names one after another, and in languages with custom operators it could be even terser.
In comparison, if-statements, loops, pattern matching, etc. were (hopefully) designed to be expressive. I think by requiring named arguments, function calls will also be much more readable, relying less on an active human effort to do so.
[+] [-] elwell|5 years ago|reply
[+] [-] laumars|5 years ago|reply
Plus they don’t always improve writability outside of IDEs because you then have to memorise the parameter names and in some functions there’s several terms that could equally apply (if you’re using an IDE with hinting then the advantages become moot as the same IDE would hint the order of parameters).
[+] [-] rstarast|5 years ago|reply
As an alternative, perhaps there's space to introduce types to make this something like
[+] [-] drdebug|5 years ago|reply
[+] [-] a1369209993|5 years ago|reply
But that doesn't actually solve the problem.
[+] [-] nybble41|5 years ago|reply
In the OOP language example the second version with the string knowing how to split itself, as it were, does make more sense than the first version. The target of the method is always the subject, and the method name is usually read as a verb, with parameters as direct or indirect objects. (Read as: String "1,2,3,4,5" (S), split yourself (V) by the string "," (I.O.).) However, in functional programming the bias is exactly the opposite, because functional programming is about programming with first-class functions on data, not performing actions. You could easily arrange to write:
in Haskell using infix notation, and it even reads fairly well reinterpreted as English prose. However, defining `splitBy` this way would imply the curried prefix version: would not be a function that splits its input by ",", as one would expect, but rather one that splits "," by its input. Which just goes to show that not every function is well suited for both infix and prefix notation. Whichever version you choose needs to be used consistently. In general the convention has been to define multi-parameter functions such that they can easily be used as combinators, via currying, which seems fitting to me given the nature of functional programming. Functions which are designed to read well as infix operators usually require operator sections or `flip` to adapt them for use as combinators.> I think, in general, requiring named arguments is a good thing.
I have no objection to optional named parameters, but making them required would necessarily eliminate currying and simple function composition, and that I have a problem with. Record parameters with named fields are generally sufficient for the situations where named parameters are useful, especially since they're ordinary data which you can manipulate at will and not some special syntax baked into the language just for function calls.
[+] [-] hashbig|5 years ago|reply
It makes me really sad that Elm turned to this niche language/framework that it is today. I had hoped for it to grow someday to have a mobile target in addition to web, to be able to use it on the server, hell even to be able to use it to build systems. So much wasted potential.
[+] [-] Kaze404|5 years ago|reply
[+] [-] submeta|5 years ago|reply
[+] [-] ducaale|5 years ago|reply
[+] [-] fsloth|5 years ago|reply
F# works great with Visual Studio, and especially well with intellisense.
F# was designed as industrial language from the beginning, and high quality tooling is a large part of that.
It might just be me, though, maybe other people use the language in ways that don't work that well there.
[+] [-] nephanth|5 years ago|reply
They cite positional parameters (that they call "prefix notation"… i wonder why ?) but ocaml already has support for named parameters (you can write things like split ~on:"," "a, b, c" for example) Moreover, guessing the order of parameters is generally not that hard :
— If you have good intellisense your IDE will tell you which parameter is which (how they are called in doc)
— If you have good intellisense, your ide will tell you the type of function, which, with strict typing is sufficient in a lot of cases (think "send : Unix.socket -> string -> unit", the type tells you the order of arguments)
— with named parameters, you don’t have to look up the order of parameters… but you have to look up their names, same problem
— This last one is pretty subjective, but in a lot of cases you can guess the order of parameters by thinking "on which argument does it make sense to do partial application ?" `splitBy ","` makes sense, `splitBy "a, b, c"` doesn’t really… so "," is the first argument (but that is subjective)
For IDE support, with the right tools, in my experience it is pretty good (ocaml-lsp or merlin do the job pretty well, and strict typing often allow for better intellisense than on non-functional languages)
Though there we arrive at the real problem I’ve experienced : the tooling. It’s not actually that bad. more like it’s badly explained. how do you know that you are supposed to install
— opam for package management (and reinstall ocaml via opam)
— dune for building
— utop as a REPL, because the basic REPL sucks
— batteries or janestreet-core for "standard library" because the standard library sucks (and which one anyway ?)
Plus these tools are hard to use at beginning / have a learning curve. (why can’t creating a project be as simple as - type "cargo new project" -> now you automatically have a project dir with a local opam switch, an opam file, a dune file, an example program, and basic utilities installed in that switch ? )
Personally, I think that’s what fp lacks the most right now (though I’m probably influenced a lot by ocaml) : easy to use and flat-learning curve tooling
[+] [-] grantjpowell|5 years ago|reply
My observation was that to feel comfortable in Java I tended to require a very powerful IDE (Intellej) to deal with refactoring and appeasing the type system.
When I write Elixir, I feel comfortable using a much less powerful (from a language integration standpoint) Vim, mostly due to the constraints Elixir has in the language. In Elixir, there is no mutable state, I can feel confident that the only things affecting a function are the things in front of my face when reading it. Elixir's alias/import syntax make it pretty easy to jump to the file that has a function definition in.
I think FP languages tend to have an edge in how much power an IDE is required to have, because those languages tend to have features like immutability and composable higher order functions)
[+] [-] gmfawcett|5 years ago|reply
Plus, named and named-optional parameters are supported in the language, and can be used to good effect for disambiguating function parameters of similar type (the split, join problem).
[+] [-] tobr|5 years ago|reply
[+] [-] pbiggar|5 years ago|reply
It seems to me there's a lot of focus on syntax, and I don't think they've gotten to a great place. For example:
In Rust, they do I can read the Keli version, but I'm note sure that `(this Int)`, `|`, and `.*` are great inventions. You need a good reason to deviate from commonly recognized syntax, and Keli doesn't feel like it's made things simpler with this.In Dark, we use piping heavily to get the benefits of the dot-syntax. In particular, it can be type directed:
The autocomplete will only show you integer functions in this case.For the problem of "which parameter" - our IDE shows parameter names when your cursor is in a position to type the next parameter, so you know what you're adding it is. We also show the parameter names when your cursor is in the completed argument so you know what parameter it is. (I couldn't see if Keli did this, it might not be possible in modern editors).
[+] [-] ciuncan|5 years ago|reply
[+] [-] beders|5 years ago|reply
The two downsides mentioned regarding FP are not an issue if you are using a REPL and a Lisp:
- excellent IDE support, since you are working inside your program (which can be inspected)
- paradigm a la carte: you want named arguments? Go for it (split :string "foo,bar" :by ",")
and a bit easier to read than: (this Int).square | Int = this.*(this) YMMV[+] [-] maest|5 years ago|reply
[+] [-] pmontra|5 years ago|reply
[+] [-] thefifthsetpin|5 years ago|reply
Consider this crude csv parser:
using splitBy:
using split: split was the clear winner here. Given splitBy, I basically recreated split as a lambda.I tried to create an analog to that where splitBy would come out looking better. I figured that if we didn't know the dimensionality of our data, then delimiters becomes an array of arbitrary length and we could pass data.!splitBy into something like delimiters.reduce. When actually writing that, however, I wound up recreating split again:
using splitBy:
using split:[+] [-] IshKebab|5 years ago|reply
[+] [-] didibus|5 years ago|reply
'Hello world' replaceFromIndex: 0 toIndex: 4 with: 'Bye'
At first sight you might think, oh great don't even need to read the documentation (which is the author argument in favour of this). But, is "toIndex" inclusive or exclusive? Don't know, so now I need to read the documentation anyways and just learn the behavior of the function. And once I did, all those names just become visual noise that my eyes need to skip over when reading and verbosity when writing.
Also, if you're going to go with this style, I'd much rather have function first (as FP should):
replace in: 'Hello world' fromIndex: 0 toIndex: 4 with: 'Bye'
Which brings me to my second point, I prefer languages that just allow both. Some functions are better positional and other named and some a mix of both. Let each function choose the most appropriate one. For example, are we really going to do this?
100 divide with: 5
or
divide theNumber: 100 with: 5
Ya, it reads like an explanation for a 5 year old, but as a professional programmer I'd much rather:
divide 100 5
My point being, certain functions are intuitive even with positional args and others arn't, only the latter should have named args.
Now for the intellisense section I agree, but I think you can solve it with IDE UX. Like just provide a command that lists all locals for you to pick and then lists all functions whose first argument is of that type. Or heck, have it that after you type a var name, it lists functions over it but the list shows to the left and when you pick it autocompletes the function text to the left of it.
[+] [-] sfvisser|5 years ago|reply
The only unconventional thing is that the function might be put in-front of the value and some parentheses need to be added, instead of just putting it after the cursor like with method syntax. I assume you get used to that rather quickly.
[+] [-] dmitriid|5 years ago|reply
On the contrary, this should give Haskell an advantage because all the types are known and the IDE should be able to autocomplete only the correct parameters. But that requires compiler-as-a-service, and GHC is not. And writing your own analyser for a language is quite a task.
[+] [-] pjmlp|5 years ago|reply
[+] [-] fouric|5 years ago|reply
That also makes the language not about a "good user experience" as given in the docs, but "being compatible with modern IDEs". No thanks, I would rather improve tooling for an existing language that already has a community and ecosystem.
[+] [-] exclipy|5 years ago|reply
[+] [-] alkonaut|5 years ago|reply
But yes, I agree since the author specifically talks about the way OO languages provide a more easily autocompleted syntax and parameter help doesn't really differ, perhaps the better term to use here would be autocomplete or code completion.
[+] [-] 52-6F-62|5 years ago|reply
[+] [-] SZJX|5 years ago|reply
Maybe it's trying to refer to stuffs such as existing libraries and package management etc. when you want to quickly boot up a real-world project. I once tried to write some web app in Haskell and that experience definitely was anything but smooth. Fortunately Elixir has a really vibrant community and libraries for the majority of common tasks (plus 20+ years of Erlang ecosystem). It's also really easy to roll your own solutions.
I don't think the issues of IDE support and parameter ordering mentioned in this article are problems to FP programmers at all. Powerful language servers exist nowadays for all major functional languages and they're not that different from language servers for OO languages. So yeah I tend to agree with a lot of commentators that this article doesn't seem to make much sense unfortunately.
[+] [-] crvdgc|5 years ago|reply
It also mitigates the parameter naming problem. With a type hole, the language server will infer the type. But in the case of two parameters having the same type, you have to consult the definition or doc.
[+] [-] centimeter|5 years ago|reply
Syntactic window dressing isn’t what makes or breaks a language.
[+] [-] ollysb|5 years ago|reply
> Ambiguous functions argument positions
It just seems to be advocating for named arguments, something which the majority of languages (OO or FP) support these days. You could say that requiring named parameters is a feature but it's hard to get excited about.
> Not Intellisense friendly
I use Elm and Elixir, both of which have decent autocomplete in vscode (and I believe many other editors).
Critique aside I definitely applaud more towards increasing FP adoption. I think Elm has brought many good ideas to the table already (though admittedly it's bias is more towards ideal than practical). On the flip side Elixir is very pragmatic and easy to get going with for developers coming from OO languages.