Ineffective coding habits many F# programmers don’t have
functional.works-hub.com
functional.works-hub.com
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 ;)
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.
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.
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?!"
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.
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.
I have no idea what causes this. I even run with Javascript completely disabled.
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.
[0] https://raw.githubusercontent.com/thblt/eziam-theme-emacs/ma...
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).
wowzers
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:
const kittenNames = cats.flatMap(cat => cat.kittens)
.map(kitten => kitten.name);
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.
Times 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.