6 ms·
"...If you are sold with F# there is one important point to highlight. Do not treat F#, just another language with different syntax especially if you are famili
by DanielBMarkham 6y ago
"...If you are sold with F# there is one important point to highlight. Do not treat F#, just another language with different syntax especially if you are familiar with Python, Ruby, JavaScript, C#, etc. You have to embrace functional programming as a paradigm...."
I want to provide a little nuance around this, because I'd give the opposite advice.
Because F# is based on OCAML and an extremely popular IDE/framework, it is first and foremost a teaching language. In my opinion, for what it's worth, the biggest mistake other F# coders make is running off into Haskell-land and trying to drag the rest of the community with them. Yes, pure FP is love and goodness, joy and love, but that's not the place to start with your average coder who just wants to see what's cool. Fable and Elmish are
I learned functional programming by reading a lot of OCAML books and the only(!) F# book out at the time. I coded like shit. I couldn't help it; I came from a strong OO background.
But then I figured out that instead of coding everything perfectly, it was more important to finish a little bit at a time, then take a look at functional smells, things like mutation. Sometimes I could fix these smells, sometimes I couldn't. Over time I found I could fix all of them. At that point I was a functional programmer.
I love these high-level F# frameworks, and like I said it's the best way to get folks involved, but wow, folks are going to create some programming disasters using them. Yes, this happens all the time withe every new thing, but functional messes are an order-of-magnitude worse than imperative/OO ones. Good luck, guys! The destination is worth the journey.
- pydry 6y ago>functional messes are an order-of-magnitude worse than imperative/OO ones Why so?
- DanielBMarkham 6y agoSymbol/Semantic leakage. In imperative/OO, I'm organizing as I go using well-established and understood paradigms. In functional programming, you're making these paradigms as you go along, or at least you should be. If you come in with an "I'm going to kick some ass by making these classes to solve my problem" attitude, you're already headed down the OO track whether you realize it or not. Then, sure as night follows day, you or somebody else starts using a functional paradigm, say HoF. There's nothing wrong with any of that. As a professional, you want to be good in multiple paradigms. The problem is that you're mixing them all up, whether you realize it or not (Most of the time, programmers don't realize it) And now, every piece of code can go down two entirely separate tracks as you suss out your architecture. Not only is that not going to work, it's going to make debugging and maintenance a nightmare. This is very, very similar to how good C++ shops work. You've got to tightly limit the way you solve problems in order for all of the programmers involved to be to reason about the code.
- sbelskie 6y agoHow are these points specific to OO vs. FP, instead of just paradigm I know vs. paradigm I don’t? F# code, from my somewhat limited experience, seems to almost always be organized logically (top to bottom, types separated from functions) using well-established patterns. Obviously if you decide to write a bunch of classes and mutating code in F# when more idiomatic approaches would work, it’s going to get hairy. I feel like I’m missing your point.
- DanielBMarkham 6y agoHappy to talk about it. Ping me. I've already been misunderstood a couple of times in this thread and I fear it's not going to get any better. After all, this is really more of a conversation than a point-counterpoint situation.
- KurtMueller 6y agoThere are plenty of well-known functional language paradigms that have been discussed (and satirized) for decades.
- thomasz 6y agoIMHO it's tooling. The industry has spend decades to help you to deal with the kind of mess OO tends to produce. The tooling isn't anywhere close when you have to deal with a mess of functions returning functions that return functions.
- samus 6y ago> a mess of functions returning functions that return functions. That is just a curried function with three arguments. The bigger problem is that programmers still have to understand how Hindley-Milner type inference works to figure out type errors. This gets worse when you add in type classes, dependent types and (Yog-Sothoth help you) lenses. Your point is still true. For any language, it is important to have an IDE ecosystem in place. The tooling must enable developers to move beyond fighting the unruly or boring parts of the language and instead leverage it to work on the domain.
- thomasz 6y agoNo, not necessarily. Curried functions have their own problems because they are missing the vital parameter names. But that's not what I meant. Think of the classic memoization example (written without a compiler at hand...) let createMemoizer f = let cache = Dictionary<_,_>() let f' = fun x -> match cache.TryGetValue(x) with | (true,value) -> value | (false, _) -> let result = f x do cache.[x] <- result result f' The signature is ```createMemoizer (f:'a->'b) -> 'a->'b```, which is not very telling, especially if it is returned by yet another function, so that even the createMemoizer name is lost. In classic OO, I'd just get a Memoizer object that carries vital documenation and what not with itself, as it is passed around. It's no even that much longer, it's just not as elegant to use. class Memoizer<TINPUT,TOUTPUT> { Dictionary<T,TOUTPUT> cache = new Dictionary<T,TOUTPUT>(); Func<TINPUT,TOUTPUT> f; /* ... */ public TOUTPUT Invoke(TINPUT parameter) { if (!cache.TryGetValue(parameter, out var result)) { result = f(parameter); cache[parameter] = result; } return result; } }
- deleted 6y ago[deleted]
- jackfoxy 6y ago>functional messes are an order-of-magnitude worse than imperative/OO ones I find them an order of magnitude easier and safer to refactor. And I have done it a few times with production messes I was not familiar with. Immutable values instead of variables and idempotent functions (those not having side effects) makes refactoring a joy and the if it builds it works mantra is pretty much true.
- gameswithgo 6y agoThere is pretty broad agreement that pure functions and immutable variables are good to strive for. And since F# doesn't force you to do that even when it is a bad idea, things are quite nice. When mutating state is the simpler thing to do, you can do it. Where things can get weird is when you start passing partially applied functions to other functions, and then you use type inference on function signatures on top of that. Glancing at the function signature its completely opaque what is going on. Hopefully you aren't in github and have working code lens and then you get a type signature to help, but it gets very verbose and confusing, and is still hard to reason about, for me anyway. A thing I find helpful with F#, no matter what style you use, is always type out the types in function signatures. Yes this has downsides occasionally when a refactor might have just worked without adjusting types through a chain of functions, but I think it is a net win for productivity.
- deleted 6y ago[deleted]
- jackfoxy 6y agoI've been doing almost nothing but F# for over 7 years, so I need to remember back to some of the things that used to throw me. The signature never lies. Although I intellectually understood this early on, sometimes when I was in the throws of not understanding code my mind would not understand the signature or somehow would not grasp the important information it was telling me. When they get a little complex and include generics they can get difficult for humans to parse. The signature is truth. Stare at it, struggle with it until you get it. One of the things that attracted me to F# is that every page of code is much more information dense than any language I had previously worked with. While I consider this good, it can also be daunting.
- hardwaregeek 6y agoI had this happen with Rust. My Rust code as a beginner had a bunch of smells like unnecessary copies, overly verbose code, etc. But in the Maslow hierarchy of programming, working code is a prerequisite for idiomatic code. Better to write some bad, working code and slowly turn it into idiomatic, working code.
- gameswithgo 6y agoI also value F# for reasons that have very little to do with functional programming. I like the sum types, I like the generics, I like the syntax, but when I make things with it I do not get very functional, in fact I find the more aggressively functional approaches to be extremely confusing to reason about. Where functions take in partially applied functions and so on, it is very hard to read. Maybe thats fundamentally true, maybe it is just me, I don't know.
- phillipcarter 6y ago> Maybe thats fundamentally true, maybe it is just me, I don't know. I think the ergonomics of a high degree of functional programming haven't panned out to be real crowd pleasers just yet, so you're not alone in thinking that.
- KurtMueller 6y agoI mean, `List.map` is often used in a partially applied manner and, used in conjunction with the pipe operator, results in, imho, readable code: [1;2;3;4] |> List.map ((*) 2) |> List.reduce (+)
- gameswithgo 6y agowhat would the equivalent code without partial application look like here? Also, this is another pet peeve about 'functional style' F#, the use of actual Linked Lists, which is a huge performance pitfall to accept on modern hardware. And also that F# Seq are not well optimized.
- jolux 6y agoF# Seqs are .NET IEnumerables, which is to say they are iterators. They're lazy, and you can do things with them that you can't do with "strict" collections. In some ways they are faster than strict collections, in others they are slower. If you plan to be iterating over a Seq multiple times, you should probably turn it into a list or array first, just like in C#. That example without pipes is List.reduce (+) (List.map ((*) 2) [1;2;3;4]) Linked lists are common in any functional programming language, the performance is bad but that only matters if you're writing something performance-critical. Otherwise, the ergonomics dominate: lists are very easy to work with.
- specialist 6y ago> is first and foremost a teaching language Terrific insight. So important. As a Java partisan, I can say without reservation that Java is a suboptimal teaching language. So I easily believe your assessment of Haskell. > I couldn't help it; I came from a strong OO background. My functional eureka moment was deleting an element from a list using recursion. It literally changed how I see the world. I haven't done any functional programming in anger (for pay) in a long time. But FP absolutely has informed, influenced all that I do. Teaching multiple paradigms and styles is so important.