C++ has never been OOP only, but it arguably introduced (Simula-style) OOP to mainstream commercial development. Java wouldn't look like Java if it wasn't for C++.
There's an anti-pattern where a function will be declared entirely in a callback, instead of declaring the function in another location and referencing it locally. It's difficult to read and logically follow. This seems especially common in Javascript. Is it just a sign of novice?
Declaring the function in another location is the antipattern.
Code locality is important for being able to maintain contextual information when reading code. Declaring a callback in a different location from where it's used means one extra level of indirection for no good reason.
Declaring functions in another location is the purpose of functions, yes? Otherwise we would code in a purely linear fashion.
Some code locality should be sacrificed for readability, reuse, and clarity. Not to make a "forest of functions", but to define separate ideas more clearly than stuffing one thought into a parameter of another thought. It's like if I was telling you about something funny that happened when I got to work, but first I have to describe every detail of my drive to work...
It's a thread on ineffective coding habits, so I thought I would piggyback with a general question. I regrettably did not pursue an undergrad CS degree, and so sometimes wonder about the gaps in my education such a degree would have filled. Thank you for your time.
If the function is large, sure, make it into it's own name thing (with as restricted a scope as you can get away with).
However, many languages support callbacks/lambdas/closures/blocks as parameters to generic library code, and these dangly bits are often one or two lines.
This allows you to use, or create your own, library code as extensions of the language to reduce boiler-plate code for visiting, filtering, resource management, etc.
Have a look at something like the Ruby language to see how this is exploited to reduce A LOT of code noise.
Some might also argue that "private methods", which are visible to every other method in a class, are a bad thing, rather than making a nested function within the only function / method that used said code.
One of the things I used to be able to do in Pascal, that C ... Java and their ilk took away. Fortunately, most FP languages gave that back, with interest (closures).
Although, as you have said, it is somewhat redundant with closures, and even more so with local type inference. C++, C# and Java all let you do what effectively amounts to a local function these days.
I've been able to use F# at work for a few months now and I'm absolutely enamored with it.
But I have not yet had any of what I call "ints, strings,and for loops-only developers," the folks who never really grasped O.O. very well at all, contribute to the code. I'll be eager see how that goes.
Good point. Most of these "look how much better we did with X tool" comparisons usually have a strong sample bias in that the X team are highly motivated and talented. X probably does help them quite a bit, but perhaps not as much as indicated, especially once the thinks-in-Blub (and loves it!) lumpen-programmers get added to the project :-(
I'd really like to move out of 1960s tech into 1970s tech, someday (e.g. - from Simula-67-inspired to Scheme/Smalltalk-inspired environments)
I am very enthused about functional languages like F# - don't know much about F# particularly and respect rather than love C#.
I just find comparisons of this kind slightly off-putting.
It's always an interesting time when you share your lovely clean concept with potential philistines - hope your project's exposure to your loop-community goes well.
Well, if the code does the same thing and it’s 1/10 the size, that would be convincing. Don’t recall seeing this sort of comparison for ML languages in the past, however.
not usually 1/10
almost always at least 1/2, sometimes approaching 1/10 if a class hierarchy is a large % of the project and it can turn into a discriminated union.
There are plenty of such comparisons (a famous one comparing Haskell to C++ comes up every so often).
Most of the time there is a generous positive bias toward the favored language and excessive abuse of the worst features of the one that is the target of ridicule.
It's not just FP versus Other, either; it's sort of an industry meme in some sense.
Readability is why I use "closing flags" so I don't get lost in a sea of closing curly brackets. Example:
if ($TreeHasLeaves) {
Write-Host -f green "The variable TreeHasLeaves is $($TreeHasLeaves)"
}; #end if TreeHasLeaves
My "GilLang" auto-adds these closing flag comments after each closing bracket - currently it only supports Powershell, but the goal is to streamline basic coding (if/then, for/foreach, do/while, try/catch) in many languages. To create the above function statement, curly brackets included, just type use "New-FunctionStatement if TreeHasLeaves -Powershell".
That would be helpful, and I hope Notepad++ gains the feature. Notepad++ does have a bracket helper that highlights the other bracket, but flagging the closing bracket helps when there are several brackets.
I think that's readable just for you, if you're used to it. For me it's just noise. Why would you need it anyway? My take is, to minimize nesting and scope, so I can see right away where the scope starts and ends.
I like F#, but I think the "F# has objectively better syntax" claim needs a lot more support than the "signal-to-noise-ratio" argument. I don't know of anything that suggests that humans are better at parsing whitespace-delineated arguments than say, comma-delineated. My experience (probably biased similarly to F#'s target audience generally, possibly with above average exposure to functional languages) suggests that C-like syntaxes are easier to read.
I am a Python dev (by day, anyway), and whitespace doesn't make Python easier to read. But Python is still much easier to read in many cases than F# because Python retains parens and commas and the single-expression lambda syntax hampers gratuitous nesting. Go and Rust are even easier to read than Python, largely thanks to curly braces. I still prefer F# to Python, but not because of the syntax.
NOTE: I'm not saying C-like syntax is objectively easier to read; I'm merely (very) skeptical of the OP's claim that F#'s syntax is somehow objectively better.
I am a C#/JS dev by day. In my own time, I write in F#, Python, TypeScript, C (used to), C++ (used to). I have trouble with brackets nowadays. Each time I wrote some, it feels rubbish. I should try go one day :)
Note that F# is the only _truly_ (not purely) functional language I am used to.
Wouldn't the same argument apply to Lisp code? Parens make it easier to read than whitespace.
I'm not convinced at all that's the case, as whitespace has never bothered me and it's less tokens to look at. But this is likely one of those subjective things. Some people prefer more syntax, some longer names. Some like duplicated code and some prefer it DRY.
My entire point is that it's subjective. I just listed my experience to counter the author's claim (implication?) that F# syntax is objectivity better than C syntax.
It's the YAML/JSON debate - would you rather have ugly curly brackets or annoyingly-precise whitespace? Personally, I choose ugly brackets, but I know many prefer the annoying whitespace.
I always find this funny, because I indent my C/JS/C#/Java/whatever-other-curly-brace code exactly the same way Python expects me to indent it, so it's never really been an issue.
Every time someone complains about significant whitespace, my first reaction is "what indentation style are you using where that's an issue?!"
I'm the same way, but lacking curly braces hurts when you're reading long functions and trying to figure out what scope you're in. It also prevents multiline lambdas or that's the excuse anyway. I program in Python all day long and there are definitely times when delimiters are nice, but I think most people make too big a deal about it. I will also say that I still have a harder time reading a Haskell function call style than a C style.
Yeah I frequently switch between F# and C# and I literally cannot tell a difference in effort. I assume the complaints are more out of familiarity than anything else. I have never had issues in either language.
I don't know that I can defend the article's claim of "objectively better syntax," but do have a few off hand thoughts of perhaps better signal-to-nose ratio (I guess using C# as a baseline): 1) single-case unions declared in one line of code 2) readonly data structures requiring much less code 3) being able to glue 2 arbitrary things together without needing a class (thinking of partial application, though of course both languages support tuples) 4) optional type annotations, not something you might want to go too overboard with, but imagine how useful in cases where the variable/value has the same name as the type 5) pipe-forward operator, which relieves you from having to declare variables (and choose names for those variables) just to pass the outcome of each line of code to the next line ...
The twice-implemented requirement mentioned near the start of this article seems to have the potential for being the basis of an informative case study, but this was not it - instead, it is just another article pontificating over the minutiae of syntax (the linked article may have a more informative analysis, but the link seems to be broken.)
At one point, we read "your abstractions should afford the right behaviours whilst make it impossible to do the wrong thing." Well, that would undoubtedly be helpful, but Turing may want to have a word with you about the feasibility of guaranteeing it. What does F# do in this regard? We are told it does not have nulls, which is nice, but what does it do to avoid someone introducing data that are semantically, rather than formally, null values (NaN, for example?)
The section on testing does not seem to contain anything that is F#-specific. it mentions QuickCheck, but that has apparently been implemented for many imperative languages, including C#. Is this sort of thing more effective for functional languages? There is no discussion of the possibility here.
I am actually persuaded that there are benefits to functional programming, but articles like this paradoxically give the impression that there is not much substance behind the large claims.
The first thing I would like to know about the case study was whether the implementers of one version benefitted from the problems encountered by those of the other, especially with regard to getting the requirements nailed down.
Turing hardly prevents it; indeed, by the Church-Turing thesis, shows us how it’s done: write an abstract model of your problem space. This is your types and type functions (like Option/Some/Maybe). Write a type that is the theorem claiming you get your output from your input. Now you guarantee you cannot do the wrong thing. Affordances for the right behaviors? Those are library functions you build bottom up, or assemble from a library of structures (functor, monad, etc.) and your types. As all affordances, there are art and craft here.
On semantic nulls, the compiler typically enforces completeness on case analysis.
Yes, I should have scrubbed that paragraph and started over, to say something like eliminating nulls is only part of the problem, and you still have to do everything right, starting with your abstract model of the problem space. With regard to semantic nulls, I offered NaN as an example, but I was thinking of the case where the programmer introduces what is, in practice, a semantic null value for one of his user-defined types - something that should be distinguished as a special case, but isn't.
Is ironic how most failed to see the main points, and focus in the syntax (syntax is easy to attack because the cargo cult notion of "syntax not matter much").
I have learned and used FOR WORK almost a dozen languages now, F# being the last of the bunch. I totally remove many ineffective coding habits, mostly:
- Inmmutability
- The type checker making me aware of problems
- Not need to over-check for nulls because now I don't need to worry
- Pattern matching + AGDT are awesome. This cut the need to make a lot of unecessary classes.
- Make me to think outside over OO. I do this also in Pascal(Delphi) and python, and hate how C# is OO/Class for all.
---
About syntax:
F# totally cut millons and millons of lines of code! (exagerating!) but more important: Far less files than similar C# project (and yes, I have made the rewrites. I do rewrites for life! I work upgrading, fixing, moving, old codebases most of time).
However, some things that I hate of F#:
- Whitespaces must be as python. Is better.
- Not like to rely on type inference. Is a good idea on paper but have the stupid habit of only work AFTER you write. Put the types manually is better, IMHO. You get more clarity, ease readability, not need to rely on a IDE, the types are less obtuse, etc.
Ironically, VS Code show the types as you write but Visual Studio Mac no. I have both opened some times because this!
So, type inference is net negative for me. Maybe is my pascal love talking here.
I don't like how everything is "let". I wish it was "let, function, etc".
I don't like the overuse of spaces for everything. And how comas are for tuples but ; for separate items.
You say that "syntax not matter much" is a cargo cult notion. I am not sure you understand the cargo cult analogy, but I take it that you disagree with the sentiment, which is itself ironic, given that the reasons you give for favoring F# are all much more than syntax issues. They could have been the basis for a more persuasive article.
But some of the things that make F# unique are surfaced with the syntax. Is not just there for the sake of it.
Pattern matching is the most obvious thing. The way the syntax present this aspect is close to perfect...
ie: I'm for fun building a language. I have tried a lot of ways to present some things, like Pattern Matching. I can't figure a better way without resorting to weird characters, unicode or text editors that not exist. (I replace "|" as separator but the basic structure is just the same).
Other ways the syntax is significant is in how it encourage curried functions, chaining and pipelines (with |>). The pipeline way is something that I truly adore. Wish F# was more into this, IMHO.
Not fan in how let have different meanings, neither the use of "," for tuples.
Also is confunsing why functions with "let sum a b" are truly different to "let sum(a,b)". Hate this (because suddenly pipelines and curried application not work anymore).
The making of "inmutable first" and marking some vars as "mutable" is something truly nice. Could be easier if it was "let" and "var" instead of "let mutable" but anyway. Is good.
Anyway, as a amateur language designer it make me more aware in how the syntax make a language better or more ergonomic and lead the way in how use it.
"Close to perfect" according to...you? Arguments from Syntax are closer to emacs vs vim or K&R vs Allman bracket/indentation flame wars than they are to meaningfully discussing differences between languages. It's so superficial compared to so many other things.
I don't see any irony. What I do see is a lot of people responding to an argument that the author seemed to think was strong enough to lead the article with.
Probably people with a lot of experience or a more narrow view of how languages work wish for more deep arguments, but for the average person, syntax as the "user interface" of a languages is certainly the most obvious and evident factor to show of.
I totally see how presenting "hello world" with python vs C++ clearly make the case "this thing is different and is good"
You need a more of information to truly make the case better, but I think is fair that a F# introduction start with the syntax. This show the mainstream audience "we are not in kansas anymore".
Certainly, make the article better in the second half is another thing. Like with movies is easier to start with a good premise and fall flat in the end ;)
Differentiating let and function would be a very bad decision. The whole point is to indicate to you that functions can be passed around and worked with just like values. There is a verbose mode that is a lot less whitespace sensitive but I personally didn't mind whitespace sensitivity in python or F#, I just turn on show whitespace.
It uses two indents for indenting the parameter list of the function, and two indents for indenting the body of the function.
Most indentation conventions suggest four space indentation for breaking up a list of parameters, and two space indentation for the body of a {}. In order to avoid the problem that the example criticizes.
I did, although I admit it was only for awful projects, where people used to comment out code instead of deleting it... hundreds of lines of commented out code and occasionally one or two lines uncommented in the middle of that. Not fun to maintain code in such state.
I take a philosophy that "executable comments" are better than "static comments". So instead of writing a comment to explain some gnarly bit of logic, I'll try to do the reverse of inlining and break pieces into little, suitably-named functions.
The vast majority of my comments are either javadoc-style function protocols or high-level discussion on subsystem purpose and architecture.
Leaning on this commenting style, I find that looking at past code of mine is much easier to re-grok than having lots of explanatory comments everywhere.
Don't you think actually breaking them into functions is excessive? I think any programmer worth their salt, with the aid of sublime text style search, can understand any codebase.
Interesting that you write "any programmer worth their salt" has to have the aid of "sublime text style search".
Having had to maintain and rebuild other programmers' monstrosities of monolithic functions, I appreciate short code routines and relevant comments, especially those that detail why the code is written that way. I then don't have to spend enormous amounts of time trying to understand the thought processes that went into making that code.
If you are worth your "salt", you will write clear code and when it needs to be "tricky", you detail "the why" that you needed to do this.
Different people have different standards as to what is clear code and often, this is dependent on the idiomatic usage within the language in question.
A real simple example for you to ponder:
if a < b & b < c & c < d & d <= e then { do something}
compare with
if a < b < c < d <= e then { do something }
In this particular case, the first is how many programmers would write the condition, whereas the second is idiomatically more correct. The second is also, in this instance directly self documenting.
Of course, how you will do this in your favourite language will be different.
Even if your assertion is true (and I've seen some codebases that would certainly put it to the test) on what grounds is spending an indeterminate (likely nontrivial) amount of time groveling a codebase with search preferable to having the code at least somewhat organized and documented in the first place?
Not at all. All else being equal, if I can write code that is understandable to more people with less effort, that seems like an obvious win. I couldn't care less about sizing up your programming saltiness.
The link to the article with the comparison doesn't work to me. Often what I find in these articles is that the second implementation takes much less time and has less code and fewer bugs primarily because of the lessons learned the first time round. It has almost nothing to do with the language of implementation.
The idea behind the article is good, but the implementation leaves to desire. I can confirm that F# develops good habits in a C# programmer. My favorite acquired habits are:
1. Intolerance to nulls and absence of object intializers in constructors. Use of zero objects a-la Array.empty.
2. Semantic typing even for simple types (e.g. type Name = string).
Completely off topic, but this site along with a few others like StackOverflow seem to hijack my browser history and break the back button. In order to back out of their domain, I have to quickly bang the back key twice.
I have no idea what causes this. I even run with Javascript completely disabled.
"controlFactoryFactory" seems to me a pretty apt name for a factory that creates controlFactories, if that's whatbitbis. What else could you name it really? "controlMetafactory" perhaps.
“The C# project took five years and peaked at ~8 devs. It never fully implemented all of the contracts. The F# project took less than a year and peaked at three devs (only one had prior experience with F#). All of the contracts were fully implemented.”
85 comments
[ 0.20 ms ] story [ 143 ms ] threadTimes had to re-order files to build a project: C# - 0, F# - 19.
Experienced non-arrogant developers found for hire: C# - 10000000, F# - 12.
I for one am tired of OOP (a la Simula 67) presented as the be-all and end-all :-(
The functional programming guys were right, and C++ set back the industry several decades. (better to have both FP and OOP, but OOP-only stinks)
Having the compiler understand that is great, because it means I no longer have to express my block structure twice.
It's one of the few things I regret about Rust's decisions, that they went for a C-like curly braces syntax.
Code locality is important for being able to maintain contextual information when reading code. Declaring a callback in a different location from where it's used means one extra level of indirection for no good reason.
Also, not sure how this is relevant to anything.
Some code locality should be sacrificed for readability, reuse, and clarity. Not to make a "forest of functions", but to define separate ideas more clearly than stuffing one thought into a parameter of another thought. It's like if I was telling you about something funny that happened when I got to work, but first I have to describe every detail of my drive to work...
It's a thread on ineffective coding habits, so I thought I would piggyback with a general question. I regrettably did not pursue an undergrad CS degree, and so sometimes wonder about the gaps in my education such a degree would have filled. Thank you for your time.
However, many languages support callbacks/lambdas/closures/blocks as parameters to generic library code, and these dangly bits are often one or two lines.
E.g. (pseudo-code) -
* list.for-each : print
* list.filter : is-wanted
* db-connection.bracket-transaction : compound-update
This allows you to use, or create your own, library code as extensions of the language to reduce boiler-plate code for visiting, filtering, resource management, etc.
Have a look at something like the Ruby language to see how this is exploited to reduce A LOT of code noise.
One of the things I used to be able to do in Pascal, that C ... Java and their ilk took away. Fortunately, most FP languages gave that back, with interest (closures).
Although, as you have said, it is somewhat redundant with closures, and even more so with local type inference. C++, C# and Java all let you do what effectively amounts to a local function these days.
For example, consider:
In this case, what would be gained by extracting the two inline functions?But I have not yet had any of what I call "ints, strings,and for loops-only developers," the folks who never really grasped O.O. very well at all, contribute to the code. I'll be eager see how that goes.
I'd really like to move out of 1960s tech into 1970s tech, someday (e.g. - from Simula-67-inspired to Scheme/Smalltalk-inspired environments)
I just find comparisons of this kind slightly off-putting.
It's always an interesting time when you share your lovely clean concept with potential philistines - hope your project's exposure to your loop-community goes well.
Most of the time there is a generous positive bias toward the favored language and excessive abuse of the worst features of the one that is the target of ridicule.
It's not just FP versus Other, either; it's sort of an industry meme in some sense.
That would be helpful, and I hope Notepad++ gains the feature. Notepad++ does have a bracket helper that highlights the other bracket, but flagging the closing bracket helps when there are several brackets.
[0] https://raw.githubusercontent.com/thblt/eziam-theme-emacs/ma...
NOTE: I'm not saying C-like syntax is objectively easier to read; I'm merely (very) skeptical of the OP's claim that F#'s syntax is somehow objectively better.
Note that F# is the only _truly_ (not purely) functional language I am used to.
I'm not convinced at all that's the case, as whitespace has never bothered me and it's less tokens to look at. But this is likely one of those subjective things. Some people prefer more syntax, some longer names. Some like duplicated code and some prefer it DRY.
Every time someone complains about significant whitespace, my first reaction is "what indentation style are you using where that's an issue?!"
At one point, we read "your abstractions should afford the right behaviours whilst make it impossible to do the wrong thing." Well, that would undoubtedly be helpful, but Turing may want to have a word with you about the feasibility of guaranteeing it. What does F# do in this regard? We are told it does not have nulls, which is nice, but what does it do to avoid someone introducing data that are semantically, rather than formally, null values (NaN, for example?)
The section on testing does not seem to contain anything that is F#-specific. it mentions QuickCheck, but that has apparently been implemented for many imperative languages, including C#. Is this sort of thing more effective for functional languages? There is no discussion of the possibility here.
I am actually persuaded that there are benefits to functional programming, but articles like this paradoxically give the impression that there is not much substance behind the large claims.
The first thing I would like to know about the case study was whether the implementers of one version benefitted from the problems encountered by those of the other, especially with regard to getting the requirements nailed down.
On semantic nulls, the compiler typically enforces completeness on case analysis.
I have learned and used FOR WORK almost a dozen languages now, F# being the last of the bunch. I totally remove many ineffective coding habits, mostly:
- Inmmutability
- The type checker making me aware of problems
- Not need to over-check for nulls because now I don't need to worry
- Pattern matching + AGDT are awesome. This cut the need to make a lot of unecessary classes.
- Make me to think outside over OO. I do this also in Pascal(Delphi) and python, and hate how C# is OO/Class for all. ---
About syntax:
F# totally cut millons and millons of lines of code! (exagerating!) but more important: Far less files than similar C# project (and yes, I have made the rewrites. I do rewrites for life! I work upgrading, fixing, moving, old codebases most of time).
However, some things that I hate of F#:
- Whitespaces must be as python. Is better.
- Not like to rely on type inference. Is a good idea on paper but have the stupid habit of only work AFTER you write. Put the types manually is better, IMHO. You get more clarity, ease readability, not need to rely on a IDE, the types are less obtuse, etc.
Ironically, VS Code show the types as you write but Visual Studio Mac no. I have both opened some times because this!
So, type inference is net negative for me. Maybe is my pascal love talking here.
I don't like how everything is "let". I wish it was "let, function, etc".
I don't like the overuse of spaces for everything. And how comas are for tuples but ; for separate items.
But overall? F# is better than C# most of time.
> F# are all much more than syntax issues
But some of the things that make F# unique are surfaced with the syntax. Is not just there for the sake of it.
Pattern matching is the most obvious thing. The way the syntax present this aspect is close to perfect...
ie: I'm for fun building a language. I have tried a lot of ways to present some things, like Pattern Matching. I can't figure a better way without resorting to weird characters, unicode or text editors that not exist. (I replace "|" as separator but the basic structure is just the same).
Other ways the syntax is significant is in how it encourage curried functions, chaining and pipelines (with |>). The pipeline way is something that I truly adore. Wish F# was more into this, IMHO.
Not fan in how let have different meanings, neither the use of "," for tuples.
Also is confunsing why functions with "let sum a b" are truly different to "let sum(a,b)". Hate this (because suddenly pipelines and curried application not work anymore).
The making of "inmutable first" and marking some vars as "mutable" is something truly nice. Could be easier if it was "let" and "var" instead of "let mutable" but anyway. Is good.
Anyway, as a amateur language designer it make me more aware in how the syntax make a language better or more ergonomic and lead the way in how use it.
So is not exactly according to me, but how is done in other languages. Anyway... try to design this, for example, and tell how you do it better ;)
I totally see how presenting "hello world" with python vs C++ clearly make the case "this thing is different and is good"
You need a more of information to truly make the case better, but I think is fair that a F# introduction start with the syntax. This show the mainstream audience "we are not in kansas anymore".
Certainly, make the article better in the second half is another thing. Like with movies is easier to start with a good premise and fall flat in the end ;)
It uses two indents for indenting the parameter list of the function, and two indents for indenting the body of the function.
Most indentation conventions suggest four space indentation for breaking up a list of parameters, and two space indentation for the body of a {}. In order to avoid the problem that the example criticizes.
It's pretty easy to be better when starting from such a low bar.
The vast majority of my comments are either javadoc-style function protocols or high-level discussion on subsystem purpose and architecture.
Leaning on this commenting style, I find that looking at past code of mine is much easier to re-grok than having lots of explanatory comments everywhere.
Having had to maintain and rebuild other programmers' monstrosities of monolithic functions, I appreciate short code routines and relevant comments, especially those that detail why the code is written that way. I then don't have to spend enormous amounts of time trying to understand the thought processes that went into making that code.
If you are worth your "salt", you will write clear code and when it needs to be "tricky", you detail "the why" that you needed to do this.
Different people have different standards as to what is clear code and often, this is dependent on the idiomatic usage within the language in question.
A real simple example for you to ponder:
if a < b & b < c & c < d & d <= e then { do something}
compare with
if a < b < c < d <= e then { do something }
In this particular case, the first is how many programmers would write the condition, whereas the second is idiomatically more correct. The second is also, in this instance directly self documenting.
Of course, how you will do this in your favourite language will be different.
1. Intolerance to nulls and absence of object intializers in constructors. Use of zero objects a-la Array.empty.
2. Semantic typing even for simple types (e.g. type Name = string).
I have no idea what causes this. I even run with Javascript completely disabled.
wowzers