[HN Gopher] My thoughts on OCaml ___________________________________________________________________ My thoughts on OCaml Author : ghuntley Score : 150 points Date : 2023-04-25 12:44 UTC (10 hours ago) (HTM) web link (osa1.net) (TXT) w3m dump (osa1.net) | Miltnoid wrote: | I used OCaml as my daily driver in grad school and my postdoc, | and recommend my students use it. I think the main benefits of | OCaml are: | | Functional Has mutable references Not lazy | | Haskell's type system is just better. But for large, complex | systems that require performant code, it can be quite difficult | to track the laziness, and sometimes life is just easier if I can | use a mutable references. | | The author is completely correct that there's a big failure in | the lack of a type-class-like system. This is why people ask so | much for modular implicits. Core is also a decent standard | library (though I don't know why they require sexp_of_t and | t_of_sexp on like all their data structures). | | So I'd gladly use a different functional language with mutable | references and strict evaluation. But there isn't any besides F#, | and I have a Mac and don't personally want to figure out .NET on | Mac. | yawaramin wrote: | I think looking at it as a 'failure' or 'deficiency' is the | wrong mindset. OCaml makes a different tradeoff than Haskell. | In exchange for not having ad-hoc polymorphism, OCaml's functor | system allows much quicker compilation and easier-to-understand | type errors. Like everything in tech, it's a tradeoff. | ywei3410 wrote: | They require `sexp_of_t` and `t_of_sexp` because of their | workflow being based on them; such as expectation testing [1] | and tools such as [2]. It's a less noisy alternative to json | for many people. | | [1] https://blog.janestreet.com/testing-with-expectations/ [2] | https://github.com/janestreet/sexp | owenm wrote: | Starting with F# on Mac is worlds away from what it was in the | past, I bet you could be up and running in 5 minutes: | brew install dotnet-sdk dotnet new console -lang F# (in | your project directory) | | Start VS Code in that directory, install ionide via extensions, | write code... dotnet run | JaggerJo wrote: | Exactly. And you can even pick between 3 IDEs | | - JetBrains Rider (best IMHO) | | - Visual Studio for Mac | | - VS Code with Ionide | devmunchies wrote: | Sublime text too with the fsautocomplete LSP server. | frou_dh wrote: | Knowing the virtually dead Standard ML is a curse when it comes | to appreciating the aesthetics of OCaml. The two languages have | similar syntax, but in just about every way they differ, OCaml | syntax took the ugly road :( | choeger wrote: | Simply put, OCaml is very principled. There is a good reason for | pretty much every design decision. | | The only thing one could argue is the lack of type classes, i.e., | overloading. (Calling it "interfaces" is somewhat wrong.) | jorkadeen wrote: | An alternative to OCaml is Flix (https://flix.dev/) which | attempts to address some of the critiques in the post. For | example: | | > Bad standard library | | A particular goal of Flix is to have a consistent standard | library based on type classes. | | > Standard types are sometimes persistent, sometimes mutable. | List, Map, and Set are persistent. Stack and Hashtbl are mutable. | | In Flix immutable types are named List, Set, Map, etc. and | mutable collections are prefixed with Mut, e.g. MutSet, MutMap, | etc. Mutable data types also carry a region. | | > Also, OCaml has no tracking of side-effects (like in Haskell) | | Flix has an effect system that tracks purity/impurity. | | (I am one of the developers of Flix) | weatherlight wrote: | Complete Feature List algebraic data types | pattern matching first-class functions | extensible records parametric polymorphism type | classes higher-kinded types light-weight | polymorphic effects type aliases Hindley-Milner | type inference CSP-style concurrency buffered & | unbuffered channels first-class datalog constraints | polymorphic datalog predicates constraints with lattice | semantics stratified negation interoperability | with Java unboxed primitives keyword-based | syntax redundancy checks monadic let\* | expressions expressions holes compilation to | JVM bytecode full tail call elimination core | standard library parallel compiler architecture | human friendly errors interactive mode Visual | Studio Code support | | Nice! | networked wrote: | I have been keeping an eye on Flix. Thanks to you and rest of | the Flix team for working on a bold but practical language. I | wish you success. I think Flix is one of the most promising | languages in the ML family. (Do you consider it an ML?) I love | that it has embedded Datalog. | | I know it is pre-1.0, but I would still like to ask about | adoption. Are there notable third-party open source projects in | Flix yet? By "third-party" I mean created outside of Aarhus | University and the University of Waterloo. | jorkadeen wrote: | Thanks for the kind words. | | Yes, I would definitely consider it a language in the ML- | family. Its core is based on Hindley-Milner (like StandardML, | OCaml, and Haskell) which I think is the hallmark of ML- | family languages. Flix sits neatly between Ocaml and Haskell | in that it allows side-effects (like Ocaml, unlike Haskell) | but they are tracked by the effect system. Hence one can | program in the spectrum between those two languages, but get | strong guarantees from the compiler. | | In terms of adoption, we see a lot of people trying it out, | and there are some packages out there already, but nothing | too big yet. We recently added support for package | management, so hopefully that will help the community grow. | networked wrote: | Nice to hear about the package management. | | Sorry, I wasn't clear. I meant to ask whether you | considered Flix "an ML" as opposed to just being in the ML | family. It's an ontological question, and not important, so | don't sweat the answer. I am just curious how Flix | developers see their project. | ywei3410 wrote: | I don't think this list is fair at all. | | > No standard and easy way of implementing interfaces | | Firstly another commenter has mentioned [1] modules as | interfaces. There is also the object system [2] which I've seen | used to great effect in similar scenarios. | | Typeclasses and modular implicits are fantastic - being a heavy | user of Scala and Rust; but I've always found that they tend to | be best when used as an alternative to macros, for derivation | purposes. | | The interface and code split in OCaml is also really nice for | value types and comes with no limitations; for years, we had | issues in Scala because of the host platform, and in Rust | exposing functions for your newtype can be oddly boilerplate | heavy. | | > Bad standard library | | The OCaml standard library is (infamously) anemic and obviously | is not as fully-featured as something like Java or Python, but | since alternative libraries tend to be compatible with the | bundled StdLib (both use the same `option` and `list` type), it | means that having libraries dependent on different ones is not a | huge issue. Learning resources, can be confusing though. | | I'm not sure why it's a problem certain data structures are | mutable and some are persistent; the documentation is usually | fairly clear and you usually have a performance characteristic in | mind when selecting your data structure. | | Universal equality and hashes are indeed a little annoying (here | an `Eq` typeclass would be great!) but I do understand the | original decision to have them inside. At the very least, it's | not pointer based equality and since records and most types don't | have subtypes, you don't run into as many issues. You can of | course use the equality methods on the modules themselves and | since `Hashtbl` and co have functors, you aren't penalized for | it. | | Strings are a problem-ish (what older language doesn't have | string issues ha!), but there is utf-8 support now with [3]. | | > but just two years ago it was common to use Makefiles to build | OCaml projects | | It is true that OCaml has a unix-y flavour, but honestly I don't | really understand the snipe at make. Sure there are alternatives, | but make is bundled on most distros, has a quick startup time and | a lot of people are familiar with it. | | More of the community is moving towards dune as well, which is | pretty good; the docs could do with better indexing but there are | some features such as promotion which are really nice. | | > was no standard way of doing compile-time metaprogramming | | I haven't used ppx much so I can't really comment here. | | Finally they've missed the biggest reasons to /use/ OCaml. | | [1] https://news.ycombinator.com/user?id=4ad [2] | https://ocaml.org/docs/objects [3] | https://v2.ocaml.org/api/Stdlib.String.html#utf_8 | boxed wrote: | > Finally they've missed the biggest reasons to /use/ OCaml. | | You missed writing what they are :P | xvilka wrote: | Building, LSP server, and immutability are definitely better in | 2023 than author describe in their article. | nequo wrote: | In what sense is immutability better? What changed? | kthielen wrote: | This piece focuses on complaints about ocaml, and the last half | seems to just be syntax complaints (which I mostly ignore just | because every language seems to have awkwardness in its syntax -- | certainly Haskell, a language otherwise held up as an example, | can be shown to accept some really weird text). | | Good things about ocaml that I think should be mentioned are | Hindley-Milner type inference (some might argue this, but it's a | very effective system for inferring types), and also | straightforward semantics (it's usually pretty easy to look at | some code and make a reasonably accurate guess about what it will | do on real hardware -- compare with Haskell). | | I'm sympathetic toward the initial complaints here, which I also | think are the most substantive. Type classes and qualified types, | as in Haskell, would be very useful in ocaml/ML. The examples | offered (show_of_int, show_of_double, ...) are valid, and also | the related complaint that dishonestly broad types like | structural equality (where runtime errors are raised for cases | where the types are actually not supported) also make a | compelling case for qualified types in ML. | | I made this project ~10 years ago after making similar | observations: https://github.com/morganstanley/hobbes | | Things like structural equality and ordering are user functions | here rather than magic internal definitions (e.g. equality is | defined as a type class, and type class instances can deconstruct | algebraic types at compile time to decide how to implement | instances). But evaluation is eager by default, so it's pretty | easy to reason about performance. And data structures can be | persisted to files and/or shared transparently in memory with | C/C++ programs without translation, also very convenient for the | places where this was used. Actually we built some very big and | complicated time series databases with it, used in both pre and | post trade settings where ~15% of daily US equity trades happen. | So I think these observations are useful and have passed through | some pretty significant real tests. | Joker_vD wrote: | > It also has the "dangling else" problem: | | God I hate this term because it's not even a _problem_. For a | human reader, it 's pretty obvious that an "else" should belong | to the closest "if"; and it's quite difficult to write a | mechanical parser that would match "else" with the furthest "if" | instead. So what is, exactly, the problem? That the grammar is | technically ambiguous? Who cares? The authors of LR-generators? | They don't, they have been resolving shift-reduce conflicts by | default in favour of shifting for half a century already. | manymanydots wrote: | > No standard and easy way of implementing interfaces. | | > In Haskell, this is done with typeclasses ... | | > In OCaml there's no way to do this. I have to explicitly pass | functions along with my values, maybe in a product type, or with | a functor, or as an argument. | | Haskell is a mixed community / paradigm language too. | | I routinely program in "Braindead Haskell" (keep stuff simple and | direct as much as possible). Funnily, in that programming style, | using typeclasses for _all_ interface types is bad style. | | Instead, the Handle pattern [0] can be used. Which really is just | shoveling functions in a product type. And imagine - it works | worderfully. | | Everyone has to find their own style I guess. | | [0]: https://jaspervdj.be/posts/2018-03-08-handle-pattern.html | Jeff_Brown wrote: | > the Handle pattern | | I love this so much. | KingOfCoders wrote: | I found that one hilarious | | "The regex module uses global state: string_match runs a regex | and sets some global state. " | debugnik wrote: | The builtin `str` regex library is obsolete anyway, most people | use `re`[1], or sometimes bindings for pcre or Google's re2. | | [1]: https://ocaml.org/p/re/latest | ronnier wrote: | Why would a company use ocaml? | | I've worked in software now for most of my life, from small to | the biggest companies. I've watched teams with senior developers | use non mainstream languages to build services and just about | every time those devs get board again, leave the team, leaving | them stuck with this system that's hard to develop and different | from the rest of the orgs, eventually resulting in a complete | rewrite. | | At companies I try to be boring, not weird, and efficient. Which | usually means for me, writing in a very mainstream language that | is easy to hire for. | kaba0 wrote: | It won't really answer your question, but I found the Signals & | Threads podcast fantastic, and it made me appreciate OCaml much | more (it is produced by someone working at Jane Street). | | I personally found the MirageOS, build systems and language | design episodes terrific. | jlouis wrote: | A language like ocaml excels when the input and output are | program-like, and the problem can't be number-crunched by | hardware. The typical example would be a compiler. Other | examples would be model checkers, code generators for number | crunching libraries, proof assistants, and so on. Complex | problems, where you need to explore different avenues of | approach quickly, and where symbolic manipulation is at the | heart of the problem. | | Thus, ocaml is best used as a "kernel" or "module". The | contextual part of your system uses the part written in ocaml | as a service. | | But ocaml doesn't excel at problems which are common to a | typical company. The people who are in the intersection of | wanting to solve those problems, and ocaml developers are few. | The same intersection with a more mainstream language is far | larger. | | However, if you find yourself with a problem at which ocaml | excels, chances are the mainstream languages ends up falling | short very quickly, because they tend to lack the abstraction | features necessary for correctly handling the complexity. | riku_iki wrote: | The question is if modern versions of popular languages | (java, c#) caught up on ocaml's syntax sugar, or maybe this | syntax sugar is trivially replaceable by some utility | functions. | | Maybe you could bring any example of such ocaml | functionality? | the_af wrote: | > _Why would a company use ocaml?_ | | PG claims using Lisp was the "secret sauce" to his early | success. Whether this is true or not, and whether this is still | true for companies today, remains to be seen. But it's an | argument _for_ things like OCaml. | decafninja wrote: | I'm sure some companies out there might find a competitive | advantage for using exotic languages and tech stacks. | | But I get the impression the more common scenario is that the | engineers were bored and given the chance, wanted to play with | something new and shiny. | | It also depends on the company's prestige. A company like Jane | Street using OCaml? Considering both their prestige and the | amount they pay, I'm sure they will have no problems hiring. | Nor will their alumni have problems getting hired elsewhere. | | Some no name startup or even some "innovation lab" inside a | boring cludgy Fortune500? Yeah, no thanks. | tikhonj wrote: | I worked on a team using Haskell at Target for a few years | and we had a _massively_ easier time hiring compared to the | other data science /ML teams. I figure it came down to two | things: | | 1. People are actively interested in using Haskell and | working with others interested in Haskell/PL/FP/etc. More | people want to do Haskell than there are Haskell jobs and | Haskell openings get massive word-of-mouth advertising. | | 2. It's a way to show that we're willing to do something | different and interesting. Everybody _says_ they are, but how | do you actually demonstrate it? I remember one of the first | great hires we had was an OR professor who didn 't even know | about Haskell--but joined in part because what we were doing | was different and innovative. | | If I ever end up starting my own company, I'm going to use | Haskell because it's so much _easier_ to hire for it-- | completely opposite to the superficial "common knowledge" | that guides executive decisions at large companies. | felixyz wrote: | Jane Street wasn't all that prestigious when they started | using OCaml. The way they put it is "it was easier to find | great programmers in the empty set of people who know OCaml | than in the huge set of other programmers" (roughly). So, on | the contrary, I think it might help boring cludgy company | find quality talent. | | I think there is a fallacy being repeated in this thread, | namely that the choice is between mainstream and weird, or | between old-and-tested and new-and-shiny. No, that's not the | choice. The choice is between tools and abstractions that are | helpful and productive, and those that aren't. Some of the | helpful and productive things are old, some are new, some are | mainstream, some are niche. | | I don't fully understand how someone wants to spend their | career in programming and not actively seek out what is | helpful and productive, rather than what currently gets the | most questions on Stack Overflow or has a big pool of people | who put it on their CV. It's not like all languages are | equally good. It's a wasteful approach. | decafninja wrote: | There's another aspect, which myself and a few others in | this thread have mentioned - hireability. Both from the | company's and employee's perspective. | | I'm pretty sure Jane Street would have paid well even when | they were an obscure name. | | Will boring cludgy Fortune500 company or noname startup pay | well for someone to come use an exotic tech stack that is | used very rarely at any other companies? Likely not. | | Likewise, would an engineer well versed in some exotic | language, and can land offers at top companies, come work | for a boring cludgy Fortune500 company that is only willing | to pay a fraction of what they can get elsewhere? | | Maybe if you've already made your fortune elsewhere and | well on the road to FIRE, and are really just looking for | interesting work regardless of pay. | felixyz wrote: | So are you saying that Jane Street more or less bribed | people who didn't really care for OCaml to come work in a | weird language, by offering a good salary? I find that | far-fetched, and there are many other examples of | companies attractive highly-competent people because they | choose -- no, not exotic, weird, or niche tech, but | _good_ tech, that aid people thinking more clearly and | abstracting better. | | As in everything else, there is a trade-off involved here | of course. And what is truly good and helpful is by no | means obvious, and might take time to assess. | decafninja wrote: | "Bribed" is a crude way to describe it, but in a manner | of speaking, yes? | | I think you might be a tad idealistic, and you might | think I'm being a tad cynical. Maybe the truth is in the | middle somewhere. | | But throwing enough money at someone can talk. And not | throwing enough money at someone can also talk. Short of | being asked to do something unethical or illegal, what's | wrong with that? | | Can you honestly say all of the talented top engineers | out there working for top paying companies are doing it | for the pursuit of technological perfection, and aren't | doing it at least partially for the good money? | | I would say the same thing about doctors - people all | love to say you should not get into medicine for money, | but can we honestly say money isn't at least a factor in | whether someone decides to pursue a MD? | felixyz wrote: | > I think you might be a tad idealistic | | Agreed :) | yawaramin wrote: | Ron Minsky of Jane Street has explicitly said, many | times, that their choice of OCaml _attracted_ great | developers who wanted to work with it. | lallysingh wrote: | .. that's a complaint for all new languages. You don't know | which ones will stick until you try. | | Code in a strongly-typed functional language is easier to | maintain because of the features of the language. The | functional part gives great abstraction, and the strongly-typed | part gives enough structure to make the whole thing tractable. | rashkov wrote: | Absolutely agreed. I'm lucky to work on a ReasonML front-end | codebase and the type system and immutability are a real | pleasure to work with. The strong typing lets you code | against the compiler, and there are many instances of "if it | compiles it works" that crop up throughout development. | There's no fear of refactoring code that you didn't write, | because the compiler will warn you about consequences on the | far end of the codebase that you didn't even know existed. | | As a result of all of that, the app feels very solid to use | and has comparatively few runtime errors. | giraffe_lady wrote: | Very cool to hear. I've been using rescript some recently | and was hoping for exactly that sort of benefit. | | I have a mixed opinion of ocaml itself having used it | professionally a moderate amount. But one thing I can't | deny is that ocaml programs seem to "age" better than any | other languages, even other strongly typed languages. | anthk wrote: | MirageOS and microkernels. | yafbum wrote: | Back when I tried it, there seemed to be a few verticals where | Ocaml abstractions made Ocaml very productive to work with. | | Once a colleague and I did a programming race between two | languages, to see how long it would take to implement a small | cryptanalysis program using Ocaml vs C++. I wrote it in a day, | and it took my desk neighbor who was doing the same in C++ a | couple of weeks. | | What made the difference: - There were a lot of things I didn't | have to worry about. For example, I was working with nontrivial | structures but didn't have to write any boilerplate functions | for deep equality or lex ordering. - Immutable structures with | GC made it much more direct to translate the math into a | working program. Ditto with guaranteed tail recursion | optimization. - The match syntax caught many issues at compile | time by forcing me to reason about every case. As a result I | had many less runtime bugs to deal with, while my colleague | needed to troubleshoot memory and correctness issues half the | time. | | Ymmv. This was for research and didn't end up in production | use. The C++ implementation was also much "closer to the metal" | so was able to wring out more performance. Still the difference | in initial velocity was striking and there could've been a case | that this would've been a better choice based on dev velocity | in that vertical. | substation13 wrote: | > Why would a company use ocaml? | | Why _wouldn 't_ a company use Brainfuck? | vrglvrglvrgl wrote: | [dead] | JaggerJo wrote: | I think you should give F# a shot instead. | | 1. No standard and easy way of implementing interfaces | | No problem in F#, you have interfaces, abstract classed, ... | | 2. Bad standard library | | In F# you have access to the full .NET standard library and | ecosystem. There are also quite a lot of libraries that are | especially designed to take advantage of F# (SQL libs for | example). | | 3. Syntax problems | | 3.1 OCaml doesn't have a single-line comment syntax. | | It's `//` for single line and `(* comment *)` for n line comments | in F#. | | 3.2 It has for and while, but no break and continue. So you use | exceptions with a try inside the loop for continue, and outside | for break. | | Same in F#, use recursion. | | Generally F# solves some of the issues but definitely not all of | them. Some are just in the nature of the Standard ML syntax I | guess. | | 4. Rest of the package is also not that good | | - NuGet is a decent/good package manager. - Easy to install a | complete dev env. - sane build system (MSBuild) - Great out of | the box support in JetBrains Rider/ Visual Studio (for Mac/ | Windows) or via a VS Code plugin (Ionide). | veec_cas_tant wrote: | Wanted to add my experience here: after writing C# code most of | my professional life, F# is a breath of fresh air. The type | system makes so many issues and annoyances I've lived with in | C# just disappear. Much like Rust, the compiler is capable of | providing so much more value when the type system is so | expressive. You can bang out a lot of code quite quickly and be | pretty darn sure it will work just because it compiles. | daxfohl wrote: | > NuGet is a decent/good package manager | | I remember F# designed its own package manager (paket) because | of NuGet's deficiencies. I haven't been on that ecosystem for a | while now. Have things changed? (My memory is that paket added | friction and I eventually dropped it for nuget on my personal | projects). | ecshafer wrote: | F# is such a great language. | | The fact Microsoft seems to pretend it doesn't exist is | bewildering. Surely the .Net ecosystem can have two languages | being promoted. | pjmlp wrote: | Before F# came into VS 2010, we already had C#, VB, C++/CLI | (nee Managed C++), IronPython, IronRuby, and all the third | parties targeting the CLR. | | It is really not having a clue what purpose they want for F#, | I bet they have repented to ship it on VS2010. | | First it was for libraries only, then during the VS Express | days it was for Web development, now they are trying to pivot | it into data analysis and ML, while DevDiv manages to bring | Guido out of retirement and finally manages to pursuade | CPython core team about improving its performance. | AlotOfReading wrote: | F# was originally just kind of a passion project that MS | insisted get productized to continue development. It's | still very much an internal favorite within Microsoft | Research and the Bing team, from my understanding. | pjmlp wrote: | Nowadays most of those folks are no longer at MSR, Don | Syme has moved into Github and I doubt they are into F#. | JaggerJo wrote: | Don is still working on F#. The F# team at MS now is | bigger than is was years ago. People from different | companies and the community are collaborating on compiler | and language features. | | F# is beyond microsoft, a lot of early adopters are still | there. | pjmlp wrote: | Stand corrected on that one. | | What are the fruits of that increased team on the VS, | VS4Mac and VSCode tooling, and .NET SDKs? | pyjarrett wrote: | > F# is such a great language. | | I very much feel this, I found it productive, but it feels | like a complete ghost town. I'm considering swapping to Go | over using .NET for tooling/scripting. | JaggerJo wrote: | Really? Were you active in the slack/discord channels? | pyjarrett wrote: | I was poking around https://forums.fsharp.org/ and | https://www.reddit.com/r/fsharp/ | hpstewed wrote: | I use F# at work. Really love the language but the connection | to the dotnet ecosystem is a curse as much as a blessing. | Having to operate in a world of nulls and exceptions can | reduce/nullify the amazing benefits of discriminated unions. | Often you are back to relying on the due dilligence of other | developers as a result. | | I use a lot of rust in my spare time. The runtime guarantees it | offers make other languages feel like a house of cards in | comparison, including F#. I coincidentally picked up an ocaml | book yesterday seeking a more 'robust' alternative to F#. | Unfortunately it seems as though ocaml is even more niche than | F# (I doubt I'll be working for jane street any time soon) | WorldMaker wrote: | The C# compiler got a ton of new smarts around reference type | nullability and almost all of the BCL (.NET standard library) | got annotated for it. I don't know when F# will finally pick | up all the new compile-time smarts for Nullable Reference | Types, but it has been proposed and prototyped, at least [1]. | | [1] https://github.com/fsharp/fslang- | design/blob/main/RFCs/FS-10... | ragnese wrote: | > Having to operate in a world of nulls and exceptions can | reduce/nullify the amazing benefits of discriminated unions. | | I've been saying this about Kotlin on the JVM for ages. Using | a Java dependency is an absolute last resort for me for those | same reasons you mention about .NET from F#. | HdS84 wrote: | I really wish to like F# but the line noise kills me every | time. I would kill for a "visual basic" f# where the absurd | terseness is replaced with a little less noise. E.g. instead of | seq use sequence, instead of abstract one letter symbols use | words. | JackMorgan wrote: | Perhaps you mean mix a little more noise into the dense | signal? There's very little noise in F#, it's almost entirely | dense signal. | | One good thing is the ability to bind new operators, so if | you don't care to learn to read <> you could make it | something else. Or you could have a local binding for | Sequence so you don't have to use just Seq. | | It's also possible to avoid almost all operators if you don't | like them. There's nothing wrong with using a limited pool as | long as you can get done the job! | | I also prefer to name variables, functions, and parameters | with nice clear names, and I've not found F# to get in the | way of that. | JaggerJo wrote: | what noise? | | I get your example of 'seq { 1; 2; 3; }' but list, array are | not even shortened. | HdS84 wrote: | Sorry, I literally forgot them. A lookup here refreshed my | memory: https://learn.microsoft.com/en- | us/dotnet/fsharp/language-ref... | vips7L wrote: | This is my exact issue with many "modern" languages. The code | is not meant to be read later. | amyjess wrote: | OCaml is one of those languages (like Raku and Nim) that I really | want to learn and do something in at some point but usually end | up not bothering because it's easy for me to just start up new | personal projects in Python like I'm used to. | spqr233 wrote: | I don't the author really gave Ocaml a chance. He argues that | every language needs to have interfaces. I agree somewhat with | that statement, but I think the truer statement is to say that | every language needs to have some way of defining composition at | a structural level. | | An interface allows you to pass different "structures" to the | same function so long as they adhere to the same spec. In Ocaml | this is accomplished through modules, functors, and module | signatures. Ocaml's module signature can play the same exact role | as interfaces in Haskell, Rust or F#. | | The module system is arguably more expressive than what can be | accomplished to interfaces. To give an example, Ocaml suffers | from the same problem as rust does with having two standard | implementations of a async runtime. Just like rusts: tokio and | async-std, ocaml has Lwt, Async, (and newly added to the mix | Eio). | | Whereas in rust most libraries just implement one of these | systems, and you'll have to use compiler directives to support | both. Ocaml's module system means that you can describe the async | runtime as a signature and make your entire library generic to | the async runtime it runs on top of. Most of the well-used | libraries do this, and so you don't have to worry too much about | which runtime you decide to use. | | Clearly, a language that can do that must have in some place a | system that can replace interfaces. | boxed wrote: | To me it seems he gave it a lot of chance in order to find this | many problems. | | It does sound to me like you stopped reading after the first | point. Maybe you didn't give the post a chance? | WastingMyTime89 wrote: | To be honest, anyone who used Ocaml understands immediately | that the author gave it no chance and went looking for | problems. It's obvious because the post lingers a lot about | things which are not actually issues like type conversion but | don't talk about the very real issues Ocaml has (opam is not | great, dune is weird). | hardwaregeek wrote: | Why are these things not issues? I think it's totally valid | for someone who doesn't understand OCaml to raise issues of | usability or documentation. It's not just the issues of | experts that are a concern. And it's very demoralizing to | get a "you're holding it wrong" response to a complaint. | WastingMyTime89 wrote: | Because they are not issues on a day to day basis when | you use the language. | | I used Ocaml for a paid internship some years ago. I was | very much a beginner. Do you know how many time I felt at | loss or annoyed by type conversion or the precedence | rules for various part of the syntax? I never did. | | The truth is you never encounter the precedence rules | when writing Ocaml normally using parentheses like a | normal human being and you don't convert that much | between exotic types. Most of the conversion you actually | do are between the same types and most of the time you | have to write converter anyway because there is logic | involved. I worked professionally as a Java developer for | a bit. I think I had to write more conversion code | between weird classes then than I ever did in Ocaml. | | The issue is not telling people they are "holding it | wrong". The issue is that this is not what's going to | annoy you as an Ocaml beginner. | | The author of this article is not even an Ocaml beginner | by the way. That's actually someone who used to work on | the Haskell compiler. | illiarian wrote: | > Because they are not issues on a day to day basis when | you use the language. | | They still are. You just _internalize_ them. Progammers | are the world 's greatest masochists with the world's | biggest Stockholm syndromes, and we don't like to admit | it. | | For example, his pain points about syntax ambiguity are | absolutely valid, and true. However, when you "use the | language every day" you learn to avoid those constructs | and extract them into separate functions for example | because "that's how it's always done, and it's a nice | little quirk of the language". | WastingMyTime89 wrote: | No, the syntax parts he criticises are not this kind | (they are not ambiguous by the way). What's in the | article is very unidiomatic OCaml incorrectly intended | purposefully to make it look like it's doing something | else than what it does. | | You don't write tuples without parentheses because it's | weird, harder to read and no one does it. Same for the | precedence rules of expression. You just use _begin_ and | _end_ for blocks like you would use braces in C because | you actually want to write a block. No one is just | randomly writing nested code without them because well it | doesn't work. | | It's not a quirk. The author is just complaining that you | have to mark blocks like in every language. That's a | weird thing to complain about. | | I agree that the lack of as-hoc polymorphism is a | downside (I don't agree with how the article presents it | but the article is disingenuous from the start anyway). | Everyone would like to have modular implicit. | illiarian wrote: | > unidiomatic OCaml | | > You don't write tuples without parentheses | | > Same for the precedence rules of expression. You just | use begin and end for blocks | | This is exactly what I wrote, but in a Stockholm- | syndrome-y way. | | > The author is just complaining that you have to mark | blocks like in every language. | | He points out a real ambiguity in the syntax where the | parser literally parses the same-looking and same- | behaving statements completely differently. | | Sure it's solved by "you just write begin-end" workaround | that you internalise. Or it could be solved at the syntax | level (like erlang and elixir) | WastingMyTime89 wrote: | > This is exactly what I wrote, but in a Stockholm- | syndrome-y way. | | Do you complain about having to put braces around C | expressions to not get unexpected behaviour from the | compiler? | | > He points out a real ambiguity in the syntax where the | parser literally parses the same-looking and same- | behaving statements completely differently. | | No, he doesn't. If that what you got from the article, | you were bamboozled. He points out that match, try and | (;) have different precedence which is not ambiguous. | Writing _begin_ and _end_ is not a workaround. That's how | you mark blocks which is what he should have done | because, well, he wants a block. | | Erlang has exactly the same issue if you write | unidiomatic code by the way. | | That's unsurprising because "don't write code implicitly | relying on precedence behaviour" is taught to virtually | all beginners in most programming languages. | lmm wrote: | If you shouldn't write code that way then it should be a | compilation error (or at the very least a warning) to | write code that way. | ModernMech wrote: | > The issue is that this is not what's going to annoy you | as an Ocaml beginner. | | Sure, but tbf, the post is about what annoys the author, | not what would annoy beginners. As you say, the author is | not a beginner, and so their problems wouldn't be | beginner problems. | WastingMyTime89 wrote: | That's not the point I was making. They are not expert | problems either. How often as an expert do you think you | encounter precedence issues you were not encountering as | a beginner? | boxed wrote: | It sounds like it's weird and annoying to convert a value | to a string. That's pretty damning. Did I misunderstand? | spqr233 wrote: | It's more that only really strongly disagreed with that | point. Frankly, there are some cons with using Ocaml. In | practice, I think the library support for ocaml isn't | completely there given the small community. | maleldil wrote: | > Ocaml's module system means that you can describe the async | runtime as a signature and make your entire library generic to | the async runtime it runs on top of | | Is this the program describing the interface it expects, and | the compiler matching that with the implementations structure | (e.g. Go interfaces, Python Protocols), or is it something | someone declares and the libraries implement? | yawaramin wrote: | Mostly the former. The compiler infers the type of the | implementation and ensures it conforms to the interface. But, | you can _further_ restrict the interface 's type to hide | details from the outside world. | actionfromafar wrote: | > Ocaml's module system means that you can describe the async | runtime as a signature and make your entire library generic to | the async runtime it runs on top of | | So ... can you ELI5 to me how that is different from how you | can for instance compile a C program against different libc | implementations? | kajaktum wrote: | I mean...you can technically have a function that take void* | and return void* be the interface. But you wouldn't be able | to represent things like iterator and chain them together | without care. In C you would be tripping all over the slight | details like forgetting to check errno or something. | actionfromafar wrote: | So, it's like C++20 then? | hardwaregeek wrote: | Politely, I feel like this is the issue with OCaml as a | community. You gave a beautiful answer about programming | language design and composition. But how does it feel to write | the language? How can you print a user defined data type? From | reading around, it seems like your options are: explicitly pass | a print function for that specific type, use a third party | library for printing, or (my "favorite") don't. Or how does it | feel to write a function that takes a generic that can be | printed, checked for equality, and read? Or to convert one type | into another type? | | And we're still talking about semantics here. How does it feel | to use the language server? How does it feel to read the code? | To build the language? Ultimately that's what users judge a | language on. Yes, Rust in some ways has a worse form of | abstraction. It is global, not fully generic and doesn't allow | for overloading. But it creates a user experience that is | nicer. It lets a user print something and compare two variables | and do type conversions without having to scratch their head, | read a forum post and import a library. | yodsanklai wrote: | > How can you print a user defined data type? | | Pretty much the same way you'd do in Haskell or Rust: you add | a `[@@deriving show]` directive to your datatype. | ywei3410 wrote: | > You gave a beautiful answer about programming language | | You do the same thing as in Rust, Scala or Haskell and derive | the printer [1]. Then at the callsite, if you know the type | then you do `T.show` to print it or `T.eq`. If you don't know | the type, then you pass it in at the top level as a module | and then do `T.show` or `T.eq`. | | > Or to convert one type into another type? | | If you want to convert a type, then you have a type that you | want to convert from such as foo and bar, then you do | `Foo.to_bar value`. | | We can keep going, but you can get the point. | | You _can't_ judge a language by doing what you want to do | with one language in another. If I judge Rust by writing | recursive data structures and complaining about performance | and verbosity that's not particularly fair correct? I can't | say that Dart is terrible for desktop because I can't use | chrome developer tools on its canvas output and ignore it's | hot-reloading server. I can't say Common Lisp code is | unreadable because I don't have type annotations and ignore | the REPL for introspection. | | [1] https://github.com/ocaml-ppx/ppx_deriving | hardwaregeek wrote: | Okay...so you have to add a third party library to print? | Yes, recursive data structures are a pain in Rust (well, | recursive multi-owner data structures). But that's like | comparing changing your oil to opening your car door. We do | one of these a lot more. And bear in mind, I had to find | this answer by googling and reading a random forum post. | That's some pretty poor documentation. | | And that's not talking about aesthetics. `T.show foo` or | `Foo.to_bar value` is a lot of syntactic overhead for a | rather common task. | | And again, these are the lesser concerns to the other ones | I outlined. Reading the code, building the code, and | understanding the compiler errors are the big ones. | agumonkey wrote: | People have to realize that printing is the least of your | concern. Visible representation is not solving problem. | ywei3410 wrote: | Not really third-party; ocaml-ppx is something similar to | `javax` in the Java space? But it is optional and doesn't | come bundled with OCaml. | | Recursive structures are only rare in Rust _because_ they | suck to write and have terrible performance | characteristics in the language. In languages like | Haskell, OCaml and Scala using tagless initial encodings | for eDSL's [2] are really, really common. | | I don't write Rust like I write any semi-functional | language with function composition because, well, it's | _painful_ and not a good fit for the language. | | > `T.show foo` or `Foo.to_bar value` is a lot of | syntactic overhead | | The `T.()` opens a scope so OCaml code generally looks | like: | | ``` let foo = T.(if equal zero x then show x else "not | here") ``` | | for, | | ``` fn foo(t: T) -> String { if(t == T::ZERO) { t.show() | } else { "not here".into() } } ``` | | Even when talking about usage, each language community | for better or worse has a predefined workflow in mind. | Your questions and workflow are specific to a particular | language; you don't, for example, ask about the REPL, the | compile times, debugging macros, the cold-start of the | compiler, whether hot-reloading is a thing, the debugger, | how it interacts with perf, how to instrument an | application. Presumably because the language you use or | the programs that you make don't have those components as | part of their main workflow. | | [1] https://github.com/ocaml-ppx [2] | https://peddie.github.io/encodings/encodings-text.html | hardwaregeek wrote: | A lot of these things that you mentioned are valid flaws | in Rust. I'm not going to dismiss them as not important | or non-problems because they are genuine issues. Compile | times suck. We could use hot reloading and a REPL. | Debugging macros, instrumentation and perf stuff is good, | but I assume it could be better. That's what I expect | from a language community. Not constant denial about the | state of usability. And it really does feel like many of | the smaller language communities fall into this hive-mind | where type signatures are valid documentation and who | needs to use an editor other than emacs? | yawaramin wrote: | If you are referring to OCaml, many people in the | community care a lot about writing good documentation so | that it's not just the type signatures. In fact the OCaml | website team have made amazing leaps and strides | integrating package documentation directly in the | website. | | And the experience using OCaml LSP Server with VSCode | nowadays is honestly pretty good--type annotations | displayed, error messages, go to definition, generating | interface files. The core functionality is all there. | hardwaregeek wrote: | That's good to hear! I don't mean to be too negative. I'm | genuinely glad that OCaml is making these strides. I'm | hopeful that OCaml can find its equivalent of Esteban | Kuber[1] and they can make the language a truly fun and | friendly experience. | | [1]: https://www.youtube.com/watch?v=Z6X7Ada0ugE | yawaramin wrote: | You don't _need_ a third-party library to derive a | pretty-printer for your types, it just makes it simpler. | OCaml takes the approach that the core compiler doesn 't | do any codegen, that is left to PPXs (basically, | middlewares which transform code) which run before the | compiler. | | You can write a pretty-printer yourself using the | conventions of the `Format` module. Is it manual and | annoying? Yeah. That's why the PPX exists. It's a | tradeoff. | yodsanklai wrote: | > `T.show foo` or `Foo.to_bar value` is a lot of | syntactic overhead for a rather common task. | | A lot of syntactic overhead sounds like a stretch. I | suppose you could drop the `T` like you do in Haskell but | I personally like my language to be a bit more explicit. | It improves readability. | pdhborges wrote: | Scala is probably not a good example because if you need | some quick and dirty printing you can override toString and | keep the rest of the generic code intact. In OCaml, Rust | and Haskell you would have to add type constraints in a | bunch of places to make println! T.to_string and show work. | v0idzer0 wrote: | Came here to make this exact point. But I totally agree with | all of his other complaints. It's a good list of the cons, but | ignored the many pros | [deleted] | WastingMyTime89 wrote: | The last paragraph show this article for what is is. That's an | Haskell fan trying to discredit Ocaml and doing it very poorly. | | It takes a special kind of writer to write an incorrect sentence | about the expression syntax while quoting the actual definition | from the specification which contradicts what's written just | after. | | I generally disagree about the complaint regarding the syntax. If | you indent the code properly and use parentheses as you should | and the language invites you to do, none of this is ambiguous. | That's bad code not bad language design. | boxed wrote: | "the language invits you to do"? What does that mean? If it's | the right thing to do it shouldn't be in invitation surely, but | a syntax requirement? | WastingMyTime89 wrote: | > What does that mean? | | That's a best practice. Like always using braces in C or Java | or not using ternary operators in hard to read way. | | The syntax is unambiguous but non obvious to read if you | don't know precedence rules as it is for most languages. You | can make it explicit using parentheses which you should. | | I personally agree that parentheses around tuples should be | mandatory but it's fairly easy to just put them. | boxed wrote: | Those are examples of C and Java being _very bad_. So yea, | I think my point stands. | WastingMyTime89 wrote: | I'm not sure I understand your point then because the | code in the article is definitely _very bad_ OCaml which | is what I have been trying to tell you from the start. | Verdex wrote: | I agree, but the languages that actually do this are in a | hyper-minority, I feel. Nearly every language allows you to | do the wrong thing and then only lets you discover this at | runtime via exception OR sometimes even just by careful | examination of subtly incorrect output. | | As such, this doesn't really feel like the best argument | against ocaml (and I believe arguments against ocaml do exist | and on a much more accessible level). | omoikane wrote: | > Int.abs can return a negative number. | | The documentation for abs[1] explains: "Warning. This may be | negative if the argument is Int.min_int." | | I think that's just the nature of two's complement and not | necessarily a fault of OCaml. C++ is also like that[2]. | | [1] https://v2.ocaml.org/api/Int.html | | [2] https://gcc.godbolt.org/z/34b3GbvvT | anderskaseorg wrote: | In C and C++, abs(INT_MIN) is undefined behavior. (C17 | SS7.22.6.1: "The abs, labs, and llabs functions compute the | absolute value of an integer j. If the result cannot be | represented, the behavior is undefined." Footnote: "The | absolute value of the most negative number cannot be | represented in two's complement.") | [deleted] | nu11ptr wrote: | I like OCaml, but if I were to start a new project with an ML I'd | probably lean towards F# to take advantage of all the .NET libs | lysecret wrote: | Same, big fan of f#. | JaggerJo wrote: | We use F# in prod. Could not be happier. | dimitrios1 wrote: | Completely ignorant here: | | - I really want to do a project in an almost "pure" | functional language. I tried with Elixir and Phoenix, and | while they are certainly great, and I wouldn't mind using it | again, Elixir, and subsequently Erlang didn't feel like FP a | lot of times, it felt like the warty Elixir/Erlang way to do | FP, so it didn't scratch that itch for me (but again, still a | great experience overall) | | - is there a way to do this openly? I.E. with dotnetcore, | running on linux containers, no windows what so ever, etc? | Guessing yes but wanted to make sure from people who have | actually done it | | - What exactly about the .NET libraries that has everyone so | hyped up about? I remember from my C# .NET days, they always | felt to be lacking. | victorNicollet wrote: | Today you can install the .NET runtime, as well as the .NET | development tools, on both Windows and Linux. They handle | F# as well as C# out of the box. | | You can `dotnet publish MyApp.fsproj` on either Windows or | Linux, copy the resulting `publish/` folder to another | machine with a different OS, and use `dotnet MyApp.dll` to | run it. There are also ways to create containers, though I | have never tried them for lack of an actual use case. | | As for libraries, .NET comes with a huge standard library, | much larger than what you would find on a different | language. Off the top of my head, here are a few things | that come with .NET that would have been a third party | library in other languages: text encodings, protocols (HTTP | client, TLS, QUIC, SMTP), mainstream hashes | (MD5/SHA1/SHA256), mainstream crypto | (AES/DES/RSA/EC/ChaCha), serialization (JSON/ XML), weird | formats (X509/ASN.1/tar/MIME), mainstream compression | (deflate/brotli/zip), binary data manipulation (e.g. | endianness conversion, AVX intrinsics, memory-mapping), | runtime code generation, in-assembly resources, | interoperability (COM, JavaScript, Objective C, SQL), | concurrent collections, async... | | Besides, since C# is popular, many tools have .NET- | compatible clients or libraries which can then be accessed | from F#. | JaggerJo wrote: | + macOS | klester79 wrote: | [dead] | mjdowney wrote: | Related: https://twitter.com/zetalyrae/status/1639474931086901248 | | My OCaml: let foo (x: bar): baz = (\* | Is this quuxable? *) match is_quuxable x with | | Yes -> (* Then, we have to ... \*) | | All the OCaml I see in the wild: qlet%bind f = | 's 't 'a 'b (fun -> ((<_<')) ) [@@ppx_gov_backdoor | (~b~e~p~i~s)] | mcguire wrote: | Hmm. I haven't seen the latter in OCaml as much. More the | opposite; when I first used it, things like list length in the | standard library weren't tail recursive. 8-| | | Now, Haskell and _shudder_ Scala _shudder,_ on the other hand. | WastingMyTime89 wrote: | Compared to Haskell, OCaml developers are generally not to | fond of custom operators which shield you from some of the | worst cases unless you are reading code an Haskeller wrote in | OCaml which happens. | | For a long time, most of the users were either writing | compilers or static analysers for low level languages and a | fair share was very knowledgeable in C and system | programming. It had a huge impact on what was seen as | idiomatic. You can still find trace of it in for exemple the | system library which looks more like the C stdlib than a | modern standard library or how the compiler finds libraries. | | Generally the community used to have a very different | "flavour" than the Haskell one (it was also very French to be | fair). I think I t's less true nowadays. Still I would fall | from my chair if I ever encounter someone working on the | OCaml compiler writing a disparaging disingenuous post on | Haskell. Meanwhile, we are here which reflects exactly my | general experience with the Haskell community. | | Ppx are used more and more often however and it did make code | harder to read. | jimsimmons wrote: | Functional programming community likes this stuff. They go into | the classical mathematicians' trap of writing more and more | general versions of something even though it's not necessary. | yawaramin wrote: | I don't think this is correlated to functional programming. | Programmers in general tend to over-architect because we're | often learning as we're going and sometimes it's more fun to | use some cool concepts. Since design patterns started | becoming en vogue back in the day, OOP codebases got filled | with those patterns. Now we are left picking up the pieces | with things like `AbstractProxyFactoryBean` or whatever. Same | thing with JavaScript where that ecosystem is infamous for | using tons and tons of npm packages to recreate simple | functionality, e.g. 'left-pad a string'. Arcane mathematics- | heavy FP code is just how that expresses in FP languages. And | in fact, OCaml is actually an antidote to that (for the most | part), because it deliberately offers a lower level of | abstraction than Haskell. I frequently say that OCaml is like | a cross between Haskell and Go. | nequo wrote: | I think this is partly because of the syntax. | | When we write human languages (that use a Latin script), we | use punctuation to delineate the beginning and end of terms. | In a language like C or Rust, terms are surrounded by commas, | parentheses, angle brackets, and so on. In OCaml and Haskell, | many things that there is syntax for in C-style languages are | done as ordinary function calls, which separate terms by only | whitespace. | | It is easier to read this (Rust): let value = | some_long_ass_function_name(some_quirky_parameter, | another_one); another_function(value) | | than this (Haskell): let value = | someLongAssFunctionName someQuirkyParameter anotherOne | in anotherFunction value | | or this (OCaml): let value = | some_long_ass_function_name some_quirky_parameter another_one | in another_function value | | Individual terms in camel case are difficult to read if they | consist of more than a couple words. Consecutive terms in | snake case are difficult to read because underscores and | whitespace look alike visually. | | Haskell and OCaml's idiomatic solution is to use extremely | terse names for arguments, type variables, and local | variables, out of what seems to be syntactic necessity. | golergka wrote: | That's because of currying. If you want to partially apply | parameters, in your example, with Haskell we would just | have to do this: | someLongAssFunctionName someQuirkyParameter | | And we get a function that accepts one parameter. But in | Rust you would have to do this: move | |another_one: i32| { | some_long_ass_function_name(some_quirky_parameter, | another_one) } | | Which is not easier to read. | nequo wrote: | It is true that currying looks comparatively awful in | Rust. But that is not because Rust uses parentheses and | commas. A hypothetical C-style language could use | dedicated syntax for currying, like | some_long_ass_function_name(some_quirky_parameter, ..) | | which would be more readable without giving up on | punctuation. | ackfoobar wrote: | Scala does that, with `_` being a "hole in the | expression". I wouldn't call it currying though. | func(param1, _) | | Every once in a while you have to make some change to | that expression. // you may want to write | func(param1, func2(_)) // but you need to write the | full lambda param2 => func(param1, func2(param2)) | oblio wrote: | Ah, now I realized, you're right. The lack of visual | distinction between different syntactic elements is what's | killing me. | actionfromafar wrote: | The latter code looks _totally_ quuxable. | gjm11 wrote: | Nah, I think it's already been quuxed up. | ubertaco wrote: | > Another example is the sequencing syntax <expr> ; <expr> and | productions with <expr> as the right-most symbol: | let test1 b = if b then print_string "1" | else print_string "2"; print_string "3" | | > Here print_string "3" is not a part of the if expression, so | this function always prints "3". | | Well, yeah. Here's the equivalent Java code: | void test1(bool b) { if (b) print_string("1"); | else print_string("2"); print_string("3"); } | | You've chosen to use a single-statement "else" form _without_ | "bracketing" it with `begin` and `end`: let | test1 b = if b then print_string "1" | else begin print_string "2"; | print_string "3" end | | ...which is equivalent to this Java code, where you _also_ would | have to "bracket" the else-block: void | test1(bool b) { if (b) print_string("1"); | else { print_string("2"); | print_string("3"); } } | | You could argue that Java (and C, and C++, and Javascript, | and...) has the same "ambiguous parsing problem", then -- except | that it doesn't, and the author is just thrown off by | `begin`/`end` as block delimiters instead of curly braces, which | might be a sign that they've also never seen Ruby before, or | Elixir, or Pascal, or.... | yakubin wrote: | _> You could argue that Java (and C, and C++, and Javascript, | and...) has the same "ambiguous parsing problem", then -- | except that it doesn't_ | | The author didn't claim there's any ambiguity in parsing, only | ambiguity in reading as a human. And I would agree that all the | languages you mentioned have the same problem. And this is a | real problem as long as people use this form of if (which they | do). New languages avoid this pitfall by mandating the use of | braces, in return dropping the parens, and I think that's | great. | yodsanklai wrote: | It's a non-issue IMHO. Just use parenthesis if you're unsure, | then auto-format. You'll catch any issue that wan't caught by | the typechecker by reading the diff. | charlesetc wrote: | Yeah auto format solves a lot of the syntax problems. | Dangling else for instance is not at all ambiguous when you | have automatic indentation. | [deleted] | electroly wrote: | Agreed except for the part at the end: Java, C, C++, and | JavaScript definitely _do_ have the "dangling else" problem! | It's a manifestation of the same basic issue. Who is saying | they don't? You just showed how they do! It is a real problem | and they all have it. The takeaway here isn't that those | languages _don 't_ have the issue. They do. It's just really | common in language design, that's all; certainly not something | to single out OCaml about. Apple's infamous "goto | vulnerability" was directly caused by this language design | issue in good old C. | | https://en.wikipedia.org/wiki/Dangling_else (just to show that | this is something people commonly consider to be a problem with | Java, C, C++, and JavaScript) | ubertaco wrote: | >Java, C, C++, and JavaScript definitely do have the | "dangling else" problem! It's a manifestation of the same | basic issue. Who is saying they don't? You just showed how | they do! It is a real problem and they all have it. | | Yeah, that's actually exactly my point: it's not some new | weird novel horrible broken parsing problem that only OCaml | has; rather, it's a common parsing rule that many languages | (including Java, C, C++, and JavaScript -- and yes, OCaml) | have. | Arnavion wrote: | Now keep reading on to literally the next sentence. | | The article's point is that the `if-else` behavior is | inconsistent with `match`. And based on the part about double | quotes in comments, the parser seems to be fucked up something | fierce in other ways too. | atbpaca wrote: | I learned OCaml two decades ago. It was great to learning | algorithmics but I already had the feeling the language was old | and awkward. Today there is Scala, Haskell, Rust, F# etc. | yawaramin wrote: | All of which are, incidentally, either directly or indirectly | inspired by OCaml or its ancestor ML :-) | [deleted] | s-zeng wrote: | A significant amount of these issues are solved by sticking to | Core, Dune, and avoiding imperative blocks | anthk wrote: | Ocaml5 is multithreaded. MLdonkey would run much better. Compared | to the classical Amule, a monstruosity writen in C++, at least it | doesn't leak memory like some youngster with a constipation. | 4ad wrote: | > No standard and easy way of implementing interfaces | | Article -> trash. | | Apparently the author doesn't know about OCaml modules, which is | _the_ defining feature of ML-derived languages. | koprulusector wrote: | Can you elaborate for us newer OCaml users? My first reaction | to this critique/point is the author must want mli, right? | [deleted] | sgeisenh wrote: | Especially if modular implicits land, you get the best of both | functions and typeclasses. | | Pretty weird way to start the post. | wk_end wrote: | That's a big "if". The author themselves mentions modular | implicits and points out that they haven't been updated in | something like five years. I don't think modular implicits | are going to happen in Ocaml in our lifetimes. | arc-in-space wrote: | Someone mind explaining how these help? I'm unfamiliar with | OCaml, but looking at the documentation of modules, I don't | understand what they have to do with interfaces in the OOP | sense(EDIT: or, rather, ad-hoc polymorphism), which I'm pretty | sure is what the author meant. | Verdex wrote: | To be fair, ocaml doesn't have something _exactly_ like c# | /java interfaces. Given some problem that you might want to | solve with a c#/java interface, you can also solve it with | ocaml modules by flipping the coding structure on it's head. | I'm not going to try to explain exactly what this looks like | because I actually don't really like that structure of | coding, so I don't feel like I'm going to do it justice. | | BUT ocaml also has row polymorphism and polymorphic | invariants, which you can use to get something much directly | closer to c#/java interfaces. The major difference is that | with ocaml it's structural typing and with c#/java it's | nominal typing. However, at a high level you can write the | code in a very similar structural pattern as with what you | get in c#/java. | WastingMyTime89 wrote: | > I'm pretty sure is what the author meant. | | Clearly not considering he mentions typeclass in the article. | | Module signatures are similar to interface and parametrized | modules allow you to do what you would do with abstract | class: you define a signature and then write a module using | these functions. Parametrized modules are more or less a | generalization of functions which goes from module to module. | arc-in-space wrote: | Sorry, I initially wrote 'interfaces as in OOP' to | distinguish them from module interfaces, but had 'ad-hoc | polymorphism' in mind. 'interfaces' is much too overloaded | of a term. | WastingMyTime89 wrote: | Ocaml has no ad-hoc polymorphism but you can use | parametrised modules to write generic code which you will | have to specialise at some point. That brings you ninety | percents of the way in term of what ad-hoc polymorphism | is used for. It's more cumbersome but I prefer it to the | complexity involved by what Scala does for exemple. | ywei3410 wrote: | Would you not consider the object system to be ad-hoc | polymorphism? | octachron wrote: | The OCaml object system is still parametric polymorphism | (trying to pass itself as subtyping polymorphism through | row variables) rather than ad-hoc polymorphism: there are | still no functions that only work on a finite subset of | types. In other words, a function may work on any objects | that has a method `m`, but you cannot have the same | method that has different implementation for `int` and | `float`. | WastingMyTime89 wrote: | It uses structural typing not ad-hoc polymorphism. Plus | it's rarely used and it used to give hard to parse error | messages when you made type mistakes. I don't really see | it as an adequate replacement for ad-hoc polymorphism. | Errancer wrote: | The point as I understand is that interfaces in the OOP sense | try to mimic what modules do. Therefore OCaml not having them | is not a serious accusation since everything you try to | accomplish with interfaces / design patters you can and | should be able to do with modules. Here is the article | explaining the concept: | https://www.pathsensitive.com/2023/03/modules-matter-most- | fo... but I have to note that I never worked in OCaml so this | is just my theoretical understanding. | sparkie wrote: | I can kind of understand the author's complaint. A problem with | OCaml is there are several different ways to achieve the same | thing, and they're not compatible. | | For example, you can do OOP-like interfaces/abstract classes | with `class virtual`, but classes and modules don't mix | together nicely. Your virtual class cannot abstract over | modules, nor do functors abstract over classes. | | So you end up either using modules/functors pervasively, or | using objects pervasively. It's like there's two separate | languages competing for use. | | Using modules seems to be more common, but it overshadows some | features of OCaml's object system which are pretty unique to it | and useful: row polymorphism and functional objects. | | Obviously, functors have a clear performance advantage because | they're expanded at compile time, and objects have the vtable | overhead. | | I think it would be interesting to see a variant of OCaml which | goes all-in on the object side of things. Strip away the | modules and functors and a provide standard library based | around functional objects. | networked wrote: | The author brings up modules and functors. A more charitable | interpretation is that they want ad hoc polymorphism but are | handed parametric polymorphism instead. | Verdex wrote: | Yeah, I'm not sure I understand the author's complaint. OCaml | has modules, associated types, anonymous modules, GADTs, | polymorphic variants, and row polymorphism. All of which are | kind of like interfaces. | | As far as I can tell, the complaint is maybe that ocaml doesn't | have something exactly like C#/Java interfaces (which I think | is a fair assessment). So maybe the author is lamenting that | they can't just rewrite their java code in every language? | | Although, they also mention rust traits. Which at first blush | look _very_ similar to c# /java interfaces (at least closer | than anything ocaml has). However, at least in my experience, | rust traits don't quite fulfill the same niche as c#/java | interfaces because rust cares a lot about when you can figure | out the size of your objects. So every time I tried to use them | like I would a C# interface, I wound up regretting the | decision. | | Ultimately, this article feels like a superficial "I don't like | ocaml" blurb. There is a lot to be warry of with respect to | ocaml. Although, the fact that ocaml still exists as a niche | language with a lot of very significant maintenance and active | development (effects + multithread recently landed), shows that | the author did not pay sufficient attention. | aseipp wrote: | The author is plenty aware of them, I can assure you (I used to | work with him on the Glasgow Haskell Compiler; most Haskell | programmers are plenty familiar with the ML module system, it's | a highly coveted feature by many.) | | Anyway, what they're referring to has been rehashed a billion | times already in the relevant communities. ML functors being | good has little to do with it; even with functors and modules, | you're required to actually pass them around and construct them | explicitly and use it at call sites. If you want a Set for | Strings, you make an instance of the Set functor with the | proper implementation of the signature which specifies the Set | ordering (or whatever, assuming that's the signature needed), | but then you still have to _use_ the functor explicitly and | pass it around. You can have more than one, for good reason, so | making this explicit usage implicit is tricky. In contrast, in | Haskell, there is one ordering for each (nominal) type, so | there is no need to refer to specific instance of `Set String` | or whatever to define the ordering. `Set` uses `Ord`, which has | an unambiguous implementation. It is resolved implicitly for | you. | | A lot of people just call this an "interface." Hell, even I do | that. It's just a generalized term for "ad hoc polymorphism" | where you don't need to guide instance resolution, I guess. | | The author even quite literally (in like, the next sentence) | says that modular implicits would help this issue -- it would | allow many uses of an instantiated module to become more | implicit (e.g. uses of something like "turn this thing into a | string for debugging" without having a menagerie of individual | string_to_ functions). But they are not yet upstream. Even | then, implicits have their own tradeoffs, and the design | spectrum here has no clear winner. Even Haskell has adopted | Backpack, which takes a different spin on the whole thing using | mixin-modules (no functors), but tries to keep the | characteristic power of functors available in a languages where | typeclasses reign. | | Much of this is also relevant in the design of theorem proving | communities, e.g. Lean4, which heavily relies on implicit | instance resolution in a similar form to modular implicits; yet | it doesn't have modules in the ML sense. They really want this | approach though, because in math you really do have lots of | instances you might refer to by name. But in programming it's | not always the same; implicit resolution of ad hoc instances is | often very useful. | | It's not really a weird or unusual complaint, it's been | rehashed to death, but maybe it could be less ambiguous. | felixyz wrote: | Great comment. Thanks! | yawaramin wrote: | When writing about a language which has a well-known feature | called 'interfaces', it behooves the writer to not use | 'interfaces' to mean 'ad-hoc polymorphism' to avoid | confusion. Not doing so led to the predictable confusion. | cies wrote: | ReasonML and ReScript are a (more or less the same) new syntax on | top of OCaml. ReScript only targets JS, while ReasonML targets | both JS and the native archs OCaml supports. Facebook and | Bloomberg are using ReScript internally, afaik. Messenger.com is | written in it. Facebook also maintains React bindings to | ReScript. | | https://rescript-lang.org/ | | https://reasonml.github.io/ | Taikonerd wrote: | Is ReasonML dead? Their last blog post is from August 2018... | https://reasonml.github.io/blog/ | cies wrote: | Quieted down, but I depend on projects with worst graphs: | | https://github.com/reasonml/reason/graphs/contributors | illiarian wrote: | The rescript-reason split killed both IMO. ReasonML was a very | tiny language to begin with. Splitting it into two split the | already tiny language and community into two minuscule ones. | | And for no good reason (pun not intended). | | And they were doing quite well, and influencing OCaml in good | and meaningful ways (like improving error reporting). | giraffe_lady wrote: | Rescript is completely excellent imo. I have used ocaml for | years and while I like it more than most languages it's also | weird and frustrating sometimes. Rescript uses the best parts | of ocaml (type system & exhaustive pattern match, first class | modules) to patch over the worst parts of js. It's almost | alarming how effective it is to combine these two gnarly | hostile languages like this. | | I also work in typescript a lot and it's the obvious | comparison/competitor there. While it has objectively "won" | it's so so much worse than rescript. Its type system is much | more complex, untrustworthy inference, and ultimately is | _still_ unsound lol. Ocaml 's type system is truly a wonder. | Extremely practically helpful, complex enough to express | anything without becoming a full blown language in its own | right like eg typescript and haskell. | | Rescript compiles to very reasonable js and writing bindings is | not particularly exhausting once you get the hang of it. I've | been using it for a number of projects including completely | irresponsible cases like deno fresh and it's been wonderful. | What typescript could and should have been. | crop_rotation wrote: | The biggest problem I have with Ocaml is not the language, but | the tooling and the libraries. The library ecosystem is not even | remotely comparable to go or even rust. Jane street core is | mentioned as a good option, but that library is the most popular | undocumented library I have ever seen. There is not even some | normal default structure every library follows (e.g go | io.Reader). | | Compare this to languages with supposedly "worse" type systems | like Java, which has a library for everything and high quality | well documented libraries like guava and apache commons. I find | it fine to use ocaml for a random weekend whim project, but | wouldn't touch it with a 10 feet pole for professional work. | illiarian wrote: | > like Java, which has a library for everything and high | quality well documented libraries | | Unless it's a library that has been around for a while and is | probably hosted at Apache. | | There was a marked shift in 2010s in expectations, and now we | expect libraries and code to be somewhat well-documented. For | older libraries... well, it's often "here's a dump of | autogenerated reference files with maybe a blob of prefacing | text at the top. Good luck." | oslac wrote: | It is for these (tooling / library) reasons it is almost | impossible to recommend it for any project, almost like Nix in | that regard for me. | mcguire wrote: | " _Lack of widely used operations such as popcount for integer | types, unicode character operations._ " | | popcount is widely used? | yodsanklai wrote: | 1. To print values, nowadays you just derive pretty printers | automatically. | | 2. Syntax? yes, it's not C-like... you'll get used to it in a few | days. | | 3. Lack of interface? would be helpful to see a concrete example | of what the author finds limiting. You have modules, functors, | includes, objects, first-order modules... Personally, I find that | plain modules are simple and go a long way. | | It's not perfect, but I find that nowadays Core/Dune/Opam/Merlin | gives you a very decent developer experience. | the_af wrote: | > _2. Syntax? yes, it 's not C-like... you'll get used to it in | a few days_ | | Since the author mentions Haskell as a positive example | multiple times, I don't think a lack of C-like syntax is the | problem for them here. | | Also, the author claims: | | > _Since 2013 I've had the chance to use OCaml a few times in | different jobs, and I got frustrated and disappointed every | time I had to use it._ | | So it's safe to assume "in a few days" their dislike for OCaml | syntax won't go away. | | For the record, I don't think syntax is a big issue. | yodsanklai wrote: | Incidentally, I find Haskell syntax more confusing, | specifically the semantic indentation. But maybe I'm like the | author, I don't use Haskell regularly enough so that the | syntax sinks in. | mattgreenrocks wrote: | Been using Haskell full-time for a year or so now, and | using it part-time a few years ago for a side project. The | reliance on indentation is nice and light but definitely | bites you every now and then. | reikonomusha wrote: | Coalton [1] is an OCaml-like dialect of ML: it's strictly | evaluated, has imperative features, and doesn't force a purity- | based development philosophy. However, Coalton has S-expression | syntax and integrates into Common Lisp. It works, and is used "in | production" by its developers, but it's still being developed | into a 1.0 product. | | Coalton made a lot of design decisions to work away from issues | that this post describes. | | - Coalton dispensed with ML's module system of structures, | signatures, and functors. No doubt a beautiful concept, and | sometimes satisfying to write, but almost always unsatisfying to | use due to how explicit one tends to need to be. Like the author | suggests, the "interface-style" of type classes or traits has | always felt more intuitive, despite losing the ability to have | multiple implementations of an interface for a single type. | (However, I've come to appreciate that different interfaces | _should require different types_ --as opposed to one type having | many interfaces.) Coalton uses type classes. | | - Coalton has S-expression syntax. No indent rules. No precedence | rules. No expression delimiters. All of the "tradition" of ML | syntax is dispensed with into something that is a _lot_ easier to | read and write for complex programs... yes, provided you use | ParEdit or similar | | - It should be no surprise that with S-expressions, you get Lisp- | style macros. Macros in Coalton are just Common Lisp macros. No | separate pre-processors. No additional language semantics to deal | with. Arbitrary compile-time computation and/or codegen, | completely integrated. | | - Because it's built on Common Lisp, Coalton gets something few | (if any?) other ML-derivatives get: Truly incremental and | interactive development. You can re-compile types, functions, | etc. and try them out immediately. There's no separate | "interpreter mode"; just a Lisp REPL. | | Coalton's language features are still settling (e.g., records are | being implemented) and the standard library [2] is still | evolving. However, it's been used for "serious" applications, | like implementing a compiler module for quantum programs [3]. | | [1] https://github.com/coalton-lang/coalton | | [2] https://coalton-lang.github.io/reference/ | | [3] https://coalton-lang.github.io/20220906-quantum-compiler/ | justinpombrio wrote: | > Coalton [1] is an OCaml-like dialect of ML: it's strictly | evaluated, has imperative features, and doesn't force a purity- | based development philosophy. | | OCaml is also strictly evaluated, has imperative features (e.g. | ref), and doesn't force a purity-based development philosophy. | | What makes you call Coalton a "dialect of ML"? ML's module | system is its defining feature, beyond that there's really not | much unique about it (by design). Would you call any language | with algebraic data types a "dialect of ML"? | | EDIT: I'm picking on this one sentence, but the rest of your | comment is a good overview of the language, thanks. | reikonomusha wrote: | That's why I said it's "OCaml-like" followed by a colon: to | highlight the similarities. | | As for why it's an ML dialect, maybe (maybe not) it's more | fair to call it "ML-inspired". Nonetheless, it's similar | enough to the ML family to be understood by ML programmers. | But yes, one of the major pillars of ML--the module system-- | is conspicuously absent in favor of Haskell-like type | classes. | wk_end wrote: | This looks excellent! I have a soft-spot for the elegance of | Lisp but can't live without a robust type system. A few | questions I didn't see immediate answers to on the GH page | (sorry if I missed something/could've found those answers if I | digged a little deeper): | | 1. Beyond the typeclasses you mentioned, how powerful is the | type system beyond standard HM stuff? Are there | GADTs/existential types? Any other interesting features? | | 2. The lack of records sounds like it might be a deal-breaker | for current use. Is that as bad as it sounds? Is it something | that is expected to be addressed soon? Are there any other | large omissions? | | 3. How does it integrate into the existing Common Lisp | ecosystem, if at all? Is there any friction there? | | 4. On that note: in the REPL example it looks like you need to | wrap your expressions with (coalton ...), which feels like it'd | get frustrating. Is there any way around that? I guess even a | reader macro would help...but still wouldn't be ideal. | | 5. Is Coalton backed by any kind of corporate funding? And | (assuming you're involved in Coalton in a professional | capacity) ...you guys hiring? | reikonomusha wrote: | 1. There are functional dependencies and multi-parameter type | classes. No GADTs (yet?) or existential types (yet?). | | 2. Records are being implemented but it's far from being a | dealbreaker, in the sense that thousands of production lines | of Coalton have been built without needing them. It's just | not ergonomic to shuffle around and pattern match against | record-like data. With pattern matching in function | arguments, things are at least easier. | | 3. All Coalton functions compile to Lisp functions. There's a | guide that describes what is promised about interop | (https://github.com/coalton- | lang/coalton/blob/main/docs/coalt...). | | 4. Since Coalton functions are Lisp functions, you can call | (unconstrained) functions directly. However there's a lot of | room for improvement. There's an open issue to make a | dedicated Coalton REPL that can show types and not require | COALTON to be typed. | | 5. Coalton is developed as an open source project to build | tools at HRL Laboratories, so it does receive sponsorship. | Yes, HRL is hiring. Feel free to send me an email to the | address in my profile. | | As seems to be the case with any "serious" programming | language, there's always another thing to do--seemingly | perpetually--to make said language useful enough for | "serious" use. :) | networked wrote: | > Coalton has S-expression syntax. No indent rules. No | precedence rules. No expression delimiters. | | This is a great selling point for a language in the ML family. | Programmers can't abuse infix operators when there are none. | :-) | | Besides this, Coalton looks appealing. A highly interactive | statically typed language is something I have thought of. I am | sure I am not the only one. It is also a good sign that you are | using it as it is being developed. | aaws11 wrote: | Almost all modern statically typed languages have closures, | higher-order functions/methods, lazy streams, and combinators | that run efficiently. Persistent/immutable data structures can be | implemented even in C. | | Also, OCaml has no tracking of side-effects (like in Haskell), | and the language and the standard library have lots of features | and functions with mutation, such as the array update syntax, | mutable record fields, Hashtbl, and the regex module. | | The only thing that makes OCaml more "functional" than e.g. Dart, | Java, or Rust is that it supports tail calls. While having tail | calls is important for functional programming, I would happily | give up on tail calls if that means not having the problems | listed above. | | When you mix imperative and functional styles tail calls become | less important. For example, I don't have to implement a stream | map function in Dart with a tail call to map the rest of the | stream, I can just use a while or for loop. | | In my opinion there is no reason to use OCaml in a new project in | 2023. | hencq wrote: | You might want to make it clearer that this is a literal quote | from the last paragraph of the article. Are you posting this | because you agree/disagree? | kajaktum wrote: | Any language that have exceptions are trash imo. It is clearly a | relic of the old day. It works for CPUs but not people. | iLoveOncall wrote: | [flagged] ___________________________________________________________________ (page generated 2023-04-25 23:00 UTC)