6 ms·
As a former OCaml hobbyist programmer my take on these kind of books or articles is that, yes OCaml is extremely elegant and beautiful as a programming language
by frankohn 4y ago
As a former OCaml hobbyist programmer my take on these kind of books or articles is that, yes OCaml is extremely elegant and beautiful as a programming language and it shines for simple applications. Some parts of it are actually not so elegant, for example the object oriented aspect completely spoil the elegance of the core language.
On the other side OCaml, as a pure functional programming language with immutable value by default doesn't scale well to large, complex application. Just the paradigm is no longer tenable and you need to switch at least partially to imperative programming with mutable variable. For example this is what it does the implementation of the OCaml itself.
To develop further the point about "what doesn't scale" there is also the function with unnamed arguments and currying. While extremely elegant for simple programs it gets confusing for real-world applications when function needs quite a lot of arguments and there is no longer any obvious order to give them. If you stick with that and you choose an order it becomes arbitrary, difficult to remember and currying no longer makes a lot of sense.
Functional programming with immutable values is a wrong pattern for programming languages. Many algorithms, almost all actually, are naturally expressed in imperative style with mutable arrays or variables.
What is needed is to bridge the good things from OCaml, the type system, the pattern matching with tagged types into a modern, imperative programming languages.
Rust is a sort of answer but they got it wrong because it is too low level about managing the memory, the ownership pardon, and everything else so programmers cannot just express the algorithm or the logic they want to implement but they have to spend a lot of mental energy thinking about ownership issues and unneeded accidental complexity like lifetime annotations.
- octachron 4y agoConcerning the issue with function arguments, this is one of the reason why OCaml has labelled arguments. And for instance, Janestreet's idiom udes labelled arguments as often as possible. Similarly, I am not sure what is the issue with using imperative OCaml for imperative algorithms when they are a better fit for the problem at hand?
- frankohn 4y ago> And for instance, Janestreet's idiom udes labelled arguments as often as possible. So you have to give up to one of pillars of the functional programming paradigm and you get a less elegant but more practical programming language. Otherwise I agree that using labeled arguments is mostly fine and doesn't completely spoil the language. The more serious compromise to the functional programming paradigm is the fact that you need to use mutable variables and imperative style programming. Once you do this you lose most of the elegance and attractiveness of functional programming. > Similarly, I am not sure what is the issue with using imperative OCaml for imperative algorithms when they are a better fit for the problem at hand? What I mean is that for any moderately complex application you need to switch to imperative style so the appeal of OCaml is mostly lost. You better choose a programming language that is designed for imperative programming since the beginning. The arguments I am giving explains why there are practically no real world applications done in OCaml. Some people insist using OCaml because they love the elegance of the language and I understand them but reality is it doesn't scale to complex applications. For people in Janestreet I think this is a sort of niche where they get an added value from OCaml thanks to its superior typing system and compile-time detection of many errors. I guess they care really a lot about the business logic of their applications and OCaml shines to ensure it is correct so for them the advantages out-weights the inconveniences.
- octachron 4y agoI am not sure which pillar of functional programming is lost with labelled arguments? Partial applications still work, higher-order function too. One might need to use anonymous functions when labels does not match but I don't see any pillar being lost. In the same way, it is perfectly possible to switch to the imperative style only in the specific code path where performance really matters and use abstraction to isolate this performance-sensitive part from the rest of your application. Ideally, you can then keep both the elegance of functional programming and the performance of imperative programming.
- deleted 4y ago[deleted]
- deleted 4y ago
- substation13 4y ago> Functional programming with immutable values is a wrong pattern for programming languages. Many algorithms, almost all actually, are naturally expressed in imperative style with mutable arrays or variables. The optimal implementation might be imperative but that doesn't mean we need to define our code that way. SQL is a good example here.
- agentultra 4y ago> The optimal implementation might be imperative There are times when it isn't. I'm thinking of Richard Bird's functional pearl, The Smallest Free Number, where the divide-and-conquer algorithm is faster than the imperative one. From the conclusion: One of the differences between a pure functional algorithm designer and a procedural one is that the former does not assume the existence of arrays with a constant-time update operation, at least not without a certain amount of plumbing. For a pure functional programmer, an update operation takes logarithmic time in the size of the array.1 That explains why there sometimes seems to be a logarithmic gap between the best functional and procedural solutions to a problem. But sometimes, as here, the gap vanishes on a closer inspection. The ability to arrive at the divide-and-conquer algorithm is quite fascinating as it uses plain, boring old mathematics and is, for some, quite straight-forward to derive on one's own. Yet people are more convinced by imperative implementations. I'm curious why this is. Is it because we "teach" people to believe programs are executed sequentially and are therefore somehow "inherently" imperative? Or is it easier to reason about algorithms in terms of their operational semantics?
- throwaway17_17 4y agoI tend to believe that there is an issue of map/territory confusion in teaching ‘programming’ and in general thinking about programming. Programs are usually envisioned as being statements which are performed in a sequence one after the other. This is comparable to the nature intuition about what an algorithm is, i.e. a series of steps to achieve a result when given some input. The problem then lies in the deeper explanation. It is said that the hardware is just taking instructions and executing them one after another, and that is then mapped to the execution of programming language statements. The issue of ‘imperative’ vs ‘non-imperative’ implementations is really a question of granularity. The map between machine instructions and language constructs is so large (for most every modern machine) that while the imperative algorithm seems more reflective of the underlying architecture it is just a layer of cover up to make programmers feel better. I really think that the appearance of control over fine grained instructional behavior give people the feeling that their imperative code is truer/more-correct in relation to the machine. This feeling leads to the presumption that imperative is more efficient. As a final caveat, the ability for a non-imperative algorithm to be equivalent to its imperative alternative tends to rely on the language’s compiler (and the restrictions inherent in the non-imperative language). This is the origin of the ‘with-a-smart-enough-compiler’ argument that people sometimes level against non-imperative programming, i.e. there is not a compiler smart enough to take your high level language and produce the same machine code I do in my low level language. So I’m sum, I think it is both a teaching error (the continual re-enforcing of confusion between what a program says vs what a machine does) and a general human level confusion as to what an algorithm is at the level of silicon in 2022.
- baby 4y agoBtw you can actually write loops and mutate things with the ref keyword. It’s usually much clearer than writing a loop recursively but not everyone uses it. The impossibility to return early in a function does create really convoluted code though. I’m wondering if there’s a solution to that in FL
- cdaringe 4y agoI’m with octaron on this one. No disrespect, but a bit of hand waving and big claims. I’m kindly invoking Hitchens razor, here
- throwaway894345 4y agoI generally agree, and I'm hoping that Gleam fits the bill for "Rust with garbage collector" (also, when I say this, people leap to OCaml, but OCaml introduces a bunch of problems which aren't present in Rust: cryptic syntax, lower quality build tooling, unbounded type inference, competing "standard" libraries, a fairly toxic community, etc).
- kitd 4y ago> OCaml introduces a bunch of problems which aren't present in Rust: cryptic syntax ... Hmm, I think "cryptic syntax" is probably in the eye of the beholder
- throwaway894345 4y agoI'm sure there's some element of subjectivity, but at a minimum there's something to be said for "unfamiliar to the overwhelming majority of programmers". I think it's also very likely that OCaml's incredibly terse syntax (and terse naming conventions) is objectively difficult to understand from a "how the human visual/symbolic processing pipeline works" perspective, but I don't have the data to back that up (I also don't think OCaml is alone in this regard).
- int_19h 4y agoThing is, if OCaml is terse and weird syntactically (and I would agree), then so is Rust. Especially once you have to spell out signatures.
- throwaway894345 4y agoRust’s syntax is based on C and C++. Yes, it introduces additional concepts and gives them new syntax, but function calls, parameters, struct definitions, struct initializers, function definitions, etc are all very familiar to most programmers.
- hedora 4y agoHow does gleam handle thread safety and asynchrony? Rust leans heavily on the borrow checker for that. The Rust with GC proposals I've looked at all break compile time thread safety checking.
- kaba0 4y agoThere are two ways to improve performance of a given program. You either go lower level, giving more control over the execution and specifying what you want exactly, or you go higher, not constraining the execution as much, allowing for better optimizations. For example a manual for loop will almost always be harder to optimize than a map. The latter gives explicit permission for reordering, allowing for vectorization and parallelization automatically. Also, I would think twice before claiming FP non-ideal for PLs - modern CPUs employ just as much FPism as they are considered imperative. OOE doesn’t sound too imperative to me. And at the end of the way, imperative steps are just sequential state changes from a different perspective.
- the_duke 4y ago> modern CPUs employ just as much FPism Do you have some references where I could learn more about that?
- kaba0 4y agoWhat I mean mostly are out-of-order-execution (reorder these instructions and return results as if they were executed in order - it is worthwhile because useful work can be done “in the background” while the CPU waits for memory. But this transformation is not too imperative in my opinion) and SIMD instructions (and in extension GPUs) are much more about transformations on data than a traditional Turing-machine - but there are no sharp boundaries anywhere here. It is just not smart to dismiss such a big and important part of CS, when it has plenty of applications.
- jimbokun 4y ago> Many algorithms, almost all actually, are naturally expressed in imperative style with mutable arrays or variables. https://www.goodreads.com/en/book/show/594288.Purely_Functional_Data_Structures https://www.goodreads.com/en/book/show/594288.Purely_Functio...
- yuppiemephisto 4y agoHave you read the book and implemented its algorithms?
- kn8 4y ago> What is needed is to bridge the good things from OCaml, the type system, the pattern matching with tagged types into a modern, imperative programming languages. Like https://rescript-lang.org/ https://rescript-lang.org/?
- zumu 4y ago> To develop further the point about "what doesn't scale" there is also the function with unnamed arguments and currying. Not sure what you mean by unnamed arguments, but criticizing automatic currying is totally valid. > Functional programming with immutable values is a wrong pattern for programming languages. Many algorithms, almost all actually, are naturally expressed in imperative style with mutable arrays or variables. This is a huge jump. Any imperative solution can just be expressed as a fold of some sort and many algorithms, esp. those that use stacks, are easily expressed recursively. Which one is more "natural" is 100% subjective, but I'm in the declarative is easier to reason about than imperative camp. Moreover, the idea that "unnatural" algorithm expression implies "doesn't scale" needs much more elaboration. > What is needed is to bridge the good things from OCaml, the type system, the pattern matching with tagged types into a modern, imperative programming languages. > Rust is a sort of answer but they got it wrong because it is too low level about managing the memory, the ownership pardon, and everything else so programmers cannot just express the algorithm or the logic they want to implement but they have to spend a lot of mental energy thinking about ownership issues and unneeded accidental complexity like lifetime annotations. It's not "wrong" just not what you want, and honestly the ownership model isn't that bad. The overhead amortizes somewhat as you get used to it. I of course agree there is room for more languages with ML-like type systems though!
- dunefox 4y agoFlix https://flix.dev/ https://flix.dev/ and coalton https://github.com/coalton-lang/coalton https://github.com/coalton-lang/coalton seem promising.
- int_19h 4y agoIronically, I find the OOP part of OCaml to be the most interesting, seeing how it manages to provide a self-consistent and powerful structurally typed object model that is no less powerful than what you get in C++, for example.
- yawaramin 4y ago> Functional programming with immutable values is a wrong pattern for programming languages. Let me refer you to the most successful programming language of all time, by far–Microsoft Excel formula language. It's: - Functional (data transforms are purely function applications, there's even a LAMBDA function now) - Immutable (functions in the system can't change values, only consume incoming values and output outgoing values).